OSDN Git Service

compiler: fix method finalization of unnamed structs.
[pf3gnuchains/gcc-fork.git] / gcc / go / gofrontend / gogo.cc
index f1404cc..ee68b69 100644 (file)
 #include "statements.h"
 #include "expressions.h"
 #include "dataflow.h"
+#include "runtime.h"
 #include "import.h"
 #include "export.h"
+#include "backend.h"
 #include "gogo.h"
 
 // Class Gogo.
 
-Gogo::Gogo(int int_type_size, int pointer_size)
-  : package_(NULL),
+Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size,
+           int pointer_size)
+  : backend_(backend),
+    linemap_(linemap),
+    package_(NULL),
     functions_(),
     globals_(new Bindings(NULL)),
     imports_(),
     imported_unsafe_(false),
     packages_(),
-    map_descriptors_(NULL),
-    type_descriptor_decls_(NULL),
     init_functions_(),
     need_init_fn_(false),
     init_fn_name_(),
     imported_init_fns_(),
     unique_prefix_(),
     unique_prefix_specified_(false),
-    interface_types_()
+    verify_types_(),
+    interface_types_(),
+    specific_type_functions_(),
+    specific_type_functions_are_written_(false),
+    named_types_are_converted_(false)
 {
-  const source_location loc = BUILTINS_LOCATION;
+  const Location loc = Linemap::predeclared_location();
 
   Named_type* uint8_type = Type::make_integer_type("uint8", true, 8,
                                                   RUNTIME_TYPE_KIND_UINT8);
@@ -52,8 +59,9 @@ Gogo::Gogo(int int_type_size, int pointer_size)
                                               RUNTIME_TYPE_KIND_INT8));
   this->add_named_type(Type::make_integer_type("int16", false,  16,
                                               RUNTIME_TYPE_KIND_INT16));
-  this->add_named_type(Type::make_integer_type("int32", false,  32,
-                                              RUNTIME_TYPE_KIND_INT32));
+  Named_type* int32_type = Type::make_integer_type("int32", false,  32,
+                                                  RUNTIME_TYPE_KIND_INT32);
+  this->add_named_type(int32_type);
   this->add_named_type(Type::make_integer_type("int64", false,  64,
                                               RUNTIME_TYPE_KIND_INT64));
 
@@ -76,21 +84,39 @@ Gogo::Gogo(int int_type_size, int pointer_size)
                                                 RUNTIME_TYPE_KIND_INT);
   this->add_named_type(int_type);
 
-  // "byte" is an alias for "uint8".  Construct a Named_object which
-  // points to UINT8_TYPE.  Note that this breaks the normal pairing
-  // in which a Named_object points to a Named_type which points back
-  // to the same Named_object.
-  Named_object* byte_type = this->declare_type("byte", loc);
-  byte_type->set_type_value(uint8_type);
-
   this->add_named_type(Type::make_integer_type("uintptr", true,
                                               pointer_size,
                                               RUNTIME_TYPE_KIND_UINTPTR));
 
+  // "byte" is an alias for "uint8".
+  uint8_type->integer_type()->set_is_byte();
+  Named_object* byte_type = Named_object::make_type("byte", NULL, uint8_type,
+                                                   loc);
+  this->add_named_type(byte_type->type_value());
+
+  // "rune" is an alias for "int32".
+  int32_type->integer_type()->set_is_rune();
+  Named_object* rune_type = Named_object::make_type("rune", NULL, int32_type,
+                                                   loc);
+  this->add_named_type(rune_type->type_value());
+
   this->add_named_type(Type::make_named_bool_type());
 
   this->add_named_type(Type::make_named_string_type());
 
+  // "error" is interface { Error() string }.
+  {
+    Typed_identifier_list *methods = new Typed_identifier_list;
+    Typed_identifier_list *results = new Typed_identifier_list;
+    results->push_back(Typed_identifier("", Type::lookup_string_type(), loc));
+    Type *method_type = Type::make_function_type(NULL, NULL, results, loc);
+    methods->push_back(Typed_identifier("Error", method_type, loc));
+    Interface_type *error_iface = Type::make_interface_type(methods, loc);
+    error_iface->finalize_methods();
+    Named_type *error_type = Named_object::make_type("error", NULL, error_iface, loc)->type_value();
+    this->add_named_type(error_type);
+  }
+
   this->globals_->add_constant(Typed_identifier("true",
                                                Type::make_boolean_type(),
                                                loc),
@@ -151,7 +177,7 @@ Gogo::Gogo(int int_type_size, int pointer_size)
   print_type->set_is_builtin();
   this->globals_->add_function_declaration("println", NULL, print_type, loc);
 
-  Type *empty = Type::make_interface_type(NULL, loc);
+  Type *empty = Type::make_empty_interface_type(loc);
   Typed_identifier_list* panic_parms = new Typed_identifier_list();
   panic_parms->push_back(Typed_identifier("e", empty, loc));
   Function_type *panic_type = Type::make_function_type(NULL, panic_parms,
@@ -172,15 +198,6 @@ Gogo::Gogo(int int_type_size, int pointer_size)
   close_type->set_is_builtin();
   this->globals_->add_function_declaration("close", NULL, close_type, loc);
 
-  Typed_identifier_list* closed_result = new Typed_identifier_list();
-  closed_result->push_back(Typed_identifier("", Type::lookup_bool_type(),
-                                           loc));
-  Function_type* closed_type = Type::make_function_type(NULL, NULL,
-                                                       closed_result, loc);
-  closed_type->set_is_varargs();
-  closed_type->set_is_builtin();
-  this->globals_->add_function_declaration("closed", NULL, closed_type, loc);
-
   Typed_identifier_list* copy_result = new Typed_identifier_list();
   copy_result->push_back(Typed_identifier("", int_type, loc));
   Function_type* copy_type = Type::make_function_type(NULL, NULL,
@@ -209,13 +226,10 @@ Gogo::Gogo(int int_type_size, int pointer_size)
   imag_type->set_is_builtin();
   this->globals_->add_function_declaration("imag", NULL, imag_type, loc);
 
-  this->define_builtin_function_trees();
-
-  // Declare "init", to ensure that it is not defined with parameters
-  // or return values.
-  this->declare_function("init",
-                        Type::make_function_type(NULL, NULL, NULL, loc),
-                        loc);
+  Function_type* delete_type = Type::make_function_type(NULL, NULL, NULL, loc);
+  delete_type->set_is_varargs();
+  delete_type->set_is_builtin();
+  this->globals_->add_function_declaration("delete", NULL, delete_type, loc);
 }
 
 // Munge name for use in an error message.
@@ -231,7 +245,7 @@ Gogo::message_name(const std::string& name)
 const std::string&
 Gogo::package_name() const
 {
-  gcc_assert(this->package_ != NULL);
+  go_assert(this->package_ != NULL);
   return this->package_->name();
 }
 
@@ -239,7 +253,7 @@ Gogo::package_name() const
 
 void
 Gogo::set_package_name(const std::string& package_name,
-                      source_location location)
+                      Location location)
 {
   if (this->package_ != NULL && this->package_->name() != package_name)
     {
@@ -264,10 +278,10 @@ Gogo::set_package_name(const std::string& package_name,
     {
       // Declare "main" as a function which takes no parameters and
       // returns no value.
+      Location uloc = Linemap::unknown_location();
       this->declare_function("main",
-                            Type::make_function_type(NULL, NULL, NULL,
-                                                     BUILTINS_LOCATION),
-                            BUILTINS_LOCATION);
+                            Type::make_function_type (NULL, NULL, NULL, uloc),
+                            uloc);
     }
 }
 
@@ -286,7 +300,7 @@ void
 Gogo::import_package(const std::string& filename,
                     const std::string& local_name,
                     bool is_local_name_exported,
-                    source_location location)
+                    Location location)
 {
   if (filename == "unsafe")
     {
@@ -307,12 +321,7 @@ Gogo::import_package(const std::string& filename,
          ln = package->name();
          is_ln_exported = Lex::is_exported_name(ln);
        }
-      if (ln != ".")
-       {
-         ln = this->pack_hidden_name(ln, is_ln_exported);
-         this->package_->bindings()->add_package(ln, package);
-       }
-      else
+      if (ln == ".")
        {
          Bindings* bindings = package->bindings();
          for (Bindings::const_declarations_iterator p =
@@ -321,6 +330,13 @@ Gogo::import_package(const std::string& filename,
               ++p)
            this->add_named_object(p->second);
        }
+      else if (ln == "_")
+       package->set_uses_sink_alias();
+      else
+       {
+         ln = this->pack_hidden_name(ln, is_ln_exported);
+         this->package_->bindings()->add_package(ln, package);
+       }
       return;
     }
 
@@ -474,8 +490,8 @@ Gogo::lookup(const std::string& name, Named_object** pfunction) const
 Named_object*
 Gogo::lookup_in_block(const std::string& name) const
 {
-  gcc_assert(!this->functions_.empty());
-  gcc_assert(!this->functions_.back().blocks.empty());
+  go_assert(!this->functions_.empty());
+  go_assert(!this->functions_.back().blocks.empty());
   return this->functions_.back().blocks.back()->bindings()->lookup_local(name);
 }
 
@@ -494,7 +510,7 @@ Gogo::add_imported_package(const std::string& real_name,
                           const std::string& alias_arg,
                           bool is_alias_exported,
                           const std::string& unique_prefix,
-                          source_location location,
+                          Location location,
                           bool* padd_to_globals)
 {
   // FIXME: Now that we compile packages as a whole, should we permit
@@ -544,9 +560,9 @@ Gogo::add_imported_package(const std::string& real_name,
 
 Named_object*
 Gogo::add_package(const std::string& real_name, const std::string& alias,
-                 const std::string& unique_prefix, source_location location)
+                 const std::string& unique_prefix, Location location)
 {
-  gcc_assert(this->in_global_scope());
+  go_assert(this->in_global_scope());
 
   // Register the package.  Note that we might have already seen it in
   // an earlier import.
@@ -562,9 +578,9 @@ Gogo::add_package(const std::string& real_name, const std::string& alias,
 Package*
 Gogo::register_package(const std::string& package_name,
                       const std::string& unique_prefix,
-                      source_location location)
+                      Location location)
 {
-  gcc_assert(!unique_prefix.empty() && !package_name.empty());
+  go_assert(!unique_prefix.empty() && !package_name.empty());
   std::string name = unique_prefix + '.' + package_name;
   Package* package = NULL;
   std::pair<Packages::iterator, bool> ins =
@@ -573,17 +589,17 @@ Gogo::register_package(const std::string& package_name,
     {
       // We have seen this package name before.
       package = ins.first->second;
-      gcc_assert(package != NULL);
-      gcc_assert(package->name() == package_name
+      go_assert(package != NULL);
+      go_assert(package->name() == package_name
                 && package->unique_prefix() == unique_prefix);
-      if (package->location() == UNKNOWN_LOCATION)
+      if (Linemap::is_unknown_location(package->location()))
        package->set_location(location);
     }
   else
     {
       // First time we have seen this package name.
       package = new Package(package_name, unique_prefix, location);
-      gcc_assert(ins.first->second == NULL);
+      go_assert(ins.first->second == NULL);
       ins.first->second = package;
     }
 
@@ -594,7 +610,7 @@ Gogo::register_package(const std::string& package_name,
 
 Named_object*
 Gogo::start_function(const std::string& name, Function_type* type,
-                    bool add_method_to_type, source_location location)
+                    bool add_method_to_type, Location location)
 {
   bool at_top_level = this->functions_.empty();
 
@@ -611,8 +627,8 @@ Gogo::start_function(const std::string& name, Function_type* type,
       const Typed_identifier* receiver = type->receiver();
       Variable* this_param = new Variable(receiver->type(), NULL, false,
                                          true, true, location);
-      std::string name = receiver->name();
-      if (name.empty())
+      std::string rname = receiver->name();
+      if (rname.empty() || Gogo::is_sink_name(rname))
        {
          // We need to give receivers a name since they wind up in
          // DECL_ARGUMENTS.  FIXME.
@@ -620,9 +636,9 @@ Gogo::start_function(const std::string& name, Function_type* type,
          char buf[50];
          snprintf(buf, sizeof buf, "r.%u", count);
          ++count;
-         name = buf;
+         rname = buf;
        }
-      block->bindings()->add_variable(name, NULL, this_param);
+      block->bindings()->add_variable(rname, NULL, this_param);
     }
 
   const Typed_identifier_list* parameters = type->parameters();
@@ -638,8 +654,8 @@ Gogo::start_function(const std::string& name, Function_type* type,
          if (is_varargs && p + 1 == parameters->end())
            param->set_is_varargs_parameter();
 
-         std::string name = p->name();
-         if (name.empty() || Gogo::is_sink_name(name))
+         std::string pname = p->name();
+         if (pname.empty() || Gogo::is_sink_name(pname))
            {
              // We need to give parameters a name since they wind up
              // in DECL_ARGUMENTS.  FIXME.
@@ -647,17 +663,34 @@ Gogo::start_function(const std::string& name, Function_type* type,
              char buf[50];
              snprintf(buf, sizeof buf, "p.%u", count);
              ++count;
-             name = buf;
+             pname = buf;
            }
-         block->bindings()->add_variable(name, NULL, param);
+         block->bindings()->add_variable(pname, NULL, param);
        }
     }
 
-  function->create_named_result_variables(this);
+  function->create_result_variables(this);
 
   const std::string* pname;
   std::string nested_name;
-  if (!name.empty())
+  bool is_init = false;
+  if (Gogo::unpack_hidden_name(name) == "init" && !type->is_method())
+    {
+      if ((type->parameters() != NULL && !type->parameters()->empty())
+         || (type->results() != NULL && !type->results()->empty()))
+       error_at(location,
+                "func init must have no arguments and no return values");
+      // There can be multiple "init" functions, so give them each a
+      // different name.
+      static int init_count;
+      char buf[30];
+      snprintf(buf, sizeof buf, ".$init%d", init_count);
+      ++init_count;
+      nested_name = buf;
+      pname = &nested_name;
+      is_init = true;
+    }
+  else if (!name.empty())
     pname = &name;
   else
     {
@@ -699,7 +732,7 @@ Gogo::start_function(const std::string& name, Function_type* type,
        ret = Named_object::make_function(name, NULL, function);
       else
        {
-         gcc_assert(at_top_level);
+         go_assert(at_top_level);
          Type* rtype = type->receiver()->type();
 
          // We want to look through the pointer created by the
@@ -733,14 +766,14 @@ Gogo::start_function(const std::string& name, Function_type* type,
                  Named_object* declared =
                    this->declare_package_type(type_no->name(),
                                               type_no->location());
-                 gcc_assert(declared
+                 go_assert(declared
                             == type_no->unknown_value()->real_named_object());
                }
              ret = rtype->forward_declaration_type()->add_method(name,
                                                                  function);
            }
          else
-           gcc_unreachable();
+           go_unreachable();
        }
       this->package_->bindings()->add_method(ret);
     }
@@ -750,7 +783,7 @@ Gogo::start_function(const std::string& name, Function_type* type,
   of.function = ret;
   of.blocks.push_back(block);
 
-  if (!type->is_method() && Gogo::unpack_hidden_name(name) == "init")
+  if (is_init)
     {
       this->init_functions_.push_back(ret);
       this->need_init_fn_ = true;
@@ -762,10 +795,10 @@ Gogo::start_function(const std::string& name, Function_type* type,
 // Finish compiling a function.
 
 void
-Gogo::finish_function(source_location location)
+Gogo::finish_function(Location location)
 {
   this->finish_block(location);
-  gcc_assert(this->functions_.back().blocks.empty());
+  go_assert(this->functions_.back().blocks.empty());
   this->functions_.pop_back();
 }
 
@@ -774,16 +807,16 @@ Gogo::finish_function(source_location location)
 Named_object*
 Gogo::current_function() const
 {
-  gcc_assert(!this->functions_.empty());
+  go_assert(!this->functions_.empty());
   return this->functions_.back().function;
 }
 
 // Start a new block.
 
 void
-Gogo::start_block(source_location location)
+Gogo::start_block(Location location)
 {
-  gcc_assert(!this->functions_.empty());
+  go_assert(!this->functions_.empty());
   Block* block = new Block(this->current_block(), location);
   this->functions_.back().blocks.push_back(block);
 }
@@ -791,20 +824,28 @@ Gogo::start_block(source_location location)
 // Finish a block.
 
 Block*
-Gogo::finish_block(source_location location)
+Gogo::finish_block(Location location)
 {
-  gcc_assert(!this->functions_.empty());
-  gcc_assert(!this->functions_.back().blocks.empty());
+  go_assert(!this->functions_.empty());
+  go_assert(!this->functions_.back().blocks.empty());
   Block* block = this->functions_.back().blocks.back();
   this->functions_.back().blocks.pop_back();
   block->set_end_location(location);
   return block;
 }
 
+// Add an erroneous name.
+
+Named_object*
+Gogo::add_erroneous_name(const std::string& name)
+{
+  return this->package_->bindings()->add_erroneous_name(name);
+}
+
 // Add an unknown name.
 
 Named_object*
-Gogo::add_unknown_name(const std::string& name, source_location location)
+Gogo::add_unknown_name(const std::string& name, Location location)
 {
   return this->package_->bindings()->add_unknown_name(name, location);
 }
@@ -813,7 +854,7 @@ Gogo::add_unknown_name(const std::string& name, source_location location)
 
 Named_object*
 Gogo::declare_function(const std::string& name, Function_type* type,
-                      source_location location)
+                      Location location)
 {
   if (!type->is_method())
     return this->current_bindings()->add_function_declaration(name, NULL, type,
@@ -838,10 +879,10 @@ Gogo::declare_function(const std::string& name, Function_type* type,
       else if (rtype->forward_declaration_type() != NULL)
        {
          Forward_declaration_type* ftype = rtype->forward_declaration_type();
-         return ftype->add_method_declaration(name, type, location);
+         return ftype->add_method_declaration(name, NULL, type, location);
        }
       else
-       gcc_unreachable();
+       go_unreachable();
     }
 }
 
@@ -849,11 +890,11 @@ Gogo::declare_function(const std::string& name, Function_type* type,
 
 Label*
 Gogo::add_label_definition(const std::string& label_name,
-                          source_location location)
+                          Location location)
 {
-  gcc_assert(!this->functions_.empty());
+  go_assert(!this->functions_.empty());
   Function* func = this->functions_.back().function->func_value();
-  Label* label = func->add_label_definition(label_name, location);
+  Label* label = func->add_label_definition(this, label_name, location);
   this->add_statement(Statement::make_label_statement(label, location));
   return label;
 }
@@ -861,11 +902,21 @@ Gogo::add_label_definition(const std::string& label_name,
 // Add a label reference.
 
 Label*
-Gogo::add_label_reference(const std::string& label_name)
+Gogo::add_label_reference(const std::string& label_name,
+                         Location location, bool issue_goto_errors)
 {
-  gcc_assert(!this->functions_.empty());
+  go_assert(!this->functions_.empty());
   Function* func = this->functions_.back().function->func_value();
-  return func->add_label_reference(label_name);
+  return func->add_label_reference(this, label_name, location,
+                                  issue_goto_errors);
+}
+
+// Return the current binding state.
+
+Bindings_snapshot*
+Gogo::bindings_snapshot(Location location)
+{
+  return new Bindings_snapshot(this->current_block(), location);
 }
 
 // Add a statement.
@@ -873,7 +924,7 @@ Gogo::add_label_reference(const std::string& label_name)
 void
 Gogo::add_statement(Statement* statement)
 {
-  gcc_assert(!this->functions_.empty()
+  go_assert(!this->functions_.empty()
             && !this->functions_.back().blocks.empty());
   this->functions_.back().blocks.back()->add_statement(statement);
 }
@@ -881,9 +932,9 @@ Gogo::add_statement(Statement* statement)
 // Add a block.
 
 void
-Gogo::add_block(Block* block, source_location location)
+Gogo::add_block(Block* block, Location location)
 {
-  gcc_assert(!this->functions_.empty()
+  go_assert(!this->functions_.empty()
             && !this->functions_.back().blocks.empty());
   Statement* statement = Statement::make_block_statement(block, location);
   this->functions_.back().blocks.back()->add_statement(statement);
@@ -901,7 +952,7 @@ Gogo::add_constant(const Typed_identifier& tid, Expression* expr,
 // Add a type.
 
 void
-Gogo::add_type(const std::string& name, Type* type, source_location location)
+Gogo::add_type(const std::string& name, Type* type, Location location)
 {
   Named_object* no = this->current_bindings()->add_type(name, NULL, type,
                                                        location);
@@ -914,14 +965,14 @@ Gogo::add_type(const std::string& name, Type* type, source_location location)
 void
 Gogo::add_named_type(Named_type* type)
 {
-  gcc_assert(this->in_global_scope());
+  go_assert(this->in_global_scope());
   this->current_bindings()->add_named_type(type);
 }
 
 // Declare a type.
 
 Named_object*
-Gogo::declare_type(const std::string& name, source_location location)
+Gogo::declare_type(const std::string& name, Location location)
 {
   Bindings* bindings = this->current_bindings();
   Named_object* no = bindings->add_type_declaration(name, NULL, location);
@@ -936,11 +987,21 @@ Gogo::declare_type(const std::string& name, source_location location)
 // Declare a type at the package level.
 
 Named_object*
-Gogo::declare_package_type(const std::string& name, source_location location)
+Gogo::declare_package_type(const std::string& name, Location location)
 {
   return this->package_->bindings()->add_type_declaration(name, NULL, location);
 }
 
+// Declare a function at the package level.
+
+Named_object*
+Gogo::declare_package_function(const std::string& name, Function_type* type,
+                              Location location)
+{
+  return this->package_->bindings()->add_function_declaration(name, NULL, type,
+                                                             location);
+}
+
 // Define a type which was already declared.
 
 void
@@ -983,6 +1044,23 @@ Gogo::add_named_object(Named_object* no)
   this->current_bindings()->add_named_object(no);
 }
 
+// Mark all local variables used.  This is used when some types of
+// parse error occur.
+
+void
+Gogo::mark_locals_used()
+{
+  for (Open_functions::iterator pf = this->functions_.begin();
+       pf != this->functions_.end();
+       ++pf)
+    {
+      for (std::vector<Block*>::iterator pb = pf->blocks.begin();
+          pb != pf->blocks.end();
+          ++pb)
+       (*pb)->bindings()->mark_locals_used();
+    }
+}
+
 // Record that we've seen an interface type.
 
 void
@@ -1042,9 +1120,9 @@ Gogo::define_global_names()
            {
              error_at(no->location(), "expected type");
              Type* errtype = Type::make_error_type();
-             Named_object* err = Named_object::make_type("error", NULL,
-                                                         errtype,
-                                                         BUILTINS_LOCATION);
+             Named_object* err =
+                Named_object::make_type("erroneous_type", NULL, errtype,
+                                        Linemap::predeclared_location());
              no->set_type_value(err->type_value());
            }
        }
@@ -1079,6 +1157,121 @@ Gogo::clear_file_scope()
     }
 }
 
+// Queue up a type specific function for later writing.  These are
+// written out in write_specific_type_functions, called after the
+// parse tree is lowered.
+
+void
+Gogo::queue_specific_type_function(Type* type, Named_type* name,
+                                  const std::string& hash_name,
+                                  Function_type* hash_fntype,
+                                  const std::string& equal_name,
+                                  Function_type* equal_fntype)
+{
+  go_assert(!this->specific_type_functions_are_written_);
+  go_assert(!this->in_global_scope());
+  Specific_type_function* tsf = new Specific_type_function(type, name,
+                                                          hash_name,
+                                                          hash_fntype,
+                                                          equal_name,
+                                                          equal_fntype);
+  this->specific_type_functions_.push_back(tsf);
+}
+
+// Look for types which need specific hash or equality functions.
+
+class Specific_type_functions : public Traverse
+{
+ public:
+  Specific_type_functions(Gogo* gogo)
+    : Traverse(traverse_types),
+      gogo_(gogo)
+  { }
+
+  int
+  type(Type*);
+
+ private:
+  Gogo* gogo_;
+};
+
+int
+Specific_type_functions::type(Type* t)
+{
+  Named_object* hash_fn;
+  Named_object* equal_fn;
+  switch (t->classification())
+    {
+    case Type::TYPE_NAMED:
+      {
+       Named_type* nt = t->named_type();
+       if (!t->compare_is_identity(this->gogo_) && t->is_comparable())
+         t->type_functions(this->gogo_, nt, NULL, NULL, &hash_fn, &equal_fn);
+
+       // If this is a struct type, we don't want to make functions
+       // for the unnamed struct.
+       Type* rt = nt->real_type();
+       if (rt->struct_type() == NULL)
+         {
+           if (Type::traverse(rt, this) == TRAVERSE_EXIT)
+             return TRAVERSE_EXIT;
+         }
+       else
+         {
+           // If this type is defined in another package, then we don't
+           // need to worry about the unexported fields.
+           bool is_defined_elsewhere = nt->named_object()->package() != NULL;
+           const Struct_field_list* fields = rt->struct_type()->fields();
+           for (Struct_field_list::const_iterator p = fields->begin();
+                p != fields->end();
+                ++p)
+             {
+               if (is_defined_elsewhere
+                   && Gogo::is_hidden_name(p->field_name()))
+                 continue;
+               if (Type::traverse(p->type(), this) == TRAVERSE_EXIT)
+                 return TRAVERSE_EXIT;
+             }
+         }
+
+       return TRAVERSE_SKIP_COMPONENTS;
+      }
+
+    case Type::TYPE_STRUCT:
+    case Type::TYPE_ARRAY:
+      if (!t->compare_is_identity(this->gogo_) && t->is_comparable())
+       t->type_functions(this->gogo_, NULL, NULL, NULL, &hash_fn, &equal_fn);
+      break;
+
+    default:
+      break;
+    }
+
+  return TRAVERSE_CONTINUE;
+}
+
+// Write out type specific functions.
+
+void
+Gogo::write_specific_type_functions()
+{
+  Specific_type_functions stf(this);
+  this->traverse(&stf);
+
+  while (!this->specific_type_functions_.empty())
+    {
+      Specific_type_function* tsf = this->specific_type_functions_.back();
+      this->specific_type_functions_.pop_back();
+      tsf->type->write_specific_type_functions(this, tsf->name,
+                                              tsf->hash_name,
+                                              tsf->hash_fntype,
+                                              tsf->equal_name,
+                                              tsf->equal_fntype);
+      delete tsf;
+    }
+  this->specific_type_functions_are_written_ = true;
+}
+
 // Traverse the tree.
 
 void
@@ -1101,6 +1294,15 @@ Gogo::traverse(Traverse* traverse)
     }
 }
 
+// Add a type to verify.  This is used for types of sink variables, in
+// order to give appropriate error messages.
+
+void
+Gogo::add_type_to_verify(Type* type)
+{
+  this->verify_types_.push_back(type);
+}
+
 // Traversal class used to verify types.
 
 class Verify_types : public Traverse
@@ -1119,11 +1321,6 @@ class Verify_types : public Traverse
 int
 Verify_types::type(Type* t)
 {
-  // Don't verify types defined in other packages.
-  Named_type* nt = t->named_type();
-  if (nt != NULL && nt->named_object()->package() != NULL)
-    return TRAVERSE_SKIP_COMPONENTS;
-
   if (!t->verify())
     return TRAVERSE_SKIP_COMPONENTS;
   return TRAVERSE_CONTINUE;
@@ -1136,6 +1333,12 @@ Gogo::verify_types()
 {
   Verify_types traverse;
   this->traverse(&traverse);
+
+  for (std::vector<Type*>::iterator p = this->verify_types_.begin();
+       p != this->verify_types_.end();
+       ++p)
+    (*p)->verify();
+  this->verify_types_.clear();
 }
 
 // Traversal class used to lower parse tree.
@@ -1149,9 +1352,13 @@ class Lower_parse_tree : public Traverse
               | traverse_functions
               | traverse_statements
               | traverse_expressions),
-      gogo_(gogo), function_(function), iota_value_(-1)
+      gogo_(gogo), function_(function), iota_value_(-1), inserter_()
   { }
 
+  void
+  set_inserter(const Statement_inserter* inserter)
+  { this->inserter_ = *inserter; }
+
   int
   variable(Named_object*);
 
@@ -1174,18 +1381,44 @@ class Lower_parse_tree : public Traverse
   Named_object* function_;
   // Value to use for the predeclared constant iota.
   int iota_value_;
+  // Current statement inserter for use by expressions.
+  Statement_inserter inserter_;
 };
 
-// Lower variables.  We handle variables specially to break loops in
-// which a variable initialization expression refers to itself.  The
-// loop breaking is in lower_init_expression.
+// Lower variables.
 
 int
 Lower_parse_tree::variable(Named_object* no)
 {
-  if (no->is_variable())
-    no->var_value()->lower_init_expression(this->gogo_, this->function_);
-  return TRAVERSE_CONTINUE;
+  if (!no->is_variable())
+    return TRAVERSE_CONTINUE;
+
+  if (no->is_variable() && no->var_value()->is_global())
+    {
+      // Global variables can have loops in their initialization
+      // expressions.  This is handled in lower_init_expression.
+      no->var_value()->lower_init_expression(this->gogo_, this->function_,
+                                            &this->inserter_);
+      return TRAVERSE_CONTINUE;
+    }
+
+  // This is a local variable.  We are going to return
+  // TRAVERSE_SKIP_COMPONENTS here because we want to traverse the
+  // initialization expression when we reach the variable declaration
+  // statement.  However, that means that we need to traverse the type
+  // ourselves.
+  if (no->var_value()->has_type())
+    {
+      Type* type = no->var_value()->type();
+      if (type != NULL)
+       {
+         if (Type::traverse(type, this) == TRAVERSE_EXIT)
+           return TRAVERSE_EXIT;
+       }
+    }
+  go_assert(!no->var_value()->has_pre_init());
+
+  return TRAVERSE_SKIP_COMPONENTS;
 }
 
 // Lower constants.  We handle constants specially so that we can set
@@ -1203,7 +1436,7 @@ Lower_parse_tree::constant(Named_object* no, bool)
     return TRAVERSE_CONTINUE;
   nc->set_lowering();
 
-  gcc_assert(this->iota_value_ == -1);
+  go_assert(this->iota_value_ == -1);
   this->iota_value_ = nc->iota_value();
   nc->traverse_expression(this);
   this->iota_value_ = -1;
@@ -1224,7 +1457,7 @@ Lower_parse_tree::function(Named_object* no)
 {
   no->func_value()->set_closure_type();
 
-  gcc_assert(this->function_ == NULL);
+  go_assert(this->function_ == NULL);
   this->function_ = no;
   int t = no->func_value()->traverse(this);
   this->function_ = NULL;
@@ -1239,27 +1472,44 @@ Lower_parse_tree::function(Named_object* no)
 int
 Lower_parse_tree::statement(Block* block, size_t* pindex, Statement* sorig)
 {
+  // Because we explicitly traverse the statement's contents
+  // ourselves, we want to skip block statements here.  There is
+  // nothing to lower in a block statement.
+  if (sorig->is_block_statement())
+    return TRAVERSE_CONTINUE;
+
+  Statement_inserter hold_inserter(this->inserter_);
+  this->inserter_ = Statement_inserter(block, pindex);
+
   // Lower the expressions first.
   int t = sorig->traverse_contents(this);
   if (t == TRAVERSE_EXIT)
-    return t;
+    {
+      this->inserter_ = hold_inserter;
+      return t;
+    }
 
   // Keep lowering until nothing changes.
   Statement* s = sorig;
   while (true)
     {
-      Statement* snew = s->lower(this->gogo_, block);
+      Statement* snew = s->lower(this->gogo_, this->function_, block,
+                                &this->inserter_);
       if (snew == s)
        break;
       s = snew;
       t = s->traverse_contents(this);
       if (t == TRAVERSE_EXIT)
-       return t;
+       {
+         this->inserter_ = hold_inserter;
+         return t;
+       }
     }
 
   if (s != sorig)
     block->replace_statement(*pindex, s);
 
+  this->inserter_ = hold_inserter;
   return TRAVERSE_SKIP_COMPONENTS;
 }
 
@@ -1278,9 +1528,11 @@ Lower_parse_tree::expression(Expression** pexpr)
     {
       Expression* e = *pexpr;
       Expression* enew = e->lower(this->gogo_, this->function_,
-                                 this->iota_value_);
+                                 &this->inserter_, this->iota_value_);
       if (enew == e)
        break;
+      if (enew->traverse_subexpressions(this) == TRAVERSE_EXIT)
+       return TRAVERSE_EXIT;
       *pexpr = enew;
     }
   return TRAVERSE_SKIP_COMPONENTS;
@@ -1296,12 +1548,25 @@ Gogo::lower_parse_tree()
   this->traverse(&lower_parse_tree);
 }
 
-// Lower an expression.
+// Lower a block.
+
+void
+Gogo::lower_block(Named_object* function, Block* block)
+{
+  Lower_parse_tree lower_parse_tree(this, function);
+  block->traverse(&lower_parse_tree);
+}
+
+// Lower an expression.  INSERTER may be NULL, in which case the
+// expression had better not need to create any temporaries.
 
 void
-Gogo::lower_expression(Named_object* function, Expression** pexpr)
+Gogo::lower_expression(Named_object* function, Statement_inserter* inserter,
+                      Expression** pexpr)
 {
   Lower_parse_tree lower_parse_tree(this, function);
+  if (inserter != NULL)
+    lower_parse_tree.set_inserter(inserter);
   lower_parse_tree.expression(pexpr);
 }
 
@@ -1312,7 +1577,7 @@ Gogo::lower_expression(Named_object* function, Expression** pexpr)
 void
 Gogo::lower_constant(Named_object* no)
 {
-  gcc_assert(no->is_const());
+  go_assert(no->is_const());
   Lower_parse_tree lower(this, NULL);
   lower.constant(no, false);
 }
@@ -1355,7 +1620,8 @@ Finalize_methods::type(Type* t)
        // finalize the methods of the field types, not of the struct
        // type itself.  We don't want to add methods to the struct,
        // since it has a name.
-       Type* rt = t->named_type()->real_type();
+       Named_type* nt = t->named_type();
+       Type* rt = nt->real_type();
        if (rt->classification() != Type::TYPE_STRUCT)
          {
            if (Type::traverse(rt, this) == TRAVERSE_EXIT)
@@ -1367,14 +1633,32 @@ Finalize_methods::type(Type* t)
              return TRAVERSE_EXIT;
          }
 
-       t->named_type()->finalize_methods(this->gogo_);
+       nt->finalize_methods(this->gogo_);
+
+       // If this type is defined in a different package, then finalize the
+       // types of all the methods, since we won't see them otherwise.
+       if (nt->named_object()->package() != NULL && nt->has_any_methods())
+         {
+           const Methods* methods = nt->methods();
+           for (Methods::const_iterator p = methods->begin();
+                p != methods->end();
+                ++p)
+             {
+               if (Type::traverse(p->second->type(), this) == TRAVERSE_EXIT)
+                 return TRAVERSE_EXIT;
+             }
+         }
 
        return TRAVERSE_SKIP_COMPONENTS;
       }
 
     case Type::TYPE_STRUCT:
+      // Traverse the field types first in case there is an embedded
+      // field with methods that the struct should inherit.
+      if (t->struct_type()->traverse_field_types(this) == TRAVERSE_EXIT)
+          return TRAVERSE_EXIT;
       t->struct_type()->finalize_methods(this->gogo_);
-      break;
+      return TRAVERSE_SKIP_COMPONENTS;
 
     default:
       break;
@@ -1454,6 +1738,7 @@ class Check_types_traverse : public Traverse
   Check_types_traverse(Gogo* gogo)
     : Traverse(traverse_variables
               | traverse_constants
+              | traverse_functions
               | traverse_statements
               | traverse_expressions),
       gogo_(gogo)
@@ -1466,6 +1751,9 @@ class Check_types_traverse : public Traverse
   constant(Named_object*, bool);
 
   int
+  function(Named_object*);
+
+  int
   statement(Block*, size_t* pindex, Statement*);
 
   int
@@ -1484,6 +1772,10 @@ Check_types_traverse::variable(Named_object* named_object)
   if (named_object->is_variable())
     {
       Variable* var = named_object->var_value();
+
+      // Give error if variable type is not defined.
+      var->type()->base();
+
       Expression* init = var->init();
       std::string reason;
       if (init != NULL
@@ -1497,6 +1789,15 @@ Check_types_traverse::variable(Named_object* named_object)
                     reason.c_str());
          var->clear_init();
        }
+      else if (!var->is_used()
+              && !var->is_global()
+              && !var->is_parameter()
+              && !var->is_receiver()
+              && !var->type()->is_error()
+              && (init == NULL || !init->is_error_expression())
+              && !Lex::is_invalid_identifier(named_object->name()))
+       error_at(var->location(), "%qs declared and not used",
+                named_object->message_name().c_str());
     }
   return TRAVERSE_CONTINUE;
 }
@@ -1514,7 +1815,9 @@ Check_types_traverse::constant(Named_object* named_object, bool)
       && !ctype->is_boolean_type()
       && !ctype->is_string_type())
     {
-      if (!ctype->is_error_type())
+      if (ctype->is_nil_type())
+       error_at(constant->location(), "const initializer cannot be nil");
+      else if (!ctype->is_error())
        error_at(constant->location(), "invalid constant type");
       constant->set_error();
     }
@@ -1533,6 +1836,16 @@ Check_types_traverse::constant(Named_object* named_object, bool)
   return TRAVERSE_CONTINUE;
 }
 
+// There are no types to check in a function, but this is where we
+// issue warnings about labels which are defined but not referenced.
+
+int
+Check_types_traverse::function(Named_object* no)
+{
+  no->func_value()->check_labels();
+  return TRAVERSE_CONTINUE;
+}
+
 // Check that types are valid in a statement.
 
 int
@@ -1616,7 +1929,7 @@ Find_shortcut::expression(Expression** pexpr)
   Operator op = be->op();
   if (op != OPERATOR_OROR && op != OPERATOR_ANDAND)
     return TRAVERSE_CONTINUE;
-  gcc_assert(this->found_ == NULL);
+  go_assert(this->found_ == NULL);
   this->found_ = pexpr;
   return TRAVERSE_EXIT;
 }
@@ -1729,12 +2042,12 @@ Shortcuts::convert_shortcut(Block* enclosing, Expression** pshortcut)
   Binary_expression* shortcut = (*pshortcut)->binary_expression();
   Expression* left = shortcut->left();
   Expression* right = shortcut->right();
-  source_location loc = shortcut->location();
+  Location loc = shortcut->location();
 
   Block* retblock = new Block(enclosing, loc);
   retblock->set_end_location(loc);
 
-  Temporary_statement* ts = Statement::make_temporary(Type::make_boolean_type(),
+  Temporary_statement* ts = Statement::make_temporary(Type::lookup_bool_type(),
                                                      left, loc);
   retblock->add_statement(ts);
 
@@ -1917,25 +2230,32 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s)
     {
       Expression** pexpr = *p;
 
-      // If the last expression is a send or receive expression, we
-      // may be ignoring the value; we don't want to evaluate it
-      // early.
-      if (p + 1 == find_eval_ordering.end()
-         && ((*pexpr)->classification() == Expression::EXPRESSION_SEND
-             || (*pexpr)->classification() == Expression::EXPRESSION_RECEIVE))
-       break;
-
       // The last expression in a thunk will be the call passed to go
       // or defer, which we must not evaluate early.
       if (is_thunk && p + 1 == find_eval_ordering.end())
        break;
 
-      source_location loc = (*pexpr)->location();
-      Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc);
-      block->insert_statement_before(*pindex, ts);
-      ++*pindex;
+      Location loc = (*pexpr)->location();
+      Statement* s;
+      if ((*pexpr)->call_expression() == NULL
+         || (*pexpr)->call_expression()->result_count() < 2)
+       {
+         Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr,
+                                                             loc);
+         s = ts;
+         *pexpr = Expression::make_temporary_reference(ts, loc);
+       }
+      else
+       {
+         // A call expression which returns multiple results needs to
+         // be handled specially.  We can't create a temporary
+         // because there is no type to give it.  Any actual uses of
+         // the values will be done via Call_result_expressions.
+         s = Statement::make_statement(*pexpr, true);
+       }
 
-      *pexpr = Expression::make_temporary_reference(ts, loc);
+      block->insert_statement_before(*pindex, s);
+      ++*pindex;
     }
 
   if (init != orig_init)
@@ -1958,7 +2278,7 @@ Order_eval::variable(Named_object* no)
     return TRAVERSE_CONTINUE;
 
   Find_eval_ordering find_eval_ordering;
-  init->traverse_subexpressions(&find_eval_ordering);
+  Expression::traverse(&init, &find_eval_ordering);
 
   if (find_eval_ordering.size() <= 1)
     {
@@ -1967,17 +2287,35 @@ Order_eval::variable(Named_object* no)
       return TRAVERSE_SKIP_COMPONENTS;
     }
 
+  Expression* orig_init = init;
+
   for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin();
        p != find_eval_ordering.end();
        ++p)
     {
       Expression** pexpr = *p;
-      source_location loc = (*pexpr)->location();
-      Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc);
-      var->add_preinit_statement(this->gogo_, ts);
-      *pexpr = Expression::make_temporary_reference(ts, loc);
+      Location loc = (*pexpr)->location();
+      Statement* s;
+      if ((*pexpr)->call_expression() == NULL
+         || (*pexpr)->call_expression()->result_count() < 2)
+       {
+         Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr,
+                                                             loc);
+         s = ts;
+         *pexpr = Expression::make_temporary_reference(ts, loc);
+       }
+      else
+       {
+         // A call expression which returns multiple results needs to
+         // be handled specially.
+         s = Statement::make_statement(*pexpr, true);
+       }
+      var->add_preinit_statement(this->gogo_, s);
     }
 
+  if (init != orig_init)
+    var->set_init(init);
+
   return TRAVERSE_SKIP_COMPONENTS;
 }
 
@@ -2038,7 +2376,7 @@ class Build_recover_thunks : public Traverse
 
  private:
   Expression*
-  can_recover_arg(source_location);
+  can_recover_arg(Location);
 
   // General IR.
   Gogo* gogo_;
@@ -2056,7 +2394,7 @@ Build_recover_thunks::function(Named_object* orig_no)
     return TRAVERSE_CONTINUE;
 
   Gogo* gogo = this->gogo_;
-  source_location location = orig_func->location();
+  Location location = orig_func->location();
 
   static int count;
   char buf[50];
@@ -2090,7 +2428,7 @@ Build_recover_thunks::function(Named_object* orig_no)
   ++count;
   std::string can_recover_name = buf;
   new_params->push_back(Typed_identifier(can_recover_name,
-                                        Type::make_boolean_type(),
+                                        Type::lookup_bool_type(),
                                         orig_fntype->location()));
 
   const Typed_identifier_list* orig_results = orig_fntype->results();
@@ -2153,7 +2491,7 @@ Build_recover_thunks::function(Named_object* orig_no)
           ++p)
        {
          Named_object* p_no = gogo->lookup(p->name(), NULL);
-         gcc_assert(p_no != NULL
+         go_assert(p_no != NULL
                     && p_no->is_variable()
                     && p_no->var_value()->is_parameter());
          args->push_back(Expression::make_var_reference(p_no, location));
@@ -2161,11 +2499,16 @@ Build_recover_thunks::function(Named_object* orig_no)
     }
   args->push_back(this->can_recover_arg(location));
 
+  gogo->start_block(location);
+
   Call_expression* call = Expression::make_call(fn, args, false, location);
 
+  // Any varargs call has already been lowered.
+  call->set_varargs_are_lowered();
+
   Statement* s;
   if (orig_fntype->results() == NULL || orig_fntype->results()->empty())
-    s = Statement::make_statement(call);
+    s = Statement::make_statement(call, true);
   else
     {
       Expression_list* vals = new Expression_list();
@@ -2177,12 +2520,18 @@ Build_recover_thunks::function(Named_object* orig_no)
          for (size_t i = 0; i < rc; ++i)
            vals->push_back(Expression::make_call_result(call, i));
        }
-      s = Statement::make_return_statement(new_func->type()->results(),
-                                          vals, location);
+      s = Statement::make_return_statement(vals, location);
     }
   s->determine_types();
   gogo->add_statement(s);
 
+  Block* b = gogo->finish_block(location);
+
+  gogo->add_block(b, location);
+
+  // Lower the call in case it returns multiple results.
+  gogo->lower_block(new_no, b);
+
   gogo->finish_function(location);
 
   // Swap the function bodies and types.
@@ -2198,31 +2547,35 @@ Build_recover_thunks::function(Named_object* orig_no)
       // We changed the receiver to be a regular parameter.  We have
       // to update the binding accordingly in both functions.
       Named_object* orig_rec_no = orig_bindings->lookup_local(receiver_name);
-      gcc_assert(orig_rec_no != NULL
+      go_assert(orig_rec_no != NULL
                 && orig_rec_no->is_variable()
                 && !orig_rec_no->var_value()->is_receiver());
       orig_rec_no->var_value()->set_is_receiver();
 
       const std::string& new_receiver_name(orig_fntype->receiver()->name());
       Named_object* new_rec_no = new_bindings->lookup_local(new_receiver_name);
-      gcc_assert(new_rec_no != NULL
-                && new_rec_no->is_variable()
-                && new_rec_no->var_value()->is_receiver());
-      new_rec_no->var_value()->set_is_not_receiver();
+      if (new_rec_no == NULL)
+       go_assert(saw_errors());
+      else
+       {
+         go_assert(new_rec_no->is_variable()
+                    && new_rec_no->var_value()->is_receiver());
+         new_rec_no->var_value()->set_is_not_receiver();
+       }
     }
 
   // Because we flipped blocks but not types, the can_recover
   // parameter appears in the (now) old bindings as a parameter.
   // Change it to a local variable, whereupon it will be discarded.
   Named_object* can_recover_no = orig_bindings->lookup_local(can_recover_name);
-  gcc_assert(can_recover_no != NULL
+  go_assert(can_recover_no != NULL
             && can_recover_no->is_variable()
             && can_recover_no->var_value()->is_parameter());
   orig_bindings->remove_binding(can_recover_no);
 
   // Add the can_recover argument to the (now) new bindings, and
   // attach it to any recover statements.
-  Variable* can_recover_var = new Variable(Type::make_boolean_type(), NULL,
+  Variable* can_recover_var = new Variable(Type::lookup_bool_type(), NULL,
                                           false, true, false, location);
   can_recover_no = new_bindings->add_variable(can_recover_name, NULL,
                                              can_recover_var);
@@ -2230,8 +2583,8 @@ Build_recover_thunks::function(Named_object* orig_no)
   new_func->traverse(&convert_recover);
 
   // Update the function pointers in any named results.
-  new_func->update_named_result_variables();
-  orig_func->update_named_result_variables();
+  new_func->update_result_variables();
+  orig_func->update_result_variables();
 
   return TRAVERSE_CONTINUE;
 }
@@ -2242,12 +2595,12 @@ Build_recover_thunks::function(Named_object* orig_no)
 // __go_can_recover(__builtin_return_address()).
 
 Expression*
-Build_recover_thunks::can_recover_arg(source_location location)
+Build_recover_thunks::can_recover_arg(Location location)
 {
   static Named_object* builtin_return_address;
   if (builtin_return_address == NULL)
     {
-      const source_location bloc = BUILTINS_LOCATION;
+      const Location bloc = Linemap::predeclared_location();
 
       Typed_identifier_list* param_types = new Typed_identifier_list();
       Type* uint_type = Type::lookup_integer_type("uint");
@@ -2269,11 +2622,11 @@ Build_recover_thunks::can_recover_arg(source_location location)
   static Named_object* can_recover;
   if (can_recover == NULL)
     {
-      const source_location bloc = BUILTINS_LOCATION;
+      const Location bloc = Linemap::predeclared_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("a", voidptr_type, bloc));
-      Type* boolean_type = Type::make_boolean_type();
+      Type* boolean_type = Type::lookup_bool_type();
       Typed_identifier_list* results = new Typed_identifier_list();
       results->push_back(Typed_identifier("", boolean_type, bloc));
       Function_type* fntype = Type::make_function_type(NULL, param_types,
@@ -2356,6 +2709,9 @@ class Build_method_tables : public Traverse
 void
 Gogo::build_interface_method_tables()
 {
+  if (saw_errors())
+    return;
+
   std::vector<Interface_type*> hidden_interfaces;
   hidden_interfaces.reserve(this->interface_types_.size());
   for (std::vector<Interface_type*>::const_iterator pi =
@@ -2465,7 +2821,7 @@ Gogo::check_return_statements()
 const std::string&
 Gogo::unique_prefix() const
 {
-  gcc_assert(!this->unique_prefix_.empty());
+  go_assert(!this->unique_prefix_.empty());
   return this->unique_prefix_;
 }
 
@@ -2475,7 +2831,7 @@ Gogo::unique_prefix() const
 void
 Gogo::set_unique_prefix(const std::string& arg)
 {
-  gcc_assert(this->unique_prefix_.empty());
+  go_assert(this->unique_prefix_.empty());
   this->unique_prefix_ = arg;
   this->unique_prefix_specified_ = true;
 }
@@ -2509,6 +2865,7 @@ Gogo::do_exports()
   exp.export_globals(this->package_name(),
                     this->unique_prefix(),
                     this->package_priority(),
+                    this->imports_,
                     (this->need_init_fn_ && !this->is_main_package()
                      ? this->get_init_fn_name()
                      : ""),
@@ -2516,30 +2873,111 @@ Gogo::do_exports()
                     this->package_->bindings());
 }
 
+// Find the blocks in order to convert named types defined in blocks.
+
+class Convert_named_types : public Traverse
+{
+ public:
+  Convert_named_types(Gogo* gogo)
+    : Traverse(traverse_blocks),
+      gogo_(gogo)
+  { }
+
+ protected:
+  int
+  block(Block* block);
+
+ private:
+  Gogo* gogo_;
+};
+
+int
+Convert_named_types::block(Block* block)
+{
+  this->gogo_->convert_named_types_in_bindings(block->bindings());
+  return TRAVERSE_CONTINUE;
+}
+
+// Convert all named types to the backend representation.  Since named
+// types can refer to other types, this needs to be done in the right
+// sequence, which is handled by Named_type::convert.  Here we arrange
+// to call that for each named type.
+
+void
+Gogo::convert_named_types()
+{
+  this->convert_named_types_in_bindings(this->globals_);
+  for (Packages::iterator p = this->packages_.begin();
+       p != this->packages_.end();
+       ++p)
+    {
+      Package* package = p->second;
+      this->convert_named_types_in_bindings(package->bindings());
+    }
+
+  Convert_named_types cnt(this);
+  this->traverse(&cnt);
+
+  // Make all the builtin named types used for type descriptors, and
+  // then convert them.  They will only be written out if they are
+  // needed.
+  Type::make_type_descriptor_type();
+  Type::make_type_descriptor_ptr_type();
+  Function_type::make_function_type_descriptor_type();
+  Pointer_type::make_pointer_type_descriptor_type();
+  Struct_type::make_struct_type_descriptor_type();
+  Array_type::make_array_type_descriptor_type();
+  Array_type::make_slice_type_descriptor_type();
+  Map_type::make_map_type_descriptor_type();
+  Map_type::make_map_descriptor_type();
+  Channel_type::make_chan_type_descriptor_type();
+  Interface_type::make_interface_type_descriptor_type();
+  Type::convert_builtin_named_types(this);
+
+  Runtime::convert_types(this);
+
+  this->named_types_are_converted_ = true;
+}
+
+// Convert all names types in a set of bindings.
+
+void
+Gogo::convert_named_types_in_bindings(Bindings* bindings)
+{
+  for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
+       p != bindings->end_definitions();
+       ++p)
+    {
+      if ((*p)->is_type())
+       (*p)->type_value()->convert(this);
+    }
+}
+
 // Class Function.
 
 Function::Function(Function_type* type, Function* enclosing, Block* block,
-                  source_location location)
-  : type_(type), enclosing_(enclosing), named_results_(NULL),
+                  Location location)
+  : type_(type), enclosing_(enclosing), results_(NULL),
     closure_var_(NULL), block_(block), location_(location), fndecl_(NULL),
-    defer_stack_(NULL), calls_recover_(false), is_recover_thunk_(false),
-    has_recover_thunk_(false)
+    defer_stack_(NULL), results_are_named_(false), calls_recover_(false),
+    is_recover_thunk_(false), has_recover_thunk_(false)
 {
 }
 
 // Create the named result variables.
 
 void
-Function::create_named_result_variables(Gogo* gogo)
+Function::create_result_variables(Gogo* gogo)
 {
   const Typed_identifier_list* results = this->type_->results();
-  if (results == NULL
-      || results->empty()
-      || results->front().name().empty())
+  if (results == NULL || results->empty())
     return;
 
-  this->named_results_ = new Named_results();
-  this->named_results_->reserve(results->size());
+  if (!results->front().name().empty())
+    this->results_are_named_ = true;
+
+  this->results_ = new Results();
+  this->results_->reserve(results->size());
 
   Block* block = this->block_;
   int index = 0;
@@ -2548,18 +2986,30 @@ Function::create_named_result_variables(Gogo* gogo)
        ++p, ++index)
     {
       std::string name = p->name();
-      if (Gogo::is_sink_name(name))
+      if (name.empty() || Gogo::is_sink_name(name))
        {
-         static int unnamed_result_counter;
+         static int result_counter;
          char buf[100];
-         snprintf(buf, sizeof buf, "_$%d", unnamed_result_counter);
-         ++unnamed_result_counter;
+         snprintf(buf, sizeof buf, "$ret%d", result_counter);
+         ++result_counter;
          name = gogo->pack_hidden_name(buf, false);
        }
-      Result_variable* result = new Result_variable(p->type(), this, index);
+      Result_variable* result = new Result_variable(p->type(), this, index,
+                                                   p->location());
       Named_object* no = block->bindings()->add_result_variable(name, result);
       if (no->is_result_variable())
-       this->named_results_->push_back(no);
+       this->results_->push_back(no);
+      else
+       {
+         static int dummy_result_count;
+         char buf[100];
+         snprintf(buf, sizeof buf, "$dret%d", dummy_result_count);
+         ++dummy_result_count;
+         name = gogo->pack_hidden_name(buf, false);
+         no = block->bindings()->add_result_variable(name, result);
+         go_assert(no->is_result_variable());
+         this->results_->push_back(no);
+       }
     }
 }
 
@@ -2567,13 +3017,13 @@ Function::create_named_result_variables(Gogo* gogo)
 // calls recover.
 
 void
-Function::update_named_result_variables()
+Function::update_result_variables()
 {
-  if (this->named_results_ == NULL)
+  if (this->results_ == NULL)
     return;
 
-  for (Named_results::iterator p = this->named_results_->begin();
-       p != this->named_results_->end();
+  for (Results::iterator p = this->results_->begin();
+       p != this->results_->end();
        ++p)
     (*p)->result_var_value()->set_function(this);
 }
@@ -2587,11 +3037,12 @@ Function::closure_var()
     {
       // We don't know the type of the variable yet.  We add fields as
       // we find them.
-      source_location loc = this->type_->location();
+      Location loc = this->type_->location();
       Struct_field_list* sfl = new Struct_field_list;
       Type* struct_type = Type::make_struct_type(sfl, loc);
       Variable* var = new Variable(Type::make_pointer_type(struct_type),
                                   NULL, false, true, false, loc);
+      var->set_is_used();
       this->closure_var_ = Named_object::make_variable("closure", NULL, var);
       // Note that the new variable is not in any binding contour.
     }
@@ -2637,59 +3088,96 @@ Function::is_method() const
 // Add a label definition.
 
 Label*
-Function::add_label_definition(const std::string& label_name,
-                              source_location location)
+Function::add_label_definition(Gogo* gogo, const std::string& label_name,
+                              Location location)
 {
   Label* lnull = NULL;
   std::pair<Labels::iterator, bool> ins =
     this->labels_.insert(std::make_pair(label_name, lnull));
+  Label* label;
   if (ins.second)
     {
       // This is a new label.
-      Label* label = new Label(label_name);
-      label->define(location);
+      label = new Label(label_name);
       ins.first->second = label;
-      return label;
     }
   else
     {
       // The label was already in the hash table.
-      Label* label = ins.first->second;
-      if (!label->is_defined())
+      label = ins.first->second;
+      if (label->is_defined())
        {
-         label->define(location);
-         return label;
-       }
-      else
-       {
-         error_at(location, "redefinition of label %qs",
+         error_at(location, "label %qs already defined",
                   Gogo::message_name(label_name).c_str());
          inform(label->location(), "previous definition of %qs was here",
                 Gogo::message_name(label_name).c_str());
          return new Label(label_name);
        }
     }
+
+  label->define(location, gogo->bindings_snapshot(location));
+
+  // Issue any errors appropriate for any previous goto's to this
+  // label.
+  const std::vector<Bindings_snapshot*>& refs(label->refs());
+  for (std::vector<Bindings_snapshot*>::const_iterator p = refs.begin();
+       p != refs.end();
+       ++p)
+    (*p)->check_goto_to(gogo->current_block());
+  label->clear_refs();
+
+  return label;
 }
 
 // Add a reference to a label.
 
 Label*
-Function::add_label_reference(const std::string& label_name)
+Function::add_label_reference(Gogo* gogo, const std::string& label_name,
+                             Location location, bool issue_goto_errors)
 {
   Label* lnull = NULL;
   std::pair<Labels::iterator, bool> ins =
     this->labels_.insert(std::make_pair(label_name, lnull));
+  Label* label;
   if (!ins.second)
     {
       // The label was already in the hash table.
-      return ins.first->second;
+      label = ins.first->second;
     }
   else
     {
-      gcc_assert(ins.first->second == NULL);
-      Label* label = new Label(label_name);
+      go_assert(ins.first->second == NULL);
+      label = new Label(label_name);
       ins.first->second = label;
-      return label;
+    }
+
+  label->set_is_used();
+
+  if (issue_goto_errors)
+    {
+      Bindings_snapshot* snapshot = label->snapshot();
+      if (snapshot != NULL)
+       snapshot->check_goto_from(gogo->current_block(), location);
+      else
+       label->add_snapshot_ref(gogo->bindings_snapshot(location));
+    }
+
+  return label;
+}
+
+// Warn about labels that are defined but not used.
+
+void
+Function::check_labels() const
+{
+  for (Labels::const_iterator p = this->labels_.begin();
+       p != this->labels_.end();
+       p++)
+    {
+      Label* label = p->second;
+      if (!label->is_used())
+       error_at(label->location(), "label %qs defined and not used",
+                Gogo::message_name(label->name()).c_str());
     }
 }
 
@@ -2700,13 +3188,13 @@ Function::add_label_reference(const std::string& label_name)
 void
 Function::swap_for_recover(Function *x)
 {
-  gcc_assert(this->enclosing_ == x->enclosing_);
-  std::swap(this->named_results_, x->named_results_);
+  go_assert(this->enclosing_ == x->enclosing_);
+  std::swap(this->results_, x->results_);
   std::swap(this->closure_var_, x->closure_var_);
   std::swap(this->block_, x->block_);
-  gcc_assert(this->location_ == x->location_);
-  gcc_assert(this->fndecl_ == NULL && x->fndecl_ == NULL);
-  gcc_assert(this->defer_stack_ == NULL && x->defer_stack_ == NULL);
+  go_assert(this->location_ == x->location_);
+  go_assert(this->fndecl_ == NULL && x->fndecl_ == NULL);
+  go_assert(this->defer_stack_ == NULL && x->defer_stack_ == NULL);
 }
 
 // Traverse the tree.
@@ -2751,6 +3239,29 @@ Function::determine_types()
     this->block_->determine_types();
 }
 
+// Get a pointer to the variable representing the defer stack for this
+// function, making it if necessary.  The value of the variable is set
+// by the runtime routines to true if the function is returning,
+// rather than panicing through.  A pointer to this variable is used
+// as a marker for the functions on the defer stack associated with
+// this function.  A function-specific variable permits inlining a
+// function which uses defer.
+
+Expression*
+Function::defer_stack(Location location)
+{
+  if (this->defer_stack_ == NULL)
+    {
+      Type* t = Type::lookup_bool_type();
+      Expression* n = Expression::make_boolean(false, location);
+      this->defer_stack_ = Statement::make_temporary(t, n, location);
+      this->defer_stack_->set_is_address_taken();
+    }
+  Expression* ref = Expression::make_temporary_reference(this->defer_stack_,
+                                                        location);
+  return Expression::make_unary(OPERATOR_AND, ref, location);
+}
+
 // Export the function.
 
 void
@@ -2770,7 +3281,10 @@ Function::export_func_with_type(Export* exp, const std::string& name,
   if (fntype->is_method())
     {
       exp->write_c_string("(");
-      exp->write_type(fntype->receiver()->type());
+      const Typed_identifier* receiver = fntype->receiver();
+      exp->write_name(receiver->name());
+      exp->write_c_string(" ");
+      exp->write_type(receiver->type());
       exp->write_c_string(") ");
     }
 
@@ -2790,6 +3304,8 @@ Function::export_func_with_type(Export* exp, const std::string& name,
            first = false;
          else
            exp->write_c_string(", ");
+         exp->write_name(p->name());
+         exp->write_c_string(" ");
          if (!is_varargs || p + 1 != parameters->end())
            exp->write_type(p->type());
          else
@@ -2804,7 +3320,7 @@ Function::export_func_with_type(Export* exp, const std::string& name,
   const Typed_identifier_list* results = fntype->results();
   if (results != NULL)
     {
-      if (results->size() == 1)
+      if (results->size() == 1 && results->begin()->name().empty())
        {
          exp->write_c_string(" ");
          exp->write_type(results->begin()->type());
@@ -2821,6 +3337,8 @@ Function::export_func_with_type(Export* exp, const std::string& name,
                first = false;
              else
                exp->write_c_string(", ");
+             exp->write_name(p->name());
+             exp->write_c_string(" ");
              exp->write_type(p->type());
            }
          exp->write_c_string(")");
@@ -2844,9 +3362,10 @@ Function::import_func(Import* imp, std::string* pname,
   if (imp->peek_char() == '(')
     {
       imp->require_c_string("(");
+      std::string name = imp->read_name();
+      imp->require_c_string(" ");
       Type* rtype = imp->read_type();
-      *preceiver = new Typed_identifier(Import::import_marker, rtype,
-                                       imp->location());
+      *preceiver = new Typed_identifier(name, rtype, imp->location());
       imp->require_c_string(") ");
     }
 
@@ -2862,6 +3381,9 @@ Function::import_func(Import* imp, std::string* pname,
       parameters = new Typed_identifier_list();
       while (true)
        {
+         std::string name = imp->read_name();
+         imp->require_c_string(" ");
+
          if (imp->match_c_string("..."))
            {
              imp->advance(3);
@@ -2871,11 +3393,11 @@ Function::import_func(Import* imp, std::string* pname,
          Type* ptype = imp->read_type();
          if (*is_varargs)
            ptype = Type::make_array_type(ptype, NULL);
-         parameters->push_back(Typed_identifier(Import::import_marker,
-                                                ptype, imp->location()));
+         parameters->push_back(Typed_identifier(name, ptype,
+                                                imp->location()));
          if (imp->peek_char() != ',')
            break;
-         gcc_assert(!*is_varargs);
+         go_assert(!*is_varargs);
          imp->require_c_string(", ");
        }
     }
@@ -2892,17 +3414,18 @@ Function::import_func(Import* imp, std::string* pname,
       if (imp->peek_char() != '(')
        {
          Type* rtype = imp->read_type();
-         results->push_back(Typed_identifier(Import::import_marker, rtype,
-                                             imp->location()));
+         results->push_back(Typed_identifier("", rtype, imp->location()));
        }
       else
        {
          imp->require_c_string("(");
          while (true)
            {
+             std::string name = imp->read_name();
+             imp->require_c_string(" ");
              Type* rtype = imp->read_type();
-             results->push_back(Typed_identifier(Import::import_marker,
-                                                 rtype, imp->location()));
+             results->push_back(Typed_identifier(name, rtype,
+                                                 imp->location()));
              if (imp->peek_char() != ',')
                break;
              imp->require_c_string(", ");
@@ -2916,7 +3439,7 @@ Function::import_func(Import* imp, std::string* pname,
 
 // Class Block.
 
-Block::Block(Block* enclosing, source_location location)
+Block::Block(Block* enclosing, Location location)
   : enclosing_(enclosing), statements_(),
     bindings_(new Bindings(enclosing == NULL
                           ? NULL
@@ -2948,7 +3471,7 @@ Block::add_statement_at_front(Statement* statement)
 void
 Block::replace_statement(size_t index, Statement* s)
 {
-  gcc_assert(index < this->statements_.size());
+  go_assert(index < this->statements_.size());
   this->statements_[index] = s;
 }
 
@@ -2957,7 +3480,7 @@ Block::replace_statement(size_t index, Statement* s)
 void
 Block::insert_statement_before(size_t index, Statement* s)
 {
-  gcc_assert(index < this->statements_.size());
+  go_assert(index < this->statements_.size());
   this->statements_.insert(this->statements_.begin() + index, s);
 }
 
@@ -2966,7 +3489,7 @@ Block::insert_statement_before(size_t index, Statement* s)
 void
 Block::insert_statement_after(size_t index, Statement* s)
 {
-  gcc_assert(index < this->statements_.size());
+  go_assert(index < this->statements_.size());
   this->statements_.insert(this->statements_.begin() + index + 1, s);
 }
 
@@ -2992,91 +3515,81 @@ Block::traverse(Traverse* traverse)
          | Traverse::traverse_expressions
          | Traverse::traverse_types)) != 0)
     {
+      const unsigned int e_or_t = (Traverse::traverse_expressions
+                                  | Traverse::traverse_types);
+      const unsigned int e_or_t_or_s = (e_or_t
+                                       | Traverse::traverse_statements);
       for (Bindings::const_definitions_iterator pb =
             this->bindings_->begin_definitions();
           pb != this->bindings_->end_definitions();
           ++pb)
        {
+         int t = TRAVERSE_CONTINUE;
          switch ((*pb)->classification())
            {
            case Named_object::NAMED_OBJECT_CONST:
              if ((traverse_mask & Traverse::traverse_constants) != 0)
+               t = traverse->constant(*pb, false);
+             if (t == TRAVERSE_CONTINUE
+                 && (traverse_mask & e_or_t) != 0)
                {
-                 if (traverse->constant(*pb, false) == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
-             if ((traverse_mask & Traverse::traverse_types) != 0
-                 || (traverse_mask & Traverse::traverse_expressions) != 0)
-               {
-                 Type* t = (*pb)->const_value()->type();
-                 if (t != NULL
-                     && Type::traverse(t, traverse) == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
-             if ((traverse_mask & Traverse::traverse_expressions) != 0
-                 || (traverse_mask & Traverse::traverse_types) != 0)
-               {
-                 if ((*pb)->const_value()->traverse_expression(traverse)
-                     == TRAVERSE_EXIT)
+                 Type* tc = (*pb)->const_value()->type();
+                 if (tc != NULL
+                     && Type::traverse(tc, traverse) == TRAVERSE_EXIT)
                    return TRAVERSE_EXIT;
+                 t = (*pb)->const_value()->traverse_expression(traverse);
                }
              break;
 
            case Named_object::NAMED_OBJECT_VAR:
            case Named_object::NAMED_OBJECT_RESULT_VAR:
              if ((traverse_mask & Traverse::traverse_variables) != 0)
+               t = traverse->variable(*pb);
+             if (t == TRAVERSE_CONTINUE
+                 && (traverse_mask & e_or_t) != 0)
                {
-                 if (traverse->variable(*pb) == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
-             if (((traverse_mask & Traverse::traverse_types) != 0
-                  || (traverse_mask & Traverse::traverse_expressions) != 0)
-                 && ((*pb)->is_result_variable()
-                     || (*pb)->var_value()->has_type()))
-               {
-                 Type* t = ((*pb)->is_variable()
-                            ? (*pb)->var_value()->type()
-                            : (*pb)->result_var_value()->type());
-                 if (t != NULL
-                     && Type::traverse(t, traverse) == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
-             if ((*pb)->is_variable()
-                 && ((traverse_mask & Traverse::traverse_expressions) != 0
-                     || (traverse_mask & Traverse::traverse_types) != 0))
-               {
-                 if ((*pb)->var_value()->traverse_expression(traverse)
-                     == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
+                 if ((*pb)->is_result_variable()
+                     || (*pb)->var_value()->has_type())
+                   {
+                     Type* tv = ((*pb)->is_variable()
+                                 ? (*pb)->var_value()->type()
+                                 : (*pb)->result_var_value()->type());
+                     if (tv != NULL
+                         && Type::traverse(tv, traverse) == TRAVERSE_EXIT)
+                       return TRAVERSE_EXIT;
+                   }
                }
+             if (t == TRAVERSE_CONTINUE
+                 && (traverse_mask & e_or_t_or_s) != 0
+                 && (*pb)->is_variable())
+               t = (*pb)->var_value()->traverse_expression(traverse,
+                                                           traverse_mask);
              break;
 
            case Named_object::NAMED_OBJECT_FUNC:
            case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
-             // FIXME: Where will nested functions be found?
-             gcc_unreachable();
+             go_unreachable();
 
            case Named_object::NAMED_OBJECT_TYPE:
-             if ((traverse_mask & Traverse::traverse_types) != 0
-                 || (traverse_mask & Traverse::traverse_expressions) != 0)
-               {
-                 if (Type::traverse((*pb)->type_value(), traverse)
-                     == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
+             if ((traverse_mask & e_or_t) != 0)
+               t = Type::traverse((*pb)->type_value(), traverse);
              break;
 
            case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
            case Named_object::NAMED_OBJECT_UNKNOWN:
+           case Named_object::NAMED_OBJECT_ERRONEOUS:
              break;
 
            case Named_object::NAMED_OBJECT_PACKAGE:
            case Named_object::NAMED_OBJECT_SINK:
-             gcc_unreachable();
+             go_unreachable();
 
            default:
-             gcc_unreachable();
+             go_unreachable();
            }
+
+         if (t == TRAVERSE_EXIT)
+           return TRAVERSE_EXIT;
        }
     }
 
@@ -3128,34 +3641,166 @@ Block::may_fall_through() const
   return this->statements_.back()->may_fall_through();
 }
 
+// Convert a block to the backend representation.
+
+Bblock*
+Block::get_backend(Translate_context* context)
+{
+  Gogo* gogo = context->gogo();
+  Named_object* function = context->function();
+  std::vector<Bvariable*> vars;
+  vars.reserve(this->bindings_->size_definitions());
+  for (Bindings::const_definitions_iterator pv =
+        this->bindings_->begin_definitions();
+       pv != this->bindings_->end_definitions();
+       ++pv)
+    {
+      if ((*pv)->is_variable() && !(*pv)->var_value()->is_parameter())
+       vars.push_back((*pv)->get_backend_variable(gogo, function));
+    }
+
+  // FIXME: Permitting FUNCTION to be NULL here is a temporary measure
+  // until we have a proper representation of the init function.
+  Bfunction* bfunction;
+  if (function == NULL)
+    bfunction = NULL;
+  else
+    bfunction = tree_to_function(function->func_value()->get_decl());
+  Bblock* ret = context->backend()->block(bfunction, context->bblock(),
+                                         vars, this->start_location_,
+                                         this->end_location_);
+
+  Translate_context subcontext(gogo, function, this, ret);
+  std::vector<Bstatement*> bstatements;
+  bstatements.reserve(this->statements_.size());
+  for (std::vector<Statement*>::const_iterator p = this->statements_.begin();
+       p != this->statements_.end();
+       ++p)
+    bstatements.push_back((*p)->get_backend(&subcontext));
+
+  context->backend()->block_add_statements(ret, bstatements);
+
+  return ret;
+}
+
+// Class Bindings_snapshot.
+
+Bindings_snapshot::Bindings_snapshot(const Block* b, Location location)
+  : block_(b), counts_(), location_(location)
+{
+  while (b != NULL)
+    {
+      this->counts_.push_back(b->bindings()->size_definitions());
+      b = b->enclosing();
+    }
+}
+
+// Report errors appropriate for a goto from B to this.
+
+void
+Bindings_snapshot::check_goto_from(const Block* b, Location loc)
+{
+  size_t dummy;
+  if (!this->check_goto_block(loc, b, this->block_, &dummy))
+    return;
+  this->check_goto_defs(loc, this->block_,
+                       this->block_->bindings()->size_definitions(),
+                       this->counts_[0]);
+}
+
+// Report errors appropriate for a goto from this to B.
+
+void
+Bindings_snapshot::check_goto_to(const Block* b)
+{
+  size_t index;
+  if (!this->check_goto_block(this->location_, this->block_, b, &index))
+    return;
+  this->check_goto_defs(this->location_, b, this->counts_[index],
+                       b->bindings()->size_definitions());
+}
+
+// Report errors appropriate for a goto at LOC from BFROM to BTO.
+// Return true if all is well, false if we reported an error.  If this
+// returns true, it sets *PINDEX to the number of blocks BTO is above
+// BFROM.
+
+bool
+Bindings_snapshot::check_goto_block(Location loc, const Block* bfrom,
+                                   const Block* bto, size_t* pindex)
+{
+  // It is an error if BTO is not either BFROM or above BFROM.
+  size_t index = 0;
+  for (const Block* pb = bfrom; pb != bto; pb = pb->enclosing(), ++index)
+    {
+      if (pb == NULL)
+       {
+         error_at(loc, "goto jumps into block");
+         inform(bto->start_location(), "goto target block starts here");
+         return false;
+       }
+    }
+  *pindex = index;
+  return true;
+}
+
+// Report errors appropriate for a goto at LOC ending at BLOCK, where
+// CFROM is the number of names defined at the point of the goto and
+// CTO is the number of names defined at the point of the label.
+
+void
+Bindings_snapshot::check_goto_defs(Location loc, const Block* block,
+                                  size_t cfrom, size_t cto)
+{
+  if (cfrom < cto)
+    {
+      Bindings::const_definitions_iterator p =
+       block->bindings()->begin_definitions();
+      for (size_t i = 0; i < cfrom; ++i)
+       {
+         go_assert(p != block->bindings()->end_definitions());
+         ++p;
+       }
+      go_assert(p != block->bindings()->end_definitions());
+
+      std::string n = (*p)->message_name();
+      error_at(loc, "goto jumps over declaration of %qs", n.c_str());
+      inform((*p)->location(), "%qs defined here", n.c_str());
+    }
+}
+
 // Class Variable.
 
 Variable::Variable(Type* type, Expression* init, bool is_global,
                   bool is_parameter, bool is_receiver,
-                  source_location location)
+                  Location location)
   : type_(type), init_(init), preinit_(NULL), location_(location),
-    is_global_(is_global), is_parameter_(is_parameter),
-    is_receiver_(is_receiver), is_varargs_parameter_(false),
-    is_address_taken_(false), seen_(false), init_is_lowered_(false),
-    type_from_init_tuple_(false), type_from_range_index_(false),
-    type_from_range_value_(false), type_from_chan_element_(false),
-    is_type_switch_var_(false)
+    backend_(NULL), is_global_(is_global), is_parameter_(is_parameter),
+    is_receiver_(is_receiver), is_varargs_parameter_(false), is_used_(false),
+    is_address_taken_(false), is_non_escaping_address_taken_(false),
+    seen_(false), init_is_lowered_(false), type_from_init_tuple_(false),
+    type_from_range_index_(false), type_from_range_value_(false),
+    type_from_chan_element_(false), is_type_switch_var_(false),
+    determined_type_(false)
 {
-  gcc_assert(type != NULL || init != NULL);
-  gcc_assert(!is_parameter || init == NULL);
+  go_assert(type != NULL || init != NULL);
+  go_assert(!is_parameter || init == NULL);
 }
 
 // Traverse the initializer expression.
 
 int
-Variable::traverse_expression(Traverse* traverse)
+Variable::traverse_expression(Traverse* traverse, unsigned int traverse_mask)
 {
   if (this->preinit_ != NULL)
     {
       if (this->preinit_->traverse(traverse) == TRAVERSE_EXIT)
        return TRAVERSE_EXIT;
     }
-  if (this->init_ != NULL)
+  if (this->init_ != NULL
+      && ((traverse_mask
+          & (Traverse::traverse_expressions | Traverse::traverse_types))
+         != 0))
     {
       if (Expression::traverse(&this->init_, traverse) == TRAVERSE_EXIT)
        return TRAVERSE_EXIT;
@@ -3166,7 +3811,8 @@ Variable::traverse_expression(Traverse* traverse)
 // Lower the initialization expression after parsing is complete.
 
 void
-Variable::lower_init_expression(Gogo* gogo, Named_object* function)
+Variable::lower_init_expression(Gogo* gogo, Named_object* function,
+                               Statement_inserter* inserter)
 {
   if (this->init_ != NULL && !this->init_is_lowered_)
     {
@@ -3178,7 +3824,14 @@ Variable::lower_init_expression(Gogo* gogo, Named_object* function)
        }
       this->seen_ = true;
 
-      gogo->lower_expression(function, &this->init_);
+      Statement_inserter global_inserter;
+      if (this->is_global_)
+       {
+         global_inserter = Statement_inserter(gogo, this);
+         inserter = &global_inserter;
+       }
+
+      gogo->lower_expression(function, inserter, &this->init_);
 
       this->seen_ = false;
 
@@ -3191,7 +3844,7 @@ Variable::lower_init_expression(Gogo* gogo, Named_object* function)
 Block*
 Variable::preinit_block(Gogo* gogo)
 {
-  gcc_assert(this->is_global_);
+  go_assert(this->is_global_);
   if (this->preinit_ == NULL)
     this->preinit_ = new Block(NULL, this->location());
 
@@ -3212,6 +3865,24 @@ Variable::add_preinit_statement(Gogo* gogo, Statement* s)
   b->set_end_location(s->location());
 }
 
+// Whether this variable has a type.
+
+bool
+Variable::has_type() const
+{
+  if (this->type_ == NULL)
+    return false;
+
+  // A variable created in a type switch case nil does not actually
+  // have a type yet.  It will be changed to use the initializer's
+  // type in determine_type.
+  if (this->is_type_switch_var_
+      && this->type_->is_nil_constant_as_type())
+    return false;
+
+  return true;
+}
+
 // In an assignment which sets a variable to a tuple of EXPR, return
 // the type of the first element of the tuple.
 
@@ -3252,7 +3923,7 @@ Variable::type_from_range(Expression* expr, bool get_index_type,
   if (t->array_type() != NULL
       || (t->points_to() != NULL
          && t->points_to()->array_type() != NULL
-         && !t->points_to()->is_open_array_type()))
+         && !t->points_to()->is_slice_type()))
     {
       if (get_index_type)
        return Type::lookup_integer_type("int");
@@ -3260,7 +3931,12 @@ Variable::type_from_range(Expression* expr, bool get_index_type,
        return t->deref()->array_type()->element_type();
     }
   else if (t->is_string_type())
-    return Type::lookup_integer_type("int");
+    {
+      if (get_index_type)
+       return Type::lookup_integer_type("int");
+      else
+       return Type::lookup_integer_type("int32");
+    }
   else if (t->map_type() != NULL)
     {
       if (get_index_type)
@@ -3320,7 +3996,7 @@ Variable::type()
       && this->type_->is_nil_constant_as_type())
     {
       Type_guard_expression* tge = this->init_->type_guard_expression();
-      gcc_assert(tge != NULL);
+      go_assert(tge != NULL);
       init = tge->expr();
       type = NULL;
     }
@@ -3347,9 +4023,9 @@ Variable::type()
     type = this->type_from_chan_element(init, false);
   else
     {
-      gcc_assert(init != NULL);
+      go_assert(init != NULL);
       type = init->type();
-      gcc_assert(type != NULL);
+      go_assert(type != NULL);
 
       // Variables should not have abstract types.
       if (type->is_abstract())
@@ -3370,7 +4046,7 @@ Variable::type()
 Type*
 Variable::type() const
 {
-  gcc_assert(this->type_ != NULL);
+  go_assert(this->type_ != NULL);
   return this->type_;
 }
 
@@ -3379,6 +4055,10 @@ Variable::type() const
 void
 Variable::determine_type()
 {
+  if (this->determined_type_)
+    return;
+  this->determined_type_ = true;
+
   if (this->preinit_ != NULL)
     this->preinit_->determine_types();
 
@@ -3389,13 +4069,13 @@ Variable::determine_type()
   if (this->is_type_switch_var_ && this->type_->is_nil_constant_as_type())
     {
       Type_guard_expression* tge = this->init_->type_guard_expression();
-      gcc_assert(tge != NULL);
+      go_assert(tge != NULL);
       this->type_ = NULL;
       this->init_ = tge->expr();
     }
 
   if (this->init_ == NULL)
-    gcc_assert(this->type_ != NULL && !this->type_->is_abstract());
+    go_assert(this->type_ != NULL && !this->type_->is_abstract());
   else if (this->type_from_init_tuple_)
     {
       Expression *init = this->init_;
@@ -3411,18 +4091,21 @@ Variable::determine_type()
                                          true);
       this->init_ = NULL;
     }
+  else if (this->type_from_chan_element_)
+    {
+      Expression* init = this->init_;
+      init->determine_type_no_context();
+      this->type_ = this->type_from_chan_element(init, true);
+      this->init_ = NULL;
+    }
   else
     {
-      // type_from_chan_element_ should have been cleared during
-      // lowering.
-      gcc_assert(!this->type_from_chan_element_);
-
       Type_context context(this->type_, false);
       this->init_->determine_type(&context);
       if (this->type_ == NULL)
        {
          Type* type = this->init_->type();
-         gcc_assert(type != NULL);
+         go_assert(type != NULL);
          if (type->is_abstract())
            type = type->make_non_abstract_type();
 
@@ -3453,7 +4136,7 @@ Variable::determine_type()
 void
 Variable::export_var(Export* exp, const std::string& name) const
 {
-  gcc_assert(this->is_global_);
+  go_assert(this->is_global_);
   exp->write_c_string("var ");
   exp->write_string(name);
   exp->write_c_string(" ");
@@ -3473,6 +4156,105 @@ Variable::import_var(Import* imp, std::string* pname, Type** ptype)
   imp->require_c_string(";\n");
 }
 
+// Convert a variable to the backend representation.
+
+Bvariable*
+Variable::get_backend_variable(Gogo* gogo, Named_object* function,
+                              const Package* package, const std::string& name)
+{
+  if (this->backend_ == NULL)
+    {
+      Backend* backend = gogo->backend();
+      Type* type = this->type_;
+      if (type->is_error_type()
+         || (type->is_undefined()
+             && (!this->is_global_ || package == NULL)))
+       this->backend_ = backend->error_variable();
+      else
+       {
+         bool is_parameter = this->is_parameter_;
+         if (this->is_receiver_ && type->points_to() == NULL)
+           is_parameter = false;
+         if (this->is_in_heap())
+           {
+             is_parameter = false;
+             type = Type::make_pointer_type(type);
+           }
+
+         std::string n = Gogo::unpack_hidden_name(name);
+         Btype* btype = type->get_backend(gogo);
+
+         Bvariable* bvar;
+         if (this->is_global_)
+           bvar = backend->global_variable((package == NULL
+                                            ? gogo->package_name()
+                                            : package->name()),
+                                           (package == NULL
+                                            ? gogo->unique_prefix()
+                                            : package->unique_prefix()),
+                                           n,
+                                           btype,
+                                           package != NULL,
+                                           Gogo::is_hidden_name(name),
+                                           this->location_);
+         else if (function == NULL)
+           {
+             go_assert(saw_errors());
+             bvar = backend->error_variable();
+           }
+         else
+           {
+             tree fndecl = function->func_value()->get_decl();
+             Bfunction* bfunction = tree_to_function(fndecl);
+             bool is_address_taken = (this->is_non_escaping_address_taken_
+                                      && !this->is_in_heap());
+             if (is_parameter)
+               bvar = backend->parameter_variable(bfunction, n, btype,
+                                                  is_address_taken,
+                                                  this->location_);
+             else
+               bvar = backend->local_variable(bfunction, n, btype,
+                                              is_address_taken,
+                                              this->location_);
+           }
+         this->backend_ = bvar;
+       }
+    }
+  return this->backend_;
+}
+
+// Class Result_variable.
+
+// Convert a result variable to the backend representation.
+
+Bvariable*
+Result_variable::get_backend_variable(Gogo* gogo, Named_object* function,
+                                     const std::string& name)
+{
+  if (this->backend_ == NULL)
+    {
+      Backend* backend = gogo->backend();
+      Type* type = this->type_;
+      if (type->is_error())
+       this->backend_ = backend->error_variable();
+      else
+       {
+         if (this->is_in_heap())
+           type = Type::make_pointer_type(type);
+         Btype* btype = type->get_backend(gogo);
+         tree fndecl = function->func_value()->get_decl();
+         Bfunction* bfunction = tree_to_function(fndecl);
+         std::string n = Gogo::unpack_hidden_name(name);
+         bool is_address_taken = (this->is_non_escaping_address_taken_
+                                  && !this->is_in_heap());
+         this->backend_ = backend->local_variable(bfunction, n, btype,
+                                                  is_address_taken,
+                                                  this->location_);
+       }
+    }
+  return this->backend_;
+}
+
 // Class Named_constant.
 
 // Traverse the initializer expression.
@@ -3499,7 +4281,7 @@ Named_constant::determine_type()
       Type_context context(NULL, true);
       this->expr_->determine_type(&context);
       this->type_ = this->expr_->type();
-      gcc_assert(this->type_ != NULL);
+      go_assert(this->type_ != NULL);
     }
 }
 
@@ -3565,11 +4347,12 @@ Type_declaration::add_method(const std::string& name, Function* function)
 
 Named_object*
 Type_declaration::add_method_declaration(const std::string&  name,
+                                        Package* package,
                                         Function_type* type,
-                                        source_location location)
+                                        Location location)
 {
-  Named_object* ret = Named_object::make_function_declaration(name, NULL, type,
-                                                             location);
+  Named_object* ret = Named_object::make_function_declaration(name, package,
+                                                             type, location);
   this->methods_.push_back(ret);
   return ret;
 }
@@ -3610,8 +4393,8 @@ Type_declaration::using_type()
 void
 Unknown_name::set_real_named_object(Named_object* no)
 {
-  gcc_assert(this->real_named_object_ == NULL);
-  gcc_assert(!no->is_unknown());
+  go_assert(this->real_named_object_ == NULL);
+  go_assert(!no->is_unknown());
   this->real_named_object_ = no;
 }
 
@@ -3624,7 +4407,7 @@ Named_object::Named_object(const std::string& name,
     tree_(NULL)
 {
   if (Gogo::is_sink_name(name))
-    gcc_assert(classification == NAMED_OBJECT_SINK);
+    go_assert(classification == NAMED_OBJECT_SINK);
 }
 
 // Make an unknown name.  This is used by the parser.  The name must
@@ -3633,7 +4416,7 @@ Named_object::Named_object(const std::string& name,
 
 Named_object*
 Named_object::make_unknown_name(const std::string& name,
-                               source_location location)
+                               Location location)
 {
   Named_object* named_object = new Named_object(name, NULL,
                                                NAMED_OBJECT_UNKNOWN);
@@ -3662,7 +4445,7 @@ Named_object::make_constant(const Typed_identifier& tid,
 
 Named_object*
 Named_object::make_type(const std::string& name, const Package* package,
-                       Type* type, source_location location)
+                       Type* type, Location location)
 {
   Named_object* named_object = new Named_object(name, package,
                                                NAMED_OBJECT_TYPE);
@@ -3676,7 +4459,7 @@ Named_object::make_type(const std::string& name, const Package* package,
 Named_object*
 Named_object::make_type_declaration(const std::string& name,
                                    const Package* package,
-                                   source_location location)
+                                   Location location)
 {
   Named_object* named_object = new Named_object(name, package,
                                                NAMED_OBJECT_TYPE_DECLARATION);
@@ -3735,7 +4518,7 @@ Named_object*
 Named_object::make_function_declaration(const std::string& name,
                                        const Package* package,
                                        Function_type* fntype,
-                                       source_location location)
+                                       Location location)
 {
   Named_object* named_object = new Named_object(name, package,
                                                NAMED_OBJECT_FUNC_DECLARATION);
@@ -3773,7 +4556,7 @@ Named_object::message_name() const
 void
 Named_object::set_type_value(Named_type* named_type)
 {
-  gcc_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
+  go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
   Type_declaration* td = this->u_.type_declaration;
   td->define_methods(named_type);
   Named_object* in_function = td->in_function();
@@ -3789,7 +4572,7 @@ Named_object::set_type_value(Named_type* named_type)
 void
 Named_object::set_function_value(Function* function)
 {
-  gcc_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
+  go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
   this->classification_ = NAMED_OBJECT_FUNC;
   // FIXME: We should free the old value.
   this->u_.func_value = function;
@@ -3800,7 +4583,7 @@ Named_object::set_function_value(Function* function)
 void
 Named_object::declare_as_type()
 {
-  gcc_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
+  go_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
   Unknown_name* unk = this->u_.unknown_value;
   this->classification_ = NAMED_OBJECT_TYPE_DECLARATION;
   this->u_.type_declaration = new Type_declaration(unk->location());
@@ -3809,14 +4592,17 @@ Named_object::declare_as_type()
 
 // Return the location of a named object.
 
-source_location
+Location
 Named_object::location() const
 {
   switch (this->classification_)
     {
     default:
     case NAMED_OBJECT_UNINITIALIZED:
-      gcc_unreachable();
+      go_unreachable();
+
+    case NAMED_OBJECT_ERRONEOUS:
+      return Linemap::unknown_location();
 
     case NAMED_OBJECT_UNKNOWN:
       return this->unknown_value()->location();
@@ -3834,10 +4620,10 @@ Named_object::location() const
       return this->var_value()->location();
 
     case NAMED_OBJECT_RESULT_VAR:
-      return this->result_var_value()->function()->location();
+      return this->result_var_value()->location();
 
     case NAMED_OBJECT_SINK:
-      gcc_unreachable();
+      go_unreachable();
 
     case NAMED_OBJECT_FUNC:
       return this->func_value()->location();
@@ -3860,7 +4646,10 @@ Named_object::export_named_object(Export* exp) const
     default:
     case NAMED_OBJECT_UNINITIALIZED:
     case NAMED_OBJECT_UNKNOWN:
-      gcc_unreachable();
+      go_unreachable();
+
+    case NAMED_OBJECT_ERRONEOUS:
+      break;
 
     case NAMED_OBJECT_CONST:
       this->const_value()->export_const(exp, this->name_);
@@ -3886,7 +4675,7 @@ Named_object::export_named_object(Export* exp) const
 
     case NAMED_OBJECT_RESULT_VAR:
     case NAMED_OBJECT_SINK:
-      gcc_unreachable();
+      go_unreachable();
 
     case NAMED_OBJECT_FUNC:
       this->func_value()->export_func(exp, this->name_);
@@ -3894,6 +4683,21 @@ Named_object::export_named_object(Export* exp) const
     }
 }
 
+// Convert a variable to the backend representation.
+
+Bvariable*
+Named_object::get_backend_variable(Gogo* gogo, Named_object* function)
+{
+  if (this->classification_ == NAMED_OBJECT_VAR)
+    return this->var_value()->get_backend_variable(gogo, function,
+                                                  this->package_, this->name_);
+  else if (this->classification_ == NAMED_OBJECT_RESULT_VAR)
+    return this->result_var_value()->get_backend_variable(gogo, function,
+                                                         this->name_);
+  else
+    go_unreachable();
+}
+
 // Class Bindings.
 
 Bindings::Bindings(Bindings* enclosing)
@@ -3960,7 +4764,7 @@ void
 Bindings::remove_binding(Named_object* no)
 {
   Contour::iterator pb = this->bindings_.find(no->name());
-  gcc_assert(pb != this->bindings_.end());
+  go_assert(pb != this->bindings_.end());
   this->bindings_.erase(pb);
   for (std::vector<Named_object*>::iterator pn = this->named_objects_.begin();
        pn != this->named_objects_.end();
@@ -3972,7 +4776,7 @@ Bindings::remove_binding(Named_object* no)
          return;
        }
     }
-  gcc_unreachable();
+  go_unreachable();
 }
 
 // Add a method to the list of objects.  This is not added to the
@@ -3992,9 +4796,9 @@ Named_object*
 Bindings::add_named_object_to_contour(Contour* contour,
                                      Named_object* named_object)
 {
-  gcc_assert(named_object == named_object->resolve());
+  go_assert(named_object == named_object->resolve());
   const std::string& name(named_object->name());
-  gcc_assert(!Gogo::is_sink_name(name));
+  go_assert(!Gogo::is_sink_name(name));
 
   std::pair<Contour::iterator, bool> ins =
     contour->insert(std::make_pair(name, named_object));
@@ -4033,19 +4837,25 @@ Bindings::add_named_object_to_contour(Contour* contour,
 Named_object*
 Bindings::new_definition(Named_object* old_object, Named_object* new_object)
 {
+  if (new_object->is_erroneous() && !old_object->is_erroneous())
+    return new_object;
+
   std::string reason;
   switch (old_object->classification())
     {
     default:
     case Named_object::NAMED_OBJECT_UNINITIALIZED:
-      gcc_unreachable();
+      go_unreachable();
+
+    case Named_object::NAMED_OBJECT_ERRONEOUS:
+      return old_object;
 
     case Named_object::NAMED_OBJECT_UNKNOWN:
       {
        Named_object* real = old_object->unknown_value()->real_named_object();
        if (real != NULL)
          return this->new_definition(real, new_object);
-       gcc_assert(!new_object->is_unknown());
+       go_assert(!new_object->is_unknown());
        old_object->unknown_value()->set_real_named_object(new_object);
        if (!new_object->is_type_declaration()
            && !new_object->is_function_declaration())
@@ -4075,10 +4885,16 @@ Bindings::new_definition(Named_object* old_object, Named_object* new_object)
 
     case Named_object::NAMED_OBJECT_VAR:
     case Named_object::NAMED_OBJECT_RESULT_VAR:
+      // We have already given an error in the parser for cases where
+      // one parameter or result variable redeclares another one.
+      if ((new_object->is_variable()
+          && new_object->var_value()->is_parameter())
+         || new_object->is_result_variable())
+       return old_object;
       break;
 
     case Named_object::NAMED_OBJECT_SINK:
-      gcc_unreachable();
+      go_unreachable();
 
     case Named_object::NAMED_OBJECT_FUNC:
       if (new_object->is_function_declaration())
@@ -4164,7 +4980,7 @@ Named_object*
 Bindings::add_function_declaration(const std::string& name,
                                   const Package* package,
                                   Function_type* type,
-                                  source_location location)
+                                  Location location)
 {
   Named_object* no = Named_object::make_function_declaration(name, package,
                                                             type, location);
@@ -4180,6 +4996,19 @@ Bindings::define_type(Named_object* no, Named_type* type)
   this->named_objects_.push_back(no);
 }
 
+// Mark all local variables as used.  This is used for some types of
+// parse error.
+
+void
+Bindings::mark_locals_used()
+{
+  for (std::vector<Named_object*>::iterator p = this->named_objects_.begin();
+       p != this->named_objects_.end();
+       ++p)
+    if ((*p)->is_variable())
+      (*p)->var_value()->set_is_used();
+}
+
 // Traverse bindings.
 
 int
@@ -4189,120 +5018,202 @@ Bindings::traverse(Traverse* traverse, bool is_global)
 
   // We don't use an iterator because we permit the traversal to add
   // new global objects.
+  const unsigned int e_or_t = (Traverse::traverse_expressions
+                              | Traverse::traverse_types);
+  const unsigned int e_or_t_or_s = (e_or_t
+                                   | Traverse::traverse_statements);
   for (size_t i = 0; i < this->named_objects_.size(); ++i)
     {
       Named_object* p = this->named_objects_[i];
+      int t = TRAVERSE_CONTINUE;
       switch (p->classification())
        {
        case Named_object::NAMED_OBJECT_CONST:
          if ((traverse_mask & Traverse::traverse_constants) != 0)
+           t = traverse->constant(p, is_global);
+         if (t == TRAVERSE_CONTINUE
+             && (traverse_mask & e_or_t) != 0)
            {
-             if (traverse->constant(p, is_global) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
-         if ((traverse_mask & Traverse::traverse_types) != 0
-             || (traverse_mask & Traverse::traverse_expressions) != 0)
-           {
-             Type* t = p->const_value()->type();
-             if (t != NULL
-                 && Type::traverse(t, traverse) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-             if (p->const_value()->traverse_expression(traverse)
-                 == TRAVERSE_EXIT)
+             Type* tc = p->const_value()->type();
+             if (tc != NULL
+                 && Type::traverse(tc, traverse) == TRAVERSE_EXIT)
                return TRAVERSE_EXIT;
+             t = p->const_value()->traverse_expression(traverse);
            }
          break;
 
        case Named_object::NAMED_OBJECT_VAR:
        case Named_object::NAMED_OBJECT_RESULT_VAR:
          if ((traverse_mask & Traverse::traverse_variables) != 0)
+           t = traverse->variable(p);
+         if (t == TRAVERSE_CONTINUE
+             && (traverse_mask & e_or_t) != 0)
            {
-             if (traverse->variable(p) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
-         if (((traverse_mask & Traverse::traverse_types) != 0
-              || (traverse_mask & Traverse::traverse_expressions) != 0)
-             && (p->is_result_variable()
-                 || p->var_value()->has_type()))
-           {
-             Type* t = (p->is_variable()
-                        ? p->var_value()->type()
-                        : p->result_var_value()->type());
-             if (t != NULL
-                 && Type::traverse(t, traverse) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
-         if (p->is_variable()
-             && ((traverse_mask & Traverse::traverse_types) != 0
-                 || (traverse_mask & Traverse::traverse_expressions) != 0))
-           {
-             if (p->var_value()->traverse_expression(traverse)
-                 == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
+             if (p->is_result_variable()
+                 || p->var_value()->has_type())
+               {
+                 Type* tv = (p->is_variable()
+                             ? p->var_value()->type()
+                             : p->result_var_value()->type());
+                 if (tv != NULL
+                     && Type::traverse(tv, traverse) == TRAVERSE_EXIT)
+                   return TRAVERSE_EXIT;
+               }
            }
+         if (t == TRAVERSE_CONTINUE
+             && (traverse_mask & e_or_t_or_s) != 0
+             && p->is_variable())
+           t = p->var_value()->traverse_expression(traverse, traverse_mask);
          break;
 
        case Named_object::NAMED_OBJECT_FUNC:
          if ((traverse_mask & Traverse::traverse_functions) != 0)
-           {
-             int t = traverse->function(p);
-             if (t == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-             else if (t == TRAVERSE_SKIP_COMPONENTS)
-               break;
-           }
-
-         if ((traverse_mask
-              & (Traverse::traverse_variables
-                 | Traverse::traverse_constants
-                 | Traverse::traverse_functions
-                 | Traverse::traverse_blocks
-                 | Traverse::traverse_statements
-                 | Traverse::traverse_expressions
-                 | Traverse::traverse_types)) != 0)
-           {
-             if (p->func_value()->traverse(traverse) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
+           t = traverse->function(p);
+
+         if (t == TRAVERSE_CONTINUE
+             && (traverse_mask
+                 & (Traverse::traverse_variables
+                    | Traverse::traverse_constants
+                    | Traverse::traverse_functions
+                    | Traverse::traverse_blocks
+                    | Traverse::traverse_statements
+                    | Traverse::traverse_expressions
+                    | Traverse::traverse_types)) != 0)
+           t = p->func_value()->traverse(traverse);
          break;
 
        case Named_object::NAMED_OBJECT_PACKAGE:
          // These are traversed in Gogo::traverse.
-         gcc_assert(is_global);
+         go_assert(is_global);
          break;
 
        case Named_object::NAMED_OBJECT_TYPE:
-         if ((traverse_mask & Traverse::traverse_types) != 0
-             || (traverse_mask & Traverse::traverse_expressions) != 0)
-           {
-             if (Type::traverse(p->type_value(), traverse) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
+         if ((traverse_mask & e_or_t) != 0)
+           t = Type::traverse(p->type_value(), traverse);
          break;
 
        case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
        case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
        case Named_object::NAMED_OBJECT_UNKNOWN:
+       case Named_object::NAMED_OBJECT_ERRONEOUS:
          break;
 
        case Named_object::NAMED_OBJECT_SINK:
        default:
-         gcc_unreachable();
+         go_unreachable();
+       }
+
+      if (t == TRAVERSE_EXIT)
+       return TRAVERSE_EXIT;
+    }
+
+  // If we need to traverse types, check the function declarations,
+  // which have types.  We don't need to check the type declarations,
+  // as those are just names.
+  if ((traverse_mask & e_or_t) != 0)
+    {
+      for (Bindings::const_declarations_iterator p =
+            this->begin_declarations();
+          p != this->end_declarations();
+          ++p)
+       {
+         if (p->second->is_function_declaration())
+           {
+             if (Type::traverse(p->second->func_declaration_value()->type(),
+                                traverse)
+                 == TRAVERSE_EXIT)
+               return TRAVERSE_EXIT;
+           }
        }
     }
 
   return TRAVERSE_CONTINUE;
 }
 
+// Class Label.
+
+// Clear any references to this label.
+
+void
+Label::clear_refs()
+{
+  for (std::vector<Bindings_snapshot*>::iterator p = this->refs_.begin();
+       p != this->refs_.end();
+       ++p)
+    delete *p;
+  this->refs_.clear();
+}
+
+// Get the backend representation for a label.
+
+Blabel*
+Label::get_backend_label(Translate_context* context)
+{
+  if (this->blabel_ == NULL)
+    {
+      Function* function = context->function()->func_value();
+      tree fndecl = function->get_decl();
+      Bfunction* bfunction = tree_to_function(fndecl);
+      this->blabel_ = context->backend()->label(bfunction, this->name_,
+                                               this->location_);
+    }
+  return this->blabel_;
+}
+
+// Return an expression for the address of this label.
+
+Bexpression*
+Label::get_addr(Translate_context* context, Location location)
+{
+  Blabel* label = this->get_backend_label(context);
+  return context->backend()->label_address(label, location);
+}
+
+// Class Unnamed_label.
+
+// Get the backend representation for an unnamed label.
+
+Blabel*
+Unnamed_label::get_blabel(Translate_context* context)
+{
+  if (this->blabel_ == NULL)
+    {
+      Function* function = context->function()->func_value();
+      tree fndecl = function->get_decl();
+      Bfunction* bfunction = tree_to_function(fndecl);
+      this->blabel_ = context->backend()->label(bfunction, "",
+                                               this->location_);
+    }
+  return this->blabel_;
+}
+
+// Return a statement which defines this unnamed label.
+
+Bstatement*
+Unnamed_label::get_definition(Translate_context* context)
+{
+  Blabel* blabel = this->get_blabel(context);
+  return context->backend()->label_definition_statement(blabel);
+}
+
+// Return a goto statement to this unnamed label.
+
+Bstatement*
+Unnamed_label::get_goto(Translate_context* context, Location location)
+{
+  Blabel* blabel = this->get_blabel(context);
+  return context->backend()->goto_statement(blabel, location);
+}
+
 // Class Package.
 
 Package::Package(const std::string& name, const std::string& unique_prefix,
-                source_location location)
+                Location location)
   : name_(name), unique_prefix_(unique_prefix), bindings_(new Bindings(NULL)),
     priority_(0), location_(location), used_(false), is_imported_(false),
     uses_sink_alias_(false)
 {
-  gcc_assert(!name.empty() && !unique_prefix.empty());
+  go_assert(!name.empty() && !unique_prefix.empty());
 }
 
 // Set the priority.  We may see multiple priorities for an imported
@@ -4352,11 +5263,14 @@ Traverse::remember_type(const Type* type)
 {
   if (type->is_error_type())
     return true;
-  gcc_assert((this->traverse_mask() & traverse_types) != 0
+  go_assert((this->traverse_mask() & traverse_types) != 0
             || (this->traverse_mask() & traverse_expressions) != 0);
-  // We only have to remember named types, as they are the only ones
-  // we can see multiple times in a traversal.
-  if (type->classification() != Type::TYPE_NAMED)
+  // We mostly only have to remember named types.  But it turns out
+  // that an interface type can refer to itself without using a name
+  // by relying on interface inheritance, as in
+  // type I interface { F() interface{I} }
+  if (type->classification() != Type::TYPE_NAMED
+      && type->classification() != Type::TYPE_INTERFACE)
     return false;
   if (this->types_seen_ == NULL)
     this->types_seen_ = new Types_seen();
@@ -4370,7 +5284,7 @@ Traverse::remember_type(const Type* type)
 bool
 Traverse::remember_expression(const Expression* expression)
 {
-  gcc_assert((this->traverse_mask() & traverse_types) != 0
+  go_assert((this->traverse_mask() & traverse_types) != 0
             || (this->traverse_mask() & traverse_expressions) != 0);
   if (this->expressions_seen_ == NULL)
     this->expressions_seen_ = new Expressions_seen();
@@ -4385,41 +5299,58 @@ Traverse::remember_expression(const Expression* expression)
 int
 Traverse::variable(Named_object*)
 {
-  gcc_unreachable();
+  go_unreachable();
 }
 
 int
 Traverse::constant(Named_object*, bool)
 {
-  gcc_unreachable();
+  go_unreachable();
 }
 
 int
 Traverse::function(Named_object*)
 {
-  gcc_unreachable();
+  go_unreachable();
 }
 
 int
 Traverse::block(Block*)
 {
-  gcc_unreachable();
+  go_unreachable();
 }
 
 int
 Traverse::statement(Block*, size_t*, Statement*)
 {
-  gcc_unreachable();
+  go_unreachable();
 }
 
 int
 Traverse::expression(Expression**)
 {
-  gcc_unreachable();
+  go_unreachable();
 }
 
 int
 Traverse::type(Type*)
 {
-  gcc_unreachable();
+  go_unreachable();
+}
+
+// Class Statement_inserter.
+
+void
+Statement_inserter::insert(Statement* s)
+{
+  if (this->block_ != NULL)
+    {
+      go_assert(this->pindex_ != NULL);
+      this->block_->insert_statement_before(*this->pindex_, s);
+      ++*this->pindex_;
+    }
+  else if (this->var_ != NULL)
+    this->var_->add_preinit_statement(this->gogo_, s);
+  else
+    go_assert(saw_errors());
 }