Index: runtime/vm/object.cc |
=================================================================== |
--- runtime/vm/object.cc (revision 19513) |
+++ runtime/vm/object.cc (working copy) |
@@ -702,6 +702,9 @@ |
cls = Class::New<TypeParameter>(); |
object_store->set_type_parameter_class(cls); |
+ cls = Class::New<BoundedType>(); |
+ object_store->set_bounded_type_class(cls); |
+ |
// Pre-allocate the OneByteString class needed by the symbol table. |
cls = Class::NewStringClass(kOneByteStringCid); |
object_store->set_one_byte_string_class(cls); |
@@ -1093,6 +1096,9 @@ |
cls = Class::New<TypeParameter>(); |
object_store->set_type_parameter_class(cls); |
+ cls = Class::New<BoundedType>(); |
+ object_store->set_bounded_type_class(cls); |
+ |
cls = Class::New<Array>(); |
object_store->set_array_class(cls); |
@@ -1663,7 +1669,7 @@ |
RawClass* Class::SuperClass() const { |
- const Type& sup_type = Type::Handle(super_type()); |
+ const AbstractType& sup_type = AbstractType::Handle(super_type()); |
if (sup_type.IsNull()) { |
return Class::null(); |
} |
@@ -1671,7 +1677,8 @@ |
} |
-void Class::set_super_type(const Type& value) const { |
+void Class::set_super_type(const AbstractType& value) const { |
+ ASSERT(value.IsNull() || value.IsType() || value.IsBoundedType()); |
StorePointer(&raw_ptr()->super_type_, value.raw()); |
} |
@@ -1686,24 +1693,11 @@ |
intptr_t num_type_params = type_params.Length(); |
TypeParameter& type_param = TypeParameter::Handle(); |
String& type_param_name = String::Handle(); |
- // TODO(regis): We do not copy the bound (= type_param.bound()), since |
- // we are not able to finalize the bounds of type parameter references |
- // without getting into cycles. Revisit. |
- const AbstractType& bound = AbstractType::Handle( |
- Isolate::Current()->object_store()->object_type()); |
for (intptr_t i = 0; i < num_type_params; i++) { |
type_param ^= type_params.TypeAt(i); |
type_param_name = type_param.name(); |
if (type_param_name.Equals(type_name)) { |
- intptr_t index = type_param.index(); |
- // Create a non-finalized new TypeParameter with the given token_pos. |
- if (type_param.IsFinalized()) { |
- // The index was adjusted during finalization. Revert. |
- index -= NumTypeArguments() - num_type_params; |
- } else { |
- ASSERT(type_param.index() == i); |
- } |
- return TypeParameter::New(*this, index, type_name, bound, token_pos); |
+ return type_param.raw(); |
} |
} |
} |
@@ -2244,6 +2238,7 @@ |
AbstractType& interface = AbstractType::Handle(); |
Class& interface_class = Class::Handle(); |
AbstractTypeArguments& interface_args = AbstractTypeArguments::Handle(); |
+ Error& args_malformed_error = Error::Handle(); |
for (intptr_t i = 0; i < interfaces.Length(); i++) { |
interface ^= interfaces.At(i); |
interface_class = interface.type_class(); |
@@ -2258,19 +2253,15 @@ |
// after the type arguments of the super type of this type. |
// The index of the type parameters is adjusted upon finalization. |
ASSERT(interface.IsFinalized()); |
- interface_args = interface_args.InstantiateFrom(type_arguments); |
- // In checked mode, verify that the instantiated interface type |
- // arguments are within the bounds specified by the interface class. |
- // Note that the additional bounds check in checked mode may lead to a |
- // dynamic type error, but it will never change the result of the type |
- // check from true in production mode to false in checked mode. |
- if (FLAG_enable_type_checks && !interface_args.IsNull()) { |
- // Pass type_arguments as bounds instantiator. |
- if (!interface_args.IsWithinBoundsOf(interface_class, |
- type_arguments, |
- malformed_error)) { |
- continue; |
+ args_malformed_error = Error::null(); |
+ interface_args = interface_args.InstantiateFrom(type_arguments, |
+ &args_malformed_error); |
+ if (!args_malformed_error.IsNull()) { |
+ // Return the first malformed error to the caller if it requests it. |
+ if ((malformed_error != NULL) && malformed_error->IsNull()) { |
+ *malformed_error = args_malformed_error.raw(); |
} |
+ continue; // Another interface may work better. |
} |
} |
if (interface_class.TypeTest(test_kind, |
@@ -2694,6 +2685,13 @@ |
} |
+bool AbstractTypeArguments::IsBounded() const { |
+ // AbstractTypeArguments is an abstract class. |
+ UNREACHABLE(); |
+ return false; |
+} |
+ |
+ |
static intptr_t FinalizeHash(uword hash) { |
hash += hash << 3; |
hash ^= hash >> 11; |
@@ -2785,7 +2783,8 @@ |
RawAbstractTypeArguments* AbstractTypeArguments::InstantiateFrom( |
- const AbstractTypeArguments& instantiator_type_arguments) const { |
+ const AbstractTypeArguments& instantiator_type_arguments, |
+ Error* malformed_error) const { |
// AbstractTypeArguments is an abstract class. |
UNREACHABLE(); |
return NULL; |
@@ -2802,10 +2801,12 @@ |
ASSERT(!type.IsNull()); |
if (!type.HasResolvedTypeClass()) { |
if (raw_instantiated && type.IsTypeParameter()) { |
- // An uninstantiated type parameter is equivalent to dynamic. |
+ // An uninstantiated type parameter is equivalent to dynamic (even in |
+ // the presence of a malformed bound in checked mode). |
continue; |
} |
ASSERT((!raw_instantiated && type.IsTypeParameter()) || |
+ type.IsBoundedType() || |
type.IsMalformed()); |
return false; |
} |
@@ -2833,69 +2834,6 @@ |
} |
-bool AbstractTypeArguments::IsWithinBoundsOf( |
- const Class& cls, |
- const AbstractTypeArguments& bounds_instantiator, |
- Error* malformed_error) const { |
- ASSERT(FLAG_enable_type_checks); |
- // This function may be called at compile time on (partially) uninstantiated |
- // type arguments and may return true, in which case a run time bounds check |
- // can be avoided. |
- ASSERT(Length() >= cls.NumTypeArguments()); |
- const intptr_t num_type_params = cls.NumTypeParameters(); |
- const intptr_t offset = cls.NumTypeArguments() - num_type_params; |
- AbstractType& this_type_arg = AbstractType::Handle(); |
- AbstractType& cls_type_arg = AbstractType::Handle(); |
- AbstractType& bound = AbstractType::Handle(); |
- const TypeArguments& cls_type_params = |
- TypeArguments::Handle(cls.type_parameters()); |
- ASSERT((cls_type_params.IsNull() && (num_type_params == 0)) || |
- (cls_type_params.Length() == num_type_params)); |
- for (intptr_t i = 0; i < num_type_params; i++) { |
- cls_type_arg = cls_type_params.TypeAt(i); |
- const TypeParameter& cls_type_param = TypeParameter::Cast(cls_type_arg); |
- bound = cls_type_param.bound(); |
- if (!bound.IsDynamicType()) { |
- this_type_arg = TypeAt(offset + i); |
- Error& malformed_bound_error = Error::Handle(); |
- if (bound.IsMalformed()) { |
- malformed_bound_error = bound.malformed_error(); |
- } else if (!bound.IsInstantiated()) { |
- bound = bound.InstantiateFrom(bounds_instantiator); |
- } |
- if (!malformed_bound_error.IsNull() || |
- !this_type_arg.IsSubtypeOf(bound, malformed_error)) { |
- // Ignore this bound error if another malformed error was already |
- // reported for this type test. |
- if ((malformed_error != NULL) && malformed_error->IsNull()) { |
- const String& type_arg_name = |
- String::Handle(this_type_arg.UserVisibleName()); |
- const String& class_name = String::Handle(cls.Name()); |
- const String& bound_name = String::Handle(bound.UserVisibleName()); |
- const Script& script = Script::Handle(cls.script()); |
- // Since the bound was canonicalized, its token index was lost, |
- // therefore, use the token index of the corresponding type parameter. |
- *malformed_error ^= FormatError(malformed_bound_error, |
- script, cls_type_param.token_pos(), |
- "type argument '%s' does not " |
- "extend bound '%s' of '%s'\n", |
- type_arg_name.ToCString(), |
- bound_name.ToCString(), |
- class_name.ToCString()); |
- } |
- return false; |
- } |
- } |
- } |
- const Class& super_class = Class::Handle(cls.SuperClass()); |
- if (!super_class.IsNull() && |
- !IsWithinBoundsOf(super_class, bounds_instantiator, malformed_error)) { |
- return false; |
- } |
- return true; |
-} |
- |
- |
bool AbstractTypeArguments::TypeTest(TypeTestKind test_kind, |
const AbstractTypeArguments& other, |
intptr_t len, |
@@ -2983,16 +2921,53 @@ |
return false; |
} |
const TypeParameter& type_param = TypeParameter::Cast(type); |
+ ASSERT(type_param.IsFinalized()); |
if ((type_param.index() != i)) { |
return false; |
} |
+ // If this type parameter specifies an upper bound, then the type argument |
+ // vector does not really represent the identity vector. It cannot be |
+ // substituted by the instantiator's type argument vector without checking |
+ // the upper bound. |
+ const AbstractType& bound = AbstractType::Handle(type_param.bound()); |
+ ASSERT(bound.IsFinalized()); |
+ if (!bound.IsObjectType() && !bound.IsDynamicType()) { |
+ return false; |
+ } |
} |
return true; |
} |
+bool TypeArguments::IsBounded() const { |
+ AbstractType& type = AbstractType::Handle(); |
+ intptr_t num_types = Length(); |
+ for (intptr_t i = 0; i < num_types; i++) { |
+ type = TypeAt(i); |
+ if (type.IsBoundedType()) { |
+ return true; |
+ } |
+ if (type.IsTypeParameter()) { |
+ const AbstractType& bound = AbstractType::Handle( |
+ TypeParameter::Cast(type).bound()); |
+ if (!bound.IsObjectType() && !bound.IsDynamicType()) { |
+ return true; |
+ } |
+ continue; |
+ } |
+ const AbstractTypeArguments& type_args = AbstractTypeArguments::Handle( |
+ Type::Cast(type).arguments()); |
+ if (!type_args.IsNull() && type_args.IsBounded()) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+ |
RawAbstractTypeArguments* TypeArguments::InstantiateFrom( |
- const AbstractTypeArguments& instantiator_type_arguments) const { |
+ const AbstractTypeArguments& instantiator_type_arguments, |
+ Error* malformed_error) const { |
ASSERT(!IsInstantiated()); |
if (!instantiator_type_arguments.IsNull() && |
IsUninstantiatedIdentity() && |
@@ -3006,7 +2981,7 @@ |
for (intptr_t i = 0; i < num_types; i++) { |
type = TypeAt(i); |
if (!type.IsInstantiated()) { |
- type = type.InstantiateFrom(instantiator_type_arguments); |
+ type = type.InstantiateFrom(instantiator_type_arguments, malformed_error); |
} |
instantiated_array.SetTypeAt(i, type); |
} |
@@ -3180,13 +3155,18 @@ |
RawAbstractType* InstantiatedTypeArguments::TypeAt(intptr_t index) const { |
- const AbstractType& type = AbstractType::Handle( |
- AbstractTypeArguments::Handle( |
- uninstantiated_type_arguments()).TypeAt(index)); |
+ AbstractType& type = AbstractType::Handle(AbstractTypeArguments::Handle( |
+ uninstantiated_type_arguments()).TypeAt(index)); |
if (!type.IsInstantiated()) { |
const AbstractTypeArguments& instantiator_type_args = |
AbstractTypeArguments::Handle(instantiator_type_arguments()); |
- return type.InstantiateFrom(instantiator_type_args); |
+ Error& malformed_error = Error::Handle(); |
+ type = type.InstantiateFrom(instantiator_type_args, &malformed_error); |
+ // InstantiatedTypeArguments cannot include unchecked bounds. |
+ // In the presence of unchecked bounds, no InstantiatedTypeArguments are |
+ // allocated, but the type arguments are instantiated individually and their |
+ // bounds are checked. |
+ ASSERT(malformed_error.IsNull()); |
} |
return type.raw(); |
} |
@@ -3984,7 +3964,9 @@ |
AbstractType& other_param_type = |
AbstractType::Handle(other.ParameterTypeAt(other_parameter_position)); |
if (!other_param_type.IsInstantiated()) { |
- other_param_type = other_param_type.InstantiateFrom(other_type_arguments); |
+ other_param_type = other_param_type.InstantiateFrom(other_type_arguments, |
+ malformed_error); |
+ ASSERT((malformed_error == NULL) || malformed_error->IsNull()); |
} |
if (other_param_type.IsDynamicType()) { |
return true; |
@@ -3992,7 +3974,8 @@ |
AbstractType& param_type = |
AbstractType::Handle(ParameterTypeAt(parameter_position)); |
if (!param_type.IsInstantiated()) { |
- param_type = param_type.InstantiateFrom(type_arguments); |
+ param_type = param_type.InstantiateFrom(type_arguments, malformed_error); |
+ ASSERT((malformed_error == NULL) || malformed_error->IsNull()); |
} |
if (param_type.IsDynamicType()) { |
return test_kind == kIsSubtypeOf; |
@@ -4033,12 +4016,15 @@ |
// Check the result type. |
AbstractType& other_res_type = AbstractType::Handle(other.result_type()); |
if (!other_res_type.IsInstantiated()) { |
- other_res_type = other_res_type.InstantiateFrom(other_type_arguments); |
+ other_res_type = other_res_type.InstantiateFrom(other_type_arguments, |
+ malformed_error); |
+ ASSERT((malformed_error == NULL) || malformed_error->IsNull()); |
} |
if (!other_res_type.IsDynamicType() && !other_res_type.IsVoidType()) { |
AbstractType& res_type = AbstractType::Handle(result_type()); |
if (!res_type.IsInstantiated()) { |
- res_type = res_type.InstantiateFrom(type_arguments); |
+ res_type = res_type.InstantiateFrom(type_arguments, malformed_error); |
+ ASSERT((malformed_error == NULL) || malformed_error->IsNull()); |
} |
if (res_type.IsVoidType()) { |
return false; |
@@ -4350,7 +4336,7 @@ |
param_type = ParameterTypeAt(i); |
ASSERT(!param_type.IsNull()); |
if (instantiate && !param_type.IsInstantiated()) { |
- param_type = param_type.InstantiateFrom(instantiator); |
+ param_type = param_type.InstantiateFrom(instantiator, NULL); |
} |
name = param_type.BuildName(name_visibility); |
pieces.Add(name); |
@@ -4375,7 +4361,7 @@ |
} |
param_type = ParameterTypeAt(i); |
if (instantiate && !param_type.IsInstantiated()) { |
- param_type = param_type.InstantiateFrom(instantiator); |
+ param_type = param_type.InstantiateFrom(instantiator, NULL); |
} |
ASSERT(!param_type.IsNull()); |
name = param_type.BuildName(name_visibility); |
@@ -4393,7 +4379,7 @@ |
pieces.Add(Symbols::RParenArrow()); |
AbstractType& res_type = AbstractType::Handle(result_type()); |
if (instantiate && !res_type.IsInstantiated()) { |
- res_type = res_type.InstantiateFrom(instantiator); |
+ res_type = res_type.InstantiateFrom(instantiator, NULL); |
} |
name = res_type.BuildName(name_visibility); |
pieces.Add(name); |
@@ -8809,7 +8795,7 @@ |
// Verify that the number of type arguments in the instance matches the |
// number of type arguments expected by the instance class. |
// A discrepancy is allowed for closures, which borrow the type argument |
- // vector of their instantiator, which may be of a super class of the class |
+ // vector of their instantiator, which may be of a subclass of the class |
// defining the closure. Truncating the vector to the correct length on |
// instantiation is unnecessary. The vector may therefore be longer. |
ASSERT(type_arguments.IsNull() || |
@@ -8819,31 +8805,19 @@ |
} |
Class& other_class = Class::Handle(); |
AbstractTypeArguments& other_type_arguments = AbstractTypeArguments::Handle(); |
- // In case 'other' is not instantiated, we could simply call |
- // other.InstantiateFrom(other_instantiator), however, we can save the |
- // allocation of a new AbstractType by inlining the code. |
- if (other.IsTypeParameter()) { |
- if (other_instantiator.IsNull()) { |
- // An uninstantiated type parameter is equivalent to dynamic. |
- return true; |
+ // Note that we may encounter a bound error in checked mode. |
+ if (!other.IsInstantiated()) { |
+ const AbstractType& instantiated_other = AbstractType::Handle( |
+ other.InstantiateFrom(other_instantiator, malformed_error)); |
+ if ((malformed_error != NULL) && !malformed_error->IsNull()) { |
+ ASSERT(FLAG_enable_type_checks); |
+ return false; |
} |
- const TypeParameter& other_type_param = TypeParameter::Cast(other); |
- AbstractType& instantiated_other = AbstractType::Handle( |
- other_instantiator.TypeAt(other_type_param.index())); |
- if (instantiated_other.IsDynamicType() || |
- instantiated_other.IsTypeParameter()) { |
- return true; |
- } |
other_class = instantiated_other.type_class(); |
other_type_arguments = instantiated_other.arguments(); |
} else { |
other_class = other.type_class(); |
other_type_arguments = other.arguments(); |
- if (!other_type_arguments.IsNull() && |
- !other_type_arguments.IsInstantiated()) { |
- other_type_arguments = |
- other_type_arguments.InstantiateFrom(other_instantiator); |
- } |
} |
return cls.IsSubtypeOf(type_arguments, other_class, other_type_arguments, |
malformed_error); |
@@ -9037,13 +9011,14 @@ |
bool AbstractType::Equals(const Instance& other) const { |
// AbstractType is an abstract class. |
- UNREACHABLE(); |
- return false; |
+ ASSERT(raw() == AbstractType::null()); |
+ return other.IsNull(); |
} |
RawAbstractType* AbstractType::InstantiateFrom( |
- const AbstractTypeArguments& instantiator_type_arguments) const { |
+ const AbstractTypeArguments& instantiator_type_arguments, |
+ Error* malformed_error) const { |
// AbstractType is an abstract class. |
UNREACHABLE(); |
return NULL; |
@@ -9058,6 +9033,13 @@ |
RawString* AbstractType::BuildName(NameVisibility name_visibility) const { |
+ if (IsBoundedType()) { |
+ // TODO(regis): Should the bound be visible in the name for debug purposes |
+ // if name_visibility is kInternalName? |
+ const AbstractType& type = AbstractType::Handle( |
+ BoundedType::Cast(*this).type()); |
+ return type.BuildName(name_visibility); |
+ } |
if (IsTypeParameter()) { |
return TypeParameter::Cast(*this).name(); |
} |
@@ -9196,42 +9178,48 @@ |
return false; |
} |
if (other.IsMalformed()) { |
- ASSERT(FLAG_enable_type_checks); |
+ // Note that 'other' may represent an unresolved bound that is checked at |
+ // compile time, even in production mode, in which case the resulting |
+ // BoundedType is ignored at run time if in production mode. |
+ // Therefore, we cannot assert that we are in checked mode here. |
if ((malformed_error != NULL) && malformed_error->IsNull()) { |
*malformed_error = other.malformed_error(); |
} |
return false; |
} |
- // AbstractType parameters cannot be handled by Class::TypeTest(). |
+ if (IsBoundedType() || other.IsBoundedType()) { |
+ if (Equals(other)) { |
+ return true; |
+ } |
+ return false; // TODO(regis): We should return "maybe after instantiation". |
+ } |
+ // Type parameters cannot be handled by Class::TypeTest(). |
// When comparing two uninstantiated function types, one returning type |
// parameter K, the other returning type parameter V, we cannot assume that K |
- // is a subtype of V, or vice versa. We only return true if K == V, i.e. if |
- // they have the same index (both are finalized, so their indices are |
- // comparable). |
- // The same rule applies When checking the upper bound of a still |
+ // is a subtype of V, or vice versa. We only return true if K equals V, as |
+ // defined by TypeParameter::Equals. |
+ // 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. But there are cases where it can be decided at compile time. |
+ // to run time. |
+ // We may think that 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 bounds check, even it T is |
+ // 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()) { |
const TypeParameter& other_type_param = TypeParameter::Cast(other); |
- return type_param.index() == other_type_param.index(); |
- } else if (FLAG_enable_type_checks) { |
- // In checked mode, if the upper bound of this type is more specific than |
- // the other type, then this type is more specific than the other type. |
- const AbstractType& type_param_bound = |
- AbstractType::Handle(type_param.bound()); |
- if (type_param_bound.IsMoreSpecificThan(other, malformed_error)) { |
+ if (type_param.Equals(other_type_param)) { |
return true; |
} |
} |
- return false; |
+ return false; // TODO(regis): We should return "maybe after instantiation". |
} |
if (other.IsTypeParameter()) { |
- return false; |
+ return false; // TODO(regis): We should return "maybe after instantiation". |
} |
const Class& cls = Class::Handle(type_class()); |
return cls.TypeTest(test_kind, |
@@ -9442,14 +9430,22 @@ |
RawAbstractType* Type::InstantiateFrom( |
- const AbstractTypeArguments& instantiator_type_arguments) const { |
+ const AbstractTypeArguments& instantiator_type_arguments, |
+ Error* malformed_error) const { |
ASSERT(IsFinalized()); |
ASSERT(!IsInstantiated()); |
+ // Return the uninstantiated type unchanged if malformed. No copy needed. |
+ if (IsMalformed()) { |
+ return raw(); |
+ } |
AbstractTypeArguments& type_arguments = |
AbstractTypeArguments::Handle(arguments()); |
- type_arguments = type_arguments.InstantiateFrom(instantiator_type_arguments); |
+ type_arguments = type_arguments.InstantiateFrom(instantiator_type_arguments, |
+ malformed_error); |
const Class& cls = Class::Handle(type_class()); |
ASSERT(cls.is_finalized()); |
+ // This uninstantiated type is not modified, as it can be instantiated |
+ // with different instantiators. |
Type& instantiated_type = Type::Handle( |
Type::New(cls, type_arguments, token_pos())); |
ASSERT(type_arguments.IsNull() || |
@@ -9466,7 +9462,7 @@ |
if (!other.IsType()) { |
return false; |
} |
- const AbstractType& other_type = AbstractType::Cast(other); |
+ const Type& other_type = Type::Cast(other); |
ASSERT(IsFinalized() && other_type.IsFinalized()); |
if (IsMalformed() || other_type.IsMalformed()) { |
return false; |
@@ -9632,6 +9628,7 @@ |
bool TypeParameter::Equals(const Instance& other) const { |
+ ASSERT(IsFinalized()); |
if (raw() == other.raw()) { |
return true; |
} |
@@ -9639,9 +9636,7 @@ |
return false; |
} |
const TypeParameter& other_type_param = TypeParameter::Cast(other); |
- if (IsFinalized() != other_type_param.IsFinalized()) { |
- return false; |
- } |
+ ASSERT(other_type_param.IsFinalized()); |
if (parameterized_class() != other_type_param.parameterized_class()) { |
return false; |
} |
@@ -9674,20 +9669,65 @@ |
StorePointer(&raw_ptr()->bound_, value.raw()); |
} |
+ |
RawAbstractType* TypeParameter::InstantiateFrom( |
- const AbstractTypeArguments& instantiator_type_arguments) const { |
+ const AbstractTypeArguments& instantiator_type_arguments, |
+ Error* malformed_error) const { |
ASSERT(IsFinalized()); |
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()); |
} |
+void TypeParameter::CheckBound(const AbstractType& bounded_type, |
+ const AbstractType& upper_bound, |
+ Error* malformed_error) const { |
+ ASSERT(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; |
+ } |
+ // 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()); |
+} |
+ |
+ |
intptr_t TypeParameter::Hash() const { |
ASSERT(IsFinalized()); |
uword result = 0; |
result += Class::Handle(parameterized_class()).id(); |
+ // Do not include the hash of the bound, which could lead to cycles. |
result <<= index(); |
return FinalizeHash(result); |
} |
@@ -9746,6 +9786,154 @@ |
} |
+bool BoundedType::IsMalformed() const { |
+ return FLAG_enable_type_checks && AbstractType::Handle(bound()).IsMalformed(); |
+} |
+ |
+ |
+RawError* BoundedType::malformed_error() const { |
+ ASSERT(FLAG_enable_type_checks); |
+ return AbstractType::Handle(bound()).malformed_error(); |
+} |
+ |
+ |
+bool BoundedType::Equals(const Instance& other) const { |
+ // BoundedType are not canonicalized, because their bound may get finalized |
+ // after the BoundedType is created and initialized. |
+ if (raw() == other.raw()) { |
+ return true; |
+ } |
+ if (!other.IsBoundedType()) { |
+ return false; |
+ } |
+ const BoundedType& other_bounded = BoundedType::Cast(other); |
+ if (type_parameter() != other_bounded.type_parameter()) { |
+ // Not a structural compare. |
+ // Note that a deep comparison of bounds could lead to cycles. |
+ return false; |
+ } |
+ const AbstractType& this_type = AbstractType::Handle(type()); |
+ const AbstractType& other_type = AbstractType::Handle(other_bounded.type()); |
+ if (!this_type.Equals(other_type)) { |
+ return false; |
+ } |
+ const AbstractType& this_bound = AbstractType::Handle(bound()); |
+ const AbstractType& other_bound = AbstractType::Handle(other_bounded.bound()); |
+ return this_bound.IsFinalized() && |
+ other_bound.IsFinalized() && |
+ this_bound.Equals(other_bound); |
+} |
+ |
+ |
+void BoundedType::set_type(const AbstractType& value) const { |
+ ASSERT(value.IsFinalized()); |
+ ASSERT(!value.IsMalformed()); |
+ StorePointer(&raw_ptr()->type_, value.raw()); |
+} |
+ |
+ |
+void BoundedType::set_bound(const AbstractType& value) const { |
+ // The bound may still be unfinalized because of legal cycles. |
+ // It must be finalized before it is checked at run time, though. |
+ StorePointer(&raw_ptr()->bound_, value.raw()); |
+} |
+ |
+ |
+void BoundedType::set_type_parameter(const TypeParameter& value) const { |
+ // A null type parameter is set when marking a type malformed because of a |
+ // bound error at compile time. |
+ ASSERT(value.IsNull() || value.IsFinalized()); |
+ StorePointer(&raw_ptr()->type_parameter_, value.raw()); |
+} |
+ |
+ |
+void BoundedType::set_is_being_checked(bool value) const { |
+ raw_ptr()->is_being_checked_ = value; |
+} |
+ |
+ |
+RawAbstractType* BoundedType::InstantiateFrom( |
+ const AbstractTypeArguments& instantiator_type_arguments, |
+ Error* malformed_error) const { |
+ ASSERT(IsFinalized()); |
+ AbstractType& bounded_type = AbstractType::Handle(type()); |
+ if (!bounded_type.IsInstantiated()) { |
+ bounded_type = bounded_type.InstantiateFrom(instantiator_type_arguments, |
+ malformed_error); |
+ } |
+ if (FLAG_enable_type_checks && |
+ malformed_error->IsNull() && |
+ !is_being_checked()) { |
+ // Avoid endless recursion while checking and instantiating bound. |
+ set_is_being_checked(true); |
+ AbstractType& upper_bound = AbstractType::Handle(bound()); |
+ ASSERT(!upper_bound.IsObjectType() && !upper_bound.IsDynamicType()); |
+ const TypeParameter& type_param = TypeParameter::Handle(type_parameter()); |
+ if (!upper_bound.IsInstantiated()) { |
+ upper_bound = upper_bound.InstantiateFrom(instantiator_type_arguments, |
+ malformed_error); |
+ } |
+ if (malformed_error->IsNull()) { |
+ type_param.CheckBound(bounded_type, upper_bound, malformed_error); |
+ } |
+ set_is_being_checked(false); |
+ } |
+ return bounded_type.raw(); |
+} |
+ |
+ |
+intptr_t BoundedType::Hash() const { |
+ uword result = 0; |
+ result += AbstractType::Handle(type()).Hash(); |
+ // Do not include the hash of the bound, which could lead to cycles. |
+ TypeParameter& type_param = TypeParameter::Handle(type_parameter()); |
+ if (!type_param.IsNull()) { |
+ result += type_param.Hash(); |
+ } |
+return FinalizeHash(result); |
+} |
+ |
+ |
+RawBoundedType* BoundedType::New() { |
+ ASSERT(Isolate::Current()->object_store()->bounded_type_class() != |
+ Class::null()); |
+ RawObject* raw = Object::Allocate(BoundedType::kClassId, |
+ BoundedType::InstanceSize(), |
+ Heap::kOld); |
+ return reinterpret_cast<RawBoundedType*>(raw); |
+} |
+ |
+ |
+RawBoundedType* BoundedType::New(const AbstractType& type, |
+ const AbstractType& bound, |
+ const TypeParameter& type_parameter) { |
+ const BoundedType& result = BoundedType::Handle(BoundedType::New()); |
+ result.set_type(type); |
+ result.set_bound(bound); |
+ result.set_type_parameter(type_parameter); |
+ result.set_is_being_checked(false); |
+ return result.raw(); |
+} |
+ |
+ |
+const char* BoundedType::ToCString() const { |
+ const char* format = "BoundedType: type %s; bound: %s; class: %s"; |
+ const char* type_cstr = String::Handle(AbstractType::Handle( |
+ type()).Name()).ToCString(); |
+ const char* bound_cstr = String::Handle(AbstractType::Handle( |
+ bound()).Name()).ToCString(); |
+ const Class& cls = Class::Handle(TypeParameter::Handle( |
+ type_parameter()).parameterized_class()); |
+ const char* cls_cstr = |
+ cls.IsNull() ? " null" : String::Handle(cls.Name()).ToCString(); |
+ intptr_t len = OS::SNPrint( |
+ NULL, 0, format, type_cstr, bound_cstr, cls_cstr) + 1; |
+ char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); |
+ OS::SNPrint(chars, len, format, type_cstr, bound_cstr, cls_cstr); |
+ return chars; |
+} |
+ |
+ |
const char* Number::ToCString() const { |
// Number is an interface. No instances of Number should exist. |
UNREACHABLE(); |