Index: runtime/vm/class_finalizer.cc |
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc |
index 873092feb4bdd7ba2226b5f27bade77d665a0b89..2a8cb59be218b99b61f473db1e8b6ee5bf5eb41d 100644 |
--- a/runtime/vm/class_finalizer.cc |
+++ b/runtime/vm/class_finalizer.cc |
@@ -674,9 +674,11 @@ void ClassFinalizer::CheckRecursiveType(const Class& cls, |
arguments.IsSubvectorInstantiated(first_type_param, num_type_params)) { |
return; |
} |
- // The type parameters are not instantiated. Verify that there is no other |
- // type pending finalization with the same type class, but different |
- // uninstantiated type parameters. |
+ // Consider mutually recursive and uninstantiated types pending finalization |
+ // with the same type class and report an error if they are not equal in their |
+ // raw form, i.e. where each type parameter is substituted with dynamic. |
+ // This test eliminates divergent types without restricting recursive types |
+ // typically found in the wild. |
TypeArguments& pending_arguments = TypeArguments::Handle(zone); |
const intptr_t num_pending_types = pending_types->length(); |
for (intptr_t i = num_pending_types - 1; i >= 0; i--) { |
@@ -693,10 +695,21 @@ void ClassFinalizer::CheckRecursiveType(const Class& cls, |
num_type_params) && |
!pending_arguments.IsSubvectorInstantiated(first_type_param, |
num_type_params)) { |
- // Reject the non-contractive recursive type. |
- const String& type_name = String::Handle(zone, type.Name()); |
- ReportError(cls, type.token_pos(), "illegal recursive type '%s'", |
- type_name.ToCString()); |
+ const TypeArguments& instantiated_arguments = TypeArguments::Handle( |
+ zone, arguments.InstantiateFrom(Object::null_type_arguments(), |
+ Object::null_type_arguments(), NULL, |
+ NULL, NULL, Heap::kNew)); |
+ const TypeArguments& instantiated_pending_arguments = |
+ TypeArguments::Handle(zone, pending_arguments.InstantiateFrom( |
+ Object::null_type_arguments(), |
+ Object::null_type_arguments(), NULL, |
+ NULL, NULL, Heap::kNew)); |
+ if (!instantiated_pending_arguments.IsSubvectorEquivalent( |
+ instantiated_arguments, first_type_param, num_type_params)) { |
+ const String& type_name = String::Handle(zone, type.Name()); |
+ ReportError(cls, type.token_pos(), "illegal recursive type '%s'", |
+ type_name.ToCString()); |
+ } |
} |
} |
} |
@@ -754,9 +767,8 @@ intptr_t ClassFinalizer::ExpandAndFinalizeTypeArguments( |
// postpone bound checking (if required) until after all types in the graph of |
// mutually recursive types are finalized. |
type.SetIsBeingFinalized(); |
- if (pending_types != NULL) { |
- pending_types->Add(type); |
- } |
+ ASSERT(pending_types != NULL); |
+ pending_types->Add(type); |
// The full type argument vector consists of the type arguments of the |
// super types of type_class, which are initialized from the parsed |
@@ -925,8 +937,9 @@ void ClassFinalizer::FinalizeTypeArguments(const Class& cls, |
// Example: class B<T>; class D<T> extends B<D<T>>; |
// While finalizing D<T>, the super type arg D<T> (a typeref) gets |
// instantiated from vector [T], yielding itself. |
- if (super_type_arg.IsTypeRef() && super_type_arg.IsBeingFinalized() && |
+ if (super_type_arg.IsTypeRef() && |
(super_type_arg.arguments() == arguments.raw())) { |
+ ASSERT(super_type_arg.IsBeingFinalized()); |
arguments.SetTypeAt(i, super_type_arg); |
continue; |
} |
@@ -950,12 +963,30 @@ void ClassFinalizer::FinalizeTypeArguments(const Class& cls, |
ASSERT(super_type_arg.IsTypeRef()); |
AbstractType& ref_super_type_arg = |
AbstractType::Handle(TypeRef::Cast(super_type_arg).type()); |
+ if (FLAG_trace_type_finalization) { |
+ THR_Print("Instantiated TypeRef '%s': '%s'\n", |
+ String::Handle(super_type_arg.Name()).ToCString(), |
+ ref_super_type_arg.ToCString()); |
+ } |
+ CheckRecursiveType(cls, ref_super_type_arg, pending_types); |
+ pending_types->Add(ref_super_type_arg); |
+ const Class& super_cls = |
+ Class::Handle(ref_super_type_arg.type_class()); |
+ const TypeArguments& super_args = |
+ TypeArguments::Handle(ref_super_type_arg.arguments()); |
+ // Mark as finalized before finalizing to avoid cycles. |
ref_super_type_arg.SetIsFinalized(); |
- const Class& cls = Class::Handle(ref_super_type_arg.type_class()); |
+ // Since the instantiator is different, do not pass the current |
+ // instantiation trail, but create a new one by passing NULL. |
FinalizeTypeArguments( |
- cls, TypeArguments::Handle(ref_super_type_arg.arguments()), |
- cls.NumTypeArguments() - cls.NumTypeParameters(), bound_error, |
- pending_types, instantiation_trail); |
+ super_cls, super_args, |
+ super_cls.NumTypeArguments() - super_cls.NumTypeParameters(), |
+ bound_error, pending_types, NULL); |
+ if (FLAG_trace_type_finalization) { |
+ THR_Print("Finalized instantiated TypeRef '%s': '%s'\n", |
+ String::Handle(super_type_arg.Name()).ToCString(), |
+ ref_super_type_arg.ToCString()); |
+ } |
} |
} |
} |
@@ -1211,9 +1242,8 @@ RawAbstractType* ClassFinalizer::FinalizeType(const Class& cls, |
ASSERT(type.IsType()); |
// This type is the root type of the type graph if no pending types queue is |
- // allocated yet, and if canonicalization is required. |
- const bool is_root_type = |
- (pending_types == NULL) && (finalization >= kCanonicalize); |
+ // allocated yet. |
+ const bool is_root_type = pending_types == NULL; |
if (is_root_type) { |
pending_types = new PendingTypes(zone, 4); |
} |