OSDN Git Service

compiler: Fix crash in go/defer of some builtin functions.
[pf3gnuchains/gcc-fork.git] / gcc / go / gofrontend / statements.cc
index da29a35..58057f8 100644 (file)
@@ -8,34 +8,19 @@
 
 #include <gmp.h>
 
-#ifndef ENABLE_BUILD_WITH_CXX
-extern "C"
-{
-#endif
-
-#include "intl.h"
-#include "tree.h"
-#include "gimple.h"
-#include "convert.h"
-#include "tree-iterator.h"
-#include "tree-flow.h"
-#include "real.h"
-
-#ifndef ENABLE_BUILD_WITH_CXX
-}
-#endif
-
 #include "go-c.h"
 #include "types.h"
 #include "expressions.h"
 #include "gogo.h"
+#include "runtime.h"
 #include "backend.h"
 #include "statements.h"
+#include "ast-dump.h"
 
 // Class Statement.
 
 Statement::Statement(Statement_classification classification,
-                    source_location location)
+                    Location location)
   : classification_(classification), location_(location)
 {
 }
@@ -147,27 +132,23 @@ Statement::thunk_statement()
   return ret;
 }
 
-// Get a tree for a Statement.  This is really done by the child
-// class.
+// Convert a Statement to the backend representation.  This is really
+// done by the child class.
 
-tree
-Statement::get_tree(Translate_context* context)
+Bstatement*
+Statement::get_backend(Translate_context* context)
 {
   if (this->classification_ == STATEMENT_ERROR)
-    return error_mark_node;
-
-  return this->do_get_tree(context);
+    return context->backend()->error_statement();
+  return this->do_get_backend(context);
 }
 
-// Build tree nodes and set locations.
+// Dump AST representation for a statement to a dump context.
 
-tree
-Statement::build_stmt_1(int tree_code_value, tree node)
+void
+Statement::dump_statement(Ast_dump_context* ast_dump_context) const
 {
-  tree ret = build1(static_cast<tree_code>(tree_code_value),
-                   void_type_node, node);
-  SET_EXPR_LOCATION(ret, this->location_);
-  return ret;
+  this->do_dump_statement(ast_dump_context);
 }
 
 // Note that this statement is erroneous.  This is called by children
@@ -194,7 +175,7 @@ Statement::report_error(const char* msg)
 class Error_statement : public Statement
 {
  public:
-  Error_statement(source_location location)
+  Error_statement(Location location)
     : Statement(STATEMENT_ERROR, location)
   { }
 
@@ -203,15 +184,27 @@ class Error_statement : public Statement
   do_traverse(Traverse*)
   { return TRAVERSE_CONTINUE; }
 
-  tree
-  do_get_tree(Translate_context*)
-  { gcc_unreachable(); }
+  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*
-Statement::make_error_statement(source_location location)
+Statement::make_error_statement(Location location)
 {
   return new Error_statement(location);
 }
@@ -245,42 +238,91 @@ Variable_declaration_statement::do_traverse_assignments(
   return true;
 }
 
-// Return the tree for a variable declaration.
+// 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.
 
-tree
-Variable_declaration_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Variable_declaration_statement::do_get_backend(Translate_context* context)
 {
-  tree val = this->var_->get_tree(context->gogo(), context->function());
-  if (val == error_mark_node || TREE_TYPE(val) == error_mark_node)
-    return error_mark_node;
-  Variable* variable = this->var_->var_value();
+  Variable* var = this->var_->var_value();
+  Bvariable* bvar = this->var_->get_backend_variable(context->gogo(),
+                                                    context->function());
+  tree init = var->get_init_tree(context->gogo(), context->function());
+  Bexpression* binit = init == NULL ? NULL : tree_to_expr(init);
 
-  tree init = variable->get_init_tree(context->gogo(), context->function());
-  if (init == error_mark_node)
-    return error_mark_node;
+  if (!var->is_in_heap())
+    {
+      go_assert(binit != NULL);
+      return context->backend()->init_statement(bvar, binit);
+    }
+
+  // Something takes the address of this variable, so the value is
+  // stored in the heap.  Initialize it to newly allocated memory
+  // space, and assign the initial value to the new space.
+  Location loc = this->location();
+  Named_object* newfn = context->gogo()->lookup_global("new");
+  go_assert(newfn != NULL && newfn->is_function_declaration());
+  Expression* func = Expression::make_func_reference(newfn, NULL, loc);
+  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(), NULL, &call);
+  Temporary_statement* temp = Statement::make_temporary(NULL, call, loc);
+  Bstatement* btemp = temp->get_backend(context);
 
-  // If this variable lives on the heap, we need to allocate it now.
-  if (!variable->is_in_heap())
+  Bstatement* set = NULL;
+  if (binit != NULL)
     {
-      DECL_INITIAL(val) = init;
-      return this->build_stmt_1(DECL_EXPR, val);
+      Expression* e = Expression::make_temporary_reference(temp, loc);
+      e = Expression::make_unary(OPERATOR_MULT, e, loc);
+      Bexpression* be = tree_to_expr(e->get_tree(context));
+      set = context->backend()->assignment_statement(be, binit, loc);
     }
-  else
+
+  Expression* ref = Expression::make_temporary_reference(temp, loc);
+  Bexpression* bref = tree_to_expr(ref->get_tree(context));
+  Bstatement* sinit = context->backend()->init_statement(bvar, bref);
+
+  std::vector<Bstatement*> stats;
+  stats.reserve(3);
+  stats.push_back(btemp);
+  if (set != NULL)
+    stats.push_back(set);
+  stats.push_back(sinit);
+  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)
     {
-      gcc_assert(TREE_CODE(val) == INDIRECT_REF);
-      tree decl = TREE_OPERAND(val, 0);
-      gcc_assert(TREE_CODE(decl) == VAR_DECL);
-      tree type = TREE_TYPE(decl);
-      gcc_assert(POINTER_TYPE_P(type));
-      tree size = TYPE_SIZE_UNIT(TREE_TYPE(type));
-      tree space = context->gogo()->allocate_memory(variable->type(), size,
-                                                   this->location());
-      space = fold_convert(TREE_TYPE(decl), space);
-      DECL_INITIAL(decl) = space;
-      return build2(COMPOUND_EXPR, void_type_node,
-                   this->build_stmt_1(DECL_EXPR, decl),
-                   build2(MODIFY_EXPR, void_type_node, val, init));
+      ast_dump_context->ostream() <<  "= ";
+      ast_dump_context->dump_expression(var->init());
     }
+  ast_dump_context->ostream() << std::endl;
 }
 
 // Make a variable declaration.
@@ -301,19 +343,6 @@ Temporary_statement::type() const
   return this->type_ != NULL ? this->type_ : this->init_->type();
 }
 
-// Return the tree for the temporary variable.
-
-tree
-Temporary_statement::get_decl() const
-{
-  if (this->decl_ == NULL)
-    {
-      gcc_assert(saw_errors());
-      return error_mark_node;
-    }
-  return this->decl_;
-}
-
 // Traversal.
 
 int
@@ -361,7 +390,7 @@ Temporary_statement::do_determine_types()
   if (this->type_ == NULL)
     {
       this->type_ = this->init_->type();
-      gcc_assert(!this->type_->is_abstract());
+      go_assert(!this->type_->is_abstract());
     }
 }
 
@@ -373,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");
@@ -385,63 +420,84 @@ Temporary_statement::do_check_types(Gogo*)
     }
 }
 
-// Return a tree.
+// Convert to backend representation.
+
+Bstatement*
+Temporary_statement::do_get_backend(Translate_context* context)
+{
+  go_assert(this->bvariable_ == NULL);
+
+  // FIXME: Permitting FUNCTION to be NULL here is a temporary measure
+  // until we have a better representation of the init function.
+  Named_object* function = context->function();
+  Bfunction* bfunction;
+  if (function == NULL)
+    bfunction = NULL;
+  else
+    bfunction = tree_to_function(function->func_value()->get_decl());
+
+  Btype* btype = this->type()->get_backend(context->gogo());
+
+  Bexpression* binit;
+  if (this->init_ == NULL)
+    binit = NULL;
+  else if (this->type_ == NULL)
+    binit = tree_to_expr(this->init_->get_tree(context));
+  else
+    {
+      Expression* init = Expression::make_cast(this->type_, this->init_,
+                                              this->location());
+      context->gogo()->lower_expression(context->function(), NULL, &init);
+      binit = tree_to_expr(init->get_tree(context));
+    }
+
+  Bstatement* statement;
+  this->bvariable_ =
+    context->backend()->temporary_variable(bfunction, context->bblock(),
+                                          btype, binit,
+                                          this->is_address_taken_,
+                                          this->location(), &statement);
+  return statement;
+}
+
+// Return the backend variable.
 
-tree
-Temporary_statement::do_get_tree(Translate_context* context)
+Bvariable*
+Temporary_statement::get_backend_variable(Translate_context* context) const
 {
-  gcc_assert(this->decl_ == NULL_TREE);
-  tree type_tree = this->type()->get_tree(context->gogo());
-  tree init_tree = (this->init_ == NULL
-                   ? NULL_TREE
-                   : this->init_->get_tree(context));
-  if (type_tree == error_mark_node || init_tree == error_mark_node)
+  if (this->bvariable_ == NULL)
     {
-      this->decl_ = error_mark_node;
-      return error_mark_node;
+      go_assert(saw_errors());
+      return context->backend()->error_variable();
     }
-  // We can only use create_tmp_var if the type is not addressable.
-  if (!TREE_ADDRESSABLE(type_tree))
+  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)
     {
-      this->decl_ = create_tmp_var(type_tree, "GOTMP");
-      DECL_SOURCE_LOCATION(this->decl_) = this->location();
+      ast_dump_context->ostream() << " ";
+      ast_dump_context->dump_type(this->type_);
     }
-  else
+  if (this->init_ != NULL)
     {
-      gcc_assert(context->function() != NULL && context->block() != NULL);
-      tree decl = build_decl(this->location(), VAR_DECL,
-                            create_tmp_var_name("GOTMP"),
-                            type_tree);
-      DECL_ARTIFICIAL(decl) = 1;
-      DECL_IGNORED_P(decl) = 1;
-      TREE_USED(decl) = 1;
-      gcc_assert(current_function_decl != NULL_TREE);
-      DECL_CONTEXT(decl) = current_function_decl;
-
-      // We have to add this variable to the block so that it winds up
-      // in a BIND_EXPR.
-      tree block_tree = context->block_tree();
-      gcc_assert(block_tree != NULL_TREE);
-      DECL_CHAIN(decl) = BLOCK_VARS(block_tree);
-      BLOCK_VARS(block_tree) = decl;
-
-      this->decl_ = decl;
-    }
-  if (init_tree != NULL_TREE)
-    DECL_INITIAL(this->decl_) =
-      Expression::convert_for_assignment(context, this->type(),
-                                        this->init_->type(), init_tree,
-                                        this->location());
-  if (this->is_address_taken_)
-    TREE_ADDRESSABLE(this->decl_) = 1;
-  return this->build_stmt_1(DECL_EXPR, this->decl_);
+      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*
 Statement::make_temporary(Type* type, Expression* init,
-                         source_location location)
+                         Location location)
 {
   return new Temporary_statement(type, init, location);
 }
@@ -452,11 +508,17 @@ class Assignment_statement : public Statement
 {
  public:
   Assignment_statement(Expression* lhs, Expression* rhs,
-                      source_location location)
+                      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);
@@ -470,14 +532,20 @@ class Assignment_statement : public Statement
   void
   do_check_types(Gogo*);
 
-  tree
-  do_get_tree(Translate_context*);
+  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.
@@ -526,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");
@@ -540,43 +613,86 @@ Assignment_statement::do_check_types(Gogo*)
     this->set_is_error();
 }
 
-// Build a tree for an assignment statement.
+// Convert an assignment statement to the backend representation.
 
-tree
-Assignment_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Assignment_statement::do_get_backend(Translate_context* context)
 {
   tree rhs_tree = this->rhs_->get_tree(context);
-
   if (this->lhs_->is_sink_expression())
-    return rhs_tree;
-
+    return context->backend()->expression_statement(tree_to_expr(rhs_tree));
   tree lhs_tree = this->lhs_->get_tree(context);
-
-  if (lhs_tree == error_mark_node || rhs_tree == error_mark_node)
-    return error_mark_node;
-
   rhs_tree = Expression::convert_for_assignment(context, this->lhs_->type(),
                                                this->rhs_->type(), rhs_tree,
                                                this->location());
-  if (rhs_tree == error_mark_node)
-    return error_mark_node;
+  return context->backend()->assignment_statement(tree_to_expr(lhs_tree),
+                                                 tree_to_expr(rhs_tree),
+                                                 this->location());
+}
 
-  Bstatement* ret;
-  ret = context->backend()->assignment_statement(tree_to_expr(lhs_tree),
-                                                tree_to_expr(rhs_tree),
-                                                this->location());
-  return statement_to_tree(ret);
+// 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*
 Statement::make_assignment(Expression* lhs, Expression* rhs,
-                          source_location location)
+                          Location location)
 {
   return new Assignment_statement(lhs, rhs, location);
 }
 
+// The Move_subexpressions class is used to move all top-level
+// subexpressions of an expression.  This is used for things like
+// index expressions in which we must evaluate the index value before
+// it can be changed by a multiple assignment.
+
+class Move_subexpressions : public Traverse
+{
+ public:
+  Move_subexpressions(int skip, Block* block)
+    : Traverse(traverse_expressions),
+      skip_(skip), block_(block)
+  { }
+
+ protected:
+  int
+  expression(Expression**);
+
+ private:
+  // The number of subexpressions to skip moving.  This is used to
+  // avoid moving the array itself, as we only need to move the index.
+  int skip_;
+  // The block where new temporary variables should be added.
+  Block* block_;
+};
+
+int
+Move_subexpressions::expression(Expression** pexpr)
+{
+  if (this->skip_ > 0)
+    --this->skip_;
+  else if ((*pexpr)->temporary_reference_expression() == NULL)
+    {
+      Location loc = (*pexpr)->location();
+      Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc);
+      this->block_->add_statement(temp);
+      *pexpr = Expression::make_temporary_reference(temp, loc);
+    }
+  // We only need to move top-level subexpressions.
+  return TRAVERSE_SKIP_COMPONENTS;
+}
+
 // The Move_ordered_evals class is used to find any subexpressions of
 // an expression that have an evaluation order dependency.  It creates
 // temporary variables to hold them.
@@ -604,9 +720,18 @@ Move_ordered_evals::expression(Expression** pexpr)
   // We have to look at subexpressions first.
   if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT)
     return TRAVERSE_EXIT;
+
+  int i;
+  if ((*pexpr)->must_eval_subexpressions_in_order(&i))
+    {
+      Move_subexpressions ms(i, this->block_);
+      if ((*pexpr)->traverse_subexpressions(&ms) == TRAVERSE_EXIT)
+       return TRAVERSE_EXIT;
+    }
+
   if ((*pexpr)->must_eval_in_order())
     {
-      source_location loc = (*pexpr)->location();
+      Location loc = (*pexpr)->location();
       Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc);
       this->block_->add_statement(temp);
       *pexpr = Expression::make_temporary_reference(temp, loc);
@@ -620,7 +745,7 @@ class Assignment_operation_statement : public Statement
 {
  public:
   Assignment_operation_statement(Operator op, Expression* lhs, Expression* rhs,
-                                source_location location)
+                                Location location)
     : Statement(STATEMENT_ASSIGNMENT_OPERATION, location),
       op_(op), lhs_(lhs), rhs_(rhs)
   { }
@@ -631,14 +756,17 @@ class Assignment_operation_statement : public Statement
 
   bool
   do_traverse_assignments(Traverse_assignments*)
-  { gcc_unreachable(); }
+  { 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(); }
 
-  tree
-  do_get_tree(Translate_context*)
-  { gcc_unreachable(); }
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
  private:
   // The operator (OPERATOR_PLUSEQ, etc.).
@@ -664,9 +792,9 @@ 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();
+  Location loc = this->location();
 
   // We have to evaluate the left hand side expression only once.  We
   // do this by moving out any expression with side effects.
@@ -713,7 +841,7 @@ Assignment_operation_statement::do_lower(Gogo*, Named_object*,
       op = OPERATOR_BITCLEAR;
       break;
     default:
-      gcc_unreachable();
+      go_unreachable();
     }
 
   Expression* binop = Expression::make_binary(op, lval, this->rhs_, loc);
@@ -730,11 +858,24 @@ 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*
 Statement::make_assignment_operation(Operator op, Expression* lhs,
-                                    Expression* rhs, source_location location)
+                                    Expression* rhs, Location location)
 {
   return new Assignment_operation_statement(op, lhs, rhs, location);
 }
@@ -747,31 +888,43 @@ class Tuple_assignment_statement : public Statement
 {
  public:
   Tuple_assignment_statement(Expression_list* lhs, Expression_list* rhs,
-                            source_location location)
+                            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);
 
   bool
   do_traverse_assignments(Traverse_assignments*)
-  { gcc_unreachable(); }
+  { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
-  tree
-  do_get_tree(Translate_context*)
-  { gcc_unreachable(); }
+  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.
@@ -788,19 +941,20 @@ 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();
+  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);
-  for (Expression_list::const_iterator plhs = this->lhs_->begin();
+  for (Expression_list::iterator plhs = this->lhs_->begin();
        plhs != this->lhs_->end();
        ++plhs)
-    (*plhs)->traverse_subexpressions(&moe);
+    Expression::traverse(&*plhs, &moe);
 
   std::vector<Temporary_statement*> temps;
   temps.reserve(this->lhs_->size());
@@ -810,7 +964,7 @@ Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
        plhs != this->lhs_->end();
        ++plhs, ++prhs)
     {
-      gcc_assert(prhs != this->rhs_->end());
+      go_assert(prhs != this->rhs_->end());
 
       if ((*plhs)->is_error_expression()
          || (*plhs)->type()->is_error()
@@ -820,17 +974,19 @@ Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
 
       if ((*plhs)->is_sink_expression())
        {
-         b->add_statement(Statement::make_statement(*prhs));
+         b->add_statement(Statement::make_statement(*prhs, true));
          continue;
        }
 
       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);
 
     }
-  gcc_assert(prhs == this->rhs_->end());
+  go_assert(prhs == this->rhs_->end());
 
   prhs = this->rhs_->begin();
   std::vector<Temporary_statement*>::const_iterator ptemp = temps.begin();
@@ -849,19 +1005,37 @@ 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;
     }
-  gcc_assert(ptemp == temps.end());
+  go_assert(ptemp == temps.end() || saw_errors());
 
   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*
 Statement::make_tuple_assignment(Expression_list* lhs, Expression_list* rhs,
-                                source_location location)
+                                Location location)
 {
   return new Tuple_assignment_statement(lhs, rhs, location);
 }
@@ -874,7 +1048,7 @@ class Tuple_map_assignment_statement : public Statement
 public:
   Tuple_map_assignment_statement(Expression* val, Expression* present,
                                 Expression* map_index,
-                                source_location location)
+                                Location location)
     : Statement(STATEMENT_TUPLE_MAP_ASSIGNMENT, location),
       val_(val), present_(present), map_index_(map_index)
   { }
@@ -885,14 +1059,17 @@ public:
 
   bool
   do_traverse_assignments(Traverse_assignments*)
-  { gcc_unreachable(); }
+  { 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(); }
 
-  tree
-  do_get_tree(Translate_context*)
-  { gcc_unreachable(); }
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
  private:
   // Lvalue which receives the value from the map.
@@ -918,9 +1095,9 @@ 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();
+  Location loc = this->location();
 
   Map_index_expression* map_index = this->map_index_->map_index_expression();
   if (map_index == NULL)
@@ -958,35 +1135,19 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
     Statement::make_temporary(Type::lookup_bool_type(), NULL, loc);
   b->add_statement(present_temp);
 
-  // func mapaccess2(hmap map[k]v, key *k, val *v) bool
-  source_location bloc = BUILTINS_LOCATION;
-  Typed_identifier_list* param_types = new Typed_identifier_list();
-  param_types->push_back(Typed_identifier("hmap", map_type, bloc));
-  Type* pkey_type = Type::make_pointer_type(map_type->key_type());
-  param_types->push_back(Typed_identifier("key", pkey_type, bloc));
-  Type* pval_type = Type::make_pointer_type(map_type->val_type());
-  param_types->push_back(Typed_identifier("val", pval_type, bloc));
-
-  Typed_identifier_list* ret_types = new Typed_identifier_list();
-  ret_types->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc));
-
-  Function_type* fntype = Type::make_function_type(NULL, param_types,
-                                                  ret_types, bloc);
-  Named_object* mapaccess2 =
-    Named_object::make_function_declaration("mapaccess2", NULL, fntype, bloc);
-  mapaccess2->func_declaration_value()->set_asm_name("runtime.mapaccess2");
-
-  // present_temp = mapaccess2(MAP, &key_temp, &val_temp)
-  Expression* func = Expression::make_func_reference(mapaccess2, NULL, loc);
-  Expression_list* params = new Expression_list();
-  params->push_back(map_index->map());
-  Expression* ref = Expression::make_temporary_reference(key_temp, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
+  // present_temp = mapaccess2(DESCRIPTOR, MAP, &key_temp, &val_temp)
+  Expression* a1 = Expression::make_type_descriptor(map_type, loc);
+  Expression* a2 = map_index->map();
+  Temporary_reference_expression* ref =
+    Expression::make_temporary_reference(key_temp, loc);
+  Expression* a3 = Expression::make_unary(OPERATOR_AND, ref, loc);
   ref = Expression::make_temporary_reference(val_temp, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
-  Expression* call = Expression::make_call(func, params, false, loc);
+  Expression* a4 = Expression::make_unary(OPERATOR_AND, ref, loc);
+  Expression* call = Runtime::make_call(Runtime::MAPACCESS2, loc, 4,
+                                       a1, a2, a3, a4);
 
   ref = Expression::make_temporary_reference(present_temp, loc);
+  ref->set_is_lvalue();
   Statement* s = Statement::make_assignment(ref, call, loc);
   b->add_statement(s);
 
@@ -1003,12 +1164,27 @@ 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*
 Statement::make_tuple_map_assignment(Expression* val, Expression* present,
                                     Expression* map_index,
-                                    source_location location)
+                                    Location location)
 {
   return new Tuple_map_assignment_statement(val, present, map_index, location);
 }
@@ -1021,7 +1197,7 @@ class Map_assignment_statement : public Statement
  public:
   Map_assignment_statement(Expression* map_index,
                           Expression* val, Expression* should_set,
-                          source_location location)
+                          Location location)
     : Statement(STATEMENT_MAP_ASSIGNMENT, location),
       map_index_(map_index), val_(val), should_set_(should_set)
   { }
@@ -1032,14 +1208,17 @@ class Map_assignment_statement : public Statement
 
   bool
   do_traverse_assignments(Traverse_assignments*)
-  { gcc_unreachable(); }
+  { 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(); }
 
-  tree
-  do_get_tree(Translate_context*)
-  { gcc_unreachable(); }
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
  private:
   // A reference to the map index which should be set or deleted.
@@ -1064,9 +1243,10 @@ 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();
+  Location loc = this->location();
 
   Map_index_expression* map_index = this->map_index_->map_index_expression();
   if (map_index == NULL)
@@ -1097,43 +1277,48 @@ Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
     Statement::make_temporary(map_type->val_type(), this->val_, loc);
   b->add_statement(val_temp);
 
-  // func mapassign2(hmap map[k]v, key *k, val *v, p)
-  source_location bloc = BUILTINS_LOCATION;
-  Typed_identifier_list* param_types = new Typed_identifier_list();
-  param_types->push_back(Typed_identifier("hmap", map_type, bloc));
-  Type* pkey_type = Type::make_pointer_type(map_type->key_type());
-  param_types->push_back(Typed_identifier("key", pkey_type, bloc));
-  Type* pval_type = Type::make_pointer_type(map_type->val_type());
-  param_types->push_back(Typed_identifier("val", pval_type, bloc));
-  param_types->push_back(Typed_identifier("p", Type::lookup_bool_type(), bloc));
-  Function_type* fntype = Type::make_function_type(NULL, param_types,
-                                                  NULL, bloc);
-  Named_object* mapassign2 =
-    Named_object::make_function_declaration("mapassign2", NULL, fntype, bloc);
-  mapassign2->func_declaration_value()->set_asm_name("runtime.mapassign2");
+  // var insert_temp bool = p
+  Temporary_statement* insert_temp =
+    Statement::make_temporary(Type::lookup_bool_type(), this->should_set_,
+                             loc);
+  b->add_statement(insert_temp);
 
   // mapassign2(map_temp, &key_temp, &val_temp, p)
-  Expression* func = Expression::make_func_reference(mapassign2, NULL, loc);
-  Expression_list* params = new Expression_list();
-  params->push_back(Expression::make_temporary_reference(map_temp, loc));
+  Expression* p1 = Expression::make_temporary_reference(map_temp, loc);
   Expression* ref = Expression::make_temporary_reference(key_temp, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
+  Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
   ref = Expression::make_temporary_reference(val_temp, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
-  params->push_back(this->should_set_);
-  Expression* call = Expression::make_call(func, params, false, loc);
-  Statement* s = Statement::make_statement(call);
+  Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc);
+  Expression* p4 = Expression::make_temporary_reference(insert_temp, loc);
+  Expression* call = Runtime::make_call(Runtime::MAPASSIGN2, loc, 4,
+                                       p1, p2, p3, p4);
+  Statement* s = Statement::make_statement(call, true);
   b->add_statement(s);
 
   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*
 Statement::make_map_assignment(Expression* map_index,
                               Expression* val, Expression* should_set,
-                              source_location location)
+                              Location location)
 {
   return new Map_assignment_statement(map_index, val, should_set, location);
 }
@@ -1144,10 +1329,9 @@ class Tuple_receive_assignment_statement : public Statement
 {
  public:
   Tuple_receive_assignment_statement(Expression* val, Expression* closed,
-                                    Expression* channel, bool for_select,
-                                    source_location location)
+                                    Expression* channel, Location location)
     : Statement(STATEMENT_TUPLE_RECEIVE_ASSIGNMENT, location),
-      val_(val), closed_(closed), channel_(channel), for_select_(for_select)
+      val_(val), closed_(closed), channel_(channel)
   { }
 
  protected:
@@ -1156,14 +1340,17 @@ class Tuple_receive_assignment_statement : public Statement
 
   bool
   do_traverse_assignments(Traverse_assignments*)
-  { gcc_unreachable(); }
+  { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
-  tree
-  do_get_tree(Translate_context*)
-  { gcc_unreachable(); }
+  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.
@@ -1172,8 +1359,6 @@ class Tuple_receive_assignment_statement : public Statement
   Expression* closed_;
   // The channel on which we receive the value.
   Expression* channel_;
-  // Whether this is for a select statement.
-  bool for_select_;
 };
 
 // Traversal.
@@ -1191,9 +1376,10 @@ 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();
+  Location loc = this->location();
 
   Channel_type* channel_type = this->channel_->type()->channel_type();
   if (channel_type == NULL)
@@ -1225,41 +1411,16 @@ Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*,
     Statement::make_temporary(Type::lookup_bool_type(), NULL, loc);
   b->add_statement(closed_temp);
 
-  // func chanrecv2(c chan T, val *T) bool
-  // func chanrecv3(c chan T, val *T) bool (if for_select)
-  source_location bloc = BUILTINS_LOCATION;
-  Typed_identifier_list* param_types = new Typed_identifier_list();
-  param_types->push_back(Typed_identifier("c", channel_type, bloc));
-  Type* pelement_type = Type::make_pointer_type(channel_type->element_type());
-  param_types->push_back(Typed_identifier("val", pelement_type, bloc));
-
-  Typed_identifier_list* ret_types = new Typed_identifier_list();
-  ret_types->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc));
-
-  Function_type* fntype = Type::make_function_type(NULL, param_types,
-                                                  ret_types, bloc);
-  Named_object* chanrecv;
-  if (!this->for_select_)
-    {
-      chanrecv = Named_object::make_function_declaration("chanrecv2", NULL,
-                                                        fntype, bloc);
-      chanrecv->func_declaration_value()->set_asm_name("runtime.chanrecv2");
-    }
-  else
-    {
-      chanrecv = Named_object::make_function_declaration("chanrecv3", NULL,
-                                                        fntype, bloc);
-      chanrecv->func_declaration_value()->set_asm_name("runtime.chanrecv3");
-    }
-
-  // closed_temp = chanrecv[23](channel, &val_temp)
-  Expression* func = Expression::make_func_reference(chanrecv, NULL, loc);
-  Expression_list* params = new Expression_list();
-  params->push_back(this->channel_);
-  Expression* ref = Expression::make_temporary_reference(val_temp, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
-  Expression* call = Expression::make_call(func, params, false, loc);
+  // closed_temp = chanrecv2(type, channel, &val_temp)
+  Expression* td = Expression::make_type_descriptor(this->channel_->type(),
+                                                   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(Runtime::CHANRECV2,
+                                       loc, 3, td, 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);
 
@@ -1276,16 +1437,30 @@ 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*
 Statement::make_tuple_receive_assignment(Expression* val, Expression* closed,
                                         Expression* channel,
-                                        bool for_select,
-                                        source_location location)
+                                        Location location)
 {
   return new Tuple_receive_assignment_statement(val, closed, channel,
-                                               for_select, location);
+                                               location);
 }
 
 // An assignment to a pair of values from a type guard.  This is a
@@ -1296,7 +1471,7 @@ class Tuple_type_guard_assignment_statement : public Statement
  public:
   Tuple_type_guard_assignment_statement(Expression* val, Expression* ok,
                                        Expression* expr, Type* type,
-                                       source_location location)
+                                       Location location)
     : Statement(STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT, location),
       val_(val), ok_(ok), expr_(expr), type_(type)
   { }
@@ -1307,24 +1482,24 @@ class Tuple_type_guard_assignment_statement : public Statement
 
   bool
   do_traverse_assignments(Traverse_assignments*)
-  { gcc_unreachable(); }
+  { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
-  tree
-  do_get_tree(Translate_context*)
-  { gcc_unreachable(); }
+  Bstatement*
+  do_get_backend(Translate_context*)
+  { go_unreachable(); }
 
- private:
-  Call_expression*
-  lower_to_empty_interface(const char*);
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
+ private:
   Call_expression*
-  lower_to_type(const char*);
+  lower_to_type(Runtime::Function);
 
   void
-  lower_to_object_type(Block*, const char*);
+  lower_to_object_type(Block*, Runtime::Function);
 
   // The variable which recieves the converted value.
   Expression* val_;
@@ -1352,9 +1527,10 @@ 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();
+  Location loc = this->location();
 
   Type* expr_type = this->expr_->type();
   if (expr_type->interface_type() == NULL)
@@ -1377,23 +1553,32 @@ Tuple_type_guard_assignment_statement::do_lower(Gogo*, Named_object*,
   if (this->type_->interface_type() != NULL)
     {
       if (this->type_->interface_type()->is_empty())
-       call = this->lower_to_empty_interface(expr_is_empty
-                                             ? "ifaceE2E2"
-                                             : "ifaceI2E2");
+       call = Runtime::make_call((expr_is_empty
+                                  ? Runtime::IFACEE2E2
+                                  : Runtime::IFACEI2E2),
+                                 loc, 1, this->expr_);
       else
-       call = this->lower_to_type(expr_is_empty ? "ifaceE2I2" : "ifaceI2I2");
+       call = this->lower_to_type(expr_is_empty
+                                  ? Runtime::IFACEE2I2
+                                  : Runtime::IFACEI2I2);
     }
   else if (this->type_->points_to() != NULL)
-    call = this->lower_to_type(expr_is_empty ? "ifaceE2T2P" : "ifaceI2T2P");
+    call = this->lower_to_type(expr_is_empty
+                              ? Runtime::IFACEE2T2P
+                              : Runtime::IFACEI2T2P);
   else
     {
-      this->lower_to_object_type(b, expr_is_empty ? "ifaceE2T2" : "ifaceI2T2");
+      this->lower_to_object_type(b,
+                                (expr_is_empty
+                                 ? Runtime::IFACEE2T2
+                                 : Runtime::IFACEI2T2));
       call = NULL;
     }
 
   if (call != NULL)
     {
       Expression* res = Expression::make_call_result(call, 0);
+      res = Expression::make_unsafe_cast(this->type_, res, loc);
       Statement* s = Statement::make_assignment(this->val_, res, loc);
       b->add_statement(s);
 
@@ -1405,109 +1590,36 @@ Tuple_type_guard_assignment_statement::do_lower(Gogo*, Named_object*,
   return Statement::make_block_statement(b, loc);
 }
 
-// Lower a conversion to an empty interface type.
-
-Call_expression*
-Tuple_type_guard_assignment_statement::lower_to_empty_interface(
-    const char *fnname)
-{
-  source_location loc = this->location();
-
-  // func FNNAME(interface) (empty, bool)
-  source_location bloc = BUILTINS_LOCATION;
-  Typed_identifier_list* param_types = new Typed_identifier_list();
-  param_types->push_back(Typed_identifier("i", this->expr_->type(), bloc));
-  Typed_identifier_list* ret_types = new Typed_identifier_list();
-  ret_types->push_back(Typed_identifier("ret", this->type_, bloc));
-  ret_types->push_back(Typed_identifier("ok", Type::lookup_bool_type(), bloc));
-  Function_type* fntype = Type::make_function_type(NULL, param_types,
-                                                  ret_types, bloc);
-  Named_object* fn =
-    Named_object::make_function_declaration(fnname, NULL, fntype, bloc);
-  std::string asm_name = "runtime.";
-  asm_name += fnname;
-  fn->func_declaration_value()->set_asm_name(asm_name);
-
-  // val, ok = FNNAME(expr)
-  Expression* func = Expression::make_func_reference(fn, NULL, loc);
-  Expression_list* params = new Expression_list();
-  params->push_back(this->expr_);
-  return Expression::make_call(func, params, false, loc);
-}
-
 // Lower a conversion to a non-empty interface type or a pointer type.
 
 Call_expression*
-Tuple_type_guard_assignment_statement::lower_to_type(const char* fnname)
-{
-  source_location loc = this->location();
-
-  // func FNNAME(*descriptor, interface) (interface, bool)
-  source_location bloc = BUILTINS_LOCATION;
-  Typed_identifier_list* param_types = new Typed_identifier_list();
-  param_types->push_back(Typed_identifier("inter",
-                                         Type::make_type_descriptor_ptr_type(),
-                                         bloc));
-  param_types->push_back(Typed_identifier("i", this->expr_->type(), bloc));
-  Typed_identifier_list* ret_types = new Typed_identifier_list();
-  ret_types->push_back(Typed_identifier("ret", this->type_, bloc));
-  ret_types->push_back(Typed_identifier("ok", Type::lookup_bool_type(), bloc));
-  Function_type* fntype = Type::make_function_type(NULL, param_types,
-                                                  ret_types, bloc);
-  Named_object* fn =
-    Named_object::make_function_declaration(fnname, NULL, fntype, bloc);
-  std::string asm_name = "runtime.";
-  asm_name += fnname;
-  fn->func_declaration_value()->set_asm_name(asm_name);
-
-  // val, ok = FNNAME(type_descriptor, expr)
-  Expression* func = Expression::make_func_reference(fn, NULL, loc);
-  Expression_list* params = new Expression_list();
-  params->push_back(Expression::make_type_descriptor(this->type_, loc));
-  params->push_back(this->expr_);
-  return Expression::make_call(func, params, false, loc);
+Tuple_type_guard_assignment_statement::lower_to_type(Runtime::Function code)
+{
+  Location loc = this->location();
+  return Runtime::make_call(code, loc, 2,
+                           Expression::make_type_descriptor(this->type_, loc),
+                           this->expr_);
 }
 
 // Lower a conversion to a non-interface non-pointer type.
 
 void
-Tuple_type_guard_assignment_statement::lower_to_object_type(Block* b,
-                                                           const char *fnname)
+Tuple_type_guard_assignment_statement::lower_to_object_type(
+    Block* b,
+    Runtime::Function code)
 {
-  source_location loc = this->location();
+  Location loc = this->location();
 
   // var val_temp TYPE
   Temporary_statement* val_temp = Statement::make_temporary(this->type_,
                                                            NULL, loc);
   b->add_statement(val_temp);
 
-  // func FNNAME(*descriptor, interface, *T) bool
-  source_location bloc = BUILTINS_LOCATION;
-  Typed_identifier_list* param_types = new Typed_identifier_list();
-  param_types->push_back(Typed_identifier("inter",
-                                         Type::make_type_descriptor_ptr_type(),
-                                         bloc));
-  param_types->push_back(Typed_identifier("i", this->expr_->type(), bloc));
-  Type* ptype = Type::make_pointer_type(this->type_);
-  param_types->push_back(Typed_identifier("v", ptype, bloc));
-  Typed_identifier_list* ret_types = new Typed_identifier_list();
-  ret_types->push_back(Typed_identifier("ok", Type::lookup_bool_type(), bloc));
-  Function_type* fntype = Type::make_function_type(NULL, param_types,
-                                                  ret_types, bloc);
-  Named_object* fn =
-    Named_object::make_function_declaration(fnname, NULL, fntype, bloc);
-  std::string asm_name = "runtime.";
-  asm_name += fnname;
-  fn->func_declaration_value()->set_asm_name(asm_name);
-
-  // ok = FNNAME(type_descriptor, expr, &val_temp)
-  Expression* func = Expression::make_func_reference(fn, NULL, loc);
-  Expression_list* params = new Expression_list();
-  params->push_back(Expression::make_type_descriptor(this->type_, loc));
-  params->push_back(this->expr_);
+  // ok = CODE(type_descriptor, expr, &val_temp)
+  Expression* p1 = Expression::make_type_descriptor(this->type_, loc);
   Expression* ref = Expression::make_temporary_reference(val_temp, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
-  Expression* call = Expression::make_call(func, params, false, loc);
+  Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc);
+  Expression* call = Runtime::make_call(code, loc, 3, p1, this->expr_, p3);
   Statement* s = Statement::make_assignment(this->ok_, call, loc);
   b->add_statement(s);
 
@@ -1517,12 +1629,29 @@ Tuple_type_guard_assignment_statement::lower_to_object_type(Block* b,
   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*
 Statement::make_tuple_type_guard_assignment(Expression* val, Expression* ok,
                                            Expression* expr, Type* type,
-                                           source_location location)
+                                           Location location)
 {
   return new Tuple_type_guard_assignment_statement(val, ok, expr, type,
                                                   location);
@@ -1533,11 +1662,15 @@ Statement::make_tuple_type_guard_assignment(Expression* val, Expression* ok,
 class Expression_statement : public Statement
 {
  public:
-  Expression_statement(Expression* expr)
+  Expression_statement(Expression* expr, bool is_ignored)
     : Statement(STATEMENT_EXPRESSION, expr->location()),
-      expr_(expr)
+      expr_(expr), is_ignored_(is_ignored)
   { }
 
+  Expression*
+  expr()
+  { return this->expr_; }
+
  protected:
   int
   do_traverse(Traverse* traverse)
@@ -1547,16 +1680,35 @@ class Expression_statement : public Statement
   do_determine_types()
   { this->expr_->determine_type_no_context(); }
 
+  void
+  do_check_types(Gogo*);
+
   bool
   do_may_fall_through() const;
 
-  tree
-  do_get_tree(Translate_context* context);
+  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_;
 };
 
+// Check the types of an expression statement.  The only check we do
+// is to possibly give an error about discarding the value of the
+// expression.
+
+void
+Expression_statement::do_check_types(Gogo*)
+{
+  if (!this->is_ignored_)
+    this->expr_->discarding_value();
+}
+
 // An expression statement may fall through unless it is a call to a
 // function which does not return.
 
@@ -1590,21 +1742,30 @@ Expression_statement::do_may_fall_through() const
 
 // Convert to backend representation.
 
-tree
-Expression_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Expression_statement::do_get_backend(Translate_context* context)
 {
   tree expr_tree = this->expr_->get_tree(context);
-  Bexpression* bexpr = tree_to_expr(expr_tree);
-  Bstatement* ret = context->backend()->expression_statement(bexpr);
-  return statement_to_tree(ret);
+  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*
-Statement::make_statement(Expression* expr)
+Statement::make_statement(Expression* expr, bool is_ignored)
 {
-  return new Expression_statement(expr);
+  return new Expression_statement(expr, is_ignored);
 }
 
 // A block statement--a list of statements which may include variable
@@ -1613,7 +1774,7 @@ Statement::make_statement(Expression* expr)
 class Block_statement : public Statement
 {
  public:
-  Block_statement(Block* block, source_location location)
+  Block_statement(Block* block, Location location)
     : Statement(STATEMENT_BLOCK, location),
       block_(block)
   { }
@@ -1631,18 +1792,37 @@ class Block_statement : public Statement
   do_may_fall_through() const
   { return this->block_->may_fall_through(); }
 
-  tree
-  do_get_tree(Translate_context* context)
-  { return this->block_->get_tree(context); }
+  Bstatement*
+  do_get_backend(Translate_context* context);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
  private:
   Block* block_;
 };
 
+// Convert a block to the backend representation of a statement.
+
+Bstatement*
+Block_statement::do_get_backend(Translate_context* context)
+{
+  Bblock* bblock = this->block_->get_backend(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*
-Statement::make_block_statement(Block* block, source_location location)
+Statement::make_block_statement(Block* block, Location location)
 {
   return new Block_statement(block, location);
 }
@@ -1664,14 +1844,17 @@ class Inc_dec_statement : public Statement
 
   bool
   do_traverse_assignments(Traverse_assignments*)
-  { gcc_unreachable(); }
+  { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
-  tree
-  do_get_tree(Translate_context*)
-  { gcc_unreachable(); }
+  Bstatement*
+  do_get_backend(Translate_context*)
+  { go_unreachable(); }
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
  private:
   // The l-value to increment or decrement.
@@ -1683,9 +1866,9 @@ 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();
+  Location loc = this->location();
 
   mpz_t oval;
   mpz_init_set_ui(oval, 1UL);
@@ -1696,6 +1879,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*
@@ -1715,15 +1908,11 @@ 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,
                                 Call_expression* call,
-                                source_location location)
+                                Location location)
     : Statement(classification, location),
       call_(call), struct_type_(NULL)
 {
@@ -1769,8 +1958,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;
@@ -1818,6 +2006,8 @@ Thunk_statement::do_determine_types()
 void
 Thunk_statement::do_check_types(Gogo*)
 {
+  if (!this->call_->discarding_value())
+    return;
   Call_expression* ce = this->call_->call_expression();
   if (ce == NULL)
     {
@@ -1825,14 +2015,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.
@@ -1841,17 +2023,39 @@ class Simplify_thunk_traverse : public Traverse
 {
  public:
   Simplify_thunk_traverse(Gogo* gogo)
-    : Traverse(traverse_blocks),
-      gogo_(gogo)
+    : Traverse(traverse_functions | traverse_blocks),
+      gogo_(gogo), function_(NULL)
   { }
 
   int
+  function(Named_object*);
+
+  int
   block(Block*);
 
  private:
+  // General IR.
   Gogo* gogo_;
+  // The function we are traversing.
+  Named_object* function_;
 };
 
+// Keep track of the current function while looking for thunks.
+
+int
+Simplify_thunk_traverse::function(Named_object* no)
+{
+  go_assert(this->function_ == NULL);
+  this->function_ = no;
+  int t = no->func_value()->traverse(this);
+  this->function_ = NULL;
+  if (t == TRAVERSE_EXIT)
+    return t;
+  return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// Look for thunks in a block.
+
 int
 Simplify_thunk_traverse::block(Block* b)
 {
@@ -1862,7 +2066,7 @@ Simplify_thunk_traverse::block(Block* b)
   Thunk_statement* stat = b->statements()->back()->thunk_statement();
   if (stat == NULL)
     return TRAVERSE_CONTINUE;
-  if (stat->simplify_statement(this->gogo_, b))
+  if (stat->simplify_statement(this->gogo_, this->function_, b))
     return TRAVERSE_SKIP_COMPONENTS;
   return TRAVERSE_CONTINUE;
 }
@@ -1876,6 +2080,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
@@ -1884,18 +2111,28 @@ Gogo::simplify_thunk_statements()
 // struct to a thunk.  The thunk does the real call.
 
 bool
-Thunk_statement::simplify_statement(Gogo* gogo, Block* block)
+Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
+                                   Block* block)
 {
   if (this->classification() == STATEMENT_ERROR)
     return false;
   if (this->call_->is_error_expression())
     return false;
 
+  if (this->classification() == STATEMENT_DEFER)
+    {
+      // Make sure that the defer stack exists for the function.  We
+      // will use when converting this statement to the backend
+      // representation, but we want it to exist when we start
+      // converting the function.
+      function->func_value()->defer_stack(this->location());
+    }
+
   Call_expression* ce = this->call_->call_expression();
   Function_type* fntype = ce->get_function_type();
   if (fntype == NULL)
     {
-      gcc_assert(saw_errors());
+      go_assert(saw_errors());
       this->set_is_error();
       return false;
     }
@@ -1903,17 +2140,15 @@ Thunk_statement::simplify_statement(Gogo* gogo, Block* block)
     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();
+  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.
 
@@ -1921,38 +2156,11 @@ Thunk_statement::simplify_statement(Gogo* gogo, Block* block)
   // 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
-    gcc_unreachable();
+  if (interface_method != NULL)
+    vals->push_back(interface_method->expr());
 
   if (ce->args() != NULL)
     {
@@ -1972,7 +2180,7 @@ Thunk_statement::simplify_statement(Gogo* gogo, Block* block)
 
   // Look up the thunk.
   Named_object* named_thunk = gogo->lookup(thunk_name, NULL);
-  gcc_assert(named_thunk != NULL && named_thunk->is_function());
+  go_assert(named_thunk != NULL && named_thunk->is_function());
 
   // Build the call.
   Expression* func = Expression::make_func_reference(named_thunk, NULL,
@@ -1988,11 +2196,11 @@ Thunk_statement::simplify_statement(Gogo* gogo, Block* block)
   else if (this->classification() == STATEMENT_DEFER)
     s = Statement::make_defer_statement(call, location);
   else
-    gcc_unreachable();
+    go_unreachable();
 
   // The current block should end with the go statement.
-  gcc_assert(block->statements()->size() >= 1);
-  gcc_assert(block->statements()->back() == this);
+  go_assert(block->statements()->size() >= 1);
+  go_assert(block->statements()->back() == this);
   block->replace_statement(block->statements()->size() - 1, s);
 
   // We already ran the determine_types pass, so we need to run it now
@@ -2020,52 +2228,40 @@ Thunk_statement::thunk_field_param(int n, char* buf, size_t buflen)
 Struct_type*
 Thunk_statement::build_struct(Function_type* fntype)
 {
-  source_location location = this->location();
+  Location location = this->location();
 
   Struct_field_list* fields = new Struct_field_list();
 
   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)
-    {
-      gcc_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)
     {
@@ -2088,10 +2284,9 @@ 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();
+  Location location = this->location();
 
   Call_expression* ce = this->call_->call_expression();
 
@@ -2139,41 +2334,17 @@ 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");
+      retaddr_label = gogo->add_label_reference("retaddr", location, false);
       Expression* arg = Expression::make_label_addr(retaddr_label, location);
-      Expression_list* args = new Expression_list();
-      args->push_back(arg);
-
-      static Named_object* set_defer_retaddr;
-      if (set_defer_retaddr == NULL)
-       {
-         const source_location bloc = BUILTINS_LOCATION;
-         Typed_identifier_list* param_types = new Typed_identifier_list();
-         Type *voidptr_type = Type::make_pointer_type(Type::make_void_type());
-         param_types->push_back(Typed_identifier("r", voidptr_type, bloc));
-
-         Typed_identifier_list* result_types = new Typed_identifier_list();
-         result_types->push_back(Typed_identifier("",
-                                                  Type::lookup_bool_type(),
-                                                  bloc));
-
-         Function_type* t = Type::make_function_type(NULL, param_types,
-                                                     result_types, bloc);
-         set_defer_retaddr =
-           Named_object::make_function_declaration("__go_set_defer_retaddr",
-                                                   NULL, t, bloc);
-         const char* n = "__go_set_defer_retaddr";
-         set_defer_retaddr->func_declaration_value()->set_asm_name(n);
-       }
-
-      Expression* fn = Expression::make_func_reference(set_defer_retaddr,
-                                                      NULL, location);
-      Expression* call = Expression::make_call(fn, args, false, location);
+      Expression* call = Runtime::make_call(Runtime::SET_DEFER_RETADDR,
+                                           location, 1, arg);
 
       // This is a hack to prevent the middle-end from deleting the
       // label.
@@ -2191,7 +2362,7 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
 
   // Get a reference to the parameter.
   Named_object* named_parameter = gogo->lookup(parameter_name, NULL);
-  gcc_assert(named_parameter != NULL && named_parameter->is_variable());
+  go_assert(named_parameter != NULL && named_parameter->is_variable());
 
   // Build the call.  Note that the field names are the same as the
   // ones used in build_struct.
@@ -2200,43 +2371,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())
-    {
-      func_to_call = Expression::make_field_reference(thunk_parameter,
-                                                     0, location);
-      next_index = 1;
-    }
-  else
+  if (this->is_constant_function())
     {
-      gcc_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();
@@ -2259,7 +2420,7 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
        call_params->push_back(param);
       else
        {
-         gcc_assert(call_params->empty());
+         go_assert(call_params->empty());
          recover_arg = param;
        }
     }
@@ -2270,25 +2431,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();
-
-  Statement* call_statement = Statement::make_statement(call);
-
-  // We already ran the determine_types pass, so we need to run it
-  // just for this statement now.
-  call_statement->determine_types();
+  Call_expression* call = Expression::make_call(func_to_call, call_params,
+                                               false, location);
 
-  // Sanity check.
-  call->check_types(gogo);
+  // 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();
 
-  if (call_ce != NULL && recover_arg != NULL)
-    call_ce->set_recover_arg(recover_arg);
+  Statement* call_statement = Statement::make_statement(call, true);
 
   gogo->add_statement(call_statement);
 
@@ -2303,121 +2455,135 @@ 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();
+      if (ce == NULL)
+       go_assert(saw_errors());
+      else
+       {
+         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);
 }
 
-// Get the function and argument trees.
+// Get the function and argument expressions.
 
-void
-Thunk_statement::get_fn_and_arg(Translate_context* context, tree* pfn,
-                               tree* parg)
+bool
+Thunk_statement::get_fn_and_arg(Expression** pfn, Expression** parg)
 {
   if (this->call_->is_error_expression())
-    {
-      *pfn = error_mark_node;
-      *parg = error_mark_node;
-      return;
-    }
+    return false;
 
   Call_expression* ce = this->call_->call_expression();
 
-  Expression* fn = ce->fn();
-  *pfn = fn->get_tree(context);
+  *pfn = ce->fn();
 
   const Expression_list* args = ce->args();
   if (args == NULL || args->empty())
-    *parg = null_pointer_node;
+    *parg = Expression::make_nil(this->location());
   else
     {
-      gcc_assert(args->size() == 1);
-      *parg = args->front()->get_tree(context);
+      go_assert(args->size() == 1);
+      *parg = args->front();
     }
+
+  return true;
 }
 
 // Class Go_statement.
 
-tree
-Go_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Go_statement::do_get_backend(Translate_context* context)
 {
-  tree fn_tree;
-  tree arg_tree;
-  this->get_fn_and_arg(context, &fn_tree, &arg_tree);
+  Expression* fn;
+  Expression* arg;
+  if (!this->get_fn_and_arg(&fn, &arg))
+    return context->backend()->error_statement();
 
-  static tree go_fndecl;
+  Expression* call = Runtime::make_call(Runtime::GO, this->location(), 2,
+                                       fn, arg);
+  tree call_tree = call->get_tree(context);
+  Bexpression* call_bexpr = tree_to_expr(call_tree);
+  return context->backend()->expression_statement(call_bexpr);
+}
 
-  tree fn_arg_type = NULL_TREE;
-  if (go_fndecl == NULL_TREE)
-    {
-      // Only build FN_ARG_TYPE if we need it.
-      tree subargtypes = tree_cons(NULL_TREE, ptr_type_node, void_list_node);
-      tree subfntype = build_function_type(ptr_type_node, subargtypes);
-      fn_arg_type = build_pointer_type(subfntype);
-    }
+// Dump the AST representation for go statement.
 
-  return Gogo::call_builtin(&go_fndecl,
-                           this->location(),
-                           "__go_go",
-                           2,
-                           void_type_node,
-                           fn_arg_type,
-                           fn_tree,
-                           ptr_type_node,
-                           arg_tree);
+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*
-Statement::make_go_statement(Call_expression* call, source_location location)
+Statement::make_go_statement(Call_expression* call, Location location)
 {
   return new Go_statement(call, location);
 }
 
 // Class Defer_statement.
 
-tree
-Defer_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Defer_statement::do_get_backend(Translate_context* context)
 {
-  source_location loc = this->location();
+  Expression* fn;
+  Expression* arg;
+  if (!this->get_fn_and_arg(&fn, &arg))
+    return context->backend()->error_statement();
 
-  tree fn_tree;
-  tree arg_tree;
-  this->get_fn_and_arg(context, &fn_tree, &arg_tree);
-  if (fn_tree == error_mark_node || arg_tree == error_mark_node)
-    return error_mark_node;
+  Location loc = this->location();
+  Expression* ds = context->function()->func_value()->defer_stack(loc);
 
-  static tree defer_fndecl;
-
-  tree fn_arg_type = NULL_TREE;
-  if (defer_fndecl == NULL_TREE)
-    {
-      // Only build FN_ARG_TYPE if we need it.
-      tree subargtypes = tree_cons(NULL_TREE, ptr_type_node, void_list_node);
-      tree subfntype = build_function_type(ptr_type_node, subargtypes);
-      fn_arg_type = build_pointer_type(subfntype);
-    }
+  Expression* call = Runtime::make_call(Runtime::DEFER, loc, 3,
+                                       ds, fn, arg);
+  tree call_tree = call->get_tree(context);
+  Bexpression* call_bexpr = tree_to_expr(call_tree);
+  return context->backend()->expression_statement(call_bexpr);
+}
 
-  tree defer_stack = context->function()->func_value()->defer_stack(loc);
+// Dump the AST representation for defer statement.
 
-  return Gogo::call_builtin(&defer_fndecl,
-                           loc,
-                           "__go_defer",
-                           3,
-                           void_type_node,
-                           ptr_type_node,
-                           defer_stack,
-                           fn_arg_type,
-                           fn_tree,
-                           ptr_type_node,
-                           arg_tree);
+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*
 Statement::make_defer_statement(Call_expression* call,
-                               source_location location)
+                               Location location)
 {
   return new Defer_statement(call, location);
 }
@@ -2443,14 +2609,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;
@@ -2459,7 +2625,7 @@ Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing)
   this->vals_ = NULL;
   this->is_lowered_ = true;
 
-  source_location loc = this->location();
+  Location loc = this->location();
 
   size_t vals_count = vals == NULL ? 0 : vals->size();
   Function::Results* results = function->func_value()->result_variables();
@@ -2531,7 +2697,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);
@@ -2547,19 +2718,34 @@ Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing)
                     i, reason.c_str());
        }
     }
-  gcc_assert(lhs->size() == rhs->size());
+  go_assert(lhs->size() == rhs->size());
 
   if (lhs->empty())
     ;
   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);
 
@@ -2570,9 +2756,11 @@ Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing)
 
 // Convert a return statement to the backend representation.
 
-tree
-Return_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Return_statement::do_get_backend(Translate_context* context)
 {
+  Location loc = this->location();
+
   Function* function = context->function()->func_value();
   tree fndecl = function->get_decl();
 
@@ -2585,22 +2773,31 @@ Return_statement::do_get_tree(Translate_context* context)
           p != results->end();
           p++)
        {
-         tree rv = (*p)->get_tree(context->gogo(), context->function());
-         retvals.push_back(tree_to_expr(rv));
+         Expression* vr = Expression::make_var_reference(*p, loc);
+         retvals.push_back(tree_to_expr(vr->get_tree(context)));
        }
     }
 
-  Bstatement* ret;
-  ret = context->backend()->return_statement(tree_to_function(fndecl),
-                                            retvals, this->location());
-  return statement_to_tree(ret);
+  return context->backend()->return_statement(tree_to_function(fndecl),
+                                             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)
+                                Location location)
 {
   return new Return_statement(vals, location);
 }
@@ -2610,7 +2807,7 @@ Statement::make_return_statement(Expression_list* vals,
 class Bc_statement : public Statement
 {
  public:
-  Bc_statement(bool is_break, Unnamed_label* label, source_location location)
+  Bc_statement(bool is_break, Unnamed_label* label, Location location)
     : Statement(STATEMENT_BREAK_OR_CONTINUE, location),
       label_(label), is_break_(is_break)
   { }
@@ -2628,12 +2825,12 @@ class Bc_statement : public Statement
   do_may_fall_through() const
   { return false; }
 
-  tree
-  do_get_tree(Translate_context* context)
-  {
-    return statement_to_tree(this->label_->get_goto(context,
-                                                   this->location()));
-  }
+  Bstatement*
+  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.
@@ -2642,10 +2839,25 @@ 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*
-Statement::make_break_statement(Unnamed_label* label, source_location location)
+Statement::make_break_statement(Unnamed_label* label, Location location)
 {
   return new Bc_statement(true, label, location);
 }
@@ -2654,7 +2866,7 @@ Statement::make_break_statement(Unnamed_label* label, source_location location)
 
 Statement*
 Statement::make_continue_statement(Unnamed_label* label,
-                                  source_location location)
+                                  Location location)
 {
   return new Bc_statement(false, label, location);
 }
@@ -2664,7 +2876,7 @@ Statement::make_continue_statement(Unnamed_label* label,
 class Goto_statement : public Statement
 {
  public:
-  Goto_statement(Label* label, source_location location)
+  Goto_statement(Label* label, Location location)
     : Statement(STATEMENT_GOTO, location),
       label_(label)
   { }
@@ -2681,8 +2893,11 @@ class Goto_statement : public Statement
   do_may_fall_through() const
   { return false; }
 
-  tree
-  do_get_tree(Translate_context*);
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
  private:
   Label* label_;
@@ -2702,21 +2917,28 @@ Goto_statement::do_check_types(Gogo*)
     }
 }
 
-// Return the tree for the goto statement.
+// Convert the goto statement to the backend representation.
 
-tree
-Goto_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Goto_statement::do_get_backend(Translate_context* context)
 {
   Blabel* blabel = this->label_->get_backend_label(context);
-  Bstatement* statement = context->backend()->goto_statement(blabel,
-                                                            this->location());
-  return statement_to_tree(statement);
+  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*
-Statement::make_goto_statement(Label* label, source_location location)
+Statement::make_goto_statement(Label* label, Location location)
 {
   return new Goto_statement(label, location);
 }
@@ -2726,7 +2948,7 @@ Statement::make_goto_statement(Label* label, source_location location)
 class Goto_unnamed_statement : public Statement
 {
  public:
-  Goto_unnamed_statement(Unnamed_label* label, source_location location)
+  Goto_unnamed_statement(Unnamed_label* label, Location location)
     : Statement(STATEMENT_GOTO_UNNAMED, location),
       label_(label)
   { }
@@ -2740,22 +2962,34 @@ class Goto_unnamed_statement : public Statement
   do_may_fall_through() const
   { return false; }
 
-  tree
-  do_get_tree(Translate_context* context)
-  {
-    return statement_to_tree(this->label_->get_goto(context,
-                                                   this->location()));
-  }
+  Bstatement*
+  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*
 Statement::make_goto_unnamed_statement(Unnamed_label* label,
-                                      source_location location)
+                                      Location location)
 {
   return new Goto_unnamed_statement(label, location);
 }
@@ -2770,21 +3004,29 @@ Label_statement::do_traverse(Traverse*)
   return TRAVERSE_CONTINUE;
 }
 
-// Return a tree defining this label.
+// Return the backend representation of the statement defining this
+// label.
 
-tree
-Label_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Label_statement::do_get_backend(Translate_context* context)
 {
   Blabel* blabel = this->label_->get_backend_label(context);
-  Bstatement* statement;
-  statement = context->backend()->label_definition_statement(blabel);
-  return statement_to_tree(statement);
+  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*
-Statement::make_label_statement(Label* label, source_location location)
+Statement::make_label_statement(Label* label, Location location)
 {
   return new Label_statement(label, location);
 }
@@ -2804,15 +3046,29 @@ class Unnamed_label_statement : public Statement
   do_traverse(Traverse*)
   { return TRAVERSE_CONTINUE; }
 
-  tree
-  do_get_tree(Translate_context* context)
-  { return statement_to_tree(this->label_->get_definition(context)); }
+  Bstatement*
+  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*
@@ -2827,7 +3083,7 @@ class If_statement : public Statement
 {
  public:
   If_statement(Expression* cond, Block* then_block, Block* else_block,
-              source_location location)
+              Location location)
     : Statement(STATEMENT_IF, location),
       cond_(cond), then_block_(then_block), else_block_(else_block)
   { }
@@ -2845,8 +3101,11 @@ class If_statement : public Statement
   bool
   do_may_fall_through() const;
 
-  tree
-  do_get_tree(Translate_context*);
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
  private:
   Expression* cond_;
@@ -2902,37 +3161,101 @@ If_statement::do_may_fall_through() const
          || this->else_block_->may_fall_through());
 }
 
-// Get tree.
+// Get the backend representation.
 
-tree
-If_statement::do_get_tree(Translate_context* context)
+Bstatement*
+If_statement::do_get_backend(Translate_context* context)
 {
-  gcc_assert(this->cond_->type()->is_boolean_type()
+  go_assert(this->cond_->type()->is_boolean_type()
             || this->cond_->type()->is_error());
   tree cond_tree = this->cond_->get_tree(context);
-  tree then_tree = this->then_block_->get_tree(context);
-  tree else_tree = (this->else_block_ == NULL
-                   ? NULL_TREE
-                   : this->else_block_->get_tree(context));
-  if (cond_tree == error_mark_node
-      || then_tree == error_mark_node
-      || else_tree == error_mark_node)
-    return error_mark_node;
-  tree ret = build3(COND_EXPR, void_type_node, cond_tree, then_tree,
-                   else_tree);
-  SET_EXPR_LOCATION(ret, this->location());
-  return ret;
+  Bexpression* cond_expr = tree_to_expr(cond_tree);
+  Bblock* then_block = this->then_block_->get_backend(context);
+  Bblock* else_block = (this->else_block_ == NULL
+                       ? NULL
+                       : this->else_block_->get_backend(context));
+  return context->backend()->if_statement(cond_expr, then_block,
+                                         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*
 Statement::make_if_statement(Expression* cond, Block* then_block,
-                            Block* else_block, source_location location)
+                            Block* else_block, Location location)
 {
   return new If_statement(cond, then_block, else_block, location);
 }
 
+// Class Case_clauses::Hash_integer_value.
+
+class Case_clauses::Hash_integer_value
+{
+ public:
+  size_t
+  operator()(Expression*) const;
+};
+
+size_t
+Case_clauses::Hash_integer_value::operator()(Expression* pe) const
+{
+  Numeric_constant nc;
+  mpz_t ival;
+  if (!pe->numeric_constant_value(&nc) || !nc.to_int(&ival))
+    go_unreachable();
+  size_t ret = mpz_get_ui(ival);
+  mpz_clear(ival);
+  return ret;
+}
+
+// Class Case_clauses::Eq_integer_value.
+
+class Case_clauses::Eq_integer_value
+{
+ public:
+  bool
+  operator()(Expression*, Expression*) const;
+};
+
+bool
+Case_clauses::Eq_integer_value::operator()(Expression* a, Expression* b) const
+{
+  Numeric_constant anc;
+  mpz_t aval;
+  Numeric_constant bnc;
+  mpz_t bval;
+  if (!a->numeric_constant_value(&anc)
+      || !anc.to_int(&aval)
+      || !b->numeric_constant_value(&bnc)
+      || !bnc.to_int(&bval))
+    go_unreachable();
+  bool ret = mpz_cmp(aval, bval) == 0;
+  mpz_clear(aval);
+  mpz_clear(bval);
+  return ret;
+}
+
 // Class Case_clauses::Case_clause.
 
 // Traversal.
@@ -2981,11 +3304,11 @@ Case_clauses::Case_clause::lower(Block* b, Temporary_statement* val_temp,
                                 Unnamed_label* start_label,
                                 Unnamed_label* finish_label) const
 {
-  source_location loc = this->location_;
+  Location loc = this->location_;
   Unnamed_label* next_case_label;
   if (this->cases_ == NULL || this->cases_->empty())
     {
-      gcc_assert(this->is_default_);
+      go_assert(this->is_default_);
       next_case_label = NULL;
     }
   else
@@ -2996,16 +3319,10 @@ Case_clauses::Case_clause::lower(Block* b, Temporary_statement* val_temp,
           p != this->cases_->end();
           ++p)
        {
-         Expression* this_cond;
-         if (val_temp == NULL)
-           this_cond = *p;
-         else
-           {
-             Expression* ref = Expression::make_temporary_reference(val_temp,
-                                                                    loc);
-             this_cond = Expression::make_binary(OPERATOR_EQEQ, ref, *p, loc);
-           }
-
+         Expression* ref = Expression::make_temporary_reference(val_temp,
+                                                                loc);
+         Expression* this_cond = Expression::make_binary(OPERATOR_EQEQ, ref,
+                                                         *p, loc);
          if (cond == NULL)
            cond = this_cond;
          else
@@ -3013,7 +3330,7 @@ Case_clauses::Case_clause::lower(Block* b, Temporary_statement* val_temp,
        }
 
       Block* then_block = new Block(b, loc);
-      next_case_label = new Unnamed_label(UNKNOWN_LOCATION);
+      next_case_label = new Unnamed_label(Linemap::unknown_location());
       Statement* s = Statement::make_goto_unnamed_statement(next_case_label,
                                                            loc);
       then_block->add_statement(s);
@@ -3089,76 +3406,104 @@ Case_clauses::Case_clause::may_fall_through() const
   return this->statements_->may_fall_through();
 }
 
-// Build up the body of a SWITCH_EXPR.
+// Convert the case values and statements to the backend
+// representation.  BREAK_LABEL is the label which break statements
+// should branch to.  CASE_CONSTANTS is used to detect duplicate
+// constants.  *CASES should be passed as an empty vector; the values
+// for this case will be added to it.  If this is the default case,
+// *CASES will remain empty.  This returns the statement to execute if
+// one of these cases is selected.
 
-void
-Case_clauses::Case_clause::get_constant_tree(Translate_context* context,
-                                            Unnamed_label* break_label,
-                                            Case_constants* case_constants,
-                                            tree* stmt_list) const
+Bstatement*
+Case_clauses::Case_clause::get_backend(Translate_context* context,
+                                      Unnamed_label* break_label,
+                                      Case_constants* case_constants,
+                                      std::vector<Bexpression*>* cases) const
 {
   if (this->cases_ != NULL)
     {
+      go_assert(!this->is_default_);
       for (Expression_list::const_iterator p = this->cases_->begin();
           p != this->cases_->end();
           ++p)
        {
-         Type* itype;
-         mpz_t ival;
-         mpz_init(ival);
-         if (!(*p)->integer_constant_value(true, ival, &itype))
+         Expression* e = *p;
+         if (e->classification() != Expression::EXPRESSION_INTEGER)
            {
-             // Something went wrong.  This can happen with a
-             // negative constant and an unsigned switch value.
-             gcc_assert(saw_errors());
-             continue;
-           }
-         gcc_assert(itype != NULL);
-         tree type_tree = itype->get_tree(context->gogo());
-         tree val = Expression::integer_constant_tree(ival, type_tree);
-         mpz_clear(ival);
-
-         if (val != error_mark_node)
-           {
-             gcc_assert(TREE_CODE(val) == INTEGER_CST);
-
-             std::pair<Case_constants::iterator, bool> ins =
-               case_constants->insert(val);
-             if (!ins.second)
+             Numeric_constant nc;
+             mpz_t ival;
+             if (!(*p)->numeric_constant_value(&nc) || !nc.to_int(&ival))
                {
-                 // Value was already present.
-                 warning_at(this->location_, 0,
-                            "duplicate case value will never match");
+                 // Something went wrong.  This can happen with a
+                 // negative constant and an unsigned switch value.
+                 go_assert(saw_errors());
                  continue;
                }
+             go_assert(nc.type() != NULL);
+             e = Expression::make_integer(&ival, nc.type(), e->location());
+             mpz_clear(ival);
+           }
 
-             tree label = create_artificial_label(this->location_);
-             append_to_statement_list(build3(CASE_LABEL_EXPR, void_type_node,
-                                             val, NULL_TREE, label),
-                                      stmt_list);
+         std::pair<Case_constants::iterator, bool> ins =
+           case_constants->insert(e);
+         if (!ins.second)
+           {
+             // Value was already present.
+             error_at(this->location_, "duplicate case in switch");
+             e = Expression::make_error(this->location_);
            }
+
+         tree case_tree = e->get_tree(context);
+         Bexpression* case_expr = tree_to_expr(case_tree);
+         cases->push_back(case_expr);
        }
     }
 
-  if (this->is_default_)
+  Bstatement* statements;
+  if (this->statements_ == NULL)
+    statements = NULL;
+  else
     {
-      tree label = create_artificial_label(this->location_);
-      append_to_statement_list(build3(CASE_LABEL_EXPR, void_type_node,
-                                     NULL_TREE, NULL_TREE, label),
-                              stmt_list);
+      Bblock* bblock = this->statements_->get_backend(context);
+      statements = context->backend()->block_statement(bblock);
     }
 
-  if (this->statements_ != NULL)
+  Bstatement* break_stat;
+  if (this->is_fallthrough_)
+    break_stat = NULL;
+  else
+    break_stat = break_label->get_goto(context, this->location_);
+
+  if (statements == NULL)
+    return break_stat;
+  else if (break_stat == NULL)
+    return statements;
+  else
+    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_)
     {
-      tree block_tree = this->statements_->get_tree(context);
-      if (block_tree != error_mark_node)
-       append_to_statement_list(block_tree, stmt_list);
+      ast_dump_context->ostream() << "default:";
     }
-
-  if (!this->is_fallthrough_)
+  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_)
     {
-      Bstatement* g = break_label->get_goto(context, this->location_);
-      append_to_statement_list(statement_to_tree(g), stmt_list);
+      ast_dump_context->print_indent();
+      ast_dump_context->ostream() <<  " (fallthrough)" << std::endl;
     }
 }
 
@@ -3247,7 +3592,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.
@@ -3296,20 +3640,43 @@ Case_clauses::may_fall_through() const
   return !found_default;
 }
 
-// Return a tree when all case expressions are constants.
+// Convert the cases to the backend representation.  This sets
+// *ALL_CASES and *ALL_STATEMENTS.
 
-tree
-Case_clauses::get_constant_tree(Translate_context* context,
-                               Unnamed_label* break_label) const
+void
+Case_clauses::get_backend(Translate_context* context,
+                         Unnamed_label* break_label,
+                         std::vector<std::vector<Bexpression*> >* all_cases,
+                         std::vector<Bstatement*>* all_statements) const
 {
   Case_constants case_constants;
-  tree stmt_list = NULL_TREE;
+
+  size_t c = this->clauses_.size();
+  all_cases->resize(c);
+  all_statements->resize(c);
+
+  size_t i = 0;
+  for (Clauses::const_iterator p = this->clauses_.begin();
+       p != this->clauses_.end();
+       ++p, ++i)
+    {
+      std::vector<Bexpression*> cases;
+      Bstatement* stat = p->get_backend(context, break_label, &case_constants,
+                                       &cases);
+      (*all_cases)[i].swap(cases);
+      (*all_statements)[i] = stat;
+    }
+}
+
+// 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->get_constant_tree(context, break_label, &case_constants,
-                        &stmt_list);
-  return stmt_list;
+    p->dump_clause(ast_dump_context);
 }
 
 // A constant switch statement.  A Switch_statement is lowered to this
@@ -3320,7 +3687,7 @@ class Constant_switch_statement : public Statement
  public:
   Constant_switch_statement(Expression* val, Case_clauses* clauses,
                            Unnamed_label* break_label,
-                           source_location location)
+                           Location location)
     : Statement(STATEMENT_CONSTANT_SWITCH, location),
       val_(val), clauses_(clauses), break_label_(break_label)
   { }
@@ -3338,8 +3705,11 @@ class Constant_switch_statement : public Statement
   bool
   do_may_fall_through() const;
 
-  tree
-  do_get_tree(Translate_context*);
+  Bstatement*
+  do_get_backend(Translate_context*);
+
+  void
+  do_dump_statement(Ast_dump_context*) const;
 
  private:
   // The value to switch on.
@@ -3396,26 +3766,48 @@ Constant_switch_statement::do_may_fall_through() const
 
 // Convert to GENERIC.
 
-tree
-Constant_switch_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Constant_switch_statement::do_get_backend(Translate_context* context)
 {
   tree switch_val_tree = this->val_->get_tree(context);
+  Bexpression* switch_val_expr = tree_to_expr(switch_val_tree);
 
   Unnamed_label* break_label = this->break_label_;
   if (break_label == NULL)
     break_label = new Unnamed_label(this->location());
 
-  tree stmt_list = NULL_TREE;
-  tree s = build3(SWITCH_EXPR, void_type_node, switch_val_tree,
-                 this->clauses_->get_constant_tree(context, break_label),
-                 NULL_TREE);
-  SET_EXPR_LOCATION(s, this->location());
-  append_to_statement_list(s, &stmt_list);
+  std::vector<std::vector<Bexpression*> > all_cases;
+  std::vector<Bstatement*> all_statements;
+  this->clauses_->get_backend(context, break_label, &all_cases,
+                             &all_statements);
 
+  Bstatement* switch_statement;
+  switch_statement = context->backend()->switch_statement(switch_val_expr,
+                                                         all_cases,
+                                                         all_statements,
+                                                         this->location());
   Bstatement* ldef = break_label->get_definition(context);
-  append_to_statement_list(statement_to_tree(ldef), &stmt_list);
+  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() << "}";
+    }
 
-  return stmt_list;
+   ast_dump_context->ostream() << std::endl;
 }
 
 // Class Switch_statement.
@@ -3437,9 +3829,10 @@ 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();
+  Location loc = this->location();
 
   if (this->val_ != NULL
       && (this->val_->is_error_expression()
@@ -3453,6 +3846,16 @@ Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
     return new Constant_switch_statement(this->val_, this->clauses_,
                                         this->break_label_, loc);
 
+  if (this->val_ != NULL
+      && !this->val_->type()->is_comparable()
+      && !Type::are_compatible_for_comparison(true, this->val_->type(),
+                                             Type::make_nil_type(), NULL))
+    {
+      error_at(this->val_->location(),
+              "cannot switch on value whose type that may not be compared");
+      return Statement::make_error_statement(loc);
+    }
+
   Block* b = new Block(enclosing, loc);
 
   if (this->clauses_->empty())
@@ -3460,18 +3863,15 @@ Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
       Expression* val = this->val_;
       if (val == NULL)
        val = Expression::make_boolean(true, loc);
-      return Statement::make_statement(val);
+      return Statement::make_statement(val, true);
     }
 
-  Temporary_statement* val_temp;
-  if (this->val_ == NULL)
-    val_temp = NULL;
-  else
-    {
-      // var val_temp VAL_TYPE = VAL
-      val_temp = Statement::make_temporary(NULL, this->val_, loc);
-      b->add_statement(val_temp);
-    }
+  // var val_temp VAL_TYPE = VAL
+  Expression* val = this->val_;
+  if (val == NULL)
+    val = Expression::make_boolean(true, loc);
+  Temporary_statement* val_temp = Statement::make_temporary(NULL, val, loc);
+  b->add_statement(val_temp);
 
   this->clauses_->lower(b, val_temp, this->break_label());
 
@@ -3492,10 +3892,31 @@ 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*
-Statement::make_switch_statement(Expression* val, source_location location)
+Statement::make_switch_statement(Expression* val, Location location)
 {
   return new Switch_statement(val, location);
 }
@@ -3524,119 +3945,66 @@ Type_case_clauses::Type_case_clause::traverse(Traverse* traverse)
 // statements.
 
 void
-Type_case_clauses::Type_case_clause::lower(Block* b,
+Type_case_clauses::Type_case_clause::lower(Type* switch_val_type,
+                                          Block* b,
                                           Temporary_statement* descriptor_temp,
                                           Unnamed_label* break_label,
                                           Unnamed_label** stmts_label) const
 {
-  source_location loc = this->location_;
+  Location loc = this->location_;
 
   Unnamed_label* next_case_label = NULL;
   if (!this->is_default_)
     {
       Type* type = this->type_;
 
+      std::string reason;
+      if (switch_val_type->interface_type() != NULL
+         && !type->is_nil_constant_as_type()
+         && type->interface_type() == NULL
+         && !switch_val_type->interface_type()->implements_interface(type,
+                                                                     &reason))
+       {
+         if (reason.empty())
+           error_at(this->location_, "impossible type switch case");
+         else
+           error_at(this->location_, "impossible type switch case (%s)",
+                    reason.c_str());
+       }
+
+      Expression* ref = Expression::make_temporary_reference(descriptor_temp,
+                                                            loc);
+
       Expression* cond;
       // The language permits case nil, which is of course a constant
       // rather than a type.  It will appear here as an invalid
       // forwarding type.
       if (type->is_nil_constant_as_type())
-       {
-         Expression* ref =
-           Expression::make_temporary_reference(descriptor_temp, loc);
-         cond = Expression::make_binary(OPERATOR_EQEQ, ref,
-                                        Expression::make_nil(loc),
-                                        loc);
-       }
+       cond = Expression::make_binary(OPERATOR_EQEQ, ref,
+                                      Expression::make_nil(loc),
+                                      loc);
       else
-       {
-         Expression* func;
-         if (type->interface_type() == NULL)
-           {
-             // func ifacetypeeq(*descriptor, *descriptor) bool
-             static Named_object* ifacetypeeq;
-             if (ifacetypeeq == NULL)
-               {
-                 const source_location bloc = BUILTINS_LOCATION;
-                 Typed_identifier_list* param_types =
-                   new Typed_identifier_list();
-                 Type* descriptor_type = Type::make_type_descriptor_ptr_type();
-                 param_types->push_back(Typed_identifier("a", descriptor_type,
-                                                         bloc));
-                 param_types->push_back(Typed_identifier("b", descriptor_type,
-                                                         bloc));
-                 Typed_identifier_list* ret_types =
-                   new Typed_identifier_list();
-                 Type* bool_type = Type::lookup_bool_type();
-                 ret_types->push_back(Typed_identifier("", bool_type, bloc));
-                 Function_type* fntype = Type::make_function_type(NULL,
-                                                                  param_types,
-                                                                  ret_types,
-                                                                  bloc);
-                 ifacetypeeq =
-                   Named_object::make_function_declaration("ifacetypeeq", NULL,
-                                                           fntype, bloc);
-                 const char* n = "runtime.ifacetypeeq";
-                 ifacetypeeq->func_declaration_value()->set_asm_name(n);
-               }
-
-             // ifacetypeeq(descriptor_temp, DESCRIPTOR)
-             func = Expression::make_func_reference(ifacetypeeq, NULL, loc);
-           }
-         else
-           {
-             // func ifaceI2Tp(*descriptor, *descriptor) bool
-             static Named_object* ifaceI2Tp;
-             if (ifaceI2Tp == NULL)
-               {
-                 const source_location bloc = BUILTINS_LOCATION;
-                 Typed_identifier_list* param_types =
-                   new Typed_identifier_list();
-                 Type* descriptor_type = Type::make_type_descriptor_ptr_type();
-                 param_types->push_back(Typed_identifier("a", descriptor_type,
-                                                         bloc));
-                 param_types->push_back(Typed_identifier("b", descriptor_type,
-                                                         bloc));
-                 Typed_identifier_list* ret_types =
-                   new Typed_identifier_list();
-                 Type* bool_type = Type::lookup_bool_type();
-                 ret_types->push_back(Typed_identifier("", bool_type, bloc));
-                 Function_type* fntype = Type::make_function_type(NULL,
-                                                                  param_types,
-                                                                  ret_types,
-                                                                  bloc);
-                 ifaceI2Tp =
-                   Named_object::make_function_declaration("ifaceI2Tp", NULL,
-                                                           fntype, bloc);
-                 const char* n = "runtime.ifaceI2Tp";
-                 ifaceI2Tp->func_declaration_value()->set_asm_name(n);
-               }
-
-             // ifaceI2Tp(descriptor_temp, DESCRIPTOR)
-             func = Expression::make_func_reference(ifaceI2Tp, NULL, loc);
-           }
-         Expression_list* params = new Expression_list();
-         params->push_back(Expression::make_type_descriptor(type, loc));
-         Expression* ref =
-           Expression::make_temporary_reference(descriptor_temp, loc);
-         params->push_back(ref);
-         cond = Expression::make_call(func, params, false, loc);
-       }
+       cond = Runtime::make_call((type->interface_type() == NULL
+                                  ? Runtime::IFACETYPEEQ
+                                  : Runtime::IFACEI2TP),
+                                 loc, 2,
+                                 Expression::make_type_descriptor(type, loc),
+                                 ref);
 
       Unnamed_label* dest;
       if (!this->is_fallthrough_)
        {
          // if !COND { goto NEXT_CASE_LABEL }
-         next_case_label = new Unnamed_label(UNKNOWN_LOCATION);
+         next_case_label = new Unnamed_label(Linemap::unknown_location());
          dest = next_case_label;
          cond = Expression::make_unary(OPERATOR_NOT, cond, loc);
        }
       else
        {
          // if COND { goto STMTS_LABEL }
-         gcc_assert(stmts_label != NULL);
+         go_assert(stmts_label != NULL);
          if (*stmts_label == NULL)
-           *stmts_label = new Unnamed_label(UNKNOWN_LOCATION);
+           *stmts_label = new Unnamed_label(Linemap::unknown_location());
          dest = *stmts_label;
        }
       Block* then_block = new Block(b, loc);
@@ -3651,10 +4019,10 @@ Type_case_clauses::Type_case_clause::lower(Block* b,
          && stmts_label != NULL
          && *stmts_label != NULL))
     {
-      gcc_assert(!this->is_fallthrough_);
+      go_assert(!this->is_fallthrough_);
       if (stmts_label != NULL && *stmts_label != NULL)
        {
-         gcc_assert(!this->is_default_);
+         go_assert(!this->is_default_);
          if (this->statements_ != NULL)
            (*stmts_label)->set_location(this->statements_->start_location());
          Statement* s = Statement::make_unnamed_label_statement(*stmts_label);
@@ -3667,10 +4035,10 @@ Type_case_clauses::Type_case_clause::lower(Block* b,
     }
 
   if (this->is_fallthrough_)
-    gcc_assert(next_case_label == NULL);
+    go_assert(next_case_label == NULL);
   else
     {
-      source_location gloc = (this->statements_ == NULL
+      Location gloc = (this->statements_ == NULL
                              ? loc
                              : this->statements_->end_location());
       b->add_statement(Statement::make_goto_unnamed_statement(break_label,
@@ -3684,6 +4052,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.
@@ -3729,7 +4122,8 @@ Type_case_clauses::check_duplicates() const
 // BREAK_LABEL is the label at the end of the type switch.
 
 void
-Type_case_clauses::lower(Block* b, Temporary_statement* descriptor_temp,
+Type_case_clauses::lower(Type* switch_val_type, Block* b,
+                        Temporary_statement* descriptor_temp,
                         Unnamed_label* break_label) const
 {
   const Type_case_clause* default_case = NULL;
@@ -3740,7 +4134,8 @@ Type_case_clauses::lower(Block* b, Temporary_statement* descriptor_temp,
        ++p)
     {
       if (!p->is_default())
-       p->lower(b, descriptor_temp, break_label, &stmts_label);
+       p->lower(switch_val_type, b, descriptor_temp, break_label,
+                &stmts_label);
       else
        {
          // We are generating a series of tests, which means that we
@@ -3748,10 +4143,22 @@ Type_case_clauses::lower(Block* b, Temporary_statement* descriptor_temp,
          default_case = &*p;
        }
     }
-  gcc_assert(stmts_label == NULL);
+  go_assert(stmts_label == NULL);
 
   if (default_case != NULL)
-    default_case->lower(b, descriptor_temp, break_label, NULL);
+    default_case->lower(switch_val_type, 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.
@@ -3778,9 +4185,10 @@ 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();
+  const Location loc = this->location();
 
   if (this->clauses_ != NULL)
     this->clauses_->check_duplicates();
@@ -3791,71 +4199,41 @@ Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
                    ? this->var_->var_value()->type()
                    : this->expr_->type());
 
+  if (val_type->interface_type() == NULL)
+    {
+      if (!val_type->is_error())
+       this->report_error(_("cannot type switch on non-interface value"));
+      return Statement::make_error_statement(loc);
+    }
+
   // var descriptor_temp DESCRIPTOR_TYPE
   Type* descriptor_type = Type::make_type_descriptor_ptr_type();
   Temporary_statement* descriptor_temp =
     Statement::make_temporary(descriptor_type, NULL, loc);
   b->add_statement(descriptor_temp);
 
-  if (val_type->interface_type() == NULL)
-    {
-      // Doing a type switch on a non-interface type.  Should we issue
-      // a warning for this case?
-      Expression* lhs = Expression::make_temporary_reference(descriptor_temp,
-                                                            loc);
-      Expression* rhs;
-      if (val_type->is_nil_type())
-       rhs = Expression::make_nil(loc);
-      else
-       {
-         if (val_type->is_abstract())
-           val_type = val_type->make_non_abstract_type();
-         rhs = Expression::make_type_descriptor(val_type, loc);
-       }
-      Statement* s = Statement::make_assignment(lhs, rhs, loc);
-      b->add_statement(s);
-    }
+  // descriptor_temp = ifacetype(val_temp) FIXME: This should be
+  // inlined.
+  bool is_empty = val_type->interface_type()->is_empty();
+  Expression* ref;
+  if (this->var_ == NULL)
+    ref = this->expr_;
   else
-    {
-      const source_location bloc = BUILTINS_LOCATION;
-
-      // func {efacetype,ifacetype}(*interface) *descriptor
-      // FIXME: This should be inlined.
-      Typed_identifier_list* param_types = new Typed_identifier_list();
-      param_types->push_back(Typed_identifier("i", val_type, bloc));
-      Typed_identifier_list* ret_types = new Typed_identifier_list();
-      ret_types->push_back(Typed_identifier("", descriptor_type, bloc));
-      Function_type* fntype = Type::make_function_type(NULL, param_types,
-                                                      ret_types, bloc);
-      bool is_empty = val_type->interface_type()->is_empty();
-      const char* fnname = is_empty ? "efacetype" : "ifacetype";
-      Named_object* fn =
-       Named_object::make_function_declaration(fnname, NULL, fntype, bloc);
-      const char* asm_name = (is_empty
-                             ? "runtime.efacetype"
-                             : "runtime.ifacetype");
-      fn->func_declaration_value()->set_asm_name(asm_name);
-
-      // descriptor_temp = ifacetype(val_temp)
-      Expression* func = Expression::make_func_reference(fn, NULL, loc);
-      Expression_list* params = new Expression_list();
-      Expression* ref;
-      if (this->var_ == NULL)
-       ref = this->expr_;
-      else
-       ref = Expression::make_var_reference(this->var_, loc);
-      params->push_back(ref);
-      Expression* call = Expression::make_call(func, params, false, loc);
-      Expression* lhs = Expression::make_temporary_reference(descriptor_temp,
-                                                            loc);
-      Statement* s = Statement::make_assignment(lhs, call, loc);
-      b->add_statement(s);
-    }
+    ref = Expression::make_var_reference(this->var_, loc);
+  Expression* call = Runtime::make_call((is_empty
+                                        ? Runtime::EFACETYPE
+                                        : Runtime::IFACETYPE),
+                                       loc, 1, ref);
+  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);
 
   if (this->clauses_ != NULL)
-    this->clauses_->lower(b, descriptor_temp, this->break_label());
+    this->clauses_->lower(val_type, b, descriptor_temp, this->break_label());
 
-  Statement* s = Statement::make_unnamed_label_statement(this->break_label_);
+  s = Statement::make_unnamed_label_statement(this->break_label_);
   b->add_statement(s);
 
   return Statement::make_block_statement(b, loc);
@@ -3872,11 +4250,30 @@ 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*
 Statement::make_type_switch_statement(Named_object* var, Expression* expr,
-                                     source_location location)
+                                     Location location)
 {
   return new Type_switch_statement(var, expr, location);
 }
@@ -3937,30 +4334,129 @@ Send_statement::do_check_types(Gogo*)
     }
 }
 
-// Get a tree for a send statement.
+// Convert a send statement to the backend representation.
 
-tree
-Send_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Send_statement::do_get_backend(Translate_context* context)
 {
-  tree channel = this->channel_->get_tree(context);
-  tree val = this->val_->get_tree(context);
-  if (channel == error_mark_node || val == error_mark_node)
-    return error_mark_node;
+  Location loc = this->location();
+
   Channel_type* channel_type = this->channel_->type()->channel_type();
-  val = Expression::convert_for_assignment(context,
-                                          channel_type->element_type(),
-                                          this->val_->type(),
-                                          val,
-                                          this->location());
-  return Gogo::send_on_channel(channel, val, true, this->for_select_,
-                              this->location());
+  Type* element_type = channel_type->element_type();
+  Expression* val = Expression::make_cast(element_type, this->val_, loc);
+
+  bool is_small;
+  bool can_take_address;
+  switch (element_type->base()->classification())
+    {
+    case Type::TYPE_BOOLEAN:
+    case Type::TYPE_INTEGER:
+    case Type::TYPE_FUNCTION:
+    case Type::TYPE_POINTER:
+    case Type::TYPE_MAP:
+    case Type::TYPE_CHANNEL:
+      is_small = true;
+      can_take_address = false;
+      break;
+
+    case Type::TYPE_FLOAT:
+    case Type::TYPE_COMPLEX:
+    case Type::TYPE_STRING:
+    case Type::TYPE_INTERFACE:
+      is_small = false;
+      can_take_address = false;
+      break;
+
+    case Type::TYPE_STRUCT:
+      is_small = false;
+      can_take_address = true;
+      break;
+
+    case Type::TYPE_ARRAY:
+      is_small = false;
+      can_take_address = !element_type->is_slice_type();
+      break;
+
+    default:
+    case Type::TYPE_ERROR:
+    case Type::TYPE_VOID:
+    case Type::TYPE_SINK:
+    case Type::TYPE_NIL:
+    case Type::TYPE_NAMED:
+    case Type::TYPE_FORWARD:
+      go_assert(saw_errors());
+      return context->backend()->error_statement();
+    }
+
+  // Only try to take the address of a variable.  We have already
+  // moved variables to the heap, so this should not cause that to
+  // happen unnecessarily.
+  if (can_take_address
+      && val->var_expression() == NULL
+      && val->temporary_reference_expression() == NULL)
+    can_take_address = false;
+
+  Expression* td = Expression::make_type_descriptor(this->channel_->type(),
+                                                   loc);
+
+  Runtime::Function code;
+  Bstatement* btemp = NULL;
+  if (is_small)
+      {
+       // Type is small enough to handle as uint64.
+       code = Runtime::SEND_SMALL;
+       val = Expression::make_unsafe_cast(Type::lookup_integer_type("uint64"),
+                                          val, loc);
+      }
+  else if (can_take_address)
+    {
+      // Must pass address of value.  The function doesn't change the
+      // value, so just take its address directly.
+      code = Runtime::SEND_BIG;
+      val = Expression::make_unary(OPERATOR_AND, val, loc);
+    }
+  else
+    {
+      // Must pass address of value, but the value is small enough
+      // that it might be in registers.  Copy value into temporary
+      // variable to take address.
+      code = Runtime::SEND_BIG;
+      Temporary_statement* temp = Statement::make_temporary(element_type,
+                                                           val, loc);
+      Expression* ref = Expression::make_temporary_reference(temp, loc);
+      val = Expression::make_unary(OPERATOR_AND, ref, loc);
+      btemp = temp->get_backend(context);
+    }
+
+  Expression* call = Runtime::make_call(code, loc, 3, td, this->channel_, val);
+
+  context->gogo()->lower_expression(context->function(), NULL, &call);
+  Bexpression* bcall = tree_to_expr(call->get_tree(context));
+  Bstatement* s = context->backend()->expression_statement(bcall);
+
+  if (btemp == NULL)
+    return s;
+  else
+    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*
 Statement::make_send_statement(Expression* channel, Expression* val,
-                              source_location location)
+                              Location location)
 {
   return new Send_statement(channel, val, location);
 }
@@ -4000,118 +4496,178 @@ Select_clauses::Select_clause::traverse(Traverse* traverse)
   return TRAVERSE_CONTINUE;
 }
 
-// Lowering.  Here we pull out the channel and the send values, to
-// enforce the order of evaluation.  We also add explicit send and
-// receive statements to the clauses.
+// Lowering.  We call a function to register this clause, and arrange
+// to set any variables in any receive clause.
 
 void
 Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
-                                    Block* b)
+                                    Block* b, Temporary_statement* sel)
 {
+  Location loc = this->location_;
+
+  Expression* selref = Expression::make_temporary_reference(sel, loc);
+
+  mpz_t ival;
+  mpz_init_set_ui(ival, this->index_);
+  Expression* index_expr = Expression::make_integer(&ival, NULL, loc);
+  mpz_clear(ival);
+
   if (this->is_default_)
     {
-      gcc_assert(this->channel_ == NULL && this->val_ == NULL);
+      go_assert(this->channel_ == NULL && this->val_ == NULL);
+      this->lower_default(b, selref, index_expr);
       this->is_lowered_ = true;
       return;
     }
 
-  source_location loc = this->location_;
-
   // Evaluate the channel before the select statement.
   Temporary_statement* channel_temp = Statement::make_temporary(NULL,
                                                                this->channel_,
                                                                loc);
   b->add_statement(channel_temp);
-  this->channel_ = Expression::make_temporary_reference(channel_temp, loc);
+  Expression* chanref = Expression::make_temporary_reference(channel_temp,
+                                                            loc);
+
+  if (this->is_send_)
+    this->lower_send(b, selref, chanref, index_expr);
+  else
+    this->lower_recv(gogo, function, b, selref, chanref, index_expr);
 
-  // If this is a send clause, evaluate the value to send before the
-  // select statement.
-  Temporary_statement* val_temp = NULL;
-  if (this->is_send_ && !this->val_->is_constant())
+  // Now all references should be handled through the statements, not
+  // through here.
+  this->is_lowered_ = true;
+  this->val_ = NULL;
+  this->var_ = NULL;
+}
+
+// Lower a default clause in a select statement.
+
+void
+Select_clauses::Select_clause::lower_default(Block* b, Expression* selref,
+                                            Expression* index_expr)
+{
+  Location loc = this->location_;
+  Expression* call = Runtime::make_call(Runtime::SELECTDEFAULT, loc, 2, selref,
+                                       index_expr);
+  b->add_statement(Statement::make_statement(call, true));
+}
+
+// Lower a send clause in a select statement.
+
+void
+Select_clauses::Select_clause::lower_send(Block* b, Expression* selref,
+                                         Expression* chanref,
+                                         Expression* index_expr)
+{
+  Location loc = this->location_;
+
+  Channel_type* ct = this->channel_->type()->channel_type();
+  if (ct == NULL)
+    return;
+
+  Type* valtype = ct->element_type();
+
+  // Note that copying the value to a temporary here means that we
+  // evaluate the send values in the required order.
+  Temporary_statement* val = Statement::make_temporary(valtype, this->val_,
+                                                      loc);
+  b->add_statement(val);
+
+  Expression* valref = Expression::make_temporary_reference(val, loc);
+  Expression* valaddr = Expression::make_unary(OPERATOR_AND, valref, loc);
+
+  Expression* call = Runtime::make_call(Runtime::SELECTSEND, loc, 4, selref,
+                                       chanref, valaddr, index_expr);
+  b->add_statement(Statement::make_statement(call, true));
+}
+
+// Lower a receive clause in a select statement.
+
+void
+Select_clauses::Select_clause::lower_recv(Gogo* gogo, Named_object* function,
+                                         Block* b, Expression* selref,
+                                         Expression* chanref,
+                                         Expression* index_expr)
+{
+  Location loc = this->location_;
+
+  Channel_type* ct = this->channel_->type()->channel_type();
+  if (ct == NULL)
+    return;
+
+  Type* valtype = ct->element_type();
+  Temporary_statement* val = Statement::make_temporary(valtype, NULL, loc);
+  b->add_statement(val);
+
+  Expression* valref = Expression::make_temporary_reference(val, loc);
+  Expression* valaddr = Expression::make_unary(OPERATOR_AND, valref, loc);
+
+  Temporary_statement* closed_temp = NULL;
+
+  Expression* call;
+  if (this->closed_ == NULL && this->closedvar_ == NULL)
+    call = Runtime::make_call(Runtime::SELECTRECV, loc, 4, selref, chanref,
+                             valaddr, index_expr);
+  else
     {
-      val_temp = Statement::make_temporary(NULL, this->val_, loc);
-      b->add_statement(val_temp);
+      closed_temp = Statement::make_temporary(Type::lookup_bool_type(), NULL,
+                                             loc);
+      b->add_statement(closed_temp);
+      Expression* cref = Expression::make_temporary_reference(closed_temp,
+                                                             loc);
+      Expression* caddr = Expression::make_unary(OPERATOR_AND, cref, loc);
+      call = Runtime::make_call(Runtime::SELECTRECV2, loc, 5, selref, chanref,
+                               valaddr, caddr, index_expr);
     }
 
-  // Add the send or receive before the rest of the statements if any.
-  Block *init = new Block(b, loc);
-  Expression* ref = Expression::make_temporary_reference(channel_temp, loc);
-  if (this->is_send_)
+  b->add_statement(Statement::make_statement(call, true));
+
+  // If the block of statements is executed, arrange for the received
+  // value to move from VAL to the place where the statements expect
+  // it.
+
+  Block* init = NULL;
+
+  if (this->var_ != NULL)
     {
-      Expression* ref2;
-      if (val_temp == NULL)
-       ref2 = this->val_;
-      else
-       ref2 = Expression::make_temporary_reference(val_temp, loc);
-      Send_statement* send = Statement::make_send_statement(ref, ref2, loc);
-      send->set_for_select();
-      init->add_statement(send);
+      go_assert(this->val_ == NULL);
+      valref = Expression::make_temporary_reference(val, loc);
+      this->var_->var_value()->set_init(valref);
+      this->var_->var_value()->clear_type_from_chan_element();
     }
-  else if (this->closed_ != NULL && !this->closed_->is_sink_expression())
+  else if (this->val_ != NULL && !this->val_->is_sink_expression())
     {
-      gcc_assert(this->var_ == NULL && this->closedvar_ == NULL);
-      if (this->val_ == NULL)
-       this->val_ = Expression::make_sink(loc);
-      Statement* s = Statement::make_tuple_receive_assignment(this->val_,
-                                                             this->closed_,
-                                                             ref, true, loc);
-      init->add_statement(s);
+      init = new Block(b, loc);
+      valref = Expression::make_temporary_reference(val, loc);
+      init->add_statement(Statement::make_assignment(this->val_, valref, loc));
     }
-  else if (this->closedvar_ != NULL)
+
+  if (this->closedvar_ != NULL)
     {
-      gcc_assert(this->val_ == NULL);
-      Expression* val;
-      if (this->var_ == NULL)
-       val = Expression::make_sink(loc);
-      else
-       val = Expression::make_var_reference(this->var_, loc);
-      Expression* closed = Expression::make_var_reference(this->closedvar_,
-                                                         loc);
-      Statement* s = Statement::make_tuple_receive_assignment(val, closed, ref,
-                                                             true, loc);
-      // We have to put S in STATEMENTS_, because that is where the
-      // variables are declared.
-      gcc_assert(this->statements_ != NULL);
-      this->statements_->add_statement_at_front(s);
-      // We have to lower STATEMENTS_ again, to lower the tuple
-      // receive assignment we just added.
-      gogo->lower_block(function, this->statements_);
+      go_assert(this->closed_ == NULL);
+      Expression* cref = Expression::make_temporary_reference(closed_temp,
+                                                             loc);
+      this->closedvar_->var_value()->set_init(cref);
     }
-  else
+  else if (this->closed_ != NULL && !this->closed_->is_sink_expression())
     {
-      Receive_expression* recv = Expression::make_receive(ref, loc);
-      recv->set_for_select();
-      if (this->val_ != NULL)
-       {
-         gcc_assert(this->var_ == NULL);
-         init->add_statement(Statement::make_assignment(this->val_, recv,
-                                                        loc));
-       }
-      else if (this->var_ != NULL)
-       {
-         this->var_->var_value()->set_init(recv);
-         this->var_->var_value()->clear_type_from_chan_element();
-       }
-      else
-       {
-         init->add_statement(Statement::make_statement(recv));
-       }
+      if (init == NULL)
+       init = new Block(b, loc);
+      Expression* cref = Expression::make_temporary_reference(closed_temp,
+                                                             loc);
+      init->add_statement(Statement::make_assignment(this->closed_, cref,
+                                                    loc));
     }
 
-  // Lower any statements we just created.
-  gogo->lower_block(function, init);
-
-  if (this->statements_ != NULL)
-    init->add_statement(Statement::make_block_statement(this->statements_,
-                                                       loc));
-
-  this->statements_ = init;
+  if (init != NULL)
+    {
+      gogo->lower_block(function, init);
 
-  // Now all references should be handled through the statements, not
-  // through here.
-  this->is_lowered_ = true;
-  this->val_ = NULL;
-  this->var_ = NULL;
+      if (this->statements_ != NULL)
+       init->add_statement(Statement::make_block_statement(this->statements_,
+                                                           loc));
+      this->statements_ = init;
+    }
 }
 
 // Determine types.
@@ -4119,11 +4675,32 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
 void
 Select_clauses::Select_clause::determine_types()
 {
-  gcc_assert(this->is_lowered_);
+  go_assert(this->is_lowered_);
   if (this->statements_ != NULL)
     this->statements_->determine_types();
 }
 
+// Check types.
+
+void
+Select_clauses::Select_clause::check_types()
+{
+  if (this->is_default_)
+    return;
+
+  Channel_type* ct = this->channel_->type()->channel_type();
+  if (ct == NULL)
+    {
+      error_at(this->channel_->location(), "expected channel");
+      return;
+    }
+
+  if (this->is_send_ && !ct->may_send())
+    error_at(this->location(), "invalid send on receive-only channel");
+  else if (!this->is_send_ && !ct->may_receive())
+    error_at(this->location(), "invalid receive on send-only channel");
+}
+
 // Whether this clause may fall through to the statement which follows
 // the overall select statement.
 
@@ -4135,14 +4712,58 @@ Select_clauses::Select_clause::may_fall_through() const
   return this->statements_->may_fall_through();
 }
 
-// Return a tree for the statements to execute.
+// Return the backend representation for the statements to execute.
 
-tree
-Select_clauses::Select_clause::get_statements_tree(Translate_context* context)
+Bstatement*
+Select_clauses::Select_clause::get_statements_backend(
+    Translate_context* context)
 {
   if (this->statements_ == NULL)
-    return NULL_TREE;
-  return this->statements_->get_tree(context);
+    return NULL;
+  Bblock* bblock = this->statements_->get_backend(context);
+  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() << " <- " ;
+         if (this->val_ != NULL)
+           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.
@@ -4167,12 +4788,13 @@ Select_clauses::traverse(Traverse* traverse)
 // receive statements to the clauses.
 
 void
-Select_clauses::lower(Gogo* gogo, Named_object* function, Block* b)
+Select_clauses::lower(Gogo* gogo, Named_object* function, Block* b,
+                     Temporary_statement* sel)
 {
   for (Clauses::iterator p = this->clauses_.begin();
        p != this->clauses_.end();
        ++p)
-    p->lower(gogo, function, b);
+    p->lower(gogo, function, b, sel);
 }
 
 // Determine types.
@@ -4186,6 +4808,17 @@ Select_clauses::determine_types()
     p->determine_types();
 }
 
+// Check types.
+
+void
+Select_clauses::check_types()
+{
+  for (Clauses::iterator p = this->clauses_.begin();
+       p != this->clauses_.end();
+       ++p)
+    p->check_types();
+}
+
 // Return whether these select clauses fall through to the statement
 // following the overall select statement.
 
@@ -4200,197 +4833,76 @@ Select_clauses::may_fall_through() const
   return false;
 }
 
-// Return a tree.  We build a call to
-//   size_t __go_select(size_t count, _Bool has_default,
-//                      channel* channels, _Bool* is_send)
-//
-// There are COUNT entries in the CHANNELS and IS_SEND arrays.  The
-// value in the IS_SEND array is true for send, false for receive.
-// __go_select returns an integer from 0 to COUNT, inclusive.  A
-// return of 0 means that the default case should be run; this only
-// happens if HAS_DEFAULT is non-zero.  Otherwise the number indicates
-// the case to run.
-
-// FIXME: This doesn't handle channels which send interface types
-// where the receiver has a static type which matches that interface.
+// Convert to the backend representation.  We have already accumulated
+// all the select information.  Now we call selectgo, which will
+// return the index of the clause to execute.
 
-tree
-Select_clauses::get_tree(Translate_context* context,
-                        Unnamed_label *break_label,
-                        source_location location)
+Bstatement*
+Select_clauses::get_backend(Translate_context* context,
+                           Temporary_statement* sel,
+                           Unnamed_label *break_label,
+                           Location location)
 {
   size_t count = this->clauses_.size();
-  VEC(constructor_elt, gc)* chan_init = VEC_alloc(constructor_elt, gc, count);
-  VEC(constructor_elt, gc)* is_send_init = VEC_alloc(constructor_elt, gc,
-                                                    count);
-  Select_clause* default_clause = NULL;
-  tree final_stmt_list = NULL_TREE;
-  tree channel_type_tree = NULL_TREE;
+  std::vector<std::vector<Bexpression*> > cases(count);
+  std::vector<Bstatement*> clauses(count);
 
-  size_t i = 0;
+  int i = 0;
   for (Clauses::iterator p = this->clauses_.begin();
        p != this->clauses_.end();
-       ++p)
-    {
-      if (p->is_default())
-       {
-         default_clause = &*p;
-         --count;
-         continue;
-       }
-
-      if (p->channel()->type()->channel_type() == NULL)
-       {
-         // We should have given an error in the send or receive
-         // statement we created via lowering.
-         gcc_assert(saw_errors());
-         return error_mark_node;
-       }
-
-      tree channel_tree = p->channel()->get_tree(context);
-      if (channel_tree == error_mark_node)
-       return error_mark_node;
-      channel_type_tree = TREE_TYPE(channel_tree);
-
-      constructor_elt* elt = VEC_quick_push(constructor_elt, chan_init, NULL);
-      elt->index = build_int_cstu(sizetype, i);
-      elt->value = channel_tree;
-
-      elt = VEC_quick_push(constructor_elt, is_send_init, NULL);
-      elt->index = build_int_cstu(sizetype, i);
-      elt->value = p->is_send() ? boolean_true_node : boolean_false_node;
-
-      ++i;
+       ++p, ++i)
+    {
+      int index = p->index();
+      mpz_t ival;
+      mpz_init_set_ui(ival, index);
+      Expression* index_expr = Expression::make_integer(&ival, NULL, location);
+      mpz_clear(ival);
+      cases[i].push_back(tree_to_expr(index_expr->get_tree(context)));
+
+      Bstatement* s = p->get_statements_backend(context);
+      Location gloc = (p->statements() == NULL
+                      ? p->location()
+                      : p->statements()->end_location());
+      Bstatement* g = break_label->get_goto(context, gloc);
+
+      if (s == NULL)
+       clauses[i] = g;
+      else
+       clauses[i] = context->backend()->compound_statement(s, g);
     }
-  gcc_assert(i == count);
 
-  if (i == 0 && default_clause != NULL)
-    {
-      // There is only a default clause.
-      gcc_assert(final_stmt_list == NULL_TREE);
-      tree stmt_list = NULL_TREE;
-      append_to_statement_list(default_clause->get_statements_tree(context),
-                              &stmt_list);
-      Bstatement* ldef = break_label->get_definition(context);
-      append_to_statement_list(statement_to_tree(ldef), &stmt_list);
-      return stmt_list;
-    }
+  Expression* selref = Expression::make_temporary_reference(sel, location);
+  Expression* call = Runtime::make_call(Runtime::SELECTGO, location, 1,
+                                       selref);
+  context->gogo()->lower_expression(context->function(), NULL, &call);
+  Bexpression* bcall = tree_to_expr(call->get_tree(context));
 
-  tree pointer_chan_type_tree = (channel_type_tree == NULL_TREE
-                                ? ptr_type_node
-                                : build_pointer_type(channel_type_tree));
-  tree chans_arg;
-  tree pointer_boolean_type_tree = build_pointer_type(boolean_type_node);
-  tree is_sends_arg;
+  if (count == 0)
+    return context->backend()->expression_statement(bcall);
 
-  if (i == 0)
-    {
-      chans_arg = fold_convert_loc(location, pointer_chan_type_tree,
-                                  null_pointer_node);
-      is_sends_arg = fold_convert_loc(location, pointer_boolean_type_tree,
-                                     null_pointer_node);
-    }
-  else
-    {
-      tree index_type_tree = build_index_type(size_int(count - 1));
-      tree chan_array_type_tree = build_array_type(channel_type_tree,
-                                                  index_type_tree);
-      tree chan_constructor = build_constructor(chan_array_type_tree,
-                                               chan_init);
-      tree chan_var = create_tmp_var(chan_array_type_tree, "CHAN");
-      DECL_IGNORED_P(chan_var) = 0;
-      DECL_INITIAL(chan_var) = chan_constructor;
-      DECL_SOURCE_LOCATION(chan_var) = location;
-      TREE_ADDRESSABLE(chan_var) = 1;
-      tree decl_expr = build1(DECL_EXPR, void_type_node, chan_var);
-      SET_EXPR_LOCATION(decl_expr, location);
-      append_to_statement_list(decl_expr, &final_stmt_list);
-
-      tree is_send_array_type_tree = build_array_type(boolean_type_node,
-                                                     index_type_tree);
-      tree is_send_constructor = build_constructor(is_send_array_type_tree,
-                                                  is_send_init);
-      tree is_send_var = create_tmp_var(is_send_array_type_tree, "ISSEND");
-      DECL_IGNORED_P(is_send_var) = 0;
-      DECL_INITIAL(is_send_var) = is_send_constructor;
-      DECL_SOURCE_LOCATION(is_send_var) = location;
-      TREE_ADDRESSABLE(is_send_var) = 1;
-      decl_expr = build1(DECL_EXPR, void_type_node, is_send_var);
-      SET_EXPR_LOCATION(decl_expr, location);
-      append_to_statement_list(decl_expr, &final_stmt_list);
-
-      chans_arg = fold_convert_loc(location, pointer_chan_type_tree,
-                                  build_fold_addr_expr_loc(location,
-                                                           chan_var));
-      is_sends_arg = fold_convert_loc(location, pointer_boolean_type_tree,
-                                     build_fold_addr_expr_loc(location,
-                                                              is_send_var));
-    }
-
-  static tree select_fndecl;
-  tree call = Gogo::call_builtin(&select_fndecl,
-                                location,
-                                "__go_select",
-                                4,
-                                sizetype,
-                                sizetype,
-                                size_int(count),
-                                boolean_type_node,
-                                (default_clause == NULL
-                                 ? boolean_false_node
-                                 : boolean_true_node),
-                                pointer_chan_type_tree,
-                                chans_arg,
-                                pointer_boolean_type_tree,
-                                is_sends_arg);
-  if (call == error_mark_node)
-    return error_mark_node;
-
-  tree stmt_list = NULL_TREE;
-
-  if (default_clause != NULL)
-    this->add_clause_tree(context, 0, default_clause, break_label, &stmt_list);
-
-  i = 1;
-  for (Clauses::iterator p = this->clauses_.begin();
-       p != this->clauses_.end();
-       ++p)
-    {
-      if (!p->is_default())
-       {
-         this->add_clause_tree(context, i, &*p, break_label, &stmt_list);
-         ++i;
-       }
-    }
+  std::vector<Bstatement*> statements;
+  statements.reserve(2);
 
-  Bstatement* ldef = break_label->get_definition(context);
-  append_to_statement_list(statement_to_tree(ldef), &stmt_list);
+  Bstatement* switch_stmt = context->backend()->switch_statement(bcall,
+                                                                cases,
+                                                                clauses,
+                                                                location);
+  statements.push_back(switch_stmt);
 
-  tree switch_stmt = build3(SWITCH_EXPR, sizetype, call, stmt_list, NULL_TREE);
-  SET_EXPR_LOCATION(switch_stmt, location);
-  append_to_statement_list(switch_stmt, &final_stmt_list);
+  Bstatement* ldef = break_label->get_definition(context);
+  statements.push_back(ldef);
 
-  return final_stmt_list;
+  return context->backend()->statement_list(statements);
 }
-
-// Add the tree for CLAUSE to STMT_LIST.
+// Dump the AST representation for select clauses.
 
 void
-Select_clauses::add_clause_tree(Translate_context* context, int case_index,
-                               Select_clause* clause,
-                               Unnamed_label* bottom_label, tree* stmt_list)
-{
-  tree label = create_artificial_label(clause->location());
-  append_to_statement_list(build3(CASE_LABEL_EXPR, void_type_node,
-                                 build_int_cst(sizetype, case_index),
-                                 NULL_TREE, label),
-                          stmt_list);
-  append_to_statement_list(clause->get_statements_tree(context), stmt_list);
-  source_location gloc = (clause->statements() == NULL
-                         ? clause->location()
-                         : clause->statements()->end_location());
-  Bstatement* g = bottom_label->get_goto(context, gloc);
-  append_to_statement_list(statement_to_tree(g), stmt_list);
+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.
@@ -4413,30 +4925,63 @@ 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;
-  Block* b = new Block(enclosing, this->location());
-  this->clauses_->lower(gogo, function, b);
+
+  Location loc = this->location();
+
+  Block* b = new Block(enclosing, loc);
+
+  go_assert(this->sel_ == NULL);
+
+  mpz_t ival;
+  mpz_init_set_ui(ival, this->clauses_->size());
+  Expression* size_expr = Expression::make_integer(&ival, NULL, loc);
+  mpz_clear(ival);
+
+  Expression* call = Runtime::make_call(Runtime::NEWSELECT, loc, 1, size_expr);
+
+  this->sel_ = Statement::make_temporary(NULL, call, loc);
+  b->add_statement(this->sel_);
+
+  this->clauses_->lower(gogo, function, b, this->sel_);
   this->is_lowered_ = true;
   b->add_statement(this);
-  return Statement::make_block_statement(b, this->location());
+
+  return Statement::make_block_statement(b, loc);
 }
 
-// Return the tree for a select statement.
+// Return the backend representation for a select statement.
 
-tree
-Select_statement::do_get_tree(Translate_context* context)
+Bstatement*
+Select_statement::do_get_backend(Translate_context* context)
 {
-  return this->clauses_->get_tree(context, this->break_label(),
-                                 this->location());
+  return this->clauses_->get_backend(context, this->sel_, this->break_label(),
+                                    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*
-Statement::make_select_statement(source_location location)
+Statement::make_select_statement(Location location)
 {
   return new Select_statement(location);
 }
@@ -4470,10 +5015,11 @@ 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();
+  Location loc = this->location();
 
   Block* b = new Block(enclosing, this->location());
   if (this->init_ != NULL)
@@ -4497,7 +5043,7 @@ For_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
                                      this->statements_->start_location());
   b->add_statement(s);
 
-  source_location end_loc = this->statements_->end_location();
+  Location end_loc = this->statements_->end_location();
 
   Unnamed_label* cont = this->continue_label_;
   if (cont != NULL)
@@ -4517,7 +5063,7 @@ For_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
     {
       b->add_statement(Statement::make_unnamed_label_statement(entry));
 
-      source_location cond_loc = this->cond_->location();
+      Location cond_loc = this->cond_->location();
       Block* then_block = new Block(b, cond_loc);
       s = Statement::make_goto_unnamed_statement(top, cond_loc);
       then_block->add_statement(s);
@@ -4562,16 +5108,53 @@ void
 For_statement::set_break_continue_labels(Unnamed_label* break_label,
                                         Unnamed_label* continue_label)
 {
-  gcc_assert(this->break_label_ == NULL && this->continue_label_ == NULL);
+  go_assert(this->break_label_ == NULL && this->continue_label_ == NULL);
   this->break_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*
 Statement::make_for_statement(Block* init, Expression* cond, Block* post,
-                             source_location location)
+                             Location location)
 {
   return new For_statement(init, cond, post, location);
 }
@@ -4601,12 +5184,13 @@ 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
       && range_type->points_to()->array_type() != NULL
-      && !range_type->points_to()->is_open_array_type())
+      && !range_type->points_to()->is_slice_type())
     range_type = range_type->points_to();
 
   Type* index_type;
@@ -4619,7 +5203,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing)
   else if (range_type->is_string_type())
     {
       index_type = Type::lookup_integer_type("int");
-      value_type = index_type;
+      value_type = Type::lookup_integer_type("int32");
     }
   else if (range_type->map_type() != NULL)
     {
@@ -4640,11 +5224,11 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing)
   else
     {
       this->report_error(_("range clause must have "
-                          "array, slice, setring, map, or channel type"));
+                          "array, slice, string, map, or channel type"));
       return Statement::make_error_statement(this->location());
     }
 
-  source_location loc = this->location();
+  Location loc = this->location();
   Block* temp_block = new Block(enclosing, loc);
 
   Named_object* range_object = NULL;
@@ -4656,6 +5240,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing)
     {
       range_temp = Statement::make_temporary(NULL, this->range_, loc);
       temp_block->add_statement(range_temp);
+      this->range_ = NULL;
     }
 
   Temporary_statement* index_temp = Statement::make_temporary(index_type,
@@ -4684,7 +5269,11 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing)
   //           original statements
   //   }
 
-  if (range_type->array_type() != NULL)
+  if (range_type->is_slice_type())
+    this->lower_range_slice(gogo, temp_block, body, range_object, range_temp,
+                           index_temp, value_temp, &init, &cond, &iter_init,
+                           &post);
+  else if (range_type->array_type() != NULL)
     this->lower_range_array(gogo, temp_block, body, range_object, range_temp,
                            index_temp, value_temp, &init, &cond, &iter_init,
                            &post);
@@ -4701,7 +5290,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing)
                              index_temp, value_temp, &init, &cond, &iter_init,
                              &post);
   else
-    gcc_unreachable();
+    go_unreachable();
 
   if (iter_init != NULL)
     body->add_statement(Statement::make_block_statement(iter_init, loc));
@@ -4746,7 +5335,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing)
 Expression*
 For_range_statement::make_range_ref(Named_object* range_object,
                                    Temporary_statement* range_temp,
-                                   source_location loc)
+                                   Location loc)
 {
   if (range_object != NULL)
     return Expression::make_var_reference(range_object, loc);
@@ -4760,17 +5349,17 @@ For_range_statement::make_range_ref(Named_object* range_object,
 Expression*
 For_range_statement::call_builtin(Gogo* gogo, const char* funcname,
                                  Expression* arg,
-                                 source_location loc)
+                                 Location loc)
 {
   Named_object* no = gogo->lookup_global(funcname);
-  gcc_assert(no != NULL && no->is_function_declaration());
+  go_assert(no != NULL && no->is_function_declaration());
   Expression* func = Expression::make_func_reference(no, NULL, loc);
   Expression_list* params = new Expression_list();
   params->push_back(arg);
   return Expression::make_call(func, params, false, loc);
 }
 
-// Lower a for range over an array or slice.
+// Lower a for range over an array.
 
 void
 For_range_statement::lower_range_array(Gogo* gogo,
@@ -4785,7 +5374,7 @@ For_range_statement::lower_range_array(Gogo* gogo,
                                       Block** piter_init,
                                       Block** ppost)
 {
-  source_location loc = this->location();
+  Location loc = this->location();
 
   // The loop we generate:
   //   len_temp := len(range)
@@ -4814,8 +5403,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;
@@ -4841,8 +5432,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);
     }
@@ -4852,8 +5444,110 @@ For_range_statement::lower_range_array(Gogo* gogo,
   //   index_temp++
 
   Block* post = new Block(enclosing, loc);
+  tref = Expression::make_temporary_reference(index_temp, loc);
+  tref->set_is_lvalue();
+  s = Statement::make_inc_statement(tref);
+  post->add_statement(s);
+  *ppost = post;
+}
+
+// Lower a for range over a slice.
+
+void
+For_range_statement::lower_range_slice(Gogo* gogo,
+                                      Block* enclosing,
+                                      Block* body_block,
+                                      Named_object* range_object,
+                                      Temporary_statement* range_temp,
+                                      Temporary_statement* index_temp,
+                                      Temporary_statement* value_temp,
+                                      Block** pinit,
+                                      Expression** pcond,
+                                      Block** piter_init,
+                                      Block** ppost)
+{
+  Location loc = this->location();
+
+  // The loop we generate:
+  //   for_temp := range
+  //   len_temp := len(for_temp)
+  //   for index_temp = 0; index_temp < len_temp; index_temp++ {
+  //           value_temp = for_temp[index_temp]
+  //           index = index_temp
+  //           value = value_temp
+  //           original body
+  //   }
+  //
+  // Using for_temp means that we don't need to check bounds when
+  // fetching range_temp[index_temp].
+
+  // Set *PINIT to
+  //   range_temp := range
+  //   var len_temp int
+  //   len_temp = len(range_temp)
+  //   index_temp = 0
+
+  Block* init = new Block(enclosing, loc);
+
+  Expression* ref = this->make_range_ref(range_object, range_temp, loc);
+  Temporary_statement* for_temp = Statement::make_temporary(NULL, ref, loc);
+  init->add_statement(for_temp);
+
+  ref = Expression::make_temporary_reference(for_temp, loc);
+  Expression* len_call = this->call_builtin(gogo, "len", ref, loc);
+  Temporary_statement* len_temp = Statement::make_temporary(index_temp->type(),
+                                                           len_call, loc);
+  init->add_statement(len_temp);
+
+  mpz_t zval;
+  mpz_init_set_ui(zval, 0UL);
+  Expression* zexpr = Expression::make_integer(&zval, NULL, loc);
+  mpz_clear(zval);
+
+  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;
+
+  // Set *PCOND to
+  //   index_temp < len_temp
+
   ref = Expression::make_temporary_reference(index_temp, loc);
-  s = Statement::make_inc_statement(ref);
+  Expression* ref2 = Expression::make_temporary_reference(len_temp, loc);
+  Expression* lt = Expression::make_binary(OPERATOR_LT, ref, ref2, loc);
+
+  *pcond = lt;
+
+  // Set *PITER_INIT to
+  //   value_temp = range[index_temp]
+
+  Block* iter_init = NULL;
+  if (value_temp != NULL)
+    {
+      iter_init = new Block(body_block, loc);
+
+      ref = Expression::make_temporary_reference(for_temp, loc);
+      Expression* ref2 = Expression::make_temporary_reference(index_temp, loc);
+      Expression* index = Expression::make_index(ref, ref2, NULL, 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);
+    }
+  *piter_init = iter_init;
+
+  // Set *PPOST to
+  //   index_temp++
+
+  Block* post = new Block(enclosing, loc);
+  tref = Expression::make_temporary_reference(index_temp, loc);
+  tref->set_is_lvalue();
+  s = Statement::make_inc_statement(tref);
   post->add_statement(s);
   *ppost = post;
 }
@@ -4861,7 +5555,7 @@ For_range_statement::lower_range_array(Gogo* gogo,
 // Lower a for range over a string.
 
 void
-For_range_statement::lower_range_string(Gogo* gogo,
+For_range_statement::lower_range_string(Gogo*,
                                        Block* enclosing,
                                        Block* body_block,
                                        Named_object* range_object,
@@ -4873,7 +5567,7 @@ For_range_statement::lower_range_string(Gogo* gogo,
                                        Block** piter_init,
                                        Block** ppost)
 {
-  source_location loc = this->location();
+  Location loc = this->location();
 
   // The loop we generate:
   //   var next_index_temp int
@@ -4901,7 +5595,9 @@ For_range_statement::lower_range_string(Gogo* 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);
@@ -4922,78 +5618,30 @@ For_range_statement::lower_range_string(Gogo* gogo,
 
   Block* iter_init = new Block(body_block, loc);
 
-  Named_object* no;
-  if (value_temp == NULL)
-    {
-      static Named_object* stringiter;
-      if (stringiter == NULL)
-       {
-         source_location bloc = BUILTINS_LOCATION;
-         Type* int_type = gogo->lookup_global("int")->type_value();
-
-         Typed_identifier_list* params = new Typed_identifier_list();
-         params->push_back(Typed_identifier("s", Type::make_string_type(),
-                                            bloc));
-         params->push_back(Typed_identifier("k", int_type, bloc));
-
-         Typed_identifier_list* results = new Typed_identifier_list();
-         results->push_back(Typed_identifier("", int_type, bloc));
-
-         Function_type* fntype = Type::make_function_type(NULL, params,
-                                                          results, bloc);
-         stringiter = Named_object::make_function_declaration("stringiter",
-                                                              NULL, fntype,
-                                                              bloc);
-         const char* n = "runtime.stringiter";
-         stringiter->func_declaration_value()->set_asm_name(n);
-       }
-      no = stringiter;
-    }
-  else
-    {
-      static Named_object* stringiter2;
-      if (stringiter2 == NULL)
-       {
-         source_location bloc = BUILTINS_LOCATION;
-         Type* int_type = gogo->lookup_global("int")->type_value();
-
-         Typed_identifier_list* params = new Typed_identifier_list();
-         params->push_back(Typed_identifier("s", Type::make_string_type(),
-                                            bloc));
-         params->push_back(Typed_identifier("k", int_type, bloc));
-
-         Typed_identifier_list* results = new Typed_identifier_list();
-         results->push_back(Typed_identifier("", int_type, bloc));
-         results->push_back(Typed_identifier("", int_type, bloc));
-
-         Function_type* fntype = Type::make_function_type(NULL, params,
-                                                          results, bloc);
-         stringiter2 = Named_object::make_function_declaration("stringiter",
-                                                               NULL, fntype,
-                                                               bloc);
-         const char* n = "runtime.stringiter2";
-         stringiter2->func_declaration_value()->set_asm_name(n);
-       }
-      no = stringiter2;
-    }
-
-  Expression* func = Expression::make_func_reference(no, NULL, loc);
-  Expression_list* params = new Expression_list();
-  params->push_back(this->make_range_ref(range_object, range_temp, loc));
-  params->push_back(Expression::make_temporary_reference(index_temp, loc));
-  Call_expression* call = Expression::make_call(func, params, false, loc);
+  Expression* p1 = this->make_range_ref(range_object, range_temp, loc);
+  Expression* p2 = Expression::make_temporary_reference(index_temp, loc);
+  Call_expression* call = Runtime::make_call((value_temp == NULL
+                                             ? Runtime::STRINGITER
+                                             : Runtime::STRINGITER2),
+                                            loc, 2, p1, p2);
 
   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));
@@ -5022,7 +5670,9 @@ For_range_statement::lower_range_string(Gogo* 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);
 
@@ -5033,7 +5683,7 @@ For_range_statement::lower_range_string(Gogo* gogo,
 // Lower a for range over a map.
 
 void
-For_range_statement::lower_range_map(Gogo* gogo,
+For_range_statement::lower_range_map(Gogo*,
                                     Block* enclosing,
                                     Block* body_block,
                                     Named_object* range_object,
@@ -5045,7 +5695,7 @@ For_range_statement::lower_range_map(Gogo* gogo,
                                     Block** piter_init,
                                     Block** ppost)
 {
-  source_location loc = this->location();
+  Location loc = this->location();
 
   // The runtime uses a struct to handle ranges over a map.  The
   // struct is four pointers long.  The first pointer is NULL when we
@@ -5066,42 +5716,16 @@ For_range_statement::lower_range_map(Gogo* gogo,
 
   Block* init = new Block(enclosing, loc);
 
-  const unsigned long map_iteration_size = 4;
-
-  mpz_t ival;
-  mpz_init_set_ui(ival, map_iteration_size);
-  Expression* iexpr = Expression::make_integer(&ival, NULL, loc);
-  mpz_clear(ival);
-
-  Type* byte_type = gogo->lookup_global("byte")->type_value();
-  Type* ptr_type = Type::make_pointer_type(byte_type);
-
-  Type* map_iteration_type = Type::make_array_type(ptr_type, iexpr);
-  Type* map_iteration_ptr = Type::make_pointer_type(map_iteration_type);
-
+  Type* map_iteration_type = Runtime::map_iteration_type();
   Temporary_statement* hiter = Statement::make_temporary(map_iteration_type,
                                                         NULL, loc);
   init->add_statement(hiter);
 
-  source_location bloc = BUILTINS_LOCATION;
-  Typed_identifier_list* param_types = new Typed_identifier_list();
-  param_types->push_back(Typed_identifier("map", this->range_->type(), bloc));
-  param_types->push_back(Typed_identifier("it", map_iteration_ptr, bloc));
-  Function_type* fntype = Type::make_function_type(NULL, param_types, NULL,
-                                                  bloc);
-
-  Named_object* mapiterinit =
-    Named_object::make_function_declaration("mapiterinit", NULL, fntype, bloc);
-  const char* n = "runtime.mapiterinit";
-  mapiterinit->func_declaration_value()->set_asm_name(n);
-
-  Expression* func = Expression::make_func_reference(mapiterinit, NULL, loc);
-  Expression_list* params = new Expression_list();
-  params->push_back(this->make_range_ref(range_object, range_temp, loc));
+  Expression* p1 = this->make_range_ref(range_object, range_temp, loc);
   Expression* ref = Expression::make_temporary_reference(hiter, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
-  Expression* call = Expression::make_call(func, params, false, loc);
-  init->add_statement(Statement::make_statement(call));
+  Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
+  Expression* call = Runtime::make_call(Runtime::MAPITERINIT, loc, 2, p1, p2);
+  init->add_statement(Statement::make_statement(call, true));
 
   *pinit = init;
 
@@ -5130,35 +5754,19 @@ For_range_statement::lower_range_map(Gogo* gogo,
 
   Block* iter_init = new Block(body_block, loc);
 
-  param_types = new Typed_identifier_list();
-  param_types->push_back(Typed_identifier("hiter", map_iteration_ptr, bloc));
-  Type* pkey_type = Type::make_pointer_type(index_temp->type());
-  param_types->push_back(Typed_identifier("key", pkey_type, bloc));
-  if (value_temp != NULL)
-    {
-      Type* pval_type = Type::make_pointer_type(value_temp->type());
-      param_types->push_back(Typed_identifier("val", pval_type, bloc));
-    }
-  fntype = Type::make_function_type(NULL, param_types, NULL, bloc);
-  n = value_temp == NULL ? "mapiter1" : "mapiter2";
-  Named_object* mapiter = Named_object::make_function_declaration(n, NULL,
-                                                                 fntype, bloc);
-  n = value_temp == NULL ? "runtime.mapiter1" : "runtime.mapiter2";
-  mapiter->func_declaration_value()->set_asm_name(n);
-
-  func = Expression::make_func_reference(mapiter, NULL, loc);
-  params = new Expression_list();
   ref = Expression::make_temporary_reference(hiter, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
+  p1 = Expression::make_unary(OPERATOR_AND, ref, loc);
   ref = Expression::make_temporary_reference(index_temp, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
-  if (value_temp != NULL)
+  p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
+  if (value_temp == NULL)
+    call = Runtime::make_call(Runtime::MAPITER1, loc, 2, p1, p2);
+  else
     {
       ref = Expression::make_temporary_reference(value_temp, loc);
-      params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
+      Expression* p3 = Expression::make_unary(OPERATOR_AND, ref, loc);
+      call = Runtime::make_call(Runtime::MAPITER2, loc, 3, p1, p2, p3);
     }
-  call = Expression::make_call(func, params, false, loc);
-  iter_init->add_statement(Statement::make_statement(call));
+  iter_init->add_statement(Statement::make_statement(call, true));
 
   *piter_init = iter_init;
 
@@ -5167,25 +5775,10 @@ For_range_statement::lower_range_map(Gogo* gogo,
 
   Block* post = new Block(enclosing, loc);
 
-  static Named_object* mapiternext;
-  if (mapiternext == NULL)
-    {
-      param_types = new Typed_identifier_list();
-      param_types->push_back(Typed_identifier("it", map_iteration_ptr, bloc));
-      fntype = Type::make_function_type(NULL, param_types, NULL, bloc);
-      mapiternext = Named_object::make_function_declaration("mapiternext",
-                                                           NULL, fntype,
-                                                           bloc);
-      const char* n = "runtime.mapiternext";
-      mapiternext->func_declaration_value()->set_asm_name(n);
-    }
-
-  func = Expression::make_func_reference(mapiternext, NULL, loc);
-  params = new Expression_list();
   ref = Expression::make_temporary_reference(hiter, loc);
-  params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
-  call = Expression::make_call(func, params, false, loc);
-  post->add_statement(Statement::make_statement(call));
+  p1 = Expression::make_unary(OPERATOR_AND, ref, loc);
+  call = Runtime::make_call(Runtime::MAPITERNEXT, loc, 1, p1);
+  post->add_statement(Statement::make_statement(call, true));
 
   *ppost = post;
 }
@@ -5205,9 +5798,9 @@ For_range_statement::lower_range_channel(Gogo*,
                                         Block** piter_init,
                                         Block** ppost)
 {
-  gcc_assert(value_temp == NULL);
+  go_assert(value_temp == NULL);
 
-  source_location loc = this->location();
+  Location loc = this->location();
 
   // The loop we generate:
   //   for {
@@ -5238,10 +5831,14 @@ 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);
+                                                         loc);
   iter_init->add_statement(s);
 
   Block* then_block = new Block(iter_init, loc);
@@ -5276,13 +5873,45 @@ 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*
 Statement::make_for_range_statement(Expression* index_var,
                                    Expression* value_var,
                                    Expression* range,
-                                   source_location location)
+                                   Location location)
 {
   return new For_range_statement(index_var, value_var, range, location);
 }