| Index: runtime/vm/object.cc
|
| ===================================================================
|
| --- runtime/vm/object.cc (revision 31568)
|
| +++ runtime/vm/object.cc (working copy)
|
| @@ -3595,7 +3595,7 @@
|
| }
|
|
|
|
|
| -bool AbstractTypeArguments::IsInstantiated() const {
|
| +bool AbstractTypeArguments::IsInstantiated(GrowableObjectArray* trail) const {
|
| // AbstractTypeArguments is an abstract class.
|
| UNREACHABLE();
|
| return false;
|
| @@ -3673,7 +3673,8 @@
|
| }
|
|
|
|
|
| -bool AbstractTypeArguments::Equals(const AbstractTypeArguments& other) const {
|
| +bool AbstractTypeArguments::IsEquivalent(const AbstractTypeArguments& other,
|
| + GrowableObjectArray* trail) const {
|
| if (this->raw() == other.raw()) {
|
| return true;
|
| }
|
| @@ -3689,7 +3690,7 @@
|
| for (intptr_t i = 0; i < num_types; i++) {
|
| type = TypeAt(i);
|
| other_type = other.TypeAt(i);
|
| - if (!type.Equals(other_type)) {
|
| + if (!type.IsEquivalent(other_type, trail)) {
|
| return false;
|
| }
|
| }
|
| @@ -3699,7 +3700,8 @@
|
|
|
| RawAbstractTypeArguments* AbstractTypeArguments::InstantiateFrom(
|
| const AbstractTypeArguments& instantiator_type_arguments,
|
| - Error* bound_error) const {
|
| + Error* bound_error,
|
| + GrowableObjectArray* trail) const {
|
| // AbstractTypeArguments is an abstract class.
|
| UNREACHABLE();
|
| return NULL;
|
| @@ -3808,13 +3810,13 @@
|
| }
|
|
|
|
|
| -bool TypeArguments::IsInstantiated() const {
|
| +bool TypeArguments::IsInstantiated(GrowableObjectArray* trail) const {
|
| AbstractType& type = AbstractType::Handle();
|
| const intptr_t num_types = Length();
|
| for (intptr_t i = 0; i < num_types; i++) {
|
| type = TypeAt(i);
|
| ASSERT(!type.IsNull());
|
| - if (!type.IsInstantiated()) {
|
| + if (!type.IsInstantiated(trail)) {
|
| return false;
|
| }
|
| }
|
| @@ -3964,7 +3966,8 @@
|
|
|
| RawAbstractTypeArguments* TypeArguments::InstantiateFrom(
|
| const AbstractTypeArguments& instantiator_type_arguments,
|
| - Error* bound_error) const {
|
| + Error* bound_error,
|
| + GrowableObjectArray* trail) const {
|
| ASSERT(!IsInstantiated());
|
| if (!instantiator_type_arguments.IsNull() &&
|
| IsUninstantiatedIdentity() &&
|
| @@ -3978,7 +3981,9 @@
|
| for (intptr_t i = 0; i < num_types; i++) {
|
| type = TypeAt(i);
|
| if (!type.IsInstantiated()) {
|
| - type = type.InstantiateFrom(instantiator_type_arguments, bound_error);
|
| + type = type.InstantiateFrom(instantiator_type_arguments,
|
| + bound_error,
|
| + trail);
|
| }
|
| instantiated_array.SetTypeAt(i, type);
|
| }
|
| @@ -4109,7 +4114,8 @@
|
| }
|
|
|
|
|
| -RawAbstractTypeArguments* TypeArguments::Canonicalize() const {
|
| +RawAbstractTypeArguments* TypeArguments::Canonicalize(
|
| + GrowableObjectArray* trail) const {
|
| if (IsNull() || IsCanonical()) {
|
| ASSERT(IsOld());
|
| return this->raw();
|
| @@ -4131,7 +4137,7 @@
|
| AbstractType& type = AbstractType::Handle(isolate);
|
| for (intptr_t i = 0; i < num_types; i++) {
|
| type = TypeAt(i);
|
| - type = type.Canonicalize();
|
| + type = type.Canonicalize(trail);
|
| SetTypeAt(i, type);
|
| }
|
| // Make sure we have an old space object and add it to the table.
|
| @@ -4205,7 +4211,8 @@
|
| }
|
|
|
|
|
| -RawAbstractTypeArguments* InstantiatedTypeArguments::Canonicalize() const {
|
| +RawAbstractTypeArguments* InstantiatedTypeArguments::Canonicalize(
|
| + GrowableObjectArray* trail) const {
|
| const intptr_t num_types = Length();
|
| const TypeArguments& type_args = TypeArguments::Handle(
|
| TypeArguments::New(num_types, Heap::kOld));
|
| @@ -4214,7 +4221,7 @@
|
| type = TypeAt(i);
|
| type_args.SetTypeAt(i, type);
|
| }
|
| - return type_args.Canonicalize();
|
| + return type_args.Canonicalize(trail);
|
| }
|
|
|
|
|
| @@ -11820,7 +11827,7 @@
|
| }
|
|
|
|
|
| -bool AbstractType::IsInstantiated() const {
|
| +bool AbstractType::IsInstantiated(GrowableObjectArray* trail) const {
|
| // AbstractType is an abstract class.
|
| UNREACHABLE();
|
| return false;
|
| @@ -11875,7 +11882,8 @@
|
| }
|
|
|
|
|
| -bool AbstractType::Equals(const Instance& other) const {
|
| +bool AbstractType::IsEquivalent(const Instance& other,
|
| + GrowableObjectArray* trail) const {
|
| // AbstractType is an abstract class.
|
| UNREACHABLE();
|
| return false;
|
| @@ -11884,7 +11892,8 @@
|
|
|
| RawAbstractType* AbstractType::InstantiateFrom(
|
| const AbstractTypeArguments& instantiator_type_arguments,
|
| - Error* bound_error) const {
|
| + Error* bound_error,
|
| + GrowableObjectArray* trail) const {
|
| // AbstractType is an abstract class.
|
| UNREACHABLE();
|
| return NULL;
|
| @@ -11898,7 +11907,7 @@
|
| }
|
|
|
|
|
| -RawAbstractType* AbstractType::Canonicalize() const {
|
| +RawAbstractType* AbstractType::Canonicalize(GrowableObjectArray* trail) const {
|
| // AbstractType is an abstract class.
|
| UNREACHABLE();
|
| return NULL;
|
| @@ -11914,7 +11923,7 @@
|
| }
|
| String& type_name = String::Handle(type.BuildName(kInternalName));
|
| type_name = String::Concat(type_name, Symbols::SpaceExtendsSpace());
|
| - // Building the bound name may lead into cycles.
|
| + // Build the bound name without causing divergence.
|
| const AbstractType& bound = AbstractType::Handle(
|
| BoundedType::Cast(*this).bound());
|
| String& bound_name = String::Handle();
|
| @@ -11975,11 +11984,11 @@
|
| }
|
| if (cls.IsSignatureClass()) {
|
| // We may be reporting an error about a malformed function type. In that
|
| - // case, avoid instantiating the signature, since it may lead to cycles.
|
| + // case, avoid instantiating the signature, since it may cause divergence.
|
| if (!IsFinalized() || IsBeingFinalized() || IsMalformed()) {
|
| return class_name.raw();
|
| }
|
| - // In order to avoid cycles, print the name of a typedef (non-canonical
|
| + // To avoid divergence, print the name of a typedef (non-canonical
|
| // signature class) as a regular, possibly parameterized, class.
|
| if (cls.IsCanonicalSignatureClass()) {
|
| const Function& signature_function = Function::Handle(
|
| @@ -12384,7 +12393,7 @@
|
| }
|
|
|
|
|
| -bool Type::IsInstantiated() const {
|
| +bool Type::IsInstantiated(GrowableObjectArray* trail) const {
|
| if (raw_ptr()->type_state_ == RawType::kFinalizedInstantiated) {
|
| return true;
|
| }
|
| @@ -12393,13 +12402,14 @@
|
| }
|
| const AbstractTypeArguments& args =
|
| AbstractTypeArguments::Handle(arguments());
|
| - return args.IsNull() || args.IsInstantiated();
|
| + return args.IsNull() || args.IsInstantiated(trail);
|
| }
|
|
|
|
|
| RawAbstractType* Type::InstantiateFrom(
|
| const AbstractTypeArguments& instantiator_type_arguments,
|
| - Error* bound_error) const {
|
| + Error* bound_error,
|
| + GrowableObjectArray* trail) const {
|
| ASSERT(IsFinalized() || IsBeingFinalized());
|
| ASSERT(!IsInstantiated());
|
| // Return the uninstantiated type unchanged if malformed. No copy needed.
|
| @@ -12409,7 +12419,8 @@
|
| AbstractTypeArguments& type_arguments =
|
| AbstractTypeArguments::Handle(arguments());
|
| type_arguments = type_arguments.InstantiateFrom(instantiator_type_arguments,
|
| - bound_error);
|
| + bound_error,
|
| + trail);
|
| // Note that the type class has to be resolved at this time, but not
|
| // necessarily finalized yet. We may be checking bounds at compile time.
|
| const Class& cls = Class::Handle(type_class());
|
| @@ -12424,14 +12435,18 @@
|
| }
|
|
|
|
|
| -bool Type::Equals(const Instance& other) const {
|
| +bool Type::IsEquivalent(const Instance& other,
|
| + GrowableObjectArray* trail) const {
|
| ASSERT(!IsNull());
|
| if (raw() == other.raw()) {
|
| return true;
|
| }
|
| if (other.IsTypeRef()) {
|
| - // TODO(regis): Use trail. For now, we "unfold" the right hand type.
|
| - return Equals(AbstractType::Handle(TypeRef::Cast(other).type()));
|
| + // Unfold right hand type. Divergence is controlled by left hand type.
|
| + const AbstractType& other_ref_type = AbstractType::Handle(
|
| + TypeRef::Cast(other).type());
|
| + ASSERT(!other_ref_type.IsTypeRef());
|
| + return IsEquivalent(other_ref_type, trail);
|
| }
|
| if (!other.IsType()) {
|
| return false;
|
| @@ -12475,7 +12490,7 @@
|
| for (intptr_t i = 0; i < num_type_params; i++) {
|
| type_arg = type_args.TypeAt(from_index + i);
|
| other_type_arg = other_type_args.TypeAt(from_index + i);
|
| - if (!type_arg.Equals(other_type_arg)) {
|
| + if (!type_arg.IsEquivalent(other_type_arg, trail)) {
|
| return false;
|
| }
|
| }
|
| @@ -12497,7 +12512,7 @@
|
| }
|
|
|
|
|
| -RawAbstractType* Type::Canonicalize() const {
|
| +RawAbstractType* Type::Canonicalize(GrowableObjectArray* trail) const {
|
| ASSERT(IsFinalized());
|
| if (IsCanonical() || IsMalformed()) {
|
| ASSERT(IsMalformed() || AbstractTypeArguments::Handle(arguments()).IsOld());
|
| @@ -12548,7 +12563,7 @@
|
| AbstractTypeArguments& type_args =
|
| AbstractTypeArguments::Handle(isolate, arguments());
|
| ASSERT(type_args.IsNull() || (type_args.Length() == cls.NumTypeArguments()));
|
| - type_args = type_args.Canonicalize();
|
| + type_args = type_args.Canonicalize(trail);
|
| set_arguments(type_args);
|
| // The type needs to be added to the list. Grow the list if it is full.
|
| if (index == canonical_types_len) {
|
| @@ -12687,53 +12702,44 @@
|
| }
|
|
|
|
|
| -bool TypeRef::IsInstantiated() const {
|
| - if (is_being_checked()) {
|
| +bool TypeRef::IsInstantiated(GrowableObjectArray* trail) const {
|
| + if (TestAndAddToTrail(&trail)) {
|
| return true;
|
| }
|
| - set_is_being_checked(true);
|
| - const bool result = AbstractType::Handle(type()).IsInstantiated();
|
| - set_is_being_checked(false);
|
| - return result;
|
| + return AbstractType::Handle(type()).IsInstantiated(trail);
|
| }
|
|
|
|
|
| -bool TypeRef::Equals(const Instance& other) const {
|
| - // TODO(regis): Use trail instead of mark bit.
|
| +bool TypeRef::IsEquivalent(const Instance& other,
|
| + GrowableObjectArray* trail) const {
|
| if (raw() == other.raw()) {
|
| return true;
|
| }
|
| - if (is_being_checked()) {
|
| + if (TestAndAddBuddyToTrail(&trail, other)) {
|
| return true;
|
| }
|
| - set_is_being_checked(true);
|
| - const bool result = AbstractType::Handle(type()).Equals(other);
|
| - set_is_being_checked(false);
|
| - return result;
|
| + return AbstractType::Handle(type()).IsEquivalent(other, trail);
|
| }
|
|
|
|
|
| RawAbstractType* TypeRef::InstantiateFrom(
|
| const AbstractTypeArguments& instantiator_type_arguments,
|
| - Error* bound_error) const {
|
| + Error* bound_error,
|
| + GrowableObjectArray* trail) const {
|
| + TypeRef& instantiated_type_ref = TypeRef::Handle();
|
| + instantiated_type_ref ^= OnlyBuddyInTrail(trail);
|
| + if (!instantiated_type_ref.IsNull()) {
|
| + return instantiated_type_ref.raw();
|
| + }
|
| + instantiated_type_ref = TypeRef::New(Type::Handle(Type::DynamicType()));
|
| + AddOnlyBuddyToTrail(&trail, instantiated_type_ref);
|
| const AbstractType& ref_type = AbstractType::Handle(type());
|
| - // TODO(regis): Use trail instead of mark bit plus temporary redirection,
|
| - // because it could be marked for another reason.
|
| - if (is_being_checked()) {
|
| - ASSERT(ref_type.IsTypeRef());
|
| - return ref_type.raw();
|
| - }
|
| - set_is_being_checked(true);
|
| ASSERT(!ref_type.IsTypeRef());
|
| - const TypeRef& instantiated_type_ref = TypeRef::Handle(
|
| - TypeRef::New(ref_type));
|
| - // TODO(regis): instantiated_type_ref should be stored in the trail instead.
|
| - set_type(instantiated_type_ref);
|
| const AbstractType& instantiated_ref_type = AbstractType::Handle(
|
| - ref_type.InstantiateFrom(instantiator_type_arguments, bound_error));
|
| + ref_type.InstantiateFrom(instantiator_type_arguments,
|
| + bound_error,
|
| + trail));
|
| instantiated_type_ref.set_type(instantiated_ref_type);
|
| - set_type(ref_type);
|
| - set_is_being_checked(false);
|
| return instantiated_type_ref.raw();
|
| }
|
|
|
| @@ -12744,43 +12750,92 @@
|
| }
|
|
|
|
|
| -void TypeRef::set_is_being_checked(bool value) const {
|
| - raw_ptr()->is_being_checked_ = value;
|
| -}
|
| -
|
| -
|
| -// This function only canonicalizes the referenced type, but not the TypeRef
|
| -// itself, since it cannot be canonical by definition.
|
| +// A TypeRef cannot be canonical by definition. Only its referenced type can be.
|
| // Consider the type Derived, where class Derived extends Base<Derived>.
|
| // The first type argument of its flattened type argument vector is Derived,
|
| // i.e. itself, but pointer equality is not possible.
|
| -RawAbstractType* TypeRef::Canonicalize() const {
|
| - // TODO(regis): Use trail, not mark bit.
|
| - if (is_being_checked()) {
|
| +RawAbstractType* TypeRef::Canonicalize(GrowableObjectArray* trail) const {
|
| + if (TestAndAddToTrail(&trail)) {
|
| return raw();
|
| }
|
| - set_is_being_checked(true);
|
| AbstractType& ref_type = AbstractType::Handle(type());
|
| - ASSERT(!ref_type.IsTypeRef());
|
| - ref_type = ref_type.Canonicalize();
|
| + ref_type = ref_type.Canonicalize(trail);
|
| set_type(ref_type);
|
| - // No need to call SetCanonical(), since a TypeRef cannot be canonical by
|
| - // definition.
|
| - set_is_being_checked(false);
|
| - // We return the referenced type instead of the TypeRef in order to provide
|
| - // pointer equality in simple cases, e.g. in language/f_bounded_equality_test.
|
| - return ref_type.raw();
|
| + return raw();
|
| }
|
|
|
|
|
| intptr_t TypeRef::Hash() const {
|
| - // TODO(regis): Use trail and hash of referenced type.
|
| - // Do not calculate the hash of the referenced type to avoid cycles.
|
| + // Do not calculate the hash of the referenced type to avoid divergence.
|
| uword result = Class::Handle(AbstractType::Handle(type()).type_class()).id();
|
| return FinalizeHash(result);
|
| }
|
|
|
|
|
| +bool TypeRef::TestAndAddToTrail(GrowableObjectArray** trail) const {
|
| + if (*trail == NULL) {
|
| + *trail = &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New());
|
| + } else {
|
| + const intptr_t len = (*trail)->Length();
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + if ((*trail)->At(i) == this->raw()) {
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + (*trail)->Add(*this);
|
| + return false;
|
| +}
|
| +
|
| +
|
| +bool TypeRef::TestAndAddBuddyToTrail(GrowableObjectArray** trail,
|
| + const Object& buddy) const {
|
| + if (*trail == NULL) {
|
| + *trail = &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New());
|
| + } else {
|
| + const intptr_t len = (*trail)->Length();
|
| + ASSERT((len % 2) == 0);
|
| + for (intptr_t i = 0; i < len; i += 2) {
|
| + if (((*trail)->At(i) == this->raw()) &&
|
| + ((*trail)->At(i + 1) == buddy.raw())) {
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + (*trail)->Add(*this);
|
| + (*trail)->Add(buddy);
|
| + return false;
|
| +}
|
| +
|
| +
|
| +RawObject* TypeRef::OnlyBuddyInTrail(GrowableObjectArray* trail) const {
|
| + if (trail == NULL) {
|
| + return Object::null();
|
| + }
|
| + const intptr_t len = trail->Length();
|
| + ASSERT((len % 2) == 0);
|
| + for (intptr_t i = 0; i < len; i += 2) {
|
| + if (trail->At(i) == this->raw()) {
|
| + ASSERT(trail->At(i + 1) != Object::null());
|
| + return trail->At(i + 1);
|
| + }
|
| + }
|
| + return Object::null();
|
| +}
|
| +
|
| +
|
| +void TypeRef::AddOnlyBuddyToTrail(GrowableObjectArray** trail,
|
| + const Object& buddy) const {
|
| + if (*trail == NULL) {
|
| + *trail = &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New());
|
| + } else {
|
| + ASSERT(OnlyBuddyInTrail(*trail) == Object::null());
|
| + }
|
| + (*trail)->Add(*this);
|
| + (*trail)->Add(buddy);
|
| +}
|
| +
|
| +
|
| RawTypeRef* TypeRef::New() {
|
| ASSERT(Isolate::Current()->object_store()->type_ref_class() != Class::null());
|
| RawObject* raw = Object::Allocate(TypeRef::kClassId,
|
| @@ -12793,7 +12848,6 @@
|
| RawTypeRef* TypeRef::New(const AbstractType& type) {
|
| const TypeRef& result = TypeRef::Handle(TypeRef::New());
|
| result.set_type(type);
|
| - result.set_is_being_checked(false);
|
| return result.raw();
|
| }
|
|
|
| @@ -12822,13 +12876,17 @@
|
| }
|
|
|
|
|
| -bool TypeParameter::Equals(const Instance& other) const {
|
| +bool TypeParameter::IsEquivalent(const Instance& other,
|
| + GrowableObjectArray* trail) const {
|
| if (raw() == other.raw()) {
|
| return true;
|
| }
|
| if (other.IsTypeRef()) {
|
| - // TODO(regis): Use trail. For now, we "unfold" the right hand type.
|
| - return Equals(AbstractType::Handle(TypeRef::Cast(other).type()));
|
| + // Unfold right hand type. Divergence is controlled by left hand type.
|
| + const AbstractType& other_ref_type = AbstractType::Handle(
|
| + TypeRef::Cast(other).type());
|
| + ASSERT(!other_ref_type.IsTypeRef());
|
| + return IsEquivalent(other_ref_type, trail);
|
| }
|
| if (!other.IsTypeParameter()) {
|
| return false;
|
| @@ -12869,7 +12927,8 @@
|
|
|
| RawAbstractType* TypeParameter::InstantiateFrom(
|
| const AbstractTypeArguments& instantiator_type_arguments,
|
| - Error* bound_error) const {
|
| + Error* bound_error,
|
| + GrowableObjectArray* trail) const {
|
| ASSERT(IsFinalized());
|
| if (instantiator_type_arguments.IsNull()) {
|
| return Type::DynamicType();
|
| @@ -12880,17 +12939,6 @@
|
| // type arguments are canonicalized at type finalization time. It would be too
|
| // early to canonicalize the returned type argument here, since instantiation
|
| // not only happens at run time, but also during type finalization.
|
| - // However, if the type argument is a reference to a canonical type, we
|
| - // return the referenced canonical type instead of the type reference to
|
| - // provide pointer equality in some simple cases, e.g. in
|
| - // language/f_bounded_equality_test.
|
| - if (type_arg.IsTypeRef()) {
|
| - const AbstractType& ref_type = AbstractType::Handle(
|
| - TypeRef::Cast(type_arg).type());
|
| - if (ref_type.IsCanonical()) {
|
| - return ref_type.raw();
|
| - }
|
| - }
|
| return type_arg.raw();
|
| }
|
|
|
| @@ -12957,7 +13005,8 @@
|
| intptr_t TypeParameter::Hash() const {
|
| ASSERT(IsFinalized());
|
| uword result = Class::Handle(parameterized_class()).id();
|
| - // Do not include the hash of the bound, which could lead to cycles.
|
| + // No need to include the hash of the bound, since the type parameter is fully
|
| + // identified by its class and index.
|
| result <<= index();
|
| return FinalizeHash(result);
|
| }
|
| @@ -13041,35 +13090,37 @@
|
| }
|
|
|
|
|
| -bool BoundedType::Equals(const Instance& other) const {
|
| +bool BoundedType::IsEquivalent(const Instance& other,
|
| + GrowableObjectArray* trail) 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.IsTypeRef()) {
|
| - // TODO(regis): Use trail. For now, we "unfold" the right hand type.
|
| - return Equals(AbstractType::Handle(TypeRef::Cast(other).type()));
|
| + // Unfold right hand type. Divergence is controlled by left hand type.
|
| + const AbstractType& other_ref_type = AbstractType::Handle(
|
| + TypeRef::Cast(other).type());
|
| + ASSERT(!other_ref_type.IsTypeRef());
|
| + return IsEquivalent(other_ref_type, trail);
|
| }
|
| 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)) {
|
| + if (!this_type.IsEquivalent(other_type, trail)) {
|
| 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);
|
| + this_bound.Equals(other_bound); // Different graph, do not pass trail.
|
| }
|
|
|
|
|
| @@ -13095,31 +13146,25 @@
|
| }
|
|
|
|
|
| -void BoundedType::set_is_being_checked(bool value) const {
|
| - raw_ptr()->is_being_checked_ = value;
|
| -}
|
| -
|
| -
|
| RawAbstractType* BoundedType::InstantiateFrom(
|
| const AbstractTypeArguments& instantiator_type_arguments,
|
| - Error* bound_error) const {
|
| + Error* bound_error,
|
| + GrowableObjectArray* trail) const {
|
| ASSERT(IsFinalized());
|
| AbstractType& bounded_type = AbstractType::Handle(type());
|
| if (!bounded_type.IsInstantiated()) {
|
| bounded_type = bounded_type.InstantiateFrom(instantiator_type_arguments,
|
| - bound_error);
|
| + bound_error,
|
| + trail);
|
| }
|
| - if (FLAG_enable_type_checks &&
|
| - bound_error->IsNull() &&
|
| - !is_being_checked()) {
|
| - // Avoid endless recursion while checking and instantiating bound.
|
| - set_is_being_checked(true);
|
| + if (FLAG_enable_type_checks && bound_error->IsNull()) {
|
| 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,
|
| - bound_error);
|
| + bound_error,
|
| + trail);
|
| }
|
| if (bound_error->IsNull()) {
|
| if (!type_param.CheckBound(bounded_type, upper_bound, bound_error) &&
|
| @@ -13132,7 +13177,6 @@
|
| bounded_type = BoundedType::New(bounded_type, upper_bound, type_param);
|
| }
|
| }
|
| - set_is_being_checked(false);
|
| }
|
| return bounded_type.raw();
|
| }
|
| @@ -13155,7 +13199,8 @@
|
|
|
| intptr_t BoundedType::Hash() const {
|
| uword result = AbstractType::Handle(type()).Hash();
|
| - // Do not include the hash of the bound, which could lead to cycles.
|
| + // No need to include the hash of the bound, since the bound is defined by the
|
| + // type parameter (modulo instantiation state).
|
| result += TypeParameter::Handle(type_parameter()).Hash();
|
| return FinalizeHash(result);
|
| }
|
| @@ -13178,7 +13223,6 @@
|
| result.set_type(type);
|
| result.set_bound(bound);
|
| result.set_type_parameter(type_parameter);
|
| - result.set_is_being_checked(false);
|
| return result.raw();
|
| }
|
|
|
|
|