Go patch committed: Finalize methods when importing types

Message ID CAOyqgcUF1+1z+CJxcOAvV_C_pqoQVXr6VycO62=7+sZRpEFGPQ@mail.gmail.com
State New
Headers show
Series
  • Go patch committed: Finalize methods when importing types
Related show

Commit Message

Ian Lance Taylor July 10, 2019, 6:13 p.m.
This patch to the Go frontend by Than McIntosh changes the compiler to
be more aggressive about finalizing methods on imported types, to
avoid problems with interface types that are imported but remain
unreachable until a later stage in the compilation.

The normal pattern prior to this change was that the import process
would leave imported interface types alone, and rely on
Gogo::finalize_methods to locate and finalize all interface types at a
later point.  This way of doing things was not working in all cases
due to the fact that we can import an interface type that is only
reachable from the body of an inlinable function, meaning that we
can't "find" the type during the methods finalize phase.

The importer's Import::read_types() now makes a pass over all imported
types to finalize methods on any newly imported type, which takes care
of the issue.

New test case for this problem in https://golang.org/cl/185517.

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

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

Ian

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 273359)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@ 
-170ecdf6b2eab8aac2b8c852fa95d3c36d6bf604
+ec754ff4617d564d3dc377121ea9ac5e55f6535a
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 273307)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -3422,24 +3422,6 @@  Gogo::create_function_descriptors()
   this->traverse(&cfd);
 }
 
-// Look for interface types to finalize methods of inherited
-// interfaces.
-
-class Finalize_methods : public Traverse
-{
- public:
-  Finalize_methods(Gogo* gogo)
-    : Traverse(traverse_types),
-      gogo_(gogo)
-  { }
-
-  int
-  type(Type*);
-
- private:
-  Gogo* gogo_;
-};
-
 // Finalize the methods of an interface type.
 
 int
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h	(revision 273307)
+++ gcc/go/gofrontend/gogo.h	(working copy)
@@ -3556,6 +3556,24 @@  class Traverse
   Expressions_seen* expressions_seen_;
 };
 
+// This class looks for interface types to finalize methods of inherited
+// interfaces.
+
+class Finalize_methods : public Traverse
+{
+ public:
+  Finalize_methods(Gogo* gogo)
+    : Traverse(traverse_types),
+      gogo_(gogo)
+  { }
+
+  int
+  type(Type*);
+
+ private:
+  Gogo* gogo_;
+};
+
 // A class which makes it easier to insert new statements before the
 // current statement during a traversal.
 
Index: gcc/go/gofrontend/import.cc
===================================================================
--- gcc/go/gofrontend/import.cc	(revision 273307)
+++ gcc/go/gofrontend/import.cc	(working copy)
@@ -290,10 +290,16 @@  Import::Import(Stream* stream, Location
   : gogo_(NULL), stream_(stream), location_(location), package_(NULL),
     add_to_globals_(false), packages_(), type_data_(), type_pos_(0),
     type_offsets_(), builtin_types_((- SMALLEST_BUILTIN_CODE) + 1),
-    types_(), version_(EXPORT_FORMAT_UNKNOWN)
+    types_(), finalizer_(NULL), version_(EXPORT_FORMAT_UNKNOWN)
 {
 }
 
+Import::~Import()
+{
+  if (this->finalizer_ != NULL)
+    delete this->finalizer_;
+}
+
 // Import the data in the associated stream.
 
 Package*
@@ -672,9 +678,40 @@  Import::read_types()
 	this->gogo_->add_named_type(nt);
     }
 
+  // Finalize methods for any imported types. This is done after most of
+  // read_types() is complete so as to avoid method finalization of a type
+  // whose methods refer to types that are only partially read in.
+  // See issue #33013 for more on why this is needed.
+  this->finalize_methods();
+
   return true;
 }
 
+void
+Import::finalize_methods()
+{
+  if (this->finalizer_ == NULL)
+    this->finalizer_ = new Finalize_methods(gogo_);
+  Unordered_set(Type*) real_for_named;
+  for (size_t i = 1; i < this->types_.size(); i++)
+    {
+      Type* type = this->types_[i];
+      if (type != NULL && type->named_type() != NULL)
+        {
+          this->finalizer_->type(type);
+          real_for_named.insert(type->named_type()->real_type());
+        }
+    }
+  for (size_t i = 1; i < this->types_.size(); i++)
+    {
+      Type* type = this->types_[i];
+      if (type != NULL
+          && type->named_type() == NULL
+          && real_for_named.find(type) == real_for_named.end())
+        this->finalizer_->type(type);
+    }
+}
+
 // Import a constant.
 
 void
Index: gcc/go/gofrontend/import.h
===================================================================
--- gcc/go/gofrontend/import.h	(revision 273307)
+++ gcc/go/gofrontend/import.h	(working copy)
@@ -20,6 +20,7 @@  class Expression;
 class Import_function_body;
 class Temporary_statement;
 class Unnamed_label;
+class Finalize_methods;
 
 // Expressions can be imported either directly from import data (for
 // simple constant expressions that can appear in a const declaration
@@ -207,8 +208,7 @@  class Import : public Import_expression
   // Constructor.
   Import(Stream*, Location);
 
-  virtual ~Import()
-  {}
+  virtual ~Import();
 
   // Register the builtin types.
   void
@@ -423,6 +423,10 @@  class Import : public Import_expression
     return true;
   }
 
+  // Finalize methods for newly imported types.
+  void
+  finalize_methods();
+
   // The general IR.
   Gogo* gogo_;
   // The stream from which to read import data.
@@ -446,6 +450,8 @@  class Import : public Import_expression
   std::vector<Named_type*> builtin_types_;
   // Mapping from exported type codes to Type structures.
   std::vector<Type*> types_;
+  // Helper for finalizing methods.
+  Finalize_methods* finalizer_;
   // Version of export data we're reading.
   Export_data_version version_;
 };