compiler: permit inlining references to global variables
authorIan Lance Taylor <ian@gcc.gnu.org>
Mon, 3 Jun 2019 23:04:23 +0000 (23:04 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Mon, 3 Jun 2019 23:04:23 +0000 (23:04 +0000)
    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.

    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/177920

From-SVN: r271891

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/export.cc
gcc/go/gofrontend/export.h
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/import.cc
gcc/go/gofrontend/import.h

index b1b36b2ac945461c491f6453610a3f67428e2b8f..43df2f7a5de3900dc1888ba38d16a5e1e907f899 100644 (file)
@@ -1,4 +1,4 @@
-95784e8eec75cfeb2363fb22b51085380e564af9
+37a47e4691b4602dd167f82c64a6569019584a80
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 066455a4ee94891d03e795a409b8771317d12c9d..108fdac22e57b3c8d2540f2c6ebe355189240900 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "gogo.h"
 #include "types.h"
+#include "expressions.h"
 #include "statements.h"
 #include "export.h"
 
@@ -89,13 +90,88 @@ typedef Unordered_map_hash(const Type*, int, Type_hash_alias_identical,
 
 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 @@ Export::export_globals(const std::string& package_name,
   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 @@ Export::export_globals(const std::string& package_name,
       // 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);
+    }
+
+  // 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);
+       }
     }
 
-  std::sort(exports.begin(), exports.end(), Sort_bindings());
+  // 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 @@ Export::export_globals(const std::string& package_name,
   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 @@ Export::write_imports(const std::map<std::string, Package*>& imports,
 
   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 @@ Export::write_imports(const std::map<std::string, Package*>& imports,
       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 @@ Export::write_imports(const std::map<std::string, Package*>& imports,
       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 @@ Export::write_unsigned(unsigned value)
   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
index 69fbd6e3bdb1a5e73d4d1382a1b42e31994aa9d2..e3932d48130136ee3b0b5805b0e0fe6396050fec 100644 (file)
@@ -201,6 +201,10 @@ class Export : public String_dump
   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 @@ class Export : public String_dump
   // 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 @@ class Export_function_body : public String_dump
   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
index 9d8c085aead90fb94e4f003952754ff02e154f9c..6aca5f86ec773df49f88834055e19d6bcb23d915 100644 (file)
@@ -87,6 +87,27 @@ Expression::do_export(Export_function_body*) const
   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 @@ Var_expression::do_address_taken(bool escapes)
     }
 }
 
-// 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 @@ Expression::import_expression(Import_expression* imp, Location loc)
     }
   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.
index ea98facf5dc27f6a21a24b63536b2b4818dc5cd2..e17e8dc0c7f5723113cd0a71fa1b491dc94cebd6 100644 (file)
@@ -1201,6 +1201,10 @@ class Expression
   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 @@ class Expression
   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 @@ class Var_expression : public Expression
   { return this; }
 
   int
-  do_inlining_cost() const;
+  do_inlining_cost() const
+  { return 1; }
 
   void
   do_export(Export_function_body*) const;
index e94c5679d6d62f732a8981eb14a61ddcdf50d7b1..b97367ca066de25189e6c8862dda480c1d1450a2 100644 (file)
@@ -5182,7 +5182,7 @@ Function::Function(Function_type* type, Named_object* enclosing, Block* block,
     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 @@ Function::defer_stack(Location location)
 // 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 @@ Function::export_func(Export* exp, const std::string& name) const
 // 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 @@ Function::export_func_with_type(Export* exp, const std::string& name,
       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 @@ Function::export_func_with_type(Export* exp, const std::string& name,
 
 // 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 @@ Function::import_func(Import* imp, std::string* pname,
       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 @@ Function::import_func(Import* imp, std::string* pname,
        {
          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 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
       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 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
       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 @@ Variable::Variable(Type* type, Expression* init, bool is_global,
     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 @@ Variable::get_init_block(Gogo* gogo, Named_object* function,
 // 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 @@ Variable::export_var(Export* exp, const std::string& name) const
 
 // 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 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
                  && 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 @@ Named_object::export_named_object(Export* exp) const
       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 @@ Named_object::export_named_object(Export* exp) const
       go_unreachable();
 
     case NAMED_OBJECT_FUNC:
-      this->func_value()->export_func(exp, this->name_);
+      this->func_value()->export_func(exp, this);
       break;
     }
 }
index fd28ed1c3df51820a362c2a7f9fa07e6bdb6e1d9..5b77d6d9327ef92e76cc11b8a83ba00b7181e632 100644 (file)
@@ -1486,6 +1486,11 @@ class Function
   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 @@ class Function
 
   // 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 @@ class Function
   // 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 @@ class Function_declaration
 
   // 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 @@ class Variable
     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 @@ class Variable
 
   // 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 @@ class Variable
   // 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_;
index c1982eb4300951dfbd9a3dcb10f8e5d5c2f76d5e..ff92b8248d6c02bbf1909094bcb81ea8dc0a34ce 100644 (file)
@@ -288,8 +288,8 @@ Import::find_object_export_data(const std::string& filename,
 
 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 @@ Import::read_one_import()
   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 @@ Import::read_one_indirect_import()
   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 @@ void
 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 @@ Import::import_var()
 // 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 @@ Import::import_func(Package* package)
        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 @@ Import::import_func(Package* package)
     }
   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 @@ Import::import_func(Package* package)
     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 @@ Import::read_identifier()
   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
index c46a37e0ce96278136a38231fe79f22718200041..ab30aed0ae5ea51ac5ee5bd4787304e6c09a583d 100644 (file)
@@ -72,6 +72,14 @@ class Import_expression
   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 @@ class Import : public Import_expression
   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 @@ class Import : public Import_expression
   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 @@ class Import : public Import_expression
   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 @@ class Import : public Import_expression
   import_var();
 
   // Import a function.
-  Named_object*
+  void
   import_func(Package*);
 
   // Parse a type definition.
@@ -401,6 +432,8 @@ class Import : public Import_expression
   // 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 @@ class Import_function_body : public Import_expression
   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 @@ class Import_function_body : public Import_expression
   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