compiler: finalize methods when importing types
This patch 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 CL 185517.
Fixes golang/go#33013
Change-Id: If168215050fdd31fa43ef125c42e0f8796259d4a
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/185518
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/gogo.cc b/go/gogo.cc
index 3abf7ea..234a4f4 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -3422,24 +3422,6 @@
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
diff --git a/go/gogo.h b/go/gogo.h
index c4d5bab..cb8e25f 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -3556,6 +3556,24 @@
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.
diff --git a/go/import.cc b/go/import.cc
index abf0b54..ad7ba7f 100644
--- a/go/import.cc
+++ b/go/import.cc
@@ -290,10 +290,16 @@
: 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 @@
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
diff --git a/go/import.h b/go/import.h
index db51f72..ea01bbc 100644
--- a/go/import.h
+++ b/go/import.h
@@ -20,6 +20,7 @@
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 @@
// Constructor.
Import(Stream*, Location);
- virtual ~Import()
- {}
+ virtual ~Import();
// Register the builtin types.
void
@@ -423,6 +423,10 @@
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 @@
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_;
};