OSDN Git Service

Fix defer when not calling recover in function with named results.
[pf3gnuchains/gcc-fork.git] / gcc / go / gofrontend / statements.cc
index 4d335bb..82be112 100644 (file)
@@ -15,6 +15,7 @@
 #include "runtime.h"
 #include "backend.h"
 #include "statements.h"
+#include "ast-dump.h"
 
 // Class Statement.
 
@@ -142,6 +143,14 @@ Statement::get_backend(Translate_context* context)
   return this->do_get_backend(context);
 }
 
+// Dump AST representation for a statement to a dump context.
+
+void
+Statement::dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  this->do_dump_statement(ast_dump_context);
+}
+
 // Note that this statement is erroneous.  This is called by children
 // when they discover an error.
 
@@ -178,8 +187,20 @@ class Error_statement : public Statement
   Bstatement*
   do_get_backend(Translate_context*)
   { go_unreachable(); }
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
 };
 
+// Dump the AST representation for an error statement.
+
+void
+Error_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "Error statement" << std::endl;
+}
+
 // Make an error statement.
 
 Statement*
@@ -217,6 +238,16 @@ Variable_declaration_statement::do_traverse_assignments(
   return true;
 }
 
+// Lower the variable's initialization expression.
+
+Statement*
+Variable_declaration_statement::do_lower(Gogo* gogo, Named_object* function,
+                                        Block*, Statement_inserter* inserter)
+{
+  this->var_->var_value()->lower_init_expression(gogo, function, inserter);
+  return this;
+}
+
 // Convert a variable declaration to the backend representation.
 
 Bstatement*
@@ -244,7 +275,7 @@ Variable_declaration_statement::do_get_backend(Translate_context* context)
   Expression_list* params = new Expression_list();
   params->push_back(Expression::make_type(var->type(), loc));
   Expression* call = Expression::make_call(func, params, false, loc);
-  context->gogo()->lower_expression(context->function(), &call);
+  context->gogo()->lower_expression(context->function(), NULL, &call);
   Temporary_statement* temp = Statement::make_temporary(NULL, call, loc);
   Bstatement* btemp = temp->get_backend(context);
 
@@ -270,6 +301,30 @@ Variable_declaration_statement::do_get_backend(Translate_context* context)
   return context->backend()->statement_list(stats);
 }
 
+// Dump the AST representation for a variable declaration.
+
+void
+Variable_declaration_statement::do_dump_statement(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+
+  go_assert(var_->is_variable());
+  ast_dump_context->ostream() << "var " << this->var_->name() <<  " ";
+  Variable* var = this->var_->var_value();
+  if (var->has_type())
+    {
+      ast_dump_context->dump_type(var->type());
+      ast_dump_context->ostream() << " ";
+    }
+  if (var->init() != NULL)
+    {
+      ast_dump_context->ostream() <<  "= ";
+      ast_dump_context->dump_expression(var->init());
+    }
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a variable declaration.
 
 Statement*
@@ -347,7 +402,13 @@ Temporary_statement::do_check_types(Gogo*)
   if (this->type_ != NULL && this->init_ != NULL)
     {
       std::string reason;
-      if (!Type::are_assignable(this->type_, this->init_->type(), &reason))
+      bool ok;
+      if (this->are_hidden_fields_ok_)
+       ok = Type::are_assignable_hidden_ok(this->type_, this->init_->type(),
+                                           &reason);
+      else
+       ok = Type::are_assignable(this->type_, this->init_->type(), &reason);
+      if (!ok)
        {
          if (reason.empty())
            error_at(this->location(), "incompatible types in assignment");
@@ -386,7 +447,7 @@ Temporary_statement::do_get_backend(Translate_context* context)
     {
       Expression* init = Expression::make_cast(this->type_, this->init_,
                                               this->location());
-      context->gogo()->lower_expression(context->function(), &init);
+      context->gogo()->lower_expression(context->function(), NULL, &init);
       binit = tree_to_expr(init->get_tree(context));
     }
 
@@ -412,6 +473,26 @@ Temporary_statement::get_backend_variable(Translate_context* context) const
   return this->bvariable_;
 }
 
+// Dump the AST represemtation for a temporary statement
+
+void
+Temporary_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_temp_variable_name(this);
+  if (this->type_ != NULL)
+    {
+      ast_dump_context->ostream() << " ";
+      ast_dump_context->dump_type(this->type_);
+    }
+  if (this->init_ != NULL)
+    {
+      ast_dump_context->ostream() << " = ";
+      ast_dump_context->dump_expression(this->init_);
+    }
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make and initialize a temporary variable in BLOCK.
 
 Temporary_statement*
@@ -429,9 +510,15 @@ class Assignment_statement : public Statement
   Assignment_statement(Expression* lhs, Expression* rhs,
                       source_location location)
     : Statement(STATEMENT_ASSIGNMENT, location),
-      lhs_(lhs), rhs_(rhs)
+      lhs_(lhs), rhs_(rhs), are_hidden_fields_ok_(false)
   { }
 
+  // Note that it is OK for this assignment statement to set hidden
+  // fields.
+  void
+  set_hidden_fields_are_ok()
+  { this->are_hidden_fields_ok_ = true; }
+
  protected:
   int
   do_traverse(Traverse* traverse);
@@ -448,11 +535,17 @@ class Assignment_statement : public Statement
   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_;
+  // True if this statement may set hidden fields in the assignment
+  // statement.  This is used for generated method stubs.
+  bool are_hidden_fields_ok_;
 };
 
 // Traversal.
@@ -501,7 +594,12 @@ Assignment_statement::do_check_types(Gogo*)
   Type* lhs_type = this->lhs_->type();
   Type* rhs_type = this->rhs_->type();
   std::string reason;
-  if (!Type::are_assignable(lhs_type, rhs_type, &reason))
+  bool ok;
+  if (this->are_hidden_fields_ok_)
+    ok = Type::are_assignable_hidden_ok(lhs_type, rhs_type, &reason);
+  else
+    ok = Type::are_assignable(lhs_type, rhs_type, &reason);
+  if (!ok)
     {
       if (reason.empty())
        error_at(this->location(), "incompatible types in assignment");
@@ -532,6 +630,19 @@ Assignment_statement::do_get_backend(Translate_context* context)
                                                  this->location());
 }
 
+// Dump the AST representation for an assignment statement.
+
+void
+Assignment_statement::do_dump_statement(Ast_dump_context* ast_dump_context)
+    const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(this->lhs_);
+  ast_dump_context->ostream() << " = " ;
+  ast_dump_context->dump_expression(this->rhs_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make an assignment statement.
 
 Statement*
@@ -598,12 +709,15 @@ class Assignment_operation_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  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:
   // The operator (OPERATOR_PLUSEQ, etc.).
   Operator op_;
@@ -628,7 +742,7 @@ Assignment_operation_statement::do_traverse(Traverse* traverse)
 
 Statement*
 Assignment_operation_statement::do_lower(Gogo*, Named_object*,
-                                        Block* enclosing)
+                                        Block* enclosing, Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -694,6 +808,19 @@ Assignment_operation_statement::do_lower(Gogo*, Named_object*,
     }
 }
 
+// Dump the AST representation for an assignment operation statement
+
+void
+Assignment_operation_statement::do_dump_statement(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(this->lhs_);
+  ast_dump_context->dump_operator(this->op_);
+  ast_dump_context->dump_expression(this->rhs_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make an assignment operation statement.
 
 Statement*
@@ -713,9 +840,15 @@ class Tuple_assignment_statement : public Statement
   Tuple_assignment_statement(Expression_list* lhs, Expression_list* rhs,
                             source_location location)
     : Statement(STATEMENT_TUPLE_ASSIGNMENT, location),
-      lhs_(lhs), rhs_(rhs)
+      lhs_(lhs), rhs_(rhs), are_hidden_fields_ok_(false)
   { }
 
+  // Note that it is OK for this assignment statement to set hidden
+  // fields.
+  void
+  set_hidden_fields_are_ok()
+  { this->are_hidden_fields_ok_ = true; }
+
  protected:
   int
   do_traverse(Traverse* traverse);
@@ -725,17 +858,23 @@ class Tuple_assignment_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  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:
   // Left hand side--a list of lvalues.
   Expression_list* lhs_;
   // Right hand side--a list of rvalues.
   Expression_list* rhs_;
+  // True if this statement may set hidden fields in the assignment
+  // statement.  This is used for generated method stubs.
+  bool are_hidden_fields_ok_;
 };
 
 // Traversal.
@@ -752,12 +891,13 @@ Tuple_assignment_statement::do_traverse(Traverse* traverse)
 // up into a set of single assignments.
 
 Statement*
-Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                                    Statement_inserter*)
 {
   source_location loc = this->location();
 
   Block* b = new Block(enclosing, loc);
-  
+
   // First move out any subexpressions on the left hand side.  The
   // right hand side will be evaluated in the required order anyhow.
   Move_ordered_evals moe(b);
@@ -790,6 +930,8 @@ Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
 
       Temporary_statement* temp = Statement::make_temporary((*plhs)->type(),
                                                            *prhs, loc);
+      if (this->are_hidden_fields_ok_)
+       temp->set_hidden_fields_are_ok();
       b->add_statement(temp);
       temps.push_back(temp);
 
@@ -813,6 +955,11 @@ Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
 
       Expression* ref = Expression::make_temporary_reference(*ptemp, loc);
       Statement* s = Statement::make_assignment(*plhs, ref, loc);
+      if (this->are_hidden_fields_ok_)
+       {
+         Assignment_statement* as = static_cast<Assignment_statement*>(s);
+         as->set_hidden_fields_are_ok();
+       }
       b->add_statement(s);
       ++ptemp;
     }
@@ -821,6 +968,19 @@ Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
   return Statement::make_block_statement(b, loc);
 }
 
+// Dump the AST representation for a tuple assignment statement.
+
+void
+Tuple_assignment_statement::do_dump_statement(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression_list(this->lhs_);
+  ast_dump_context->ostream() << " = ";
+  ast_dump_context->dump_expression_list(this->rhs_);
+  ast_dump_context->ostream()  << std::endl;
+}
+
 // Make a tuple assignment statement.
 
 Statement*
@@ -852,12 +1012,15 @@ public:
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  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:
   // Lvalue which receives the value from the map.
   Expression* val_;
@@ -882,7 +1045,7 @@ Tuple_map_assignment_statement::do_traverse(Traverse* traverse)
 
 Statement*
 Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
-                                        Block* enclosing)
+                                        Block* enclosing, Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -923,7 +1086,8 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
   b->add_statement(present_temp);
 
   // present_temp = mapaccess2(MAP, &key_temp, &val_temp)
-  Expression* ref = Expression::make_temporary_reference(key_temp, loc);
+  Temporary_reference_expression* ref =
+    Expression::make_temporary_reference(key_temp, loc);
   Expression* a1 = Expression::make_unary(OPERATOR_AND, ref, loc);
   ref = Expression::make_temporary_reference(val_temp, loc);
   Expression* a2 = Expression::make_unary(OPERATOR_AND, ref, loc);
@@ -931,6 +1095,7 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
                                        map_index->map(), a1, a2);
 
   ref = Expression::make_temporary_reference(present_temp, loc);
+  ref->set_is_lvalue();
   Statement* s = Statement::make_assignment(ref, call, loc);
   b->add_statement(s);
 
@@ -947,6 +1112,21 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
   return Statement::make_block_statement(b, loc);
 }
 
+// Dump the AST representation for a tuple map assignment statement.
+
+void
+Tuple_map_assignment_statement::do_dump_statement(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(this->val_);
+  ast_dump_context->ostream() << ", ";
+  ast_dump_context->dump_expression(this->present_);
+  ast_dump_context->ostream() << " = ";
+  ast_dump_context->dump_expression(this->map_index_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a map assignment statement which returns a pair of values.
 
 Statement*
@@ -979,12 +1159,15 @@ class Map_assignment_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  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:
   // A reference to the map index which should be set or deleted.
   Expression* map_index_;
@@ -1008,7 +1191,8 @@ Map_assignment_statement::do_traverse(Traverse* traverse)
 // Lower a map assignment to a function call.
 
 Statement*
-Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                                  Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -1062,6 +1246,21 @@ Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
   return Statement::make_block_statement(b, loc);
 }
 
+// Dump the AST representation for a map assignment statement.
+
+void
+Map_assignment_statement::do_dump_statement(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(this->map_index_);
+  ast_dump_context->ostream() << " = ";
+  ast_dump_context->dump_expression(this->val_);
+  ast_dump_context->ostream() << ", ";
+  ast_dump_context->dump_expression(this->should_set_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a statement which assigns a pair of entries to a map.
 
 Statement*
@@ -1093,12 +1292,15 @@ class Tuple_receive_assignment_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  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:
   // Lvalue which receives the value from the channel.
   Expression* val_;
@@ -1125,7 +1327,8 @@ Tuple_receive_assignment_statement::do_traverse(Traverse* traverse)
 
 Statement*
 Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*,
-                                            Block* enclosing)
+                                            Block* enclosing,
+                                            Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -1160,13 +1363,15 @@ Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*,
   b->add_statement(closed_temp);
 
   // closed_temp = chanrecv[23](channel, &val_temp)
-  Expression* ref = Expression::make_temporary_reference(val_temp, loc);
+  Temporary_reference_expression* ref =
+    Expression::make_temporary_reference(val_temp, loc);
   Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
   Expression* call = Runtime::make_call((this->for_select_
                                         ? Runtime::CHANRECV3
                                         : Runtime::CHANRECV2),
                                        loc, 2, this->channel_, p2);
   ref = Expression::make_temporary_reference(closed_temp, loc);
+  ref->set_is_lvalue();
   Statement* s = Statement::make_assignment(ref, call, loc);
   b->add_statement(s);
 
@@ -1183,6 +1388,21 @@ Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*,
   return Statement::make_block_statement(b, loc);
 }
 
+// Dump the AST representation for a tuple receive statement.
+
+void
+Tuple_receive_assignment_statement::do_dump_statement(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(this->val_);
+  ast_dump_context->ostream() << ", ";
+  ast_dump_context->dump_expression(this->closed_);
+  ast_dump_context->ostream() << " <- ";
+  ast_dump_context->dump_expression(this->channel_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a nonblocking receive statement.
 
 Statement*
@@ -1217,12 +1437,15 @@ class Tuple_type_guard_assignment_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  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:
   Call_expression*
   lower_to_type(Runtime::Function);
@@ -1256,7 +1479,8 @@ Tuple_type_guard_assignment_statement::do_traverse(Traverse* traverse)
 
 Statement*
 Tuple_type_guard_assignment_statement::do_lower(Gogo*, Named_object*,
-                                               Block* enclosing)
+                                               Block* enclosing,
+                                               Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -1357,6 +1581,23 @@ Tuple_type_guard_assignment_statement::lower_to_object_type(
   b->add_statement(s);
 }
 
+// Dump the AST representation for a tuple type guard statement.
+
+void
+Tuple_type_guard_assignment_statement::do_dump_statement(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(this->val_);
+  ast_dump_context->ostream() << ", ";
+  ast_dump_context->dump_expression(this->ok_);
+  ast_dump_context->ostream() << " = ";
+  ast_dump_context->dump_expression(this->expr_);
+  ast_dump_context->ostream() << " . ";
+  ast_dump_context->dump_type(this->type_);
+  ast_dump_context->ostream()  << std::endl;
+}
+
 // Make an assignment from a type guard to a pair of variables.
 
 Statement*
@@ -1378,6 +1619,10 @@ class Expression_statement : public Statement
       expr_(expr)
   { }
 
+  Expression*
+  expr()
+  { return this->expr_; }
+
  protected:
   int
   do_traverse(Traverse* traverse)
@@ -1393,6 +1638,9 @@ class Expression_statement : public Statement
   Bstatement*
   do_get_backend(Translate_context* context);
 
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
  private:
   Expression* expr_;
 };
@@ -1437,6 +1685,17 @@ Expression_statement::do_get_backend(Translate_context* context)
   return context->backend()->expression_statement(tree_to_expr(expr_tree));
 }
 
+// Dump the AST representation for an expression statement
+
+void
+Expression_statement::do_dump_statement(Ast_dump_context* ast_dump_context)
+    const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(expr_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make an expression statement from an Expression.
 
 Statement*
@@ -1472,6 +1731,9 @@ class Block_statement : public Statement
   Bstatement*
   do_get_backend(Translate_context* context);
 
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
  private:
   Block* block_;
 };
@@ -1485,6 +1747,14 @@ Block_statement::do_get_backend(Translate_context* context)
   return context->backend()->block_statement(bblock);
 }
 
+// Dump the AST for a block statement
+
+void
+Block_statement::do_dump_statement(Ast_dump_context*) const
+{
+  // block statement braces are dumped when traversing.
+}
+
 // Make a block statement.
 
 Statement*
@@ -1513,12 +1783,15 @@ class Inc_dec_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  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:
   // The l-value to increment or decrement.
   Expression* expr_;
@@ -1529,7 +1802,7 @@ class Inc_dec_statement : public Statement
 // Lower to += or -=.
 
 Statement*
-Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*)
+Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*, Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -1542,6 +1815,16 @@ Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*)
   return Statement::make_assignment_operation(op, this->expr_, oexpr, loc);
 }
 
+// Dump the AST representation for a inc/dec statement.
+
+void
+Inc_dec_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(expr_);
+  ast_dump_context->ostream() << (is_inc_? "++": "--") << std::endl;
+}
+
 // Make an increment statement.
 
 Statement*
@@ -1561,10 +1844,6 @@ Statement::make_dec_statement(Expression* expr)
 // Class Thunk_statement.  This is the base class for go and defer
 // statements.
 
-const char* const Thunk_statement::thunk_field_fn = "fn";
-
-const char* const Thunk_statement::thunk_field_receiver = "receiver";
-
 // Constructor.
 
 Thunk_statement::Thunk_statement(Statement_classification classification,
@@ -1615,8 +1894,7 @@ Thunk_statement::is_simple(Function_type* fntype) const
   // If this calls something which is not a simple function, then we
   // need a thunk.
   Expression* fn = this->call_->call_expression()->fn();
-  if (fn->bound_method_expression() != NULL
-      || fn->interface_field_reference_expression() != NULL)
+  if (fn->interface_field_reference_expression() != NULL)
     return false;
 
   return true;
@@ -1671,14 +1949,6 @@ Thunk_statement::do_check_types(Gogo*)
        this->report_error("expected call expression");
       return;
     }
-  Function_type* fntype = ce->get_function_type();
-  if (fntype != NULL && fntype->is_method())
-    {
-      Expression* fn = ce->fn();
-      if (fn->bound_method_expression() == NULL
-         && fn->interface_field_reference_expression() == NULL)
-       this->report_error(_("no object for method call"));
-    }
 }
 
 // The Traverse class used to find and simplify thunk statements.
@@ -1744,6 +2014,29 @@ Gogo::simplify_thunk_statements()
   this->traverse(&thunk_traverse);
 }
 
+// Return true if the thunk function is a constant, which means that
+// it does not need to be passed to the thunk routine.
+
+bool
+Thunk_statement::is_constant_function() const
+{
+  Call_expression* ce = this->call_->call_expression();
+  Function_type* fntype = ce->get_function_type();
+  if (fntype == NULL)
+    {
+      go_assert(saw_errors());
+      return false;
+    }
+  if (fntype->is_builtin())
+    return true;
+  Expression* fn = ce->fn();
+  if (fn->func_expression() != NULL)
+    return fn->func_expression()->closure() == NULL;
+  if (fn->interface_field_reference_expression() != NULL)
+    return true;
+  return false;
+}
+
 // Simplify complex thunk statements into simple ones.  A complicated
 // thunk statement is one which takes anything other than zero
 // parameters or a single pointer parameter.  We rewrite it into code
@@ -1781,17 +2074,15 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
     return false;
 
   Expression* fn = ce->fn();
-  Bound_method_expression* bound_method = fn->bound_method_expression();
   Interface_field_reference_expression* interface_method =
     fn->interface_field_reference_expression();
-  const bool is_method = bound_method != NULL || interface_method != NULL;
 
   source_location location = this->location();
 
   std::string thunk_name = Gogo::thunk_name();
 
   // Build the thunk.
-  this->build_thunk(gogo, thunk_name, fntype);
+  this->build_thunk(gogo, thunk_name);
 
   // Generate code to call the thunk.
 
@@ -1799,38 +2090,11 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
   // argument to the thunk.
 
   Expression_list* vals = new Expression_list();
-  if (fntype->is_builtin())
-    ;
-  else if (!is_method)
+  if (!this->is_constant_function())
     vals->push_back(fn);
-  else if (interface_method != NULL)
-    vals->push_back(interface_method->expr());
-  else if (bound_method != NULL)
-    {
-      vals->push_back(bound_method->method());
-      Expression* first_arg = bound_method->first_argument();
-
-      // We always pass a pointer when calling a method.
-      if (first_arg->type()->points_to() == NULL)
-       first_arg = Expression::make_unary(OPERATOR_AND, first_arg, location);
 
-      // If we are calling a method which was inherited from an
-      // embedded struct, and the method did not get a stub, then the
-      // first type may be wrong.
-      Type* fatype = bound_method->first_argument_type();
-      if (fatype != NULL)
-       {
-         if (fatype->points_to() == NULL)
-           fatype = Type::make_pointer_type(fatype);
-         Type* unsafe = Type::make_pointer_type(Type::make_void_type());
-         first_arg = Expression::make_cast(unsafe, first_arg, location);
-         first_arg = Expression::make_cast(fatype, first_arg, location);
-       }
-
-      vals->push_back(first_arg);
-    }
-  else
-    go_unreachable();
+  if (interface_method != NULL)
+    vals->push_back(interface_method->expr());
 
   if (ce->args() != NULL)
     {
@@ -1905,45 +2169,33 @@ Thunk_statement::build_struct(Function_type* fntype)
   Call_expression* ce = this->call_->call_expression();
   Expression* fn = ce->fn();
 
+  if (!this->is_constant_function())
+    {
+      // The function to call.
+      fields->push_back(Struct_field(Typed_identifier("fn", fntype,
+                                                     location)));
+    }
+
+  // If this thunk statement calls a method on an interface, we pass
+  // the interface object to the thunk.
   Interface_field_reference_expression* interface_method =
     fn->interface_field_reference_expression();
   if (interface_method != NULL)
     {
-      // If this thunk statement calls a method on an interface, we
-      // pass the interface object to the thunk.
-      Typed_identifier tid(Thunk_statement::thunk_field_fn,
-                          interface_method->expr()->type(),
+      Typed_identifier tid("object", interface_method->expr()->type(),
                           location);
       fields->push_back(Struct_field(tid));
     }
-  else if (!fntype->is_builtin())
-    {
-      // The function to call.
-      Typed_identifier tid(Go_statement::thunk_field_fn, fntype, location);
-      fields->push_back(Struct_field(tid));
-    }
-  else if (ce->is_recover_call())
+
+  // The predeclared recover function has no argument.  However, we
+  // add an argument when building recover thunks.  Handle that here.
+  if (ce->is_recover_call())
     {
-      // The predeclared recover function has no argument.  However,
-      // we add an argument when building recover thunks.  Handle that
-      // here.
       fields->push_back(Struct_field(Typed_identifier("can_recover",
                                                      Type::lookup_bool_type(),
                                                      location)));
     }
 
-  if (fn->bound_method_expression() != NULL)
-    {
-      go_assert(fntype->is_method());
-      Type* rtype = fntype->receiver()->type();
-      // We always pass the receiver as a pointer.
-      if (rtype->points_to() == NULL)
-       rtype = Type::make_pointer_type(rtype);
-      Typed_identifier tid(Thunk_statement::thunk_field_receiver, rtype,
-                          location);
-      fields->push_back(Struct_field(tid));
-    }
-
   const Expression_list* args = ce->args();
   if (args != NULL)
     {
@@ -1966,8 +2218,7 @@ Thunk_statement::build_struct(Function_type* fntype)
 // artificial, function.
 
 void
-Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
-                            Function_type* fntype)
+Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name)
 {
   source_location location = this->location();
 
@@ -2017,9 +2268,11 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
   Named_object* function = gogo->start_function(thunk_name, thunk_type, true,
                                                location);
 
+  gogo->start_block(location);
+
   // For a defer statement, start with a call to
   // __go_set_defer_retaddr.  */
-  Label* retaddr_label = NULL; 
+  Label* retaddr_label = NULL;
   if (may_call_recover)
     {
       retaddr_label = gogo->add_label_reference("retaddr");
@@ -2052,43 +2305,33 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
   thunk_parameter = Expression::make_unary(OPERATOR_MULT, thunk_parameter,
                                           location);
 
-  Bound_method_expression* bound_method = ce->fn()->bound_method_expression();
   Interface_field_reference_expression* interface_method =
     ce->fn()->interface_field_reference_expression();
 
   Expression* func_to_call;
   unsigned int next_index;
-  if (!fntype->is_builtin())
+  if (this->is_constant_function())
     {
-      func_to_call = Expression::make_field_reference(thunk_parameter,
-                                                     0, location);
-      next_index = 1;
-    }
-  else
-    {
-      go_assert(bound_method == NULL && interface_method == NULL);
       func_to_call = ce->fn();
       next_index = 0;
     }
-
-  if (bound_method != NULL)
+  else
     {
-      Expression* r = Expression::make_field_reference(thunk_parameter, 1,
-                                                      location);
-      // The main program passes in a function pointer from the
-      // interface expression, so here we can make a bound method in
-      // all cases.
-      func_to_call = Expression::make_bound_method(r, func_to_call,
-                                                  location);
-      next_index = 2;
+      func_to_call = Expression::make_field_reference(thunk_parameter,
+                                                     0, location);
+      next_index = 1;
     }
-  else if (interface_method != NULL)
+
+  if (interface_method != NULL)
     {
       // The main program passes the interface object.
+      go_assert(next_index == 0);
+      Expression* r = Expression::make_field_reference(thunk_parameter, 0,
+                                                      location);
       const std::string& name(interface_method->name());
-      func_to_call = Expression::make_interface_field_reference(func_to_call,
-                                                               name,
+      func_to_call = Expression::make_interface_field_reference(r, name,
                                                                location);
+      next_index = 1;
     }
 
   Expression_list* call_params = new Expression_list();
@@ -2122,25 +2365,16 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
       call_params = NULL;
     }
 
-  Expression* call = Expression::make_call(func_to_call, call_params, false,
-                                          location);
-  // We need to lower in case this is a builtin function.
-  call = call->lower(gogo, function, -1);
-  Call_expression* call_ce = call->call_expression();
-  if (call_ce != NULL && may_call_recover)
-    call_ce->set_is_deferred();
+  Call_expression* call = Expression::make_call(func_to_call, call_params,
+                                               false, location);
 
-  Statement* call_statement = Statement::make_statement(call);
+  // This call expression was already lowered before entering the
+  // thunk statement.  Don't try to lower varargs again, as that will
+  // cause confusion for, e.g., method calls which already have a
+  // receiver parameter.
+  call->set_varargs_are_lowered();
 
-  // We already ran the determine_types pass, so we need to run it
-  // just for this statement now.
-  call_statement->determine_types();
-
-  // Sanity check.
-  call->check_types(gogo);
-
-  if (call_ce != NULL && recover_arg != NULL)
-    call_ce->set_recover_arg(recover_arg);
+  Statement* call_statement = Statement::make_statement(call);
 
   gogo->add_statement(call_statement);
 
@@ -2155,6 +2389,31 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
       gogo->add_statement(Statement::make_return_statement(vals, location));
     }
 
+  Block* b = gogo->finish_block(location);
+
+  gogo->add_block(b, location);
+
+  gogo->lower_block(function, b);
+
+  // We already ran the determine_types pass, so we need to run it
+  // just for the call statement now.  The other types are known.
+  call_statement->determine_types();
+
+  if (may_call_recover || recover_arg != NULL)
+    {
+      // Dig up the call expression, which may have been changed
+      // during lowering.
+      go_assert(call_statement->classification() == STATEMENT_EXPRESSION);
+      Expression_statement* es =
+       static_cast<Expression_statement*>(call_statement);
+      Call_expression* ce = es->expr()->call_expression();
+      go_assert(ce != NULL);
+      if (may_call_recover)
+       ce->set_is_deferred();
+      if (recover_arg != NULL)
+       ce->set_recover_arg(recover_arg);
+    }
+
   // That is all the thunk has to do.
   gogo->finish_function(location);
 }
@@ -2200,6 +2459,17 @@ Go_statement::do_get_backend(Translate_context* context)
   return context->backend()->expression_statement(call_bexpr);
 }
 
+// Dump the AST representation for go statement.
+
+void
+Go_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "go ";
+  ast_dump_context->dump_expression(this->call());
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a go statement.
 
 Statement*
@@ -2228,6 +2498,17 @@ Defer_statement::do_get_backend(Translate_context* context)
   return context->backend()->expression_statement(call_bexpr);
 }
 
+// Dump the AST representation for defer statement.
+
+void
+Defer_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "defer ";
+  ast_dump_context->dump_expression(this->call());
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a defer statement.
 
 Statement*
@@ -2258,14 +2539,14 @@ Return_statement::do_traverse_assignments(Traverse_assignments* tassign)
 
 // Lower a return statement.  If we are returning a function call
 // which returns multiple values which match the current function,
-// split up the call's results.  If the function has named result
-// variables, and the return statement lists explicit values, then
-// implement it by assigning the values to the result variables and
-// changing the statement to not list any values.  This lets
-// panic/recover work correctly.
+// split up the call's results.  If the return statement lists
+// explicit values, implement this statement by assigning the values
+// to the result variables and change this statement to a naked
+// return.  This lets panic/recover work correctly.
 
 Statement*
-Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing)
+Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing,
+                          Statement_inserter*)
 {
   if (this->is_lowered_)
     return this;
@@ -2346,7 +2627,12 @@ Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing)
       e->determine_type(&type_context);
 
       std::string reason;
-      if (Type::are_assignable(rvtype, e->type(), &reason))
+      bool ok;
+      if (this->are_hidden_fields_ok_)
+       ok = Type::are_assignable_hidden_ok(rvtype, e->type(), &reason);
+      else
+       ok = Type::are_assignable(rvtype, e->type(), &reason);
+      if (ok)
        {
          Expression* ve = Expression::make_var_reference(rv, e->location());
          lhs->push_back(ve);
@@ -2368,13 +2654,28 @@ Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing)
     ;
   else if (lhs->size() == 1)
     {
-      b->add_statement(Statement::make_assignment(lhs->front(), rhs->front(),
-                                                 loc));
+      Statement* s = Statement::make_assignment(lhs->front(), rhs->front(),
+                                               loc);
+      if (this->are_hidden_fields_ok_)
+       {
+         Assignment_statement* as = static_cast<Assignment_statement*>(s);
+         as->set_hidden_fields_are_ok();
+       }
+      b->add_statement(s);
       delete lhs;
       delete rhs;
     }
   else
-    b->add_statement(Statement::make_tuple_assignment(lhs, rhs, loc));
+    {
+      Statement* s = Statement::make_tuple_assignment(lhs, rhs, loc);
+      if (this->are_hidden_fields_ok_)
+       {
+         Tuple_assignment_statement* tas =
+           static_cast<Tuple_assignment_statement*>(s);
+         tas->set_hidden_fields_are_ok();
+       }
+      b->add_statement(s);
+    }
 
   b->add_statement(this);
 
@@ -2411,9 +2712,20 @@ Return_statement::do_get_backend(Translate_context* context)
                                              retvals, loc);
 }
 
+// Dump the AST representation for a return statement.
+
+void
+Return_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "return " ;
+  ast_dump_context->dump_expression_list(this->vals_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a return statement.
 
-Statement*
+Return_statement*
 Statement::make_return_statement(Expression_list* vals,
                                 source_location location)
 {
@@ -2447,6 +2759,9 @@ class Bc_statement : public Statement
   do_get_backend(Translate_context* context)
   { return this->label_->get_goto(context, this->location()); }
 
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
  private:
   // The label that this branches to.
   Unnamed_label* label_;
@@ -2454,6 +2769,21 @@ class Bc_statement : public Statement
   bool is_break_;
 };
 
+// Dump the AST representation for a break/continue statement
+
+void
+Bc_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << (this->is_break_ ? "break" : "continue");
+  if (this->label_ != NULL)
+    {
+      ast_dump_context->ostream() << " ";
+      ast_dump_context->dump_label_name(this->label_);
+    }
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a break statement.
 
 Statement*
@@ -2496,6 +2826,9 @@ class Goto_statement : public Statement
   Bstatement*
   do_get_backend(Translate_context*);
 
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
  private:
   Label* label_;
 };
@@ -2523,6 +2856,15 @@ Goto_statement::do_get_backend(Translate_context* context)
   return context->backend()->goto_statement(blabel, this->location());
 }
 
+// Dump the AST representation for a goto statement.
+
+void
+Goto_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "goto " << this->label_->name() << std::endl;
+}
+
 // Make a goto statement.
 
 Statement*
@@ -2554,10 +2896,25 @@ class Goto_unnamed_statement : public Statement
   do_get_backend(Translate_context* context)
   { return this->label_->get_goto(context, this->location()); }
 
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
  private:
   Unnamed_label* label_;
 };
 
+// Dump the AST representation for an unnamed goto statement
+
+void
+Goto_unnamed_statement::do_dump_statement(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "goto ";
+  ast_dump_context->dump_label_name(this->label_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a goto statement to an unnamed label.
 
 Statement*
@@ -2587,6 +2944,15 @@ Label_statement::do_get_backend(Translate_context* context)
   return context->backend()->label_definition_statement(blabel);
 }
 
+// Dump the AST for a label definition statement.
+
+void
+Label_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << this->label_->name() << ":" << std::endl;
+}
+
 // Make a label statement.
 
 Statement*
@@ -2614,11 +2980,25 @@ class Unnamed_label_statement : public Statement
   do_get_backend(Translate_context* context)
   { return this->label_->get_definition(context); }
 
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
  private:
   // The label.
   Unnamed_label* label_;
 };
 
+// Dump the AST representation for an unnamed label definition statement.
+
+void
+Unnamed_label_statement::do_dump_statement(Ast_dump_context* ast_dump_context)
+    const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_label_name(this->label_);
+  ast_dump_context->ostream() << ":" << std::endl;
+}
+
 // Make an unnamed label statement.
 
 Statement*
@@ -2654,6 +3034,9 @@ class If_statement : public Statement
   Bstatement*
   do_get_backend(Translate_context*);
 
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
  private:
   Expression* cond_;
   Block* then_block_;
@@ -2725,6 +3108,27 @@ If_statement::do_get_backend(Translate_context* context)
                                          else_block, this->location());
 }
 
+// Dump the AST representation for an if statement
+
+void
+If_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "if ";
+  ast_dump_context->dump_expression(this->cond_);
+  ast_dump_context->ostream() << std::endl;
+  if (ast_dump_context->dump_subblocks())
+    {
+      ast_dump_context->dump_block(this->then_block_);
+      if (this->else_block_ != NULL)
+       {
+         ast_dump_context->print_indent();
+         ast_dump_context->ostream() << "else" << std::endl;
+         ast_dump_context->dump_block(this->else_block_);
+       }
+    }
+}
+
 // Make an if statement.
 
 Statement*
@@ -3016,6 +3420,31 @@ Case_clauses::Case_clause::get_backend(Translate_context* context,
     return context->backend()->compound_statement(statements, break_stat);
 }
 
+// Dump the AST representation for a case clause
+
+void
+Case_clauses::Case_clause::dump_clause(Ast_dump_context* ast_dump_context)
+    const
+{
+  ast_dump_context->print_indent();
+  if (this->is_default_)
+    {
+      ast_dump_context->ostream() << "default:";
+    }
+  else
+    {
+      ast_dump_context->ostream() << "case ";
+      ast_dump_context->dump_expression_list(this->cases_);
+      ast_dump_context->ostream() << ":" ;
+    }
+  ast_dump_context->dump_block(this->statements_);
+  if (this->is_fallthrough_)
+    {
+      ast_dump_context->print_indent();
+      ast_dump_context->ostream() <<  " (fallthrough)" << std::endl;
+    }
+}
+
 // Class Case_clauses.
 
 // Traversal.
@@ -3101,7 +3530,6 @@ Case_clauses::lower(Block* b, Temporary_statement* val_temp,
   if (default_case != NULL)
     default_case->lower(b, val_temp, default_start_label,
                        default_finish_label);
-      
 }
 
 // Determine types.
@@ -3178,6 +3606,17 @@ Case_clauses::get_backend(Translate_context* context,
     }
 }
 
+// Dump the AST representation for case clauses (from a switch statement)
+
+void
+Case_clauses::dump_clauses(Ast_dump_context* ast_dump_context) const
+{
+  for (Clauses::const_iterator p = this->clauses_.begin();
+       p != this->clauses_.end();
+       ++p)
+    p->dump_clause(ast_dump_context);
+}
+
 // A constant switch statement.  A Switch_statement is lowered to this
 // when all the cases are constants.
 
@@ -3207,6 +3646,9 @@ class Constant_switch_statement : public Statement
   Bstatement*
   do_get_backend(Translate_context*);
 
+  void
+  do_dump_statement(Ast_dump_context*) const;
+
  private:
   // The value to switch on.
   Expression* val_;
@@ -3286,6 +3728,26 @@ Constant_switch_statement::do_get_backend(Translate_context* context)
   return context->backend()->compound_statement(switch_statement, ldef);
 }
 
+// Dump the AST representation for a constant switch statement.
+
+void
+Constant_switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context)
+    const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "switch ";
+  ast_dump_context->dump_expression(this->val_);
+
+  if (ast_dump_context->dump_subblocks())
+    {
+      ast_dump_context->ostream() << " {" << std::endl;
+      this->clauses_->dump_clauses(ast_dump_context);
+      ast_dump_context->ostream() << "}";
+    }
+
+   ast_dump_context->ostream() << std::endl;
+}
+
 // Class Switch_statement.
 
 // Traversal.
@@ -3305,7 +3767,8 @@ Switch_statement::do_traverse(Traverse* traverse)
 // of if statements.
 
 Statement*
-Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                          Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -3360,6 +3823,27 @@ Switch_statement::break_label()
   return this->break_label_;
 }
 
+// Dump the AST representation for a switch statement.
+
+void
+Switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "switch ";
+  if (this->val_ != NULL)
+    {
+      ast_dump_context->dump_expression(this->val_);
+    }
+  if (ast_dump_context->dump_subblocks())
+    {
+      ast_dump_context->ostream() << " {" << std::endl;
+      this->clauses_->dump_clauses(ast_dump_context);
+      ast_dump_context->print_indent();
+      ast_dump_context->ostream() << "}";
+    }
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a switch statement.
 
 Switch_statement*
@@ -3484,6 +3968,31 @@ Type_case_clauses::Type_case_clause::lower(Block* b,
     }
 }
 
+// Dump the AST representation for a type case clause
+
+void
+Type_case_clauses::Type_case_clause::dump_clause(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  if (this->is_default_)
+    {
+      ast_dump_context->ostream() << "default:";
+    }
+  else
+    {
+      ast_dump_context->ostream() << "case ";
+      ast_dump_context->dump_type(this->type_);
+      ast_dump_context->ostream() << ":" ;
+    }
+  ast_dump_context->dump_block(this->statements_);
+  if (this->is_fallthrough_)
+    {
+      ast_dump_context->print_indent();
+      ast_dump_context->ostream() <<  " (fallthrough)" << std::endl;
+    }
+}
+
 // Class Type_case_clauses.
 
 // Traversal.
@@ -3554,6 +4063,17 @@ Type_case_clauses::lower(Block* b, Temporary_statement* descriptor_temp,
     default_case->lower(b, descriptor_temp, break_label, NULL);
 }
 
+// Dump the AST representation for case clauses (from a switch statement)
+
+void
+Type_case_clauses::dump_clauses(Ast_dump_context* ast_dump_context) const
+{
+  for (Type_clauses::const_iterator p = this->clauses_.begin();
+       p != this->clauses_.end();
+       ++p)
+    p->dump_clause(ast_dump_context);
+}
+
 // Class Type_switch_statement.
 
 // Traversal.
@@ -3578,7 +4098,8 @@ Type_switch_statement::do_traverse(Traverse* traverse)
 // equality testing.
 
 Statement*
-Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                               Statement_inserter*)
 {
   const source_location loc = this->location();
 
@@ -3629,8 +4150,9 @@ Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
                                             ? Runtime::EFACETYPE
                                             : Runtime::IFACETYPE),
                                            loc, 1, ref);
-      Expression* lhs = Expression::make_temporary_reference(descriptor_temp,
-                                                            loc);
+      Temporary_reference_expression* lhs =
+       Expression::make_temporary_reference(descriptor_temp, loc);
+      lhs->set_is_lvalue();
       Statement* s = Statement::make_assignment(lhs, call, loc);
       b->add_statement(s);
     }
@@ -3655,6 +4177,25 @@ Type_switch_statement::break_label()
   return this->break_label_;
 }
 
+// Dump the AST representation for a type switch statement
+
+void
+Type_switch_statement::do_dump_statement(Ast_dump_context* ast_dump_context)
+    const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "switch " << this->var_->name() << " = ";
+  ast_dump_context->dump_expression(this->expr_);
+  ast_dump_context->ostream() << " .(type)";
+  if (ast_dump_context->dump_subblocks())
+    {
+      ast_dump_context->ostream() << " {" << std::endl;
+      this->clauses_->dump_clauses(ast_dump_context);
+      ast_dump_context->ostream() << "}";
+    }
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a type switch statement.
 
 Type_switch_statement*
@@ -3815,7 +4356,7 @@ Send_statement::do_get_backend(Translate_context* context)
   call = Runtime::make_call(code, loc, 3, this->channel_, val,
                            Expression::make_boolean(this->for_select_, loc));
 
-  context->gogo()->lower_expression(context->function(), &call);
+  context->gogo()->lower_expression(context->function(), NULL, &call);
   Bexpression* bcall = tree_to_expr(call->get_tree(context));
   Bstatement* s = context->backend()->expression_statement(bcall);
 
@@ -3825,6 +4366,18 @@ Send_statement::do_get_backend(Translate_context* context)
     return context->backend()->compound_statement(btemp, s);
 }
 
+// Dump the AST representation for a send statement
+
+void
+Send_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->dump_expression(this->channel_);
+  ast_dump_context->ostream() << " <- ";
+  ast_dump_context->dump_expression(this->val_);
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a send statement.
 
 Send_statement*
@@ -4016,6 +4569,48 @@ Select_clauses::Select_clause::get_statements_backend(
   return context->backend()->block_statement(bblock);
 }
 
+// Dump the AST representation for a select case clause
+
+void
+Select_clauses::Select_clause::dump_clause(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  if (this->is_default_)
+    {
+      ast_dump_context->ostream() << "default:";
+    }
+  else
+    {
+      ast_dump_context->ostream() << "case "  ;
+      if (this->is_send_)
+        {
+          ast_dump_context->dump_expression(this->channel_);
+          ast_dump_context->ostream() << " <- " ;
+          ast_dump_context->dump_expression(this->val_);
+        }
+      else
+        {
+         if (this->val_ != NULL)
+           ast_dump_context->dump_expression(this->val_);
+          if (this->closed_ != NULL)
+            {
+             // FIXME: can val_ == NULL and closed_ ! = NULL?
+              ast_dump_context->ostream() << " , " ;
+              ast_dump_context->dump_expression(this->closed_);
+            }
+          if (this->closedvar_ != NULL ||
+              this->var_ != NULL)
+            ast_dump_context->ostream() << " := " ;
+
+          ast_dump_context->ostream() << " <- " ;
+          ast_dump_context->dump_expression(this->channel_);
+        }
+      ast_dump_context->ostream() << ":" ;
+    }
+  ast_dump_context->dump_block(this->statements_);
+}
+
 // Class Select_clauses.
 
 // Traversal.
@@ -4154,7 +4749,7 @@ Select_clauses::get_backend(Translate_context* context,
          Expression* nil2 = nil1->copy();
          Expression* call = Runtime::make_call(Runtime::SELECT, location, 4,
                                                zero, default_arg, nil1, nil2);
-         context->gogo()->lower_expression(context->function(), &call);
+         context->gogo()->lower_expression(context->function(), NULL, &call);
          Bexpression* bcall = tree_to_expr(call->get_tree(context));
          s = context->backend()->expression_statement(bcall);
        }
@@ -4175,7 +4770,7 @@ Select_clauses::get_backend(Translate_context* context,
   Expression* chans = Expression::make_composite_literal(chan_array_type, 0,
                                                         false, chan_init,
                                                         location);
-  context->gogo()->lower_expression(context->function(), &chans);
+  context->gogo()->lower_expression(context->function(), NULL, &chans);
   Temporary_statement* chan_temp = Statement::make_temporary(chan_array_type,
                                                             chans,
                                                             location);
@@ -4187,7 +4782,7 @@ Select_clauses::get_backend(Translate_context* context,
                                                            0, false,
                                                            is_send_init,
                                                            location);
-  context->gogo()->lower_expression(context->function(), &is_sends);
+  context->gogo()->lower_expression(context->function(), NULL, &is_sends);
   Temporary_statement* is_send_temp =
     Statement::make_temporary(is_send_array_type, is_sends, location);
   statements.push_back(is_send_temp->get_backend(context));
@@ -4213,7 +4808,7 @@ Select_clauses::get_backend(Translate_context* context,
   Expression* call = Runtime::make_call(Runtime::SELECT, location, 4,
                                        ecount->copy(), default_arg,
                                        chan_arg, is_send_arg);
-  context->gogo()->lower_expression(context->function(), &call);
+  context->gogo()->lower_expression(context->function(), NULL, &call);
   Bexpression* bcall = tree_to_expr(call->get_tree(context));
 
   std::vector<std::vector<Bexpression*> > cases;
@@ -4282,13 +4877,24 @@ Select_clauses::add_clause_backend(
                          ? clause->location()
                          : clause->statements()->end_location());
   Bstatement* g = bottom_label->get_goto(context, gloc);
-                               
+
   if (s == NULL)
     (*clauses)[index] = g;
   else
     (*clauses)[index] = context->backend()->compound_statement(s, g);
 }
 
+// Dump the AST representation for select clauses.
+
+void
+Select_clauses::dump_clauses(Ast_dump_context* ast_dump_context) const
+{
+  for (Clauses::const_iterator p = this->clauses_.begin();
+       p != this->clauses_.end();
+       ++p)
+    p->dump_clause(ast_dump_context);
+}
+
 // Class Select_statement.
 
 // Return the break label for this switch statement, creating it if
@@ -4309,7 +4915,7 @@ Select_statement::break_label()
 
 Statement*
 Select_statement::do_lower(Gogo* gogo, Named_object* function,
-                          Block* enclosing)
+                          Block* enclosing, Statement_inserter*)
 {
   if (this->is_lowered_)
     return this;
@@ -4329,6 +4935,22 @@ Select_statement::do_get_backend(Translate_context* context)
                                     this->location());
 }
 
+// Dump the AST representation for a select statement.
+
+void
+Select_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "select";
+  if (ast_dump_context->dump_subblocks())
+    {
+      ast_dump_context->ostream() << " {" << std::endl;
+      this->clauses_->dump_clauses(ast_dump_context);
+      ast_dump_context->ostream() << "}";
+    }
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a select statement.
 
 Select_statement*
@@ -4366,7 +4988,8 @@ For_statement::do_traverse(Traverse* traverse)
 // complex statements make it easier to handle garbage collection.
 
 Statement*
-For_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+For_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                       Statement_inserter*)
 {
   Statement* s;
   source_location loc = this->location();
@@ -4463,6 +5086,43 @@ For_statement::set_break_continue_labels(Unnamed_label* break_label,
   this->continue_label_ = continue_label;
 }
 
+// Dump the AST representation for a for statement.
+
+void
+For_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+  if (this->init_ != NULL && ast_dump_context->dump_subblocks())
+    {
+      ast_dump_context->print_indent();
+      ast_dump_context->indent();
+      ast_dump_context->ostream() << "// INIT  " << std::endl;
+      ast_dump_context->dump_block(this->init_);
+      ast_dump_context->unindent();
+    }
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "for ";
+  if (this->cond_ != NULL)
+    ast_dump_context->dump_expression(this->cond_);
+
+  if (ast_dump_context->dump_subblocks())
+    {
+      ast_dump_context->ostream() << " {" << std::endl;
+      ast_dump_context->dump_block(this->statements_);
+      if (this->init_ != NULL)
+       {
+         ast_dump_context->print_indent();
+         ast_dump_context->ostream() << "// POST " << std::endl;
+         ast_dump_context->dump_block(this->post_);
+       }
+      ast_dump_context->unindent();
+
+      ast_dump_context->print_indent();
+      ast_dump_context->ostream() << "}";
+    }
+
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a for statement.
 
 For_statement*
@@ -4497,7 +5157,8 @@ For_range_statement::do_traverse(Traverse* traverse)
 // statements.
 
 Statement*
-For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing)
+For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
+                             Statement_inserter*)
 {
   Type* range_type = this->range_->type();
   if (range_type->points_to() != NULL
@@ -4711,8 +5372,10 @@ For_range_statement::lower_range_array(Gogo* gogo,
   Expression* zexpr = Expression::make_integer(&zval, NULL, loc);
   mpz_clear(zval);
 
-  ref = Expression::make_temporary_reference(index_temp, loc);
-  Statement* s = Statement::make_assignment(ref, zexpr, loc);
+  Temporary_reference_expression* tref =
+    Expression::make_temporary_reference(index_temp, loc);
+  tref->set_is_lvalue();
+  Statement* s = Statement::make_assignment(tref, zexpr, loc);
   init->add_statement(s);
 
   *pinit = init;
@@ -4738,8 +5401,9 @@ For_range_statement::lower_range_array(Gogo* gogo,
       Expression* ref2 = Expression::make_temporary_reference(index_temp, loc);
       Expression* index = Expression::make_index(ref, ref2, NULL, loc);
 
-      ref = Expression::make_temporary_reference(value_temp, loc);
-      s = Statement::make_assignment(ref, index, loc);
+      tref = Expression::make_temporary_reference(value_temp, loc);
+      tref->set_is_lvalue();
+      s = Statement::make_assignment(tref, index, loc);
 
       iter_init->add_statement(s);
     }
@@ -4749,8 +5413,9 @@ For_range_statement::lower_range_array(Gogo* gogo,
   //   index_temp++
 
   Block* post = new Block(enclosing, loc);
-  ref = Expression::make_temporary_reference(index_temp, loc);
-  s = Statement::make_inc_statement(ref);
+  tref = Expression::make_temporary_reference(index_temp, loc);
+  tref->set_is_lvalue();
+  s = Statement::make_inc_statement(tref);
   post->add_statement(s);
   *ppost = post;
 }
@@ -4798,7 +5463,9 @@ For_range_statement::lower_range_string(Gogo*,
   mpz_init_set_ui(zval, 0UL);
   Expression* zexpr = Expression::make_integer(&zval, NULL, loc);
 
-  Expression* ref = Expression::make_temporary_reference(index_temp, loc);
+  Temporary_reference_expression* ref =
+    Expression::make_temporary_reference(index_temp, loc);
+  ref->set_is_lvalue();
   Statement* s = Statement::make_assignment(ref, zexpr, loc);
 
   init->add_statement(s);
@@ -4829,14 +5496,20 @@ For_range_statement::lower_range_string(Gogo*,
   if (value_temp == NULL)
     {
       ref = Expression::make_temporary_reference(next_index_temp, loc);
+      ref->set_is_lvalue();
       s = Statement::make_assignment(ref, call, loc);
     }
   else
     {
       Expression_list* lhs = new Expression_list();
-      lhs->push_back(Expression::make_temporary_reference(next_index_temp,
-                                                         loc));
-      lhs->push_back(Expression::make_temporary_reference(value_temp, loc));
+
+      ref = Expression::make_temporary_reference(next_index_temp, loc);
+      ref->set_is_lvalue();
+      lhs->push_back(ref);
+
+      ref = Expression::make_temporary_reference(value_temp, loc);
+      ref->set_is_lvalue();
+      lhs->push_back(ref);
 
       Expression_list* rhs = new Expression_list();
       rhs->push_back(Expression::make_call_result(call, 0));
@@ -4865,7 +5538,9 @@ For_range_statement::lower_range_string(Gogo*,
 
   Block* post = new Block(enclosing, loc);
 
-  Expression* lhs = Expression::make_temporary_reference(index_temp, loc);
+  Temporary_reference_expression* lhs =
+    Expression::make_temporary_reference(index_temp, loc);
+  lhs->set_is_lvalue();
   Expression* rhs = Expression::make_temporary_reference(next_index_temp, loc);
   s = Statement::make_assignment(lhs, rhs, loc);
 
@@ -5024,8 +5699,12 @@ For_range_statement::lower_range_channel(Gogo*,
   iter_init->add_statement(ok_temp);
 
   Expression* cref = this->make_range_ref(range_object, range_temp, loc);
-  Expression* iref = Expression::make_temporary_reference(index_temp, loc);
-  Expression* oref = Expression::make_temporary_reference(ok_temp, loc);
+  Temporary_reference_expression* iref =
+    Expression::make_temporary_reference(index_temp, loc);
+  iref->set_is_lvalue();
+  Temporary_reference_expression* oref =
+    Expression::make_temporary_reference(ok_temp, loc);
+  oref->set_is_lvalue();
   Statement* s = Statement::make_tuple_receive_assignment(iref, oref, cref,
                                                          false, loc);
   iter_init->add_statement(s);
@@ -5062,6 +5741,38 @@ For_range_statement::continue_label()
   return this->continue_label_;
 }
 
+// Dump the AST representation for a for range statement.
+
+void
+For_range_statement::do_dump_statement(Ast_dump_context* ast_dump_context) const
+{
+
+  ast_dump_context->print_indent();
+  ast_dump_context->ostream() << "for ";
+  ast_dump_context->dump_expression(this->index_var_);
+  if (this->value_var_ != NULL)
+    {
+      ast_dump_context->ostream() << ", ";
+      ast_dump_context->dump_expression(this->value_var_);
+    }
+
+  ast_dump_context->ostream() << " = range ";
+  ast_dump_context->dump_expression(this->range_);
+  if (ast_dump_context->dump_subblocks())
+    {
+      ast_dump_context->ostream() << " {" << std::endl;
+
+      ast_dump_context->indent();
+
+      ast_dump_context->dump_block(this->statements_);
+
+      ast_dump_context->unindent();
+      ast_dump_context->print_indent();
+      ast_dump_context->ostream() << "}";
+    }
+  ast_dump_context->ostream() << std::endl;
+}
+
 // Make a for statement with a range clause.
 
 For_range_statement*