Index: runtime/vm/class_finalizer.cc |
=================================================================== |
--- runtime/vm/class_finalizer.cc (revision 31085) |
+++ runtime/vm/class_finalizer.cc (working copy) |
@@ -491,11 +491,13 @@ |
} |
-void ClassFinalizer::FinalizeTypeParameters(const Class& cls) { |
+void ClassFinalizer::FinalizeTypeParameters( |
+ const Class& cls, |
+ GrowableObjectArray* pending_types) { |
if (cls.IsMixinApplication()) { |
// Setup the type parameters of the mixin application and finalize the |
// mixin type. |
- ApplyMixinType(cls); |
+ ApplyMixinType(cls, pending_types); |
} |
// The type parameter bounds are not finalized here. |
const TypeArguments& type_parameters = |
@@ -505,9 +507,8 @@ |
const intptr_t num_types = type_parameters.Length(); |
for (intptr_t i = 0; i < num_types; i++) { |
type_parameter ^= type_parameters.TypeAt(i); |
- type_parameter ^= FinalizeType(cls, |
- type_parameter, |
- kCanonicalizeWellFormed); |
+ type_parameter ^= FinalizeType( |
+ cls, type_parameter, kFinalize, pending_types); |
type_parameters.SetTypeAt(i, type_parameter); |
} |
} |
@@ -541,50 +542,46 @@ |
// num_uninitialized_arguments = 1, |
// i.e. cls_args = [String, double], offset = 1, length = 2. |
// Output: arguments = [int, String, double] |
+// |
+// It is too early to canonicalize the type arguments of the vector, because |
+// several type argument vectors may be mutually recursive and finalized at the |
+// same time. Canonicalization happens when pending types are processed. |
void ClassFinalizer::FinalizeTypeArguments( |
const Class& cls, |
const AbstractTypeArguments& arguments, |
intptr_t num_uninitialized_arguments, |
- FinalizationKind finalization, |
- Error* bound_error) { |
+ Error* bound_error, |
+ GrowableObjectArray* pending_types) { |
ASSERT(arguments.Length() >= cls.NumTypeArguments()); |
if (!cls.is_type_finalized()) { |
- FinalizeTypeParameters(cls); |
+ FinalizeTypeParameters(cls, pending_types); |
ResolveUpperBounds(cls); |
} |
AbstractType& super_type = AbstractType::Handle(cls.super_type()); |
if (!super_type.IsNull()) { |
const Class& super_class = Class::Handle(super_type.type_class()); |
- AbstractTypeArguments& super_type_args = AbstractTypeArguments::Handle(); |
- if (super_type.IsBeingFinalized()) { |
- // This type references itself via its type arguments. This is legal, but |
- // we must avoid endless recursion. We therefore map the innermost |
- // super type to dynamic. |
- // Note that a direct self-reference via the super class chain is illegal |
- // and reported as an error earlier. |
- // Such legal self-references occur with F-bounded quantification. |
- // Example 1: class Derived extends Base<Derived>. |
- // The type 'Derived' forms a cycle by pointing to itself via its |
- // flattened type argument vector: Derived[Derived[...]] |
- // We break the cycle as follows: Derived[Derived[dynamic]] |
- // Example 2: class Derived extends Base<Middle<Derived>> results in |
- // Derived[Middle[Derived[dynamic]]] |
- // Example 3: class Derived<T> extends Base<Derived<T>> results in |
- // Derived[Derived[dynamic], T]. |
- ASSERT(super_type_args.IsNull()); // Same as a vector of dynamic. |
- } else { |
- super_type ^= FinalizeType(cls, super_type, finalization); |
+ const intptr_t num_super_type_params = super_class.NumTypeParameters(); |
+ const intptr_t num_super_type_args = super_class.NumTypeArguments(); |
+ ASSERT(num_super_type_args == |
+ (cls.NumTypeArguments() - cls.NumOwnTypeArguments())); |
+ if (!super_type.IsFinalized() && !super_type.IsBeingFinalized()) { |
+ super_type ^= FinalizeType( |
+ cls, super_type, kFinalize, pending_types); |
cls.set_super_type(super_type); |
- super_type_args = super_type.arguments(); |
} |
- const intptr_t num_super_type_params = super_class.NumTypeParameters(); |
- const intptr_t offset = super_class.NumTypeArguments(); |
- const intptr_t super_offset = offset - num_super_type_params; |
- ASSERT(offset == (cls.NumTypeArguments() - cls.NumOwnTypeArguments())); |
+ AbstractTypeArguments& super_type_args = AbstractTypeArguments::Handle( |
+ super_type.arguments()); |
+ // Offset of super type's type parameters in cls' type argument vector. |
+ const intptr_t super_offset = num_super_type_args - num_super_type_params; |
AbstractType& super_type_arg = AbstractType::Handle(Type::DynamicType()); |
- for (intptr_t i = 0; super_offset + i < num_uninitialized_arguments; i++) { |
+ for (intptr_t i = super_offset; i < num_uninitialized_arguments; i++) { |
if (!super_type_args.IsNull()) { |
- super_type_arg = super_type_args.TypeAt(super_offset + i); |
+ super_type_arg = super_type_args.TypeAt(i); |
+ if (!super_type_arg.IsFinalized()) { |
+ super_type_arg ^= FinalizeType( |
+ cls, super_type_arg, kFinalize, pending_types); |
+ super_type_args.SetTypeAt(i, super_type_arg); |
+ } |
if (!super_type_arg.IsInstantiated()) { |
Error& error = Error::Handle(); |
super_type_arg = super_type_arg.InstantiateFrom(arguments, &error); |
@@ -599,14 +596,11 @@ |
} |
} |
} |
- if (finalization >= kCanonicalize) { |
- super_type_arg = super_type_arg.Canonicalize(); |
- } |
} |
- arguments.SetTypeAt(super_offset + i, super_type_arg); |
+ arguments.SetTypeAt(i, super_type_arg); |
} |
FinalizeTypeArguments(super_class, arguments, super_offset, |
- finalization, bound_error); |
+ bound_error, pending_types); |
} |
} |
@@ -647,6 +641,7 @@ |
if (type_arg.IsDynamicType()) { |
continue; |
} |
+ ASSERT(type_arg.IsFinalized()); |
cls_type_param = cls_type_params.TypeAt(i); |
const TypeParameter& type_param = TypeParameter::Cast(cls_type_param); |
ASSERT(type_param.IsFinalized()); |
@@ -700,9 +695,53 @@ |
} |
-RawAbstractType* ClassFinalizer::FinalizeType(const Class& cls, |
- const AbstractType& type, |
- FinalizationKind finalization) { |
+void ClassFinalizer::CheckTypeBounds(const Class& cls, const Type& type) { |
+ ASSERT(type.IsFinalized()); |
+ AbstractTypeArguments& arguments = |
+ AbstractTypeArguments::Handle(type.arguments()); |
+ if (arguments.IsNull()) { |
+ return; |
+ } |
+ Class& owner_class = Class::Handle(); |
+ Class& type_class = Class::Handle(type.type_class()); |
+ if (type_class.IsSignatureClass()) { |
+ const Function& signature_fun = |
+ Function::Handle(type_class.signature_function()); |
+ ASSERT(!signature_fun.is_static()); |
+ owner_class = signature_fun.Owner(); |
+ } else { |
+ owner_class = type_class.raw(); |
+ } |
+ Error& bound_error = Error::Handle(); |
+ CheckTypeArgumentBounds(owner_class, arguments, &bound_error); |
+ type.set_arguments(arguments); |
+ // If a bound error occurred, mark the type as malbounded. |
+ // The bound error will be ignored in production mode. |
+ if (!bound_error.IsNull()) { |
+ // No compile-time error during finalization. |
+ const String& type_name = String::Handle(type.UserVisibleName()); |
+ FinalizeMalboundedType(bound_error, |
+ Script::Handle(cls.script()), |
+ type, |
+ "type '%s' has an out of bound type argument", |
+ type_name.ToCString()); |
+ if (FLAG_trace_type_finalization) { |
+ OS::Print("Marking type '%s' as malbounded: %s\n", |
+ String::Handle(type.Name()).ToCString(), |
+ bound_error.ToCString()); |
+ } |
+ } |
+} |
+ |
+ |
+RawAbstractType* ClassFinalizer::FinalizeType( |
+ const Class& cls, |
+ const AbstractType& type, |
+ FinalizationKind finalization, |
+ GrowableObjectArray* pending_types) { |
+ // Only the 'root' type of the graph can be canonicalized, after all depending |
+ // types have been bound checked. |
+ ASSERT((pending_types == NULL) || (finalization < kCanonicalize)); |
if (type.IsFinalized()) { |
// Ensure type is canonical if canonicalization is requested, unless type is |
// malformed. |
@@ -714,6 +753,21 @@ |
ASSERT(type.IsResolved()); |
ASSERT(finalization >= kFinalize); |
+ if (type.IsTypeRef()) { |
+ // The referenced type will be finalized later by the code that set the |
+ // is_being_finalized mark bit. |
+ return type.raw(); |
+ } |
+ |
+ if (type.IsBeingFinalized()) { |
+ if (FLAG_trace_type_finalization) { |
+ OS::Print("Creating TypeRef '%s' for class '%s'\n", |
+ String::Handle(type.Name()).ToCString(), |
+ cls.ToCString()); |
+ } |
+ return TypeRef::New(type); |
+ } |
+ |
if (FLAG_trace_type_finalization) { |
OS::Print("Finalizing type '%s' for class '%s'\n", |
String::Handle(type.Name()).ToCString(), |
@@ -754,12 +808,15 @@ |
// At this point, we can only have a parameterized_type. |
const Type& parameterized_type = Type::Cast(type); |
- // Types illegally referring to themselves should have been detected earlier. |
- ASSERT(!parameterized_type.IsBeingFinalized()); |
+ // This type is the root type of the type graph if no pending types queue is |
+ // allocated yet. |
+ const bool is_root_type = (pending_types == NULL); |
+ GrowableObjectArray& types = GrowableObjectArray::Handle(); |
+ if (is_root_type) { |
+ types = GrowableObjectArray::New(); |
+ pending_types = &types; |
+ } |
- // Mark type as being finalized in order to detect illegal self reference. |
- parameterized_type.set_is_being_finalized(); |
- |
// The type class does not need to be finalized in order to finalize the type, |
// however, it must at least be resolved (this was done as part of resolving |
// the type itself, a precondition to calling FinalizeType). |
@@ -767,28 +824,10 @@ |
// parameters of the type class must be finalized. |
Class& type_class = Class::Handle(parameterized_type.type_class()); |
if (!type_class.is_type_finalized()) { |
- FinalizeTypeParameters(type_class); |
+ FinalizeTypeParameters(type_class, pending_types); |
ResolveUpperBounds(type_class); |
} |
- // Finalize the current type arguments of the type, which are still the |
- // parsed type arguments. |
- AbstractTypeArguments& arguments = |
- AbstractTypeArguments::Handle(parameterized_type.arguments()); |
- if (!arguments.IsNull()) { |
- const intptr_t num_arguments = arguments.Length(); |
- AbstractType& type_argument = AbstractType::Handle(); |
- for (intptr_t i = 0; i < num_arguments; i++) { |
- type_argument = arguments.TypeAt(i); |
- type_argument = FinalizeType(cls, type_argument, finalization); |
- if (type_argument.IsMalformed()) { |
- // Malformed type arguments are mapped to dynamic. |
- type_argument = Type::DynamicType(); |
- } |
- arguments.SetTypeAt(i, type_argument); |
- } |
- } |
- |
// The finalized type argument vector needs num_type_arguments types. |
const intptr_t num_type_arguments = type_class.NumTypeArguments(); |
// The type class has num_type_parameters type parameters. |
@@ -798,6 +837,8 @@ |
// Check the number of parsed type arguments, if any. |
// Specifying no type arguments indicates a raw type, which is not an error. |
// However, type parameter bounds are checked below, even for a raw type. |
+ AbstractTypeArguments& arguments = |
+ AbstractTypeArguments::Handle(parameterized_type.arguments()); |
if (!arguments.IsNull() && (arguments.Length() != num_type_parameters)) { |
// Wrong number of type arguments. The type is mapped to the raw type. |
if (FLAG_error_on_bad_type) { |
@@ -813,6 +854,7 @@ |
arguments = AbstractTypeArguments::null(); |
parameterized_type.set_arguments(arguments); |
} |
+ |
// The full type argument vector consists of the type arguments of the |
// super types of type_class, which are initialized from the parsed |
// type arguments, followed by the parsed type arguments. |
@@ -827,6 +869,7 @@ |
// argument vector. |
const intptr_t offset = num_type_arguments - num_type_parameters; |
AbstractType& type_arg = AbstractType::Handle(Type::DynamicType()); |
+ // TODO(regis): Leave the temporary type argument values as null. |
for (intptr_t i = 0; i < offset; i++) { |
// Temporarily set the type arguments of the super classes to dynamic. |
full_arguments.SetTypeAt(i, type_arg); |
@@ -836,8 +879,8 @@ |
// create a vector of dynamic. |
if (!arguments.IsNull()) { |
type_arg = arguments.TypeAt(i); |
+ // The parsed type_arg may or may not be finalized. |
} |
- ASSERT(type_arg.IsFinalized()); // Index of type parameter is adjusted. |
full_arguments.SetTypeAt(offset + i, type_arg); |
} |
// Replace the compile-time argument vector (of length zero or |
@@ -847,6 +890,22 @@ |
// checking, in which case type arguments of super classes will be seen |
// as dynamic. |
parameterized_type.set_arguments(full_arguments); |
+ // Mark type as being finalized in order to detect self reference. |
+ parameterized_type.set_is_being_finalized(); |
+ // Finalize the current type arguments of the type, which are still the |
+ // parsed type arguments. |
+ if (!arguments.IsNull()) { |
+ for (intptr_t i = 0; i < num_type_parameters; i++) { |
+ type_arg = full_arguments.TypeAt(offset + i); |
+ ASSERT(!type_arg.IsBeingFinalized()); |
+ type_arg = FinalizeType(cls, type_arg, kFinalize, pending_types); |
+ if (type_arg.IsMalformed()) { |
+ // Malformed type arguments are mapped to dynamic. |
+ type_arg = Type::DynamicType(); |
+ } |
+ full_arguments.SetTypeAt(offset + i, type_arg); |
+ } |
+ } |
// If the type class is a signature class, the full argument vector |
// must include the argument vector of the super type. |
// If the signature class is a function type alias, it is also the owner |
@@ -855,32 +914,27 @@ |
// signature function may either be an alias or the enclosing class of a |
// local function, in which case the super type of the enclosing class is |
// also considered when filling up the argument vector. |
+ Class& owner_class = Class::Handle(); |
if (type_class.IsSignatureClass()) { |
const Function& signature_fun = |
Function::Handle(type_class.signature_function()); |
ASSERT(!signature_fun.is_static()); |
- const Class& sig_fun_owner = Class::Handle(signature_fun.Owner()); |
- if (offset > 0) { |
- FinalizeTypeArguments(sig_fun_owner, full_arguments, offset, |
- finalization, &bound_error); |
- } |
- CheckTypeArgumentBounds(sig_fun_owner, full_arguments, &bound_error); |
+ owner_class = signature_fun.Owner(); |
} else { |
- if (offset > 0) { |
- FinalizeTypeArguments(type_class, full_arguments, offset, |
- finalization, &bound_error); |
- } |
- CheckTypeArgumentBounds(type_class, full_arguments, &bound_error); |
+ owner_class = type_class.raw(); |
} |
+ if (offset > 0) { |
+ FinalizeTypeArguments(owner_class, full_arguments, offset, |
+ &bound_error, pending_types); |
+ } |
if (full_arguments.IsRaw(0, num_type_arguments)) { |
// The parameterized_type is raw. Set its argument vector to null, which |
// is more efficient in type tests. |
full_arguments = TypeArguments::null(); |
- } else if (finalization >= kCanonicalize) { |
- // FinalizeTypeArguments can modify 'full_arguments', |
- // canonicalize afterwards. |
- full_arguments ^= full_arguments.Canonicalize(); |
- ASSERT(full_arguments.Length() == num_type_arguments); |
+ } else { |
+ // Postpone bound checking until after all types in the graph of |
+ // mutually recursive types are finalized. |
+ pending_types->Add(parameterized_type); |
} |
parameterized_type.set_arguments(full_arguments); |
} else { |
@@ -890,10 +944,22 @@ |
// Self referencing types may get finalized indirectly. |
if (!parameterized_type.IsFinalized()) { |
+ ASSERT(full_arguments.IsNull() || |
+ !full_arguments.IsRaw(0, num_type_arguments)); |
// Mark the type as finalized. |
parameterized_type.SetIsFinalized(); |
} |
+ // If we are done finalizing a graph of mutually recursive types, check their |
+ // bounds. |
+ if (is_root_type) { |
+ Type& type = Type::Handle(); |
+ for (intptr_t i = 0; i < types.Length(); i++) { |
+ type ^= types.At(i); |
+ CheckTypeBounds(cls, type); |
+ } |
+ } |
+ |
// If the type class is a signature class, we are currently finalizing a |
// signature type, i.e. finalizing the result type and parameter types of the |
// signature function of this signature type. |
@@ -905,32 +971,12 @@ |
FinalizeTypesInClass(type_class); |
} |
- // If a bound error occurred, mark the type as malbounded. |
- // The bound error will be ignored in production mode. |
- if (!bound_error.IsNull()) { |
- // No compile-time error during finalization. |
- const String& parameterized_type_name = String::Handle( |
- parameterized_type.UserVisibleName()); |
- FinalizeMalboundedType(bound_error, |
- Script::Handle(cls.script()), |
- parameterized_type, |
- "type '%s' has an out of bound type argument", |
- parameterized_type_name.ToCString()); |
- |
- if (FLAG_trace_type_finalization) { |
- OS::Print("Done finalizing malbounded type '%s' with bound error: %s\n", |
- String::Handle(parameterized_type.Name()).ToCString(), |
- bound_error.ToCString()); |
- } |
- |
- return parameterized_type.raw();; |
- } |
- |
if (FLAG_trace_type_finalization) { |
- OS::Print("Done finalizing type '%s' with %" Pd " type args\n", |
+ OS::Print("Done finalizing type '%s' with %" Pd " type args: %s\n", |
String::Handle(parameterized_type.Name()).ToCString(), |
parameterized_type.arguments() == AbstractTypeArguments::null() ? |
- 0 : num_type_arguments); |
+ 0 : num_type_arguments, |
+ parameterized_type.ToCString()); |
} |
if (finalization >= kCanonicalize) { |
@@ -1689,7 +1735,8 @@ |
} |
-void ClassFinalizer::ApplyMixinType(const Class& mixin_app_class) { |
+void ClassFinalizer::ApplyMixinType(const Class& mixin_app_class, |
+ GrowableObjectArray* pending_types) { |
if (mixin_app_class.is_mixin_type_applied()) { |
return; |
} |
@@ -1760,7 +1807,7 @@ |
mixin_type = mixin_app_class.mixin(); |
ASSERT(!mixin_type.IsBeingFinalized()); |
mixin_type ^= |
- FinalizeType(mixin_app_class, mixin_type, kCanonicalizeWellFormed); |
+ FinalizeType(mixin_app_class, mixin_type, kFinalize, pending_types); |
// TODO(14453): Check for a malbounded mixin_type. |
mixin_app_class.set_mixin(mixin_type); |
} |