Go patch committed: Include transitive imports in type descriptor list

Ian Lance Taylor iant@golang.org
Wed Jul 3 13:35:00 GMT 2019


This patch to the Go frontend by Cherry Zhang includes transitive
imports in the type descriptor list.

In https://golang.org/cl/179598, we were using Gogo::packages_, when
compiling the main package, as the list of packages of which we need
to register the type descriptors.  This is not complete.  It only
includes main's direct import and one-level indirect imports.  It does
not include all the packages transitively imported.

To fix that, we need to track all the transitive imports.  We have
almost already done that, for init functions.  However, there may be
packages that don't need init functions but do need to register type
descriptors.  For them, we add a dummy init function to its export
data.  So when we compile the main package we will see all the
transitive imports.  The dummy init functions are not real functions
and are not called.

This fixes https://golang.org/issue/32901.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
-------------- next part --------------
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 272955)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@
-aebd2d6303e4bb970b088e84f6c66279095dfea6
+ae7d7e05bce19aefaa27efe2ee797933aafbef06
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/export.cc
===================================================================
--- gcc/go/gofrontend/export.cc	(revision 272955)
+++ gcc/go/gofrontend/export.cc	(working copy)
@@ -909,6 +909,8 @@ Export::populate_init_graph(Init_graph*
        ++p)
     {
       const Import_init* ii = *p;
+      if (ii->is_dummy())
+        continue;
       std::map<std::string, unsigned>::const_iterator srcit =
           init_idx.find(ii->init_name());
       go_assert(srcit != init_idx.end());
@@ -1007,7 +1009,7 @@ Export::write_imported_init_fns(const st
 
   // Now add edges from the local init function to each of the
   // imported fcns.
-  if (!import_init_fn.empty())
+  if (!import_init_fn.empty() && import_init_fn[0] != '~')
     {
       unsigned src = 0;
       go_assert(init_idx[import_init_fn] == 0);
@@ -1016,6 +1018,8 @@ Export::write_imported_init_fns(const st
            ++p)
 	{
           const Import_init* ii = *p;
+          if (ii->is_dummy())
+            continue;
 	  unsigned sink = init_idx[ii->init_name()];
 	  add_init_graph_edge(&init_graph, src, sink);
 	}
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 272608)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -724,6 +724,9 @@ Gogo::init_imports(std::vector<Bstatemen
        p != this->imported_init_fns_.end();
        ++p)
     {
+      // Don't include dummy inits. They are not real functions.
+      if ((*p)->is_dummy())
+        continue;
       if ((*p)->priority() < 0)
 	go_error_at(Linemap::unknown_location(),
 		    "internal error: failed to set init priority for %s",
@@ -941,7 +944,7 @@ Gogo::build_type_descriptor_list()
   Btype* bat = list_type->field(1)->type()->get_backend(this);
 
   // Create the variable
-  std::string name = this->type_descriptor_list_symbol(this->package_);
+  std::string name = this->type_descriptor_list_symbol(this->pkgpath_symbol());
   Bvariable* bv = this->backend()->implicit_variable(name, name, bt,
                                                      false, true, false,
                                                      0);
@@ -986,20 +989,29 @@ Gogo::register_type_descriptors(std::vec
   Struct_type* list_type = type_descriptor_list_type(1);
   Btype* bt = list_type->get_backend(this);
 
+  // Collect type lists from transitive imports.
+  std::vector<std::string> list_names;
+  for (Import_init_set::iterator it = this->imported_init_fns_.begin();
+       it != this->imported_init_fns_.end();
+       ++it)
+    {
+      std::string pkgpath =
+        this->pkgpath_from_init_fn_name((*it)->init_name());
+      list_names.push_back(this->type_descriptor_list_symbol(pkgpath));
+    }
+  // Add the main package itself.
+  list_names.push_back(this->type_descriptor_list_symbol("main"));
+
   // Build a list of lists.
   std::vector<unsigned long> indexes;
   std::vector<Bexpression*> vals;
   unsigned long i = 0;
-  for (Packages::iterator it = this->packages_.begin();
-       it != this->packages_.end();
-       ++it)
+  for (std::vector<std::string>::iterator p = list_names.begin();
+       p != list_names.end();
+       ++p)
     {
-      if (it->second->pkgpath() == "unsafe")
-        continue;
-
-      std::string name = this->type_descriptor_list_symbol(it->second);
       Bvariable* bv =
-        this->backend()->implicit_variable_reference(name, name, bt);
+        this->backend()->implicit_variable_reference(*p, *p, bt);
       Bexpression* bexpr = this->backend()->var_expression(bv, builtin_loc);
       bexpr = this->backend()->address_expression(bexpr, builtin_loc);
 
@@ -5158,6 +5170,14 @@ Gogo::do_exports()
   else
     prefix = "go";
 
+  std::string init_fn_name;
+  if (this->is_main_package())
+    init_fn_name = "";
+  else if (this->need_init_fn_)
+    init_fn_name = this->get_init_fn_name();
+  else
+    init_fn_name = this->dummy_init_fn_name();
+
   Export exp(&stream);
   exp.register_builtin_types(this);
   exp.export_globals(this->package_name(),
@@ -5165,9 +5185,7 @@ Gogo::do_exports()
 		     pkgpath,
 		     this->packages_,
 		     this->imports_,
-		     (this->need_init_fn_ && !this->is_main_package()
-		      ? this->get_init_fn_name()
-		      : ""),
+		     init_fn_name,
 		     this->imported_init_fns_,
 		     this->package_->bindings());
 
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h	(revision 272955)
+++ gcc/go/gofrontend/gogo.h	(working copy)
@@ -103,6 +103,11 @@ class Import_init
   precursors() const
   { return this->precursor_functions_; }
 
+  // Whether this is a dummy init, which is used only to record transitive import.
+  bool
+  is_dummy() const
+  { return this->init_name_[0] == '~'; }
+
  private:
   // The name of the package being imported.
   std::string package_name_;
@@ -912,13 +917,23 @@ class Gogo
   const std::string&
   get_init_fn_name();
 
+  // Return the name for a dummy init function, which is not a real
+  // function but only for tracking transitive import.
+  std::string
+  dummy_init_fn_name();
+
+  // Return the package path symbol from an init function name, which
+  // can be a real init function or a dummy one.
+  std::string
+  pkgpath_from_init_fn_name(std::string);
+
   // Return the name for a type descriptor symbol.
   std::string
   type_descriptor_name(const Type*, Named_type*);
 
   // Return the name of the type descriptor list symbol of a package.
   std::string
-  type_descriptor_list_symbol(Package*);
+  type_descriptor_list_symbol(std::string);
 
   // Return the name of the list of all type descriptor lists.
   std::string
Index: gcc/go/gofrontend/names.cc
===================================================================
--- gcc/go/gofrontend/names.cc	(revision 272955)
+++ gcc/go/gofrontend/names.cc	(working copy)
@@ -144,7 +144,8 @@
 //
 // The import function for the main package is referenced by C code,
 // and is named __go_init_main.  For other packages it is
-// PKGPATH..import.
+// PKGPATH..import.  If a package doesn't need an init function, it
+// will have a dummy one, named ~PKGPATH.
 //
 // In each pacakge there is a list of all the type descriptors defined
 // in this package.  The name of the list is PKGPATH..types.
@@ -531,6 +532,30 @@ Gogo::get_init_fn_name()
   return this->init_fn_name_;
 }
 
+// Return the name for a dummy init function, which is not a real
+// function but only for tracking transitive import.
+
+std::string
+Gogo::dummy_init_fn_name()
+{
+  return "~" + this->pkgpath_symbol();
+}
+
+// Return the package path symbol from an init function name, which
+// can be a real init function or a dummy one.
+
+std::string
+Gogo::pkgpath_from_init_fn_name(std::string name)
+{
+  go_assert(!name.empty());
+  if (name[0] == '~')
+    return name.substr(1);
+  size_t pos = name.find("..import");
+  if (pos != std::string::npos)
+    return name.substr(0, pos);
+  go_unreachable();
+}
+
 // Return a mangled name for a type.  These names appear in symbol
 // names in the assembler file for things like type descriptors and
 // methods.
@@ -994,9 +1019,9 @@ Gogo::type_descriptor_name(const Type* t
 // Return the name of the type descriptor list symbol of a package.
 
 std::string
-Gogo::type_descriptor_list_symbol(Package* pkg)
+Gogo::type_descriptor_list_symbol(std::string pkgpath)
 {
-  return pkg->pkgpath_symbol() + "..types";
+  return pkgpath + "..types";
 }
 
 // Return the name of the list of all type descriptor lists.  This is


More information about the Gcc-patches mailing list