compiler: list indirect imports separately in export data
authorIan Lance Taylor <ian@gcc.gnu.org>
Thu, 18 Oct 2018 23:22:01 +0000 (23:22 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Thu, 18 Oct 2018 23:22:01 +0000 (23:22 +0000)
    Previously when export data referred to a type that was not defined in
    a directly imported package, we would write the package name as
    additional information in the type's export data.  That approach
    required all type information to be read in order.  This patch changes
    the compiler to find all references to indirectly imported packages,
    and write them out as an indirectimport line in the import data.  This
    will permit us to read exported type data out of order.

    The type traversal used to find indirect imports is a little more
    complicated than necessary in preparation for later patches in this
    series.

    Reviewed-on: https://go-review.googlesource.com/c/143020

From-SVN: r265296

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/export.cc
gcc/go/gofrontend/export.h
gcc/go/gofrontend/import.cc
gcc/go/gofrontend/import.h
gcc/go/gofrontend/types.cc
gcc/go/gofrontend/types.h
libgo/go/go/internal/gccgoimporter/parser.go

index a2cf0af0634e4105e85ef4add09de6099d55a82a..28b39847bfa072130bd764dc27575a95c16f9e95 100644 (file)
@@ -1,4 +1,4 @@
-84531ef21230307773daa438a50bf095edcdbf93
+9c985ce6f76dd65b8eb0e4b03c09ad0100712e04
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index db57ab58850f5c93ea3e0d918c75a495e0327d09..da3d67f115215f3ee5eb9473ba645e4fe58519b3 100644 (file)
@@ -143,6 +143,10 @@ Export::export_globals(const std::string& package_name,
 
   std::sort(exports.begin(), exports.end(), Sort_bindings());
 
+  // Find all packages not explicitly imported but mentioned by types.
+  Unordered_set(const Package*) type_imports;
+  this->prepare_types(&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
   // version number.
@@ -169,7 +173,7 @@ Export::export_globals(const std::string& package_name,
 
   this->write_packages(packages);
 
-  this->write_imports(imports);
+  this->write_imports(imports, type_imports);
 
   this->write_imported_init_fns(package_name, import_init_fn,
                                imported_init_fns);
@@ -199,6 +203,179 @@ Export::export_globals(const std::string& package_name,
   this->stream_->write_checksum(s);
 }
 
+// Traversal class to find referenced types.
+
+class Find_types_to_prepare : public Traverse
+{
+ public:
+  Find_types_to_prepare(Unordered_set(const Package*)* imports)
+    : Traverse(traverse_types),
+      imports_(imports)
+  { }
+
+  int
+  type(Type* type);
+
+  // Traverse the components of a function type.
+  void
+  traverse_function(Function_type*);
+
+  // Traverse the methods of a named type, and register its package.
+  void
+  traverse_named_type(Named_type*);
+
+ private:
+  // List of packages we are building.
+  Unordered_set(const Package*)* imports_;
+};
+
+// Traverse a type.
+
+int
+Find_types_to_prepare::type(Type* type)
+{
+  // Skip forwarders.
+  if (type->forward_declaration_type() != NULL)
+    return TRAVERSE_CONTINUE;
+
+  // At this stage of compilation traversing interface types traverses
+  // the final list of methods, but we export the locally defined
+  // methods.  If there is an embedded interface type we need to make
+  // sure to export that.  Check classification, rather than calling
+  // the interface_type method, because we want to handle named types
+  // below.
+  if (type->classification() == Type::TYPE_INTERFACE)
+    {
+      Interface_type* it = type->interface_type();
+      const Typed_identifier_list* methods = it->local_methods();
+      if (methods != NULL)
+       {
+         for (Typed_identifier_list::const_iterator p = methods->begin();
+              p != methods->end();
+              ++p)
+           {
+             if (p->name().empty())
+               Type::traverse(p->type(), this);
+             else
+               this->traverse_function(p->type()->function_type());
+           }
+       }
+      return TRAVERSE_SKIP_COMPONENTS;
+    }
+
+  Named_type* nt = type->named_type();
+  if (nt != NULL)
+    this->traverse_named_type(nt);
+
+  return TRAVERSE_CONTINUE;
+}
+
+// Traverse the types in a function type.  We don't need the function
+// type tself, just the receiver, parameter, and result types.
+
+void
+Find_types_to_prepare::traverse_function(Function_type* type)
+{
+  go_assert(type != NULL);
+  if (this->remember_type(type))
+    return;
+  const Typed_identifier* receiver = type->receiver();
+  if (receiver != NULL)
+    Type::traverse(receiver->type(), this);
+  const Typed_identifier_list* parameters = type->parameters();
+  if (parameters != NULL)
+    parameters->traverse(this);
+  const Typed_identifier_list* results = type->results();
+  if (results != NULL)
+    results->traverse(this);
+}
+
+// Traverse the methods of a named type, and record its package.
+
+void
+Find_types_to_prepare::traverse_named_type(Named_type* nt)
+{
+  const Package* package = nt->named_object()->package();
+  if (package != NULL)
+    this->imports_->insert(package);
+
+  // We have to traverse the methods of named types, because we are
+  // going to export them.  This is not done by ordinary type
+  // traversal.
+  const Bindings* methods = nt->local_methods();
+  if (methods != NULL)
+    {
+      for (Bindings::const_definitions_iterator pm =
+            methods->begin_definitions();
+          pm != methods->end_definitions();
+          ++pm)
+       this->traverse_function((*pm)->func_value()->type());
+
+      for (Bindings::const_declarations_iterator pm =
+            methods->begin_declarations();
+          pm != methods->end_declarations();
+          ++pm)
+       {
+         Named_object* mno = pm->second;
+         if (mno->is_function_declaration())
+           this->traverse_function(mno->func_declaration_value()->type());
+       }
+    }
+}
+
+// Collect all the pacakges we see in types, so that if we refer to
+// any types from indirectly importe packages we can tell the importer
+// about the package.
+
+void
+Export::prepare_types(const std::vector<Named_object*>* exports,
+                     Unordered_set(const Package*)* imports)
+{
+  // Use a single index of the traversal class because traversal
+  // classes keep track of which types they've already seen.  That
+  // lets us avoid type reference loops.
+  Find_types_to_prepare find(imports);
+
+  // Traverse all the exported objects.
+  for (std::vector<Named_object*>::const_iterator p = exports->begin();
+       p != exports->end();
+       ++p)
+    {
+      Named_object* no = *p;
+      switch (no->classification())
+       {
+       case Named_object::NAMED_OBJECT_CONST:
+         {
+           Type* t = no->const_value()->type();
+           if (t != NULL && !t->is_abstract())
+             Type::traverse(t, &find);
+         }
+         break;
+
+       case Named_object::NAMED_OBJECT_TYPE:
+         Type::traverse(no->type_value(), &find);
+         break;
+
+       case Named_object::NAMED_OBJECT_VAR:
+         Type::traverse(no->var_value()->type(), &find);
+         break;
+
+       case Named_object::NAMED_OBJECT_FUNC:
+         find.traverse_function(no->func_value()->type());
+         break;
+
+       case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
+         find.traverse_function(no->func_declaration_value()->type());
+         break;
+
+       default:
+         // We shouldn't see anything else.  If we do we'll give an
+         // error later when we try to actually export it.
+         break;
+       }
+    }
+}
+
 // Sort packages.
 
 static bool
@@ -253,14 +430,19 @@ import_compare(const std::pair<std::string, Package*>& a,
 // Write out the imported packages.
 
 void
-Export::write_imports(const std::map<std::string, Package*>& imports)
+Export::write_imports(const std::map<std::string, Package*>& imports,
+                     const Unordered_set(const Package*)& type_imports)
 {
   // Sort the imports for more consistent output.
+  Unordered_set(const Package*) seen;
   std::vector<std::pair<std::string, Package*> > sorted_imports;
   for (std::map<std::string, Package*>::const_iterator p = imports.begin();
        p != imports.end();
        ++p)
-    sorted_imports.push_back(std::make_pair(p->first, p->second));
+    {
+      sorted_imports.push_back(std::make_pair(p->first, p->second));
+      seen.insert(p->second);
+    }
 
   std::sort(sorted_imports.begin(), sorted_imports.end(), import_compare);
 
@@ -279,6 +461,32 @@ Export::write_imports(const std::map<std::string, Package*>& imports)
 
       this->packages_.insert(p->second);
     }
+
+  // Write out a separate list of indirectly imported packages.
+  std::vector<const Package*> indirect_imports;
+  for (Unordered_set(const Package*)::const_iterator p =
+        type_imports.begin();
+       p != type_imports.end();
+       ++p)
+    {
+      if (seen.find(*p) == seen.end())
+       indirect_imports.push_back(*p);
+    }
+
+  std::sort(indirect_imports.begin(), indirect_imports.end(),
+           packages_compare);
+
+  for (std::vector<const Package*>::const_iterator p =
+        indirect_imports.begin();
+       p != indirect_imports.end();
+       ++p)
+    {
+      this->write_c_string("indirectimport ");
+      this->write_string((*p)->package_name());
+      this->write_c_string(" ");
+      this->write_string((*p)->pkgpath());
+      this->write_c_string("\n");
+    }
 }
 
 void
@@ -602,19 +810,6 @@ Export::write_type(const Type* type)
       s += "\" ";
       this->write_string(s);
 
-      // It is possible that this type was imported indirectly, and is
-      // not in a package in the import list.  If we have not
-      // mentioned this package before, write out the package name
-      // here so that any package importing this one will know it.
-      if (package != NULL
-         && this->packages_.find(package) == this->packages_.end())
-       {
-         this->write_c_string("\"");
-         this->write_string(package->package_name());
-         this->packages_.insert(package);
-         this->write_c_string("\" ");
-       }
-
       // We must add a named type to the table now, since the
       // definition of the type may refer to the named type via a
       // pointer.
index b08bf8513182ae99125d8c84fe0f49947ee300c4..55942818eec931994815b174dd41d3eab4628efb 100644 (file)
@@ -12,6 +12,7 @@
 class Go_sha1_helper;
 class Gogo;
 class Import_init;
+class Named_object;
 class Bindings;
 class Type;
 class Package;
@@ -194,6 +195,11 @@ class Export : public String_dump
   Export(const Export&);
   Export& operator=(const Export&);
 
+  // Prepare types for exporting.
+  void
+  prepare_types(const std::vector<Named_object*>* exports,
+               Unordered_set(const Package*)* imports);
+
   // Write out all known packages.
   void
   write_packages(const std::map<std::string, Package*>& packages);
@@ -210,7 +216,8 @@ class Export : public String_dump
 
   // Write out the imported packages.
   void
-  write_imports(const std::map<std::string, Package*>& imports);
+  write_imports(const std::map<std::string, Package*>& imports,
+               const Unordered_set(const Package*)& type_imports);
 
   // Write out the imported initialization functions and init graph.
   void
index ad30e6ee488e9d7d6093fc2b06d5284c27030afe..8d17df708fc189a54017d9078b971c186ab4b24d 100644 (file)
@@ -397,6 +397,9 @@ Import::import(Gogo* gogo, const std::string& local_name,
       while (stream->match_c_string("import"))
        this->read_one_import();
 
+      while (stream->match_c_string("indirectimport"))
+       this->read_one_indirect_import();
+
       if (stream->match_c_string("init"))
        this->read_import_init_fns(gogo);
 
@@ -458,7 +461,7 @@ Import::read_one_package()
   p->set_package_name(package_name, this->location());
 }
 
-// Read an import line.  We don't actually care about these.
+// Read an import line.
 
 void
 Import::read_one_import()
@@ -480,6 +483,22 @@ Import::read_one_import()
   p->set_package_name(package_name, this->location());
 }
 
+// Read an indirectimport line.
+
+void
+Import::read_one_indirect_import()
+{
+  this->require_c_string("indirectimport ");
+  std::string package_name = this->read_identifier();
+  this->require_c_string(" ");
+  std::string pkgpath = this->read_identifier();
+  this->require_c_string("\n");
+
+  Package* p = this->gogo_->register_package(pkgpath, "",
+                                            Linemap::unknown_location());
+  p->set_package_name(package_name, this->location());
+}
+
 // Read the list of import control functions and/or init graph.
 
 void
index 84f5fc7eb86ef255e71aca44030568cc05db9896..dbdcc8fb4896c8e6e9e357b220c4c8ec42236ad9 100644 (file)
@@ -241,6 +241,10 @@ class Import
   void
   read_one_import();
 
+  // Read an indirectimport line.
+  void
+  read_one_indirect_import();
+
   // Read the import control functions and init graph.
   void
   read_import_init_fns(Gogo*);
index e368ee0a754a6e654c9a5df80f34868dd5adc50c..e766c77ab0afb89e0a58aef7c848bc6ca5ab7499 100644 (file)
@@ -12113,7 +12113,7 @@ Typed_identifier_list::sort_by_name()
 // Traverse types.
 
 int
-Typed_identifier_list::traverse(Traverse* traverse)
+Typed_identifier_list::traverse(Traverse* traverse) const
 {
   for (Typed_identifier_list::const_iterator p = this->begin();
        p != this->end();
index 8d0faad8b8c9c80ac3e2afd500bca1dfa49bc9fd..18cc2575bf544719978934a300add520f350063f 100644 (file)
@@ -1499,7 +1499,7 @@ class Typed_identifier_list
 
   // Traverse types.
   int
-  traverse(Traverse*);
+  traverse(Traverse*) const;
 
   // Return the first and last elements.
   Typed_identifier&
@@ -3056,8 +3056,15 @@ class Interface_type : public Type
     return this->all_methods_ == NULL;
   }
 
-  // Return the list of methods.  This will return NULL for an empty
-  // interface.
+  // Return the list of locally defined methos.  This will return NULL
+  // for an empty interface.  Embedded interfaces will appear in this
+  // list as an entry with no name.
+  const Typed_identifier_list*
+  local_methods() const
+  { return this->parse_methods_; }
+
+  // Return the list of all methods.  This will return NULL for an
+  // empty interface.
   const Typed_identifier_list*
   methods() const;
 
index 5988c5d36738f117319cfb76eb84ab9c762f7466..cd4e1d9288e041ef78ad40d28cf11d336245caa1 100644 (file)
@@ -832,6 +832,7 @@ func (p *parser) parseInitDataDirective() {
 //             "pkgpath" unquotedString ";" |
 //             "prefix" unquotedString ";" |
 //             "import" unquotedString unquotedString string ";" |
+//             "indirectimport" unquotedString unquotedstring ";" |
 //             "func" Func ";" |
 //             "type" Type ";" |
 //             "var" Var ";" |
@@ -875,6 +876,13 @@ func (p *parser) parseDirective() {
                p.parseString()
                p.expectEOL()
 
+       case "indirectimport":
+               p.next()
+               pkgname := p.parseUnquotedString()
+               pkgpath := p.parseUnquotedString()
+               p.getPkg(pkgpath, pkgname)
+               p.expectEOL()
+
        case "func":
                p.next()
                fun := p.parseFunc(p.pkg)