compiler: permit inlining references to global variables

This requires tracking all references to unexported variables, so that
we can make them global symbols in the object file, and can export
them so that other compilations can see the right definition for their
own inline bodies.

This introduces a syntax for referencing names defined in other
packages: a <pNN> prefix, where NN is the package index.  This will
need to be added to gccgoimporter, but I didn't do it yet since it
isn't yet possible to create an object for which gccgoimporter will
see a <pNN> prefix.

This increases the number of inlinable functions in the standard
library from 181 to 215, adding functions like context.Background.

Change-Id: If21e548e635be1c5b0df64fa072bc7b209f08f73
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/177920
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/go/export.cc b/go/export.cc
index 066455a..108fdac 100644
--- a/go/export.cc
+++ b/go/export.cc
@@ -11,6 +11,7 @@
 
 #include "gogo.h"
 #include "types.h"
+#include "expressions.h"
 #include "statements.h"
 #include "export.h"
 
@@ -89,13 +90,88 @@
 
 static Type_refs type_refs;
 
+// A traversal class to collect functions and global variables
+// referenced by inlined functions.
+
+class Collect_references_from_inline : public Traverse
+{
+ public:
+  Collect_references_from_inline(Unordered_set(Named_object*)* exports,
+				 std::vector<Named_object*>* check_inline_refs)
+    : Traverse(traverse_expressions),
+      exports_(exports), check_inline_refs_(check_inline_refs)
+  { }
+
+  int
+  expression(Expression**);
+
+ private:
+  // The set of named objects to export.
+  Unordered_set(Named_object*)* exports_;
+  // Functions we are exporting with inline bodies that need to be checked.
+  std::vector<Named_object*>* check_inline_refs_;
+};
+
+int
+Collect_references_from_inline::expression(Expression** pexpr)
+{
+  const Expression* expr = *pexpr;
+
+  const Var_expression* ve = expr->var_expression();
+  if (ve != NULL)
+    {
+      Named_object* no = ve->named_object();
+      if (no->is_variable() && no->var_value()->is_global())
+	{
+	  this->exports_->insert(no);
+	  no->var_value()->set_is_referenced_by_inline();
+	}
+      return TRAVERSE_CONTINUE;
+    }
+
+  const Func_expression* fe = expr->func_expression();
+  if (fe != NULL)
+    {
+      Named_object* no = fe->named_object();
+      std::pair<Unordered_set(Named_object*)::iterator, bool> ins =
+	this->exports_->insert(no);
+
+      if (no->is_function())
+	no->func_value()->set_is_referenced_by_inline();
+
+      // If ins.second is false then this object was already in
+      // exports_, in which case it was already added to
+      // check_inline_refs_ the first time we added it to exports_, so
+      // we don't need to add it again.
+      if (ins.second
+	  && no->is_function()
+	  && no->func_value()->export_for_inlining())
+	this->check_inline_refs_->push_back(no);
+
+      return TRAVERSE_CONTINUE;
+    }
+
+  return TRAVERSE_CONTINUE;
+}
+
 // A functor to sort Named_object pointers by name.
 
 struct Sort_bindings
 {
   bool
   operator()(const Named_object* n1, const Named_object* n2) const
-  { return n1->name() < n2->name(); }
+  {
+    if (n1->package() != n2->package())
+      {
+	if (n1->package() == NULL)
+	  return true;
+	if (n2->package() == NULL)
+	  return false;
+	return n1->package()->pkgpath() < n2->package()->pkgpath();
+      }
+
+    return n1->name() < n2->name();
+  }
 };
 
 // Return true if we should export NO.
@@ -153,17 +229,26 @@
   if (saw_errors())
     return;
 
-  // Export the symbols in sorted order.  That will reduce cases where
-  // irrelevant changes to the source code affect the exported
-  // interface.
-  std::vector<Named_object*> exports;
-  exports.reserve(bindings->size_definitions());
+  // EXPORTS is the set of objects to export.  CHECK_INLINE_REFS is a
+  // list of exported function with inline bodies that need to be
+  // checked for references to other objects.  Every function on
+  // CHECK_INLINE_REFS is also on EXPORTS.
+  Unordered_set(Named_object*) exports;
+  std::vector<Named_object*> check_inline_refs;
 
   for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
        p != bindings->end_definitions();
        ++p)
-    if (should_export(*p))
-      exports.push_back(*p);
+    {
+      if (should_export(*p))
+	{
+	  exports.insert(*p);
+
+	  if ((*p)->is_function()
+	      && (*p)->func_value()->export_for_inlining())
+	    check_inline_refs.push_back(*p);
+	}
+    }
 
   for (Bindings::const_declarations_iterator p =
 	 bindings->begin_declarations();
@@ -174,15 +259,47 @@
       // supporting C code.  We do not export type declarations.
       if (p->second->is_function_declaration()
 	  && should_export(p->second))
-	exports.push_back(p->second);
+	exports.insert(p->second);
     }
 
-  std::sort(exports.begin(), exports.end(), Sort_bindings());
+  // Look through the bodies of the functions in CHECK_INLINE_REFS to
+  // find other names we may need to export, to satisfy those
+  // references.  Use CHECKED to skip checking function bodies more
+  // than once.
+  Unordered_set(Named_object*) checked;
+  Collect_references_from_inline refs(&exports, &check_inline_refs);
+  while (!check_inline_refs.empty())
+    {
+      Named_object* no = check_inline_refs.back();
+      check_inline_refs.pop_back();
+      std::pair<Unordered_set(Named_object*)::iterator, bool> ins =
+	checked.insert(no);
+      if (ins.second)
+	{
+	  // This traversal may add new objects to EXPORTS and new
+	  // functions to CHECK_INLINE_REFS.
+	  no->func_value()->block()->traverse(&refs);
+	}
+    }
+
+  // Export the symbols in sorted order.  That will reduce cases where
+  // irrelevant changes to the source code affect the exported
+  // interface.
+  std::vector<Named_object*> sorted_exports;
+  sorted_exports.reserve(exports.size());
+
+  for (Unordered_set(Named_object*)::const_iterator p = exports.begin();
+       p != exports.end();
+       ++p)
+    sorted_exports.push_back(*p);
+
+  std::sort(sorted_exports.begin(), sorted_exports.end(), Sort_bindings());
 
   // Assign indexes to all exported types and types referenced by
   // exported types, and collect all packages mentioned.
   Unordered_set(const Package*) type_imports;
-  int unexported_type_index = this->prepare_types(&exports, &type_imports);
+  int unexported_type_index = this->prepare_types(&sorted_exports,
+						  &type_imports);
 
   // Although the export data is readable, at least this version is,
   // it is conceptually a binary format.  Start with a four byte
@@ -223,8 +340,8 @@
   this->write_types(unexported_type_index);
 
   // Write out the non-type export data.
-  for (std::vector<Named_object*>::const_iterator p = exports.begin();
-       p != exports.end();
+  for (std::vector<Named_object*>::const_iterator p = sorted_exports.begin();
+       p != sorted_exports.end();
        ++p)
     {
       if (!(*p)->is_type())
@@ -591,6 +708,7 @@
 
   std::sort(sorted_imports.begin(), sorted_imports.end(), import_compare);
 
+  int package_index = 1;
   for (std::vector<std::pair<std::string, Package*> >::const_iterator p =
 	 sorted_imports.begin();
        p != sorted_imports.end();
@@ -604,7 +722,8 @@
       this->write_string(p->first);
       this->write_c_string("\"\n");
 
-      this->packages_.insert(p->second);
+      this->packages_[p->second] = package_index;
+      package_index++;
     }
 
   // Write out a separate list of indirectly imported packages.
@@ -631,6 +750,9 @@
       this->write_c_string(" ");
       this->write_string((*p)->pkgpath());
       this->write_c_string("\n");
+
+      this->packages_[*p] = package_index;
+      package_index++;
     }
 }
 
@@ -983,6 +1105,19 @@
   this->write_c_string(buf);
 }
 
+// Return the index of a package.
+
+int
+Export::package_index(const Package* pkg) const
+{
+  Unordered_map(const Package *, int)::const_iterator p =
+    this->packages_.find(pkg);
+  go_assert(p != this->packages_.end());
+  int index = p->second;
+  go_assert(index != 0);
+  return index;
+}
+
 // Return the index of a type.
 
 int
diff --git a/go/export.h b/go/export.h
index 69fbd6e..e3932d4 100644
--- a/go/export.h
+++ b/go/export.h
@@ -201,6 +201,10 @@
   void
   write_unsigned(unsigned);
 
+  // Return the index of a package.
+  int
+  package_index(const Package* p) const;
+
  private:
   Export(const Export&);
   Export& operator=(const Export&);
@@ -255,7 +259,7 @@
   // Index number of next type.
   int type_index_;
   // Packages we have written out.
-  Unordered_set(const Package*) packages_;
+  Unordered_map(const Package*, int) packages_;
 };
 
 // An export streamer that puts the export stream in a named section.
@@ -354,6 +358,11 @@
   decrement_indent()
   { --this->indent_; }
 
+  // Return the index of a package.
+  int
+  package_index(const Package* p) const
+  { return this->exp_->package_index(p); }
+
   // Return a reference to the completed body.
   const std::string&
   body() const
diff --git a/go/expressions.cc b/go/expressions.cc
index 9d8c085..6aca5f8 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -87,6 +87,27 @@
   go_unreachable();
 }
 
+// Write a name to the export data.
+
+void
+Expression::export_name(Export_function_body* efb, const Named_object* no)
+{
+  if (no->package() != NULL)
+    {
+      char buf[50];
+      snprintf(buf, sizeof buf, "<p%d>", efb->package_index(no->package()));
+      efb->write_c_string(buf);
+    }
+
+  if (!Gogo::is_hidden_name(no->name()))
+    efb->write_string(no->name());
+  else
+    {
+      efb->write_c_string(".");
+      efb->write_string(Gogo::unpack_hidden_name(no->name()));
+    }
+}
+
 // Give an error saying that the value of the expression is not used.
 
 void
@@ -842,29 +863,16 @@
     }
 }
 
-// The cost to inline a variable reference.  We currently only support
-// references to parameters and local variables.
-
-int
-Var_expression::do_inlining_cost() const
-{
-  if (this->variable_->is_variable())
-    {
-      if (!this->variable_->var_value()->is_global())
-	return 1;
-    }
-  else if (this->variable_->is_result_variable())
-    return 1;
-
-  return 0x100000;
-}
-
 // Export a reference to a variable.
 
 void
 Var_expression::do_export(Export_function_body* efb) const
 {
-  efb->write_string(Gogo::unpack_hidden_name(this->variable_->name()));
+  Named_object* no = this->variable_;
+  if (no->is_result_variable() || !no->var_value()->is_global())
+    efb->write_string(Gogo::unpack_hidden_name(no->name()));
+  else
+    Expression::export_name(efb, no);
 }
 
 // Get the backend representation for a reference to a variable.
@@ -17535,26 +17543,55 @@
     }
   if (ifb->saw_error())
     return Expression::make_error(loc);
-  std::string id = ifb->read_identifier();
-  if (id.empty())
+  return Expression::import_identifier(ifb, loc);
+}
+
+// Import an identifier in an expression.  This is a reference to a
+// variable or function.
+
+Expression*
+Expression::import_identifier(Import_function_body* ifb, Location loc)
+{
+  std::string id;
+  Package* pkg;
+  bool is_exported;
+  if (!Import::read_qualified_identifier(ifb, &id, &pkg, &is_exported))
     {
       if (!ifb->saw_error())
-	go_error_at(imp->location(),
-		    "import error: expected identifier at %lu",
+	go_error_at(ifb->location(),
+		    "import error for %qs: bad qualified identifier at %lu",
+		    ifb->name().c_str(),
 		    static_cast<unsigned long>(ifb->off()));
       ifb->set_saw_error();
       return Expression::make_error(loc);
     }
-  Named_object* var = ifb->block()->bindings()->lookup(id);
-  if (var == NULL)
+
+  Named_object* no = NULL;
+  if (pkg == NULL && is_exported)
+    no = ifb->block()->bindings()->lookup(id);
+  if (no == NULL)
+    {
+      const Package* ipkg = pkg;
+      if (ipkg == NULL)
+	ipkg = ifb->function()->package();
+      if (!is_exported)
+	id = '.' + ipkg->pkgpath() + '.' + id;
+      no = ipkg->bindings()->lookup(id);
+    }
+  if (no == NULL)
+    no = ifb->gogo()->lookup_global(id.c_str());
+
+  if (no == NULL)
     {
       if (!ifb->saw_error())
-	go_error_at(imp->location(), "import error: lookup of %qs failed",
-		    id.c_str());
+	go_error_at(ifb->location(),
+		    "import error for %qs: lookup of %qs failed",
+		    ifb->name().c_str(), id.c_str());
       ifb->set_saw_error();
       return Expression::make_error(loc);
     }
-  return Expression::make_var_reference(var, loc);
+
+  return Expression::make_var_reference(no, loc);
 }
 
 // Class Expression_list.
diff --git a/go/expressions.h b/go/expressions.h
index ea98fac..e17e8dc 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -1201,6 +1201,10 @@
   void
   report_error(const char*);
 
+  // Write a name to export data.
+  static void
+  export_name(Export_function_body* efb, const Named_object*);
+
   // Child class implements dumping to a dump context.
   virtual void
   do_dump_expression(Ast_dump_context*) const = 0;
@@ -1246,6 +1250,9 @@
   static Expression*
   convert_interface_to_type(Type*, Expression*, Location);
 
+  static Expression*
+  import_identifier(Import_function_body*, Location);
+
   // The expression classification.
   Expression_classification classification_;
   // The location in the input file.
@@ -1410,7 +1417,8 @@
   { return this; }
 
   int
-  do_inlining_cost() const;
+  do_inlining_cost() const
+  { return 1; }
 
   void
   do_export(Export_function_body*) const;
diff --git a/go/gogo.cc b/go/gogo.cc
index e94c567..b97367c 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -5182,7 +5182,7 @@
     calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false),
     calls_defer_retaddr_(false), is_type_specific_function_(false),
     in_unique_section_(false), export_for_inlining_(false),
-    is_inline_only_(false)
+    is_inline_only_(false), is_referenced_by_inline_(false)
 {
 }
 
@@ -5534,12 +5534,12 @@
 // Export the function.
 
 void
-Function::export_func(Export* exp, const std::string& name) const
+Function::export_func(Export* exp, const Named_object* no) const
 {
   Block* block = NULL;
   if (this->export_for_inlining())
     block = this->block_;
-  Function::export_func_with_type(exp, name, this->type_, this->results_,
+  Function::export_func_with_type(exp, no, this->type_, this->results_,
 				  this->is_method() && this->nointerface(),
 				  block, this->location_);
 }
@@ -5547,7 +5547,7 @@
 // Export a function with a type.
 
 void
-Function::export_func_with_type(Export* exp, const std::string& name,
+Function::export_func_with_type(Export* exp, const Named_object* no,
 				const Function_type* fntype,
 				Function::Results* result_vars,
 				bool nointerface, Block* block, Location loc)
@@ -5571,7 +5571,21 @@
       exp->write_c_string(") ");
     }
 
-  exp->write_string(name);
+  if (no->package() != NULL && !fntype->is_method())
+    {
+      char buf[50];
+      snprintf(buf, sizeof buf, "<p%d>", exp->package_index(no->package()));
+      exp->write_c_string(buf);
+    }
+
+  const std::string& name(no->name());
+  if (!Gogo::is_hidden_name(name))
+    exp->write_string(name);
+  else
+    {
+      exp->write_c_string(".");
+      exp->write_string(Gogo::unpack_hidden_name(name));
+    }
 
   exp->write_c_string(" (");
   const Typed_identifier_list* parameters = fntype->parameters();
@@ -5677,8 +5691,9 @@
 
 // Import a function.
 
-void
+bool
 Function::import_func(Import* imp, std::string* pname,
+		      Package** ppkg, bool* pis_exported,
 		      Typed_identifier** preceiver,
 		      Typed_identifier_list** pparameters,
 		      Typed_identifier_list** presults,
@@ -5711,7 +5726,13 @@
       imp->require_c_string(") ");
     }
 
-  *pname = imp->read_identifier();
+  if (!Import::read_qualified_identifier(imp, pname, ppkg, pis_exported))
+    {
+      go_error_at(imp->location(),
+		  "import error at %d: bad function name in export data",
+		  imp->pos());
+      return false;
+    }
 
   Typed_identifier_list* parameters;
   *is_varargs = false;
@@ -5812,11 +5833,13 @@
 	{
 	  go_error_at(imp->location(), "invalid inline function length %s",
 		      lenstr.c_str());
-	  return;
+	  return false;
 	}
 
       *body = imp->read(static_cast<size_t>(llen));
     }
+
+  return true;
 }
 
 // Get the backend representation.
@@ -5829,7 +5852,10 @@
       unsigned int flags = 0;
       bool is_init_fn = false;
       if (no->package() != NULL)
-        ;
+	{
+	  // Functions defined in other packages must be visible.
+	  flags |= Backend::function_is_visible;
+	}
       else if (this->enclosing_ != NULL || Gogo::is_thunk(no))
         ;
       else if (Gogo::unpack_hidden_name(no->name()) == "init"
@@ -5877,6 +5903,11 @@
       else
 	asm_name = gogo->function_asm_name(no->name(), no->package(), rtype);
 
+      // If an inline body refers to this function, then it
+      // needs to be visible in the symbol table.
+      if (this->is_referenced_by_inline_)
+	flags |= Backend::function_is_visible;
+
       // If a function calls the predeclared recover function, we
       // can't inline it, because recover behaves differently in a
       // function passed directly to defer.  If this is a recover
@@ -7015,7 +7046,8 @@
     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),
-    in_unique_section_(false), toplevel_decl_(NULL)
+    in_unique_section_(false), is_referenced_by_inline_(false),
+    toplevel_decl_(NULL)
 {
   go_assert(type != NULL || init != NULL);
   go_assert(!is_parameter || init == NULL);
@@ -7497,11 +7529,25 @@
 // Export the variable
 
 void
-Variable::export_var(Export* exp, const std::string& name) const
+Variable::export_var(Export* exp, const Named_object* no) const
 {
   go_assert(this->is_global_);
   exp->write_c_string("var ");
-  exp->write_string(name);
+  if (no->package() != NULL)
+    {
+      char buf[50];
+      snprintf(buf, sizeof buf, "<p%d>", exp->package_index(no->package()));
+      exp->write_c_string(buf);
+    }
+
+  if (!Gogo::is_hidden_name(no->name()))
+    exp->write_string(no->name());
+  else
+    {
+      exp->write_c_string(".");
+      exp->write_string(Gogo::unpack_hidden_name(no->name()));
+    }
+
   exp->write_c_string(" ");
   exp->write_type(this->type());
   exp->write_c_string("\n");
@@ -7509,15 +7555,23 @@
 
 // Import a variable.
 
-void
-Variable::import_var(Import* imp, std::string* pname, Type** ptype)
+bool
+Variable::import_var(Import* imp, std::string* pname, Package** ppkg,
+		     bool* pis_exported, Type** ptype)
 {
   imp->require_c_string("var ");
-  *pname = imp->read_identifier();
+  if (!Import::read_qualified_identifier(imp, pname, ppkg, pis_exported))
+    {
+      go_error_at(imp->location(),
+		  "import error at %d: bad variable name in export data",
+		  imp->pos());
+      return false;
+    }
   imp->require_c_string(" ");
   *ptype = imp->read_type();
   imp->require_semicolon_if_old_version();
   imp->require_c_string("\n");
+  return true;
 }
 
 // Convert a variable to the backend representation.
@@ -7568,6 +7622,18 @@
 		  && var_name == "runtime.writeBarrier")
 		is_hidden = false;
 
+	      // If an inline body refers to this variable, then it
+	      // needs to be visible in the symbol table.
+	      if (this->is_referenced_by_inline_)
+		is_hidden = false;
+
+	      // If this variable is in a different package, then it
+	      // can't be treated as a hidden symbol.  This case can
+	      // arise when an inlined function refers to a
+	      // package-scope unexported variable.
+	      if (package != NULL)
+		is_hidden = false;
+
 	      bvar = backend->global_variable(var_name,
 					      asm_name,
 					      btype,
@@ -8145,11 +8211,11 @@
       break;
 
     case NAMED_OBJECT_FUNC_DECLARATION:
-      this->func_declaration_value()->export_func(exp, this->name_);
+      this->func_declaration_value()->export_func(exp, this);
       break;
 
     case NAMED_OBJECT_VAR:
-      this->var_value()->export_var(exp, this->name_);
+      this->var_value()->export_var(exp, this);
       break;
 
     case NAMED_OBJECT_RESULT_VAR:
@@ -8157,7 +8223,7 @@
       go_unreachable();
 
     case NAMED_OBJECT_FUNC:
-      this->func_value()->export_func(exp, this->name_);
+      this->func_value()->export_func(exp, this);
       break;
     }
 }
diff --git a/go/gogo.h b/go/gogo.h
index fd28ed1..5b77d6d 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -1486,6 +1486,11 @@
   set_is_inline_only()
   { this->is_inline_only_ = true; }
 
+  // Mark the function as referenced by an inline body.
+  void
+  set_is_referenced_by_inline()
+  { this->is_referenced_by_inline_ = true; }
+
   // Swap with another function.  Used only for the thunk which calls
   // recover.
   void
@@ -1538,17 +1543,18 @@
 
   // Export the function.
   void
-  export_func(Export*, const std::string& name) const;
+  export_func(Export*, const Named_object*) const;
 
   // Export a function with a type.
   static void
-  export_func_with_type(Export*, const std::string& name,
+  export_func_with_type(Export*, const Named_object*,
 			const Function_type*, Results*, bool nointerface,
 			Block* block, Location);
 
-  // Import a function.
-  static void
-  import_func(Import*, std::string* pname, Typed_identifier** receiver,
+  // Import a function.  Reports whether the import succeeded.
+  static bool
+  import_func(Import*, std::string* pname, Package** pkg,
+	      bool* is_exported, Typed_identifier** receiver,
 	      Typed_identifier_list** pparameters,
 	      Typed_identifier_list** presults, bool* is_varargs,
 	      bool* nointerface, std::string* body);
@@ -1628,6 +1634,9 @@
   // True if this function is inline only: if it should not be emitted
   // if it is not inlined.
   bool is_inline_only_ : 1;
+  // True if this function is referenced from an inlined body that
+  // will be put into the export data.
+  bool is_referenced_by_inline_ : 1;
 };
 
 // A snapshot of the current binding state.
@@ -1768,9 +1777,9 @@
 
   // Export a function declaration.
   void
-  export_func(Export* exp, const std::string& name) const
+  export_func(Export* exp, const Named_object* no) const
   {
-    Function::export_func_with_type(exp, name, this->fntype_, NULL,
+    Function::export_func_with_type(exp, no, this->fntype_, NULL,
 				    this->is_method() && this->nointerface(),
 				    NULL, this->location_);
   }
@@ -2022,6 +2031,14 @@
     this->in_unique_section_ = true;
   }
 
+  // Mark the variable as referenced by an inline body.
+  void
+  set_is_referenced_by_inline()
+  {
+    go_assert(this->is_global_);
+    this->is_referenced_by_inline_ = true;
+  }
+
   // Return the top-level declaration for this variable.
   Statement*
   toplevel_decl()
@@ -2062,11 +2079,12 @@
 
   // Export the variable.
   void
-  export_var(Export*, const std::string& name) const;
+  export_var(Export*, const Named_object*) const;
 
-  // Import a variable.
-  static void
-  import_var(Import*, std::string* pname, Type** ptype);
+  // Import a variable.  Reports whether the import succeeded.
+  static bool
+  import_var(Import*, std::string* pname, Package** pkg, bool* is_exported,
+	     Type** ptype);
 
  private:
   // The type of a tuple.
@@ -2133,6 +2151,9 @@
   // True if this variable should be put in a unique section.  This is
   // used for field tracking.
   bool in_unique_section_ : 1;
+  // True if this variable is referenced from an inlined body that
+  // will be put into the export data.
+  bool is_referenced_by_inline_ : 1;
   // The top-level declaration for this variable. Only used for local
   // variables. Must be a Temporary_statement if not NULL.
   Statement* toplevel_decl_;
diff --git a/go/import.cc b/go/import.cc
index c1982eb..ff92b82 100644
--- a/go/import.cc
+++ b/go/import.cc
@@ -288,8 +288,8 @@
 
 Import::Import(Stream* stream, Location location)
   : gogo_(NULL), stream_(stream), location_(location), package_(NULL),
-    add_to_globals_(false), type_data_(), type_pos_(0), type_offsets_(),
-    builtin_types_((- SMALLEST_BUILTIN_CODE) + 1),
+    add_to_globals_(false), packages_(), type_data_(), type_pos_(0),
+    type_offsets_(), builtin_types_((- SMALLEST_BUILTIN_CODE) + 1),
     types_(), version_(EXPORT_FORMAT_UNKNOWN)
 {
 }
@@ -487,6 +487,8 @@
   Package* p = this->gogo_->register_package(pkgpath, "",
 					     Linemap::unknown_location());
   p->set_package_name(package_name, this->location());
+
+  this->packages_.push_back(p);
 }
 
 // Read an indirectimport line.
@@ -503,6 +505,8 @@
   Package* p = this->gogo_->register_package(pkgpath, "",
 					     Linemap::unknown_location());
   p->set_package_name(package_name, this->location());
+
+  this->packages_.push_back(p);
 }
 
 // Read the list of import control functions and/or init graph.
@@ -721,12 +725,19 @@
 Import::import_var()
 {
   std::string name;
+  Package* vpkg;
+  bool is_exported;
   Type* type;
-  Variable::import_var(this, &name, &type);
+  if (!Variable::import_var(this, &name, &vpkg, &is_exported, &type))
+    return;
+  if (vpkg == NULL)
+    vpkg = this->package_;
+  if (!is_exported)
+    name = '.' + vpkg->pkgpath() + '.' + name;
   Variable* var = new Variable(type, NULL, true, false, false,
 			       this->location_);
   Named_object* no;
-  no = this->package_->add_variable(name, var);
+  no = vpkg->add_variable(name, var);
   if (this->add_to_globals_)
     this->gogo_->add_dot_import_object(no);
 }
@@ -735,18 +746,26 @@
 // THIS->PACKAGE_, but it will be different for a method associated
 // with a type defined in a different package.
 
-Named_object*
+void
 Import::import_func(Package* package)
 {
   std::string name;
+  Package* fpkg;
+  bool is_exported;
   Typed_identifier* receiver;
   Typed_identifier_list* parameters;
   Typed_identifier_list* results;
   bool is_varargs;
   bool nointerface;
   std::string body;
-  Function::import_func(this, &name, &receiver, &parameters, &results,
-			&is_varargs, &nointerface, &body);
+  if (!Function::import_func(this, &name, &fpkg, &is_exported, &receiver,
+			     &parameters, &results, &is_varargs, &nointerface,
+			     &body))
+    return;
+  if (fpkg == NULL)
+    fpkg = package;
+  if (!is_exported)
+    name = '.' + fpkg->pkgpath() + '.' + name;
   Function_type *fntype = Type::make_function_type(receiver, parameters,
 						   results, this->location_);
   if (is_varargs)
@@ -768,13 +787,13 @@
 	rtype = rtype->points_to();
 
       if (rtype->is_error_type())
-	return NULL;
+	return;
       else if (rtype->named_type() != NULL)
-	no = rtype->named_type()->add_method_declaration(name, package, fntype,
+	no = rtype->named_type()->add_method_declaration(name, fpkg, fntype,
 							 loc);
       else if (rtype->forward_declaration_type() != NULL)
 	no = rtype->forward_declaration_type()->add_method_declaration(name,
-								       package,
+								       fpkg,
 								       fntype,
 								       loc);
       else
@@ -782,7 +801,7 @@
     }
   else
     {
-      no = package->add_function_declaration(name, fntype, loc);
+      no = fpkg->add_function_declaration(name, fntype, loc);
       if (this->add_to_globals_)
 	this->gogo_->add_dot_import_object(no);
     }
@@ -791,8 +810,6 @@
     no->func_declaration_value()->set_nointerface();
   if (!body.empty() && !no->func_declaration_value()->has_imported_body())
     no->func_declaration_value()->set_imported_body(this, body);
-
-  return no;
 }
 
 // Read a type definition and initialize the entry in this->types_.
@@ -1233,6 +1250,60 @@
   return ret;
 }
 
+// Read a possibly qualified identifier from IMP.  The qualification
+// is <pID>, where ID is a package number.  If the name has a leading
+// '.', it is not exported; otherwise, it is.  Set *NAME, *PKG and
+// *IS_EXPORTED.  Reports whether the read succeeded.
+
+bool
+Import::read_qualified_identifier(Import_expression* imp, std::string* name,
+				  Package** pkg, bool* is_exported)
+{
+  *pkg = NULL;
+  if (imp->match_c_string("<p"))
+    {
+      imp->advance(2);
+      char buf[50];
+      char *pbuf = &buf[0];
+      while (true)
+	{
+	  int next = imp->peek_char();
+	  if (next == -1 || static_cast<size_t>(pbuf - buf) >= sizeof buf - 1)
+	    return false;
+	  if (next == '>')
+	    {
+	      imp->advance(1);
+	      break;
+	    }
+	  *pbuf = static_cast<char>(next);
+	  ++pbuf;
+	  imp->advance(1);
+	}
+
+      *pbuf = '\0';
+      char *end;
+      long index = strtol(buf, &end, 10);
+      if (*end != '\0'
+	  || index <= 0
+	  || static_cast<size_t>(index) > imp->max_package_index())
+	return false;
+
+      *pkg = imp->package_at_index(index);
+      go_assert(*pkg != NULL);
+    }
+
+  *is_exported = true;
+  if (imp->match_c_string("."))
+    {
+      imp->advance(1);
+      *is_exported = false;
+    }
+
+  *name = imp->read_identifier();
+
+  return !name->empty();
+}
+
 // Read a name from the stream.
 
 std::string
diff --git a/go/import.h b/go/import.h
index c46a37e..ab30aed 100644
--- a/go/import.h
+++ b/go/import.h
@@ -72,6 +72,14 @@
   virtual Type*
   read_type() = 0;
 
+  // Return the maximum valid package index.
+  virtual size_t
+  max_package_index() const = 0;
+
+  // Return the package for a package index.
+  virtual Package*
+  package_at_index(int index) = 0;
+
   // Return the version number of the export data we're reading.
   virtual Export_data_version
   version() const = 0;
@@ -257,6 +265,11 @@
   advance(size_t skip)
   { this->stream_->advance(skip); }
 
+  // Stream position, for error reporting.
+  int
+  pos()
+  { return this->stream_->pos(); }
+
   // Return the version number of the export data we're reading.
   Export_data_version
   version() const { return this->version_; }
@@ -279,6 +292,18 @@
   std::string
   read_name();
 
+  // Return the maximum valid package index.  This is the size of
+  // packages_ because we will subtract 1 in package_at_index.
+  size_t
+  max_package_index() const
+  { return this->packages_.size(); }
+
+  // Return the package at an index.  (We subtract 1 because package
+  // index 0 is not used.)
+  Package*
+  package_at_index(int index)
+  { return this->packages_.at(index - 1); }
+
   // Read a type.
   Type*
   read_type();
@@ -304,6 +329,12 @@
   ifb()
   { return NULL; }
 
+  // Read a qualified identifier from an Import_expression.  Sets
+  // *NAME, *PKG, and *IS_EXPORTED, and reports whether it succeeded.
+  static bool
+  read_qualified_identifier(Import_expression*, std::string* name,
+			    Package** pkg, bool* is_exported);
+
  private:
   static Stream*
   try_package_in_directory(const std::string&, Location);
@@ -360,7 +391,7 @@
   import_var();
 
   // Import a function.
-  Named_object*
+  void
   import_func(Package*);
 
   // Parse a type definition.
@@ -401,6 +432,8 @@
   // Whether to add new objects to the global scope, rather than to a
   // package scope.
   bool add_to_globals_;
+  // Mapping from package index to package.
+  std::vector<Package*> packages_;
   // All type data.
   std::string type_data_;
   // Position of type data in the stream.
@@ -567,6 +600,11 @@
   location() const
   { return this->imp_->location(); }
 
+  // The function we are importing.
+  Named_object*
+  function() const
+  { return this->named_object_; }
+
   // A reference to the body we are reading.
   const std::string&
   body() const
@@ -662,6 +700,16 @@
   ifb()
   { return this; }
 
+  // Return the maximum valid package index.
+  size_t
+  max_package_index() const
+  { return this->imp_->max_package_index(); }
+
+  // Return the package at an index.
+  Package*
+  package_at_index(int index)
+  { return this->imp_->package_at_index(index); }
+
   // Return whether we have seen an error.
   bool
   saw_error() const