diff gcc/go/gofrontend/statements.h @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/go/gofrontend/statements.h	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,1921 @@
+// statements.h -- Go frontend statements.     -*- C++ -*-
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef GO_STATEMENTS_H
+#define GO_STATEMENTS_H
+
+#include "operator.h"
+
+class Gogo;
+class Traverse;
+class Statement_inserter;
+class Block;
+class Function;
+class Unnamed_label;
+class Assignment_statement;
+class Temporary_statement;
+class Variable_declaration_statement;
+class Expression_statement;
+class Block_statement;
+class Return_statement;
+class Thunk_statement;
+class Goto_statement;
+class Goto_unnamed_statement;
+class Label_statement;
+class Unnamed_label_statement;
+class If_statement;
+class For_statement;
+class For_range_statement;
+class Switch_statement;
+class Type_switch_statement;
+class Send_statement;
+class Select_statement;
+class Variable;
+class Named_object;
+class Label;
+class Translate_context;
+class Expression;
+class Expression_list;
+class Struct_type;
+class Call_expression;
+class Map_index_expression;
+class Receive_expression;
+class Case_clauses;
+class Type_case_clauses;
+class Select_clauses;
+class Typed_identifier_list;
+class Bexpression;
+class Bstatement;
+class Bvariable;
+class Ast_dump_context;
+
+// This class is used to traverse assignments made by a statement
+// which makes assignments.
+
+class Traverse_assignments
+{
+ public:
+  Traverse_assignments()
+  { }
+
+  virtual ~Traverse_assignments()
+  { }
+
+  // This is called for a variable initialization.
+  virtual void
+  initialize_variable(Named_object*) = 0;
+
+  // This is called for each assignment made by the statement.  PLHS
+  // points to the left hand side, and PRHS points to the right hand
+  // side.  PRHS may be NULL if there is no associated expression, as
+  // in the bool set by a non-blocking receive.
+  virtual void
+  assignment(Expression** plhs, Expression** prhs) = 0;
+
+  // This is called for each expression which is not passed to the
+  // assignment function.  This is used for some of the statements
+  // which assign two values, for which there is no expression which
+  // describes the value.  For ++ and -- the value is passed to both
+  // the assignment method and the rhs method.  IS_STORED is true if
+  // this value is being stored directly.  It is false if the value is
+  // computed but not stored.  IS_LOCAL is true if the value is being
+  // stored in a local variable or this is being called by a return
+  // statement.
+  virtual void
+  value(Expression**, bool is_stored, bool is_local) = 0;
+};
+
+// A single statement.
+
+class Statement
+{
+ public:
+  // The types of statements.
+  enum Statement_classification
+  {
+    STATEMENT_ERROR,
+    STATEMENT_VARIABLE_DECLARATION,
+    STATEMENT_TEMPORARY,
+    STATEMENT_ASSIGNMENT,
+    STATEMENT_EXPRESSION,
+    STATEMENT_BLOCK,
+    STATEMENT_GO,
+    STATEMENT_DEFER,
+    STATEMENT_RETURN,
+    STATEMENT_BREAK_OR_CONTINUE,
+    STATEMENT_GOTO,
+    STATEMENT_GOTO_UNNAMED,
+    STATEMENT_LABEL,
+    STATEMENT_UNNAMED_LABEL,
+    STATEMENT_IF,
+    STATEMENT_CONSTANT_SWITCH,
+    STATEMENT_SEND,
+    STATEMENT_SELECT,
+
+    // These statements types are created by the parser, but they
+    // disappear during the lowering pass.
+    STATEMENT_ASSIGNMENT_OPERATION,
+    STATEMENT_TUPLE_ASSIGNMENT,
+    STATEMENT_TUPLE_MAP_ASSIGNMENT,
+    STATEMENT_TUPLE_RECEIVE_ASSIGNMENT,
+    STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT,
+    STATEMENT_INCDEC,
+    STATEMENT_FOR,
+    STATEMENT_FOR_RANGE,
+    STATEMENT_SWITCH,
+    STATEMENT_TYPE_SWITCH
+  };
+
+  Statement(Statement_classification, Location);
+
+  virtual ~Statement();
+
+  // Make a variable declaration.
+  static Statement*
+  make_variable_declaration(Named_object*);
+
+  // Make a statement which creates a temporary variable and
+  // initializes it to an expression.  The block is used if the
+  // temporary variable has to be explicitly destroyed; the variable
+  // must still be added to the block.  References to the temporary
+  // variable may be constructed using make_temporary_reference.
+  // Either the type or the initialization expression may be NULL, but
+  // not both.
+  static Temporary_statement*
+  make_temporary(Type*, Expression*, Location);
+
+  // Make an assignment statement.
+  static Statement*
+  make_assignment(Expression*, Expression*, Location);
+
+  // Make an assignment operation (+=, etc.).
+  static Statement*
+  make_assignment_operation(Operator, Expression*, Expression*,
+			    Location);
+
+  // Make a tuple assignment statement.
+  static Statement*
+  make_tuple_assignment(Expression_list*, Expression_list*, Location);
+
+  // Make an assignment from a map index to a pair of variables.
+  static Statement*
+  make_tuple_map_assignment(Expression* val, Expression* present,
+			    Expression*, Location);
+
+  // Make an assignment from a nonblocking receive to a pair of
+  // variables.
+  static Statement*
+  make_tuple_receive_assignment(Expression* val, Expression* closed,
+				Expression* channel, Location);
+
+  // Make an assignment from a type guard to a pair of variables.
+  static Statement*
+  make_tuple_type_guard_assignment(Expression* val, Expression* ok,
+				   Expression* expr, Type* type,
+				   Location);
+
+  // Make an expression statement from an Expression.  IS_IGNORED is
+  // true if the value is being explicitly ignored, as in an
+  // assignment to _.
+  static Statement*
+  make_statement(Expression*, bool is_ignored);
+
+  // Make a block statement from a Block.  This is an embedded list of
+  // statements which may also include variable definitions.
+  static Statement*
+  make_block_statement(Block*, Location);
+
+  // Make an increment statement.
+  static Statement*
+  make_inc_statement(Expression*);
+
+  // Make a decrement statement.
+  static Statement*
+  make_dec_statement(Expression*);
+
+  // Make a go statement.
+  static Statement*
+  make_go_statement(Call_expression* call, Location);
+
+  // Make a defer statement.
+  static Statement*
+  make_defer_statement(Call_expression* call, Location);
+
+  // Make a return statement.
+  static Return_statement*
+  make_return_statement(Expression_list*, Location);
+
+  // Make a statement that returns the result of a call expression.
+  // If the call does not return any results, this just returns the
+  // call expression as a statement, assuming that the function will
+  // end immediately afterward.
+  static Statement*
+  make_return_from_call(Call_expression*, Location);
+
+  // Make a break statement.
+  static Statement*
+  make_break_statement(Unnamed_label* label, Location);
+
+  // Make a continue statement.
+  static Statement*
+  make_continue_statement(Unnamed_label* label, Location);
+
+  // Make a goto statement.
+  static Statement*
+  make_goto_statement(Label* label, Location);
+
+  // Make a goto statement to an unnamed label.
+  static Statement*
+  make_goto_unnamed_statement(Unnamed_label* label, Location);
+
+  // Make a label statement--where the label is defined.
+  static Statement*
+  make_label_statement(Label* label, Location);
+
+  // Make an unnamed label statement--where the label is defined.
+  static Statement*
+  make_unnamed_label_statement(Unnamed_label* label);
+
+  // Make an if statement.
+  static Statement*
+  make_if_statement(Expression* cond, Block* then_block, Block* else_block,
+		    Location);
+
+  // Make a switch statement.
+  static Switch_statement*
+  make_switch_statement(Expression* switch_val, Location);
+
+  // Make a type switch statement.
+  static Type_switch_statement*
+  make_type_switch_statement(const std::string&, Expression*, Location);
+
+  // Make a send statement.
+  static Send_statement*
+  make_send_statement(Expression* channel, Expression* val, Location);
+
+  // Make a select statement.
+  static Select_statement*
+  make_select_statement(Location);
+
+  // Make a for statement.
+  static For_statement*
+  make_for_statement(Block* init, Expression* cond, Block* post,
+		     Location location);
+
+  // Make a for statement with a range clause.
+  static For_range_statement*
+  make_for_range_statement(Expression* index_var, Expression* value_var,
+			   Expression* range, Location);
+
+  // Return the statement classification.
+  Statement_classification
+  classification() const
+  { return this->classification_; }
+
+  // Get the statement location.
+  Location
+  location() const
+  { return this->location_; }
+
+  // Traverse the tree.
+  int
+  traverse(Block*, size_t* index, Traverse*);
+
+  // Traverse the contents of this statement--the expressions and
+  // statements which it contains.
+  int
+  traverse_contents(Traverse*);
+
+  // If this statement assigns some values, it calls a function for
+  // each value to which this statement assigns a value, and returns
+  // true.  If this statement does not assign any values, it returns
+  // false.
+  bool
+  traverse_assignments(Traverse_assignments* tassign);
+
+  // Lower a statement.  This is called immediately after parsing to
+  // simplify statements for further processing.  It returns the same
+  // Statement or a new one.  FUNCTION is the function containing this
+  // statement.  BLOCK is the block containing this statement.
+  // INSERTER can be used to insert new statements before this one.
+  Statement*
+  lower(Gogo* gogo, Named_object* function, Block* block,
+	Statement_inserter* inserter)
+  { return this->do_lower(gogo, function, block, inserter); }
+
+  // Flatten a statement.  This is called immediately after the order of
+  // evaluation rules are applied to statements.  It returns the same
+  // Statement or a new one.  FUNCTION is the function containing this
+  // statement.  BLOCK is the block containing this statement.
+  // INSERTER can be used to insert new statements before this one.
+  Statement*
+  flatten(Gogo* gogo, Named_object* function, Block* block,
+          Statement_inserter* inserter)
+  { return this->do_flatten(gogo, function, block, inserter); }
+
+  // Set type information for unnamed constants.
+  void
+  determine_types();
+
+  // Check types in a statement.  This simply checks that any
+  // expressions used by the statement have the right type.
+  void
+  check_types(Gogo* gogo)
+  { this->do_check_types(gogo); }
+
+  // Return whether this is a block statement.
+  bool
+  is_block_statement() const
+  { return this->classification_ == STATEMENT_BLOCK; }
+
+  // If this is an assignment statement, return it.  Otherwise return
+  // NULL.
+  Assignment_statement*
+  assignment_statement()
+  {
+    return this->convert<Assignment_statement, STATEMENT_ASSIGNMENT>();
+  }
+
+  // If this is an temporary statement, return it.  Otherwise return
+  // NULL.
+  Temporary_statement*
+  temporary_statement()
+  {
+    return this->convert<Temporary_statement, STATEMENT_TEMPORARY>();
+  }
+
+  // If this is a variable declaration statement, return it.
+  // Otherwise return NULL.
+  Variable_declaration_statement*
+  variable_declaration_statement()
+  {
+    return this->convert<Variable_declaration_statement,
+			 STATEMENT_VARIABLE_DECLARATION>();
+  }
+
+  // If this is an expression statement, return it.  Otherwise return
+  // NULL.
+  Expression_statement*
+  expression_statement()
+  {
+    return this->convert<Expression_statement, STATEMENT_EXPRESSION>();
+  }
+
+  // If this is an block statement, return it.  Otherwise return
+  // NULL.
+  Block_statement*
+  block_statement()
+  { return this->convert<Block_statement, STATEMENT_BLOCK>(); }
+
+  // If this is a return statement, return it.  Otherwise return NULL.
+  Return_statement*
+  return_statement()
+  { return this->convert<Return_statement, STATEMENT_RETURN>(); }
+
+  // If this is a thunk statement (a go or defer statement), return
+  // it.  Otherwise return NULL.
+  Thunk_statement*
+  thunk_statement();
+
+  // If this is a goto statement, return it.  Otherwise return NULL.
+  Goto_statement*
+  goto_statement()
+  { return this->convert<Goto_statement, STATEMENT_GOTO>(); }
+
+  // If this is a goto_unnamed statement, return it.  Otherwise return NULL.
+  Goto_unnamed_statement*
+  goto_unnamed_statement()
+  { return this->convert<Goto_unnamed_statement, STATEMENT_GOTO_UNNAMED>(); }
+
+  // If this is a label statement, return it.  Otherwise return NULL.
+  Label_statement*
+  label_statement()
+  { return this->convert<Label_statement, STATEMENT_LABEL>(); }
+
+  // If this is an unnamed_label statement, return it.  Otherwise return NULL.
+  Unnamed_label_statement*
+  unnamed_label_statement()
+  { return this->convert<Unnamed_label_statement, STATEMENT_UNNAMED_LABEL>(); }
+
+  // If this is an if statement, return it.  Otherwise return NULL.
+  If_statement*
+  if_statement()
+  { return this->convert<If_statement, STATEMENT_IF>(); }
+
+  // If this is a for statement, return it.  Otherwise return NULL.
+  For_statement*
+  for_statement()
+  { return this->convert<For_statement, STATEMENT_FOR>(); }
+
+  // If this is a for statement over a range clause, return it.
+  // Otherwise return NULL.
+  For_range_statement*
+  for_range_statement()
+  { return this->convert<For_range_statement, STATEMENT_FOR_RANGE>(); }
+
+  // If this is a switch statement, return it.  Otherwise return NULL.
+  Switch_statement*
+  switch_statement()
+  { return this->convert<Switch_statement, STATEMENT_SWITCH>(); }
+
+  // If this is a type switch statement, return it.  Otherwise return
+  // NULL.
+  Type_switch_statement*
+  type_switch_statement()
+  { return this->convert<Type_switch_statement, STATEMENT_TYPE_SWITCH>(); }
+
+  // If this is a send statement, return it.  Otherwise return NULL.
+  Send_statement*
+  send_statement()
+  { return this->convert<Send_statement, STATEMENT_SEND>(); }
+
+  // If this is a select statement, return it.  Otherwise return NULL.
+  Select_statement*
+  select_statement()
+  { return this->convert<Select_statement, STATEMENT_SELECT>(); }
+
+  // Return true if this statement may fall through--if after
+  // executing this statement we may go on to execute the following
+  // statement, if any.
+  bool
+  may_fall_through() const
+  { return this->do_may_fall_through(); }
+
+  // Convert the statement to the backend representation.
+  Bstatement*
+  get_backend(Translate_context*);
+
+  // Dump AST representation of a statement to a dump context.
+  void
+  dump_statement(Ast_dump_context*) const;
+
+ protected:
+  // Implemented by child class: traverse the tree.
+  virtual int
+  do_traverse(Traverse*) = 0;
+
+  // Implemented by child class: traverse assignments.  Any statement
+  // which includes an assignment should implement this.
+  virtual bool
+  do_traverse_assignments(Traverse_assignments*)
+  { return false; }
+
+  // Implemented by the child class: lower this statement to a simpler
+  // one.
+  virtual Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*)
+  { return this; }
+
+  // Implemented by the child class: lower this statement to a simpler
+  // one.
+  virtual Statement*
+  do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*)
+  { return this; }
+
+  // Implemented by child class: set type information for unnamed
+  // constants.  Any statement which includes an expression needs to
+  // implement this.
+  virtual void
+  do_determine_types()
+  { }
+
+  // Implemented by child class: check types of expressions used in a
+  // statement.
+  virtual void
+  do_check_types(Gogo*)
+  { }
+
+  // Implemented by child class: return true if this statement may
+  // fall through.
+  virtual bool
+  do_may_fall_through() const
+  { return true; }
+
+  // Implemented by child class: convert to backend representation.
+  virtual Bstatement*
+  do_get_backend(Translate_context*) = 0;
+
+  // Implemented by child class: dump ast representation.
+  virtual void
+  do_dump_statement(Ast_dump_context*) const = 0;
+
+  // Traverse an expression in a statement.
+  int
+  traverse_expression(Traverse*, Expression**);
+
+  // Traverse an expression list in a statement.  The Expression_list
+  // may be NULL.
+  int
+  traverse_expression_list(Traverse*, Expression_list*);
+
+  // Traverse a type in a statement.
+  int
+  traverse_type(Traverse*, Type*);
+
+  // For children to call when they detect that they are in error.
+  void
+  set_is_error();
+
+  // For children to call to report an error conveniently.
+  void
+  report_error(const char*);
+
+  // For children to return an error statement from lower().
+  static Statement*
+  make_error_statement(Location);
+
+ private:
+  // Convert to the desired statement classification, or return NULL.
+  // This is a controlled dynamic cast.
+  template<typename Statement_class, Statement_classification sc>
+  Statement_class*
+  convert()
+  {
+    return (this->classification_ == sc
+	    ? static_cast<Statement_class*>(this)
+	    : NULL);
+  }
+
+  template<typename Statement_class, Statement_classification sc>
+  const Statement_class*
+  convert() const
+  {
+    return (this->classification_ == sc
+	    ? static_cast<const Statement_class*>(this)
+	    : NULL);
+  }
+
+  // The statement classification.
+  Statement_classification classification_;
+  // The location in the input file of the start of this statement.
+  Location location_;
+};
+
+// An assignment statement.
+
+class Assignment_statement : public Statement
+{
+ public:
+  Assignment_statement(Expression* lhs, Expression* rhs,
+		       Location location)
+    : Statement(STATEMENT_ASSIGNMENT, location),
+      lhs_(lhs), rhs_(rhs)
+  { }
+
+  Expression*
+  lhs() const
+  { return this->lhs_; }
+
+  Expression*
+  rhs() const
+  { return this->rhs_; }
+
+ protected:
+  int
+  do_traverse(Traverse* traverse);
+
+  bool
+  do_traverse_assignments(Traverse_assignments*);
+
+  virtual Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  void
+  do_determine_types();
+
+  void
+  do_check_types(Gogo*);
+
+  Statement*
+  do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  // Left hand side--the lvalue.
+  Expression* lhs_;
+  // Right hand side--the rvalue.
+  Expression* rhs_;
+};
+
+// A statement which creates and initializes a temporary variable.
+
+class Temporary_statement : public Statement
+{
+ public:
+  Temporary_statement(Type* type, Expression* init, Location location)
+    : Statement(STATEMENT_TEMPORARY, location),
+      type_(type), init_(init), bvariable_(NULL), is_address_taken_(false)
+  { }
+
+  // Return the type of the temporary variable.
+  Type*
+  type() const;
+
+  // Return the initializer if there is one.
+  Expression*
+  init() const
+  { return this->init_; }
+
+  // Record that something takes the address of this temporary
+  // variable.
+  void
+  set_is_address_taken()
+  { this->is_address_taken_ = true; }
+
+  // Return the temporary variable.  This should not be called until
+  // after the statement itself has been converted.
+  Bvariable*
+  get_backend_variable(Translate_context*) const;
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  bool
+  do_traverse_assignments(Traverse_assignments*);
+
+  void
+  do_determine_types();
+
+  void
+  do_check_types(Gogo*);
+
+  Statement*
+  do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  // The type of the temporary variable.
+  Type* type_;
+  // The initial value of the temporary variable.  This may be NULL.
+  Expression* init_;
+  // The backend representation of the temporary variable.
+  Bvariable* bvariable_;
+  // True if something takes the address of this temporary variable.
+  bool is_address_taken_;
+};
+
+// A variable declaration.  This marks the point in the code where a
+// variable is declared.  The Variable is also attached to a Block.
+
+class Variable_declaration_statement : public Statement
+{
+ public:
+  Variable_declaration_statement(Named_object* var);
+
+  // The variable being declared.
+  Named_object*
+  var()
+  { return this->var_; }
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  bool
+  do_traverse_assignments(Traverse_assignments*);
+
+  Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  Statement*
+  do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  Named_object* var_;
+};
+
+// A return statement.
+
+class Return_statement : public Statement
+{
+ public:
+  Return_statement(Expression_list* vals, Location location)
+    : Statement(STATEMENT_RETURN, location),
+      vals_(vals), is_lowered_(false)
+  { }
+
+  // The list of values being returned.  This may be NULL.
+  const Expression_list*
+  vals() const
+  { return this->vals_; }
+
+ protected:
+  int
+  do_traverse(Traverse* traverse)
+  { return this->traverse_expression_list(traverse, this->vals_); }
+
+  bool
+  do_traverse_assignments(Traverse_assignments*);
+
+  Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  bool
+  do_may_fall_through() const
+  { return false; }
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  // Return values.  This may be NULL.
+  Expression_list* vals_;
+  // True if this statement has been lowered.
+  bool is_lowered_;
+};
+
+// An expression statement.
+
+class Expression_statement : public Statement
+{
+ public:
+  Expression_statement(Expression* expr, bool is_ignored);
+
+  Expression*
+  expr()
+  { return this->expr_; }
+
+ protected:
+  int
+  do_traverse(Traverse* traverse)
+  { return this->traverse_expression(traverse, &this->expr_); }
+
+  void
+  do_determine_types();
+
+  void
+  do_check_types(Gogo*);
+
+  bool
+  do_may_fall_through() const;
+
+  Bstatement*
+  do_get_backend(Translate_context* context);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  Expression* expr_;
+  // Whether the value of this expression is being explicitly ignored.
+  bool is_ignored_;
+};
+
+// A block statement--a list of statements which may include variable
+// definitions.
+
+class Block_statement : public Statement
+{
+ public:
+  Block_statement(Block* block, Location location)
+    : Statement(STATEMENT_BLOCK, location),
+      block_(block), is_lowered_for_statement_(false)
+  { }
+
+  void
+  set_is_lowered_for_statement()
+  { this->is_lowered_for_statement_ = true; }
+
+  bool
+  is_lowered_for_statement()
+  { return this->is_lowered_for_statement_; }
+
+ protected:
+  int
+  do_traverse(Traverse* traverse)
+  { return this->block_->traverse(traverse); }
+
+  void
+  do_determine_types()
+  { this->block_->determine_types(); }
+
+  bool
+  do_may_fall_through() const
+  { return this->block_->may_fall_through(); }
+
+  Bstatement*
+  do_get_backend(Translate_context* context);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  Block* block_;
+  // True if this block statement represents a lowered for statement.
+  bool is_lowered_for_statement_;
+};
+
+// A send statement.
+
+class Send_statement : public Statement
+{
+ public:
+  Send_statement(Expression* channel, Expression* val,
+		 Location location)
+    : Statement(STATEMENT_SEND, location),
+      channel_(channel), val_(val)
+  { }
+
+  Expression*
+  channel()
+  { return this->channel_; }  
+
+  Expression*
+  val()
+  { return this->val_; }
+
+ protected:
+  int
+  do_traverse(Traverse* traverse);
+
+  void
+  do_determine_types();
+
+  void
+  do_check_types(Gogo*);
+
+  Statement*
+  do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  // The channel on which to send the value.
+  Expression* channel_;
+  // The value to send.
+  Expression* val_;
+};
+
+// Select_clauses holds the clauses of a select statement.  This is
+// built by the parser.
+
+class Select_clauses
+{
+ public:
+  Select_clauses()
+    : clauses_()
+  { }
+
+  // Add a new clause.  IS_SEND is true if this is a send clause,
+  // false for a receive clause.  For a send clause CHANNEL is the
+  // channel and VAL is the value to send.  For a receive clause
+  // CHANNEL is the channel, VAL is either NULL or a Var_expression
+  // for the variable to set, and CLOSED is either NULL or a
+  // Var_expression to set to whether the channel is closed.  If VAL
+  // is NULL, VAR may be a variable to be initialized with the
+  // received value, and CLOSEDVAR ma be a variable to be initialized
+  // with whether the channel is closed.  IS_DEFAULT is true if this
+  // is the default clause.  STATEMENTS is the list of statements to
+  // execute.
+  void
+  add(bool is_send, Expression* channel, Expression* val, Expression* closed,
+      Named_object* var, Named_object* closedvar, bool is_default,
+      Block* statements, Location location)
+  {
+    this->clauses_.push_back(Select_clause(is_send, channel, val, closed, var,
+					   closedvar, is_default, statements,
+					   location));
+  }
+
+  size_t
+  size() const
+  { return this->clauses_.size(); }
+
+  // Traverse the select clauses.
+  int
+  traverse(Traverse*);
+
+  // Lower statements.
+  void
+  lower(Gogo*, Named_object*, Block*, Temporary_statement*);
+
+  // Determine types.
+  void
+  determine_types();
+
+  // Check types.
+  void
+  check_types();
+
+  // Whether the select clauses may fall through to the statement
+  // which follows the overall select statement.
+  bool
+  may_fall_through() const;
+
+  // Convert to the backend representation.
+  Bstatement*
+  get_backend(Translate_context*, Temporary_statement* sel,
+	      Unnamed_label* break_label, Location);
+
+  // Dump AST representation.
+  void
+  dump_clauses(Ast_dump_context*) const;
+
+ private:
+  // A single clause.
+  class Select_clause
+  {
+   public:
+    Select_clause()
+      : channel_(NULL), val_(NULL), closed_(NULL), var_(NULL),
+	closedvar_(NULL), statements_(NULL), is_send_(false),
+	is_default_(false)
+    { }
+
+    Select_clause(bool is_send, Expression* channel, Expression* val,
+		  Expression* closed, Named_object* var,
+		  Named_object* closedvar, bool is_default, Block* statements,
+		  Location location)
+      : channel_(channel), val_(val), closed_(closed), var_(var),
+	closedvar_(closedvar), statements_(statements), location_(location),
+	is_send_(is_send), is_default_(is_default), is_lowered_(false)
+    { go_assert(is_default ? channel == NULL : channel != NULL); }
+
+    // Traverse the select clause.
+    int
+    traverse(Traverse*);
+
+    // Lower statements.
+    void
+    lower(Gogo*, Named_object*, Block*, Temporary_statement*);
+
+    // Determine types.
+    void
+    determine_types();
+
+    // Check types.
+    void
+    check_types();
+
+    // Return true if this is the default clause.
+    bool
+    is_default() const
+    { return this->is_default_; }
+
+    // Return the channel.  This will return NULL for the default
+    // clause.
+    Expression*
+    channel() const
+    { return this->channel_; }
+
+    // Return true for a send, false for a receive.
+    bool
+    is_send() const
+    {
+      go_assert(!this->is_default_);
+      return this->is_send_;
+    }
+
+    // Return the statements.
+    const Block*
+    statements() const
+    { return this->statements_; }
+
+    // Return the location.
+    Location
+    location() const
+    { return this->location_; }
+
+    // Whether this clause may fall through to the statement which
+    // follows the overall select statement.
+    bool
+    may_fall_through() const;
+
+    // Convert the statements to the backend representation.
+    Bstatement*
+    get_statements_backend(Translate_context*);
+
+    // Dump AST representation.
+    void
+    dump_clause(Ast_dump_context*) const;
+
+   private:
+    void
+    lower_default(Block*, Expression*);
+
+    void
+    lower_send(Block*, Expression*, Expression*);
+
+    void
+    lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*);
+
+    // The channel.
+    Expression* channel_;
+    // The value to send or the lvalue to receive into.
+    Expression* val_;
+    // The lvalue to set to whether the channel is closed on a
+    // receive.
+    Expression* closed_;
+    // The variable to initialize, for "case a := <-ch".
+    Named_object* var_;
+    // The variable to initialize to whether the channel is closed,
+    // for "case a, c := <-ch".
+    Named_object* closedvar_;
+    // The statements to execute.
+    Block* statements_;
+    // The location of this clause.
+    Location location_;
+    // Whether this is a send or a receive.
+    bool is_send_;
+    // Whether this is the default.
+    bool is_default_;
+    // Whether this has been lowered.
+    bool is_lowered_;
+  };
+
+  typedef std::vector<Select_clause> Clauses;
+
+  Clauses clauses_;
+};
+
+// A select statement.
+
+class Select_statement : public Statement
+{
+ public:
+  Select_statement(Location location)
+    : Statement(STATEMENT_SELECT, location),
+      clauses_(NULL), sel_(NULL), break_label_(NULL), is_lowered_(false)
+  { }
+
+  // Add the clauses.
+  void
+  add_clauses(Select_clauses* clauses)
+  {
+    go_assert(this->clauses_ == NULL);
+    this->clauses_ = clauses;
+  }
+
+  // Return the break label for this select statement.
+  Unnamed_label*
+  break_label();
+
+ protected:
+  int
+  do_traverse(Traverse* traverse)
+  { return this->clauses_->traverse(traverse); }
+
+  Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  void
+  do_determine_types()
+  { this->clauses_->determine_types(); }
+
+  void
+  do_check_types(Gogo*)
+  { this->clauses_->check_types(); }
+
+  bool
+  do_may_fall_through() const;
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  // The select clauses.
+  Select_clauses* clauses_;
+  // A temporary which holds the select structure we build up at runtime.
+  Temporary_statement* sel_;
+  // The break label.
+  Unnamed_label* break_label_;
+  // Whether this statement has been lowered.
+  bool is_lowered_;
+};
+
+// A statement which requires a thunk: go or defer.
+
+class Thunk_statement : public Statement
+{
+ public:
+  Thunk_statement(Statement_classification, Call_expression*,
+		  Location);
+
+  // Return the call expression.
+  Expression*
+  call() const
+  { return this->call_; }
+
+  // Simplify a go or defer statement so that it only uses a single
+  // parameter.
+  bool
+  simplify_statement(Gogo*, Named_object*, Block*);
+
+ protected:
+  int
+  do_traverse(Traverse* traverse);
+
+  bool
+  do_traverse_assignments(Traverse_assignments*);
+
+  void
+  do_determine_types();
+
+  void
+  do_check_types(Gogo*);
+
+  // Return the function and argument for the call.
+  bool
+  get_fn_and_arg(Expression** pfn, Expression** parg);
+
+ private:
+  // Return whether this is a simple go statement.
+  bool
+  is_simple(Function_type*) const;
+
+  // Return whether the thunk function is a constant.
+  bool
+  is_constant_function() const;
+
+  // Build the struct to use for a complex case.
+  Struct_type*
+  build_struct(Function_type* fntype);
+
+  // Build the thunk.
+  void
+  build_thunk(Gogo*, const std::string&);
+
+  // Set the name to use for thunk field N.
+  void
+  thunk_field_param(int n, char* buf, size_t buflen);
+
+  // The function call to be executed in a separate thread (go) or
+  // later (defer).
+  Expression* call_;
+  // The type used for a struct to pass to a thunk, if this is not a
+  // simple call.
+  Struct_type* struct_type_;
+};
+
+// A go statement.
+
+class Go_statement : public Thunk_statement
+{
+ public:
+  Go_statement(Call_expression* call, Location location)
+    : Thunk_statement(STATEMENT_GO, call, location)
+  { }
+
+ protected:
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+};
+
+// A defer statement.
+
+class Defer_statement : public Thunk_statement
+{
+ public:
+  Defer_statement(Call_expression* call, Location location)
+    : Thunk_statement(STATEMENT_DEFER, call, location)
+  { }
+
+ protected:
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+};
+
+// A goto statement.
+
+class Goto_statement : public Statement
+{
+ public:
+  Goto_statement(Label* label, Location location)
+    : Statement(STATEMENT_GOTO, location),
+      label_(label)
+  { }
+
+  // Return the label being jumped to.
+  Label*
+  label() const
+  { return this->label_; }
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  void
+  do_check_types(Gogo*);
+
+  bool
+  do_may_fall_through() const
+  { return false; }
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  Label* label_;
+};
+
+// A goto statement to an unnamed label.
+
+class Goto_unnamed_statement : public Statement
+{
+ public:
+  Goto_unnamed_statement(Unnamed_label* label, Location location)
+    : Statement(STATEMENT_GOTO_UNNAMED, location),
+      label_(label)
+  { }
+
+  Unnamed_label*
+  unnamed_label() const
+  { return this->label_; }
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  bool
+  do_may_fall_through() const
+  { return false; }
+
+  Bstatement*
+  do_get_backend(Translate_context* context);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  Unnamed_label* label_;
+};
+
+// A label statement.
+
+class Label_statement : public Statement
+{
+ public:
+  Label_statement(Label* label, Location location)
+    : Statement(STATEMENT_LABEL, location),
+      label_(label)
+  { }
+
+  // Return the label itself.
+  Label*
+  label() const
+  { return this->label_; }
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  // The label.
+  Label* label_;
+};
+
+// An unnamed label statement.
+
+class Unnamed_label_statement : public Statement
+{
+ public:
+  Unnamed_label_statement(Unnamed_label* label);
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  Bstatement*
+  do_get_backend(Translate_context* context);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  // The label.
+  Unnamed_label* label_;
+};
+
+// An if statement.
+
+class If_statement : public Statement
+{
+ public:
+  If_statement(Expression* cond, Block* then_block, Block* else_block,
+	       Location location)
+    : Statement(STATEMENT_IF, location),
+      cond_(cond), then_block_(then_block), else_block_(else_block)
+  { }
+
+  Expression*
+  condition() const
+  { return this->cond_; }
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  void
+  do_determine_types();
+
+  void
+  do_check_types(Gogo*);
+
+  bool
+  do_may_fall_through() const;
+
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  Expression* cond_;
+  Block* then_block_;
+  Block* else_block_;
+};
+
+// A for statement.
+
+class For_statement : public Statement
+{
+ public:
+  For_statement(Block* init, Expression* cond, Block* post,
+		Location location)
+    : Statement(STATEMENT_FOR, location),
+      init_(init), cond_(cond), post_(post), statements_(NULL),
+      break_label_(NULL), continue_label_(NULL)
+  { }
+
+  // Add the statements.
+  void
+  add_statements(Block* statements)
+  {
+    go_assert(this->statements_ == NULL);
+    this->statements_ = statements;
+  }
+
+  // Return the break label for this for statement.
+  Unnamed_label*
+  break_label();
+
+  // Return the continue label for this for statement.
+  Unnamed_label*
+  continue_label();
+
+  // Set the break and continue labels for this statement.
+  void
+  set_break_continue_labels(Unnamed_label* break_label,
+			    Unnamed_label* continue_label);
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  bool
+  do_traverse_assignments(Traverse_assignments*)
+  { go_unreachable(); }
+
+  Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  bool
+  do_may_fall_through() const;
+
+  Bstatement*
+  do_get_backend(Translate_context*)
+  { go_unreachable(); }
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  // The initialization statements.  This may be NULL.
+  Block* init_;
+  // The condition.  This may be NULL.
+  Expression* cond_;
+  // The statements to run after each iteration.  This may be NULL.
+  Block* post_;
+  // The statements in the loop itself.
+  Block* statements_;
+  // The break label, if needed.
+  Unnamed_label* break_label_;
+  // The continue label, if needed.
+  Unnamed_label* continue_label_;
+};
+
+// A for statement over a range clause.
+
+class For_range_statement : public Statement
+{
+ public:
+  For_range_statement(Expression* index_var, Expression* value_var,
+		      Expression* range, Location location)
+    : Statement(STATEMENT_FOR_RANGE, location),
+      index_var_(index_var), value_var_(value_var), range_(range),
+      statements_(NULL), break_label_(NULL), continue_label_(NULL)
+  { }
+
+  // Add the statements.
+  void
+  add_statements(Block* statements)
+  {
+    go_assert(this->statements_ == NULL);
+    this->statements_ = statements;
+  }
+
+  // Return the break label for this for statement.
+  Unnamed_label*
+  break_label();
+
+  // Return the continue label for this for statement.
+  Unnamed_label*
+  continue_label();
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  bool
+  do_traverse_assignments(Traverse_assignments*)
+  { go_unreachable(); }
+
+  Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  Bstatement*
+  do_get_backend(Translate_context*)
+  { go_unreachable(); }
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+ private:
+  Expression*
+  make_range_ref(Named_object*, Temporary_statement*, Location);
+
+  Call_expression*
+  call_builtin(Gogo*, const char* funcname, Expression* arg, Location);
+
+  void
+  lower_range_array(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
+		    Temporary_statement*, Temporary_statement*,
+		    Block**, Expression**, Block**, Block**);
+
+  void
+  lower_range_slice(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
+		    Temporary_statement*, Temporary_statement*,
+		    Block**, Expression**, Block**, Block**);
+
+  void
+  lower_range_string(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
+		     Temporary_statement*, Temporary_statement*,
+		     Block**, Expression**, Block**, Block**);
+
+  void
+  lower_range_map(Gogo*, Map_type*, Block*, Block*, Named_object*,
+		  Temporary_statement*, Temporary_statement*,
+		  Temporary_statement*, Block**, Expression**, Block**,
+		  Block**);
+
+  void
+  lower_range_channel(Gogo*, Block*, Block*, Named_object*,
+		      Temporary_statement*, Temporary_statement*,
+		      Temporary_statement*, Block**, Expression**, Block**,
+		      Block**);
+
+  // The variable which is set to the index value.
+  Expression* index_var_;
+  // The variable which is set to the element value.  This may be
+  // NULL.
+  Expression* value_var_;
+  // The expression we are ranging over.
+  Expression* range_;
+  // The statements in the block.
+  Block* statements_;
+  // The break label, if needed.
+  Unnamed_label* break_label_;
+  // The continue label, if needed.
+  Unnamed_label* continue_label_;
+};
+
+// Class Case_clauses holds the clauses of a switch statement.  This
+// is built by the parser.
+
+class Case_clauses
+{
+ public:
+  Case_clauses()
+    : clauses_()
+  { }
+
+  // Add a new clause.  CASES is a list of case expressions; it may be
+  // NULL.  IS_DEFAULT is true if this is the default case.
+  // STATEMENTS is a block of statements.  IS_FALLTHROUGH is true if
+  // after the statements the case clause should fall through to the
+  // next clause.
+  void
+  add(Expression_list* cases, bool is_default, Block* statements,
+      bool is_fallthrough, Location location)
+  {
+    this->clauses_.push_back(Case_clause(cases, is_default, statements,
+					 is_fallthrough, location));
+  }
+
+  // Return whether there are no clauses.
+  bool
+  empty() const
+  { return this->clauses_.empty(); }
+
+  // Traverse the case clauses.
+  int
+  traverse(Traverse*);
+
+  // Lower for a nonconstant switch.
+  void
+  lower(Block*, Temporary_statement*, Unnamed_label*) const;
+
+  // Determine types of expressions.  The Type parameter is the type
+  // of the switch value.
+  void
+  determine_types(Type*);
+
+  // Check types.  The Type parameter is the type of the switch value.
+  bool
+  check_types(Type*);
+
+  // Return true if all the clauses are constant values.
+  bool
+  is_constant() const;
+
+  // Return true if these clauses may fall through to the statements
+  // following the switch statement.
+  bool
+  may_fall_through() const;
+
+  // Return the body of a SWITCH_EXPR when all the clauses are
+  // constants.
+  void
+  get_backend(Translate_context*, Unnamed_label* break_label,
+	      std::vector<std::vector<Bexpression*> >* all_cases,
+	      std::vector<Bstatement*>* all_statements) const;
+
+  // Dump the AST representation to a dump context.
+  void
+  dump_clauses(Ast_dump_context*) const;
+  
+ private:
+  // For a constant switch we need to keep a record of constants we
+  // have already seen.
+  class Hash_integer_value;
+  class Eq_integer_value;
+  typedef Unordered_set_hash(Expression*, Hash_integer_value,
+			     Eq_integer_value) Case_constants;
+
+  // One case clause.
+  class Case_clause
+  {
+   public:
+    Case_clause()
+      : cases_(NULL), statements_(NULL), is_default_(false),
+	is_fallthrough_(false), location_(Linemap::unknown_location())
+    { }
+
+    Case_clause(Expression_list* cases, bool is_default, Block* statements,
+		bool is_fallthrough, Location location)
+      : cases_(cases), statements_(statements), is_default_(is_default),
+	is_fallthrough_(is_fallthrough), location_(location)
+    { }
+
+    // Whether this clause falls through to the next clause.
+    bool
+    is_fallthrough() const
+    { return this->is_fallthrough_; }
+
+    // Whether this is the default.
+    bool
+    is_default() const
+    { return this->is_default_; }
+
+    // The location of this clause.
+    Location
+    location() const
+    { return this->location_; }
+
+    // Traversal.
+    int
+    traverse(Traverse*);
+
+    // Lower for a nonconstant switch.
+    void
+    lower(Block*, Temporary_statement*, Unnamed_label*, Unnamed_label*) const;
+
+    // Determine types.
+    void
+    determine_types(Type*);
+
+    // Check types.
+    bool
+    check_types(Type*);
+
+    // Return true if all the case expressions are constant.
+    bool
+    is_constant() const;
+
+    // Return true if this clause may fall through to execute the
+    // statements following the switch statement.  This is not the
+    // same as whether this clause falls through to the next clause.
+    bool
+    may_fall_through() const;
+
+    // Convert the case values and statements to the backend
+    // representation.
+    Bstatement*
+    get_backend(Translate_context*, Unnamed_label* break_label,
+		Case_constants*, std::vector<Bexpression*>* cases) const;
+
+    // Dump the AST representation to a dump context.
+    void
+    dump_clause(Ast_dump_context*) const;
+  
+   private:
+    // The list of case expressions.
+    Expression_list* cases_;
+    // The statements to execute.
+    Block* statements_;
+    // Whether this is the default case.
+    bool is_default_;
+    // Whether this falls through after the statements.
+    bool is_fallthrough_;
+    // The location of this case clause.
+    Location location_;
+  };
+
+  friend class Case_clause;
+
+  // The type of the list of clauses.
+  typedef std::vector<Case_clause> Clauses;
+
+  // All the case clauses.
+  Clauses clauses_;
+};
+
+// A switch statement.
+
+class Switch_statement : public Statement
+{
+ public:
+  Switch_statement(Expression* val, Location location)
+    : Statement(STATEMENT_SWITCH, location),
+      val_(val), clauses_(NULL), break_label_(NULL)
+  { }
+
+  // Add the clauses.
+  void
+  add_clauses(Case_clauses* clauses)
+  {
+    go_assert(this->clauses_ == NULL);
+    this->clauses_ = clauses;
+  }
+
+  // Return the break label for this switch statement.
+  Unnamed_label*
+  break_label();
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  Bstatement*
+  do_get_backend(Translate_context*)
+  { go_unreachable(); }
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+  bool
+  do_may_fall_through() const;
+
+ private:
+  // The value to switch on.  This may be NULL.
+  Expression* val_;
+  // The case clauses.
+  Case_clauses* clauses_;
+  // The break label, if needed.
+  Unnamed_label* break_label_;
+};
+
+// Class Type_case_clauses holds the clauses of a type switch
+// statement.  This is built by the parser.
+
+class Type_case_clauses
+{
+ public:
+  Type_case_clauses()
+    : clauses_()
+  { }
+
+  // Add a new clause.  TYPE is the type for this clause; it may be
+  // NULL.  IS_FALLTHROUGH is true if this falls through to the next
+  // clause; in this case STATEMENTS will be NULL.  IS_DEFAULT is true
+  // if this is the default case.  STATEMENTS is a block of
+  // statements; it may be NULL.
+  void
+  add(Type* type, bool is_fallthrough, bool is_default, Block* statements,
+      Location location)
+  {
+    this->clauses_.push_back(Type_case_clause(type, is_fallthrough, is_default,
+					      statements, location));
+  }
+
+  // Return whether there are no clauses.
+  bool
+  empty() const
+  { return this->clauses_.empty(); }
+
+  // Traverse the type case clauses.
+  int
+  traverse(Traverse*);
+
+  // Check for duplicates.
+  void
+  check_duplicates() const;
+
+  // Lower to if and goto statements.
+  void
+  lower(Type*, Block*, Temporary_statement* descriptor_temp,
+	Unnamed_label* break_label) const;
+
+  // Return true if these clauses may fall through to the statements
+  // following the switch statement.
+  bool
+  may_fall_through() const;
+
+  // Dump the AST representation to a dump context.
+  void
+  dump_clauses(Ast_dump_context*) const;
+
+ private:
+  // One type case clause.
+  class Type_case_clause
+  {
+   public:
+    Type_case_clause()
+      : type_(NULL), statements_(NULL), is_default_(false),
+	location_(Linemap::unknown_location())
+    { }
+
+    Type_case_clause(Type* type, bool is_fallthrough, bool is_default,
+		     Block* statements, Location location)
+      : type_(type), statements_(statements), is_fallthrough_(is_fallthrough),
+	is_default_(is_default), location_(location)
+    { }
+
+    // The type.
+    Type*
+    type() const
+    { return this->type_; }
+
+    // Whether this is the default.
+    bool
+    is_default() const
+    { return this->is_default_; }
+
+    // The location of this type clause.
+    Location
+    location() const
+    { return this->location_; }
+
+    // Traversal.
+    int
+    traverse(Traverse*);
+
+    // Lower to if and goto statements.
+    void
+    lower(Type*, Block*, Temporary_statement* descriptor_temp,
+	  Unnamed_label* break_label, Unnamed_label** stmts_label) const;
+
+    // Return true if this clause may fall through to execute the
+    // statements following the switch statement.  This is not the
+    // same as whether this clause falls through to the next clause.
+    bool
+    may_fall_through() const;
+
+    // Dump the AST representation to a dump context.
+    void
+    dump_clause(Ast_dump_context*) const;
+
+   private:
+    // The type for this type clause.
+    Type* type_;
+    // The statements to execute.
+    Block* statements_;
+    // Whether this falls through--this is true for "case T1, T2".
+    bool is_fallthrough_;
+    // Whether this is the default case.
+    bool is_default_;
+    // The location of this type case clause.
+    Location location_;
+  };
+
+  friend class Type_case_clause;
+
+  // The type of the list of type clauses.
+  typedef std::vector<Type_case_clause> Type_clauses;
+
+  // All the type case clauses.
+  Type_clauses clauses_;
+};
+
+// A type switch statement.
+
+class Type_switch_statement : public Statement
+{
+ public:
+  Type_switch_statement(const std::string& name, Expression* expr,
+			Location location)
+    : Statement(STATEMENT_TYPE_SWITCH, location),
+      name_(name), expr_(expr), clauses_(NULL), break_label_(NULL)
+  { }
+
+  // Add the clauses.
+  void
+  add_clauses(Type_case_clauses* clauses)
+  {
+    go_assert(this->clauses_ == NULL);
+    this->clauses_ = clauses;
+  }
+
+  // Return the break label for this type switch statement.
+  Unnamed_label*
+  break_label();
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
+  Bstatement*
+  do_get_backend(Translate_context*)
+  { go_unreachable(); }
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
+  bool
+  do_may_fall_through() const;
+
+ private:
+  // The name of the variable declared in the type switch guard.  Empty if there
+  // is no variable declared.
+  std::string name_;
+  // The expression we are switching on if there is no variable.
+  Expression* expr_;
+  // The type case clauses.
+  Type_case_clauses* clauses_;
+  // The break label, if needed.
+  Unnamed_label* break_label_;
+};
+
+#endif // !defined(GO_STATEMENTS_H)