Index: runtime/vm/object.cc |
=================================================================== |
--- runtime/vm/object.cc (revision 20937) |
+++ runtime/vm/object.cc (working copy) |
@@ -2861,7 +2861,7 @@ |
// substituted by the instantiator's type argument vector without checking |
// the upper bound. |
const AbstractType& bound = AbstractType::Handle(type_param.bound()); |
- ASSERT(bound.IsFinalized()); |
+ ASSERT(bound.IsResolved()); |
if (!bound.IsObjectType() && !bound.IsDynamicType()) { |
return false; |
} |
@@ -9263,8 +9263,8 @@ |
bool AbstractType::TypeTest(TypeTestKind test_kind, |
const AbstractType& other, |
Error* malformed_error) const { |
- ASSERT(IsFinalized()); |
- ASSERT(other.IsFinalized()); |
+ ASSERT(IsResolved()); |
+ ASSERT(other.IsResolved()); |
// In case the type checked in a type test is malformed, the code generator |
// may compile a throw instead of a run time call performing the type check. |
// However, in checked mode, a function type may include malformed result type |
@@ -9301,13 +9301,10 @@ |
// The same rule applies when checking the upper bound of a still |
// uninstantiated type at compile time. Returning false will defer the test |
// to run time. |
- // We may think that some cases can be decided at compile time. |
+ // There are however some cases can be decided at compile time. |
// For example, with class A<K, V extends K>, new A<T, T> called from within |
// a class B<T> will never require a run time bound check, even if T is |
// uninstantiated at compile time. |
- // However, this is not true, because bounds are ignored in production mode, |
- // and even if we are running in checked mode, we may generate a snapshot |
- // that will be executed in production mode. |
if (IsTypeParameter()) { |
const TypeParameter& type_param = TypeParameter::Cast(*this); |
if (other.IsTypeParameter()) { |
@@ -9316,6 +9313,10 @@ |
return true; |
} |
} |
+ const AbstractType& bound = AbstractType::Handle(type_param.bound()); |
+ if (bound.IsMoreSpecificThan(other, malformed_error)) { |
+ return true; |
+ } |
return false; // TODO(regis): We should return "maybe after instantiation". |
} |
if (other.IsTypeParameter()) { |
@@ -9532,7 +9533,7 @@ |
RawAbstractType* Type::InstantiateFrom( |
const AbstractTypeArguments& instantiator_type_arguments, |
Error* malformed_error) const { |
- ASSERT(IsFinalized()); |
+ ASSERT(IsResolved()); |
ASSERT(!IsInstantiated()); |
// Return the uninstantiated type unchanged if malformed. No copy needed. |
if (IsMalformed()) { |
@@ -9564,13 +9565,16 @@ |
return false; |
} |
const Type& other_type = Type::Cast(other); |
- ASSERT(IsFinalized() && other_type.IsFinalized()); |
+ ASSERT(IsResolved() && other_type.IsResolved()); |
if (IsMalformed() || other_type.IsMalformed()) { |
return false; |
} |
if (type_class() != other_type.type_class()) { |
return false; |
} |
+ if (!IsFinalized() || !other_type.IsFinalized()) { |
+ return false; |
+ } |
return AbstractTypeArguments::AreEqual( |
AbstractTypeArguments::Handle(arguments()), |
AbstractTypeArguments::Handle(other_type.arguments())); |
@@ -9780,49 +9784,60 @@ |
if (instantiator_type_arguments.IsNull()) { |
return Type::DynamicType(); |
} |
- // Bound checks should never appear in the instantiator type arguments. |
- ASSERT(!AbstractType::Handle( |
- instantiator_type_arguments.TypeAt(index())).IsBoundedType()); |
- return instantiator_type_arguments.TypeAt(index()); |
+ // Bound checks may appear in the instantiator type arguments, as is the case |
+ // with a pair of type parameters of the same class referring to each other |
+ // via their bounds. |
+ AbstractType& type_arg = AbstractType::Handle( |
+ instantiator_type_arguments.TypeAt(index())); |
+ if (type_arg.IsBoundedType()) { |
+ const BoundedType& bounded_type = BoundedType::Cast(type_arg); |
+ ASSERT(!bounded_type.IsInstantiated()); |
+ ASSERT(AbstractType::Handle(bounded_type.bound()).IsInstantiated()); |
+ type_arg = bounded_type.InstantiateFrom(AbstractTypeArguments::Handle(), |
+ malformed_error); |
+ } |
+ return type_arg.raw(); |
} |
-void TypeParameter::CheckBound(const AbstractType& bounded_type, |
+bool TypeParameter::CheckBound(const AbstractType& bounded_type, |
const AbstractType& upper_bound, |
Error* malformed_error) const { |
- ASSERT(malformed_error->IsNull()); |
+ ASSERT((malformed_error == NULL) || malformed_error->IsNull()); |
ASSERT(bounded_type.IsFinalized()); |
ASSERT(upper_bound.IsFinalized()); |
ASSERT(!bounded_type.IsMalformed()); |
- if (bounded_type.IsSubtypeOf(upper_bound, malformed_error) || |
- !malformed_error->IsNull()) { |
- return; |
+ if (bounded_type.IsSubtypeOf(upper_bound, malformed_error)) { |
+ return true; |
} |
- // Report the bound error. |
- const String& bounded_type_name = String::Handle( |
- bounded_type.UserVisibleName()); |
- const String& upper_bound_name = String::Handle( |
- upper_bound.UserVisibleName()); |
- const AbstractType& declared_bound = AbstractType::Handle(bound()); |
- const String& declared_bound_name = String::Handle( |
- declared_bound.UserVisibleName()); |
- const String& type_param_name = String::Handle(UserVisibleName()); |
- const Class& cls = Class::Handle(parameterized_class()); |
- const String& class_name = String::Handle(cls.Name()); |
- const Script& script = Script::Handle(cls.script()); |
- // Since the bound may have been canonicalized, its token index is |
- // meaningless, therefore use the token index of this type parameter. |
- *malformed_error = FormatError( |
- *malformed_error, |
- script, |
- token_pos(), |
- "type parameter '%s' of class '%s' must extend bound '%s', " |
- "but type argument '%s' is not a subtype of '%s'\n", |
- type_param_name.ToCString(), |
- class_name.ToCString(), |
- declared_bound_name.ToCString(), |
- bounded_type_name.ToCString(), |
- upper_bound_name.ToCString()); |
+ if ((malformed_error != NULL) && malformed_error->IsNull()) { |
+ // Report the bound error. |
+ const String& bounded_type_name = String::Handle( |
+ bounded_type.UserVisibleName()); |
+ const String& upper_bound_name = String::Handle( |
+ upper_bound.UserVisibleName()); |
+ const AbstractType& declared_bound = AbstractType::Handle(bound()); |
+ const String& declared_bound_name = String::Handle( |
+ declared_bound.UserVisibleName()); |
+ const String& type_param_name = String::Handle(UserVisibleName()); |
+ const Class& cls = Class::Handle(parameterized_class()); |
+ const String& class_name = String::Handle(cls.Name()); |
+ const Script& script = Script::Handle(cls.script()); |
+ // Since the bound may have been canonicalized, its token index is |
+ // meaningless, therefore use the token index of this type parameter. |
+ *malformed_error = FormatError( |
+ *malformed_error, |
+ script, |
+ token_pos(), |
+ "type parameter '%s' of class '%s' must extend bound '%s', " |
+ "but type argument '%s' is not a subtype of '%s'\n", |
+ type_param_name.ToCString(), |
+ class_name.ToCString(), |
+ declared_bound_name.ToCString(), |
+ bounded_type_name.ToCString(), |
+ upper_bound_name.ToCString()); |
+ } |
+ return false; |
} |