Commit 599b9f72 by Ian Lance Taylor

compiler: compare parse methods when indexing interface types for export

    
    This change fixes a bug in which two interface types were being
    incorrectly commoned (considered identical) in the initial stages of
    writing out types to export data. The indexer does a walk to collect
    candidates for export, inserting types into a table to eliminate
    duplicates; as part of this process a local interface type T1 was
    being commoned with a different interface type T2. This caused a cycle
    in the exported type graph due to the way embedded interfaces are
    handled.
    
    The fix was to add a new flag to the Type::is_identical utility
    routine to request that interface type comparison be done by examining
    the original parse methods, as opposed to the expanded method set,
    then use the new flag when creating the hash map for the exporter.
    
    Fixes golang/go#30659.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/166638

From-SVN: r269634
parent f4390da0
565b5cd0f49a00ca20941ea042c07ebe6ddf3553 946aa5ab2e82d045a2a3b2f18ba2c5b00e957c4b
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -60,6 +60,7 @@ class Type_hash_alias_identical ...@@ -60,6 +60,7 @@ class Type_hash_alias_identical
return type->hash_for_method(NULL, return type->hash_for_method(NULL,
(Type::COMPARE_ERRORS (Type::COMPARE_ERRORS
| Type::COMPARE_TAGS | Type::COMPARE_TAGS
| Type::COMPARE_EMBEDDED_INTERFACES
| Type::COMPARE_ALIASES)); | Type::COMPARE_ALIASES));
} }
}; };
...@@ -73,6 +74,7 @@ class Type_alias_identical ...@@ -73,6 +74,7 @@ class Type_alias_identical
return Type::are_identical(t1, t2, return Type::are_identical(t1, t2,
(Type::COMPARE_ERRORS (Type::COMPARE_ERRORS
| Type::COMPARE_TAGS | Type::COMPARE_TAGS
| Type::COMPARE_EMBEDDED_INTERFACES
| Type::COMPARE_ALIASES), | Type::COMPARE_ALIASES),
NULL); NULL);
} }
...@@ -295,6 +297,16 @@ Find_types_to_prepare::type(Type* type) ...@@ -295,6 +297,16 @@ Find_types_to_prepare::type(Type* type)
if (type->is_abstract()) if (type->is_abstract())
return TRAVERSE_SKIP_COMPONENTS; return TRAVERSE_SKIP_COMPONENTS;
// For interfaces make sure that embedded methods are sorted, since the
// comparison function we use for indexing types relies on it (this call has
// to happen before the set_type_index call below).
if (type->classification() == Type::TYPE_INTERFACE)
{
Interface_type* it = type->interface_type();
if (it != NULL)
it->sort_embedded();
}
if (!this->exp_->set_type_index(type)) if (!this->exp_->set_type_index(type))
{ {
// We've already seen this type. // We've already seen this type.
...@@ -408,6 +420,9 @@ Export::prepare_types(const std::vector<Named_object*>* exports, ...@@ -408,6 +420,9 @@ Export::prepare_types(const std::vector<Named_object*>* exports,
{ {
if (!(*p)->is_type()) if (!(*p)->is_type())
continue; continue;
Interface_type* it = (*p)->type_value()->interface_type();
if (it != NULL)
it->sort_embedded();
this->set_type_index((*p)->type_value()); this->set_type_index((*p)->type_value());
} }
......
...@@ -8808,10 +8808,19 @@ Interface_type::is_identical(const Interface_type* t, int flags) const ...@@ -8808,10 +8808,19 @@ Interface_type::is_identical(const Interface_type* t, int flags) const
if (!this->methods_are_finalized_ || !t->methods_are_finalized_) if (!this->methods_are_finalized_ || !t->methods_are_finalized_)
return false; return false;
// Consult a flag to see whether we need to compare based on
// parse methods or all methods.
Typed_identifier_list* methods = (((flags & COMPARE_EMBEDDED_INTERFACES) != 0)
? this->parse_methods_
: this->all_methods_);
Typed_identifier_list* tmethods = (((flags & COMPARE_EMBEDDED_INTERFACES) != 0)
? t->parse_methods_
: t->all_methods_);
// We require the same methods with the same types. The methods // We require the same methods with the same types. The methods
// have already been sorted. // have already been sorted.
if (this->all_methods_ == NULL || t->all_methods_ == NULL) if (methods == NULL || tmethods == NULL)
return this->all_methods_ == t->all_methods_; return methods == tmethods;
if (this->assume_identical(this, t) || t->assume_identical(t, this)) if (this->assume_identical(this, t) || t->assume_identical(t, this))
return true; return true;
...@@ -8823,11 +8832,11 @@ Interface_type::is_identical(const Interface_type* t, int flags) const ...@@ -8823,11 +8832,11 @@ Interface_type::is_identical(const Interface_type* t, int flags) const
ai.next = hold_ai; ai.next = hold_ai;
this->assume_identical_ = &ai; this->assume_identical_ = &ai;
Typed_identifier_list::const_iterator p1 = this->all_methods_->begin(); Typed_identifier_list::const_iterator p1 = methods->begin();
Typed_identifier_list::const_iterator p2; Typed_identifier_list::const_iterator p2;
for (p2 = t->all_methods_->begin(); p2 != t->all_methods_->end(); ++p1, ++p2) for (p2 = tmethods->begin(); p2 != tmethods->end(); ++p1, ++p2)
{ {
if (p1 == this->all_methods_->end()) if (p1 == methods->end())
break; break;
if (p1->name() != p2->name() if (p1->name() != p2->name()
|| !Type::are_identical(p1->type(), p2->type(), flags, NULL)) || !Type::are_identical(p1->type(), p2->type(), flags, NULL))
...@@ -8836,7 +8845,7 @@ Interface_type::is_identical(const Interface_type* t, int flags) const ...@@ -8836,7 +8845,7 @@ Interface_type::is_identical(const Interface_type* t, int flags) const
this->assume_identical_ = hold_ai; this->assume_identical_ = hold_ai;
return p1 == this->all_methods_->end() && p2 == t->all_methods_->end(); return p1 == methods->end() && p2 == tmethods->end();
} }
// Return true if T1 and T2 are assumed to be identical during a type // Return true if T1 and T2 are assumed to be identical during a type
......
...@@ -577,6 +577,11 @@ class Type ...@@ -577,6 +577,11 @@ class Type
// Compare aliases: treat an alias to T as distinct from T. // Compare aliases: treat an alias to T as distinct from T.
static const int COMPARE_ALIASES = 4; static const int COMPARE_ALIASES = 4;
// When comparing interface types compare the interface embedding heirarchy,
// if any, rather than only comparing method sets. Useful primarily when
// exporting types.
static const int COMPARE_EMBEDDED_INTERFACES = 8;
// Return true if two types are identical. If this returns false, // Return true if two types are identical. If this returns false,
// and REASON is not NULL, it may set *REASON. // and REASON is not NULL, it may set *REASON.
static bool static bool
...@@ -3165,6 +3170,15 @@ class Interface_type : public Type ...@@ -3165,6 +3170,15 @@ class Interface_type : public Type
methods_are_finalized() const methods_are_finalized() const
{ return this->methods_are_finalized_; } { return this->methods_are_finalized_; }
// Sort embedded interfaces by name. Needed when we are preparing
// to emit types into the export data.
void
sort_embedded()
{
if (parse_methods_ != NULL)
parse_methods_->sort_by_name();
}
protected: protected:
int int
do_traverse(Traverse*); do_traverse(Traverse*);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment