diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index de890859fab..58327ce5303 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -1052,6 +1052,7 @@ void Object::Init(Isolate* isolate) { Smi::New(0)); empty_type_arguments_->StoreSmi(&empty_type_arguments_->raw_ptr()->hash_, Smi::New(0)); + empty_type_arguments_->ComputeHash(); empty_type_arguments_->SetCanonical(); } @@ -4991,9 +4992,9 @@ void Class::set_declaration_type(const Type& value) const { ASSERT(!value.IsNull() && value.IsCanonical() && value.IsOld()); ASSERT((declaration_type() == Object::null()) || (declaration_type() == value.raw())); // Set during own finalization. - // Since declaration type is used as the runtime type of instances of a - // non-generic class, the nullability is set to kNonNullable instead of - // kLegacy when the non-nullable experiment is enabled. + // Since DeclarationType is used as the runtime type of instances of a + // non-generic class, its nullability must be kNonNullable. + // The exception is DeclarationType of Null which is kNullable. ASSERT(value.type_class_id() != kNullCid || value.IsNullable()); ASSERT(value.type_class_id() == kNullCid || value.IsNonNullable()); StorePointer(&raw_ptr()->declaration_type_, value.raw()); @@ -5863,14 +5864,13 @@ void TypeArguments::set_nullability(intptr_t value) const { StoreSmi(&raw_ptr()->nullability_, Smi::New(value)); } -intptr_t TypeArguments::ComputeHash() const { - if (IsNull()) return 0; - const intptr_t num_types = Length(); - if (IsRaw(0, num_types)) return 0; +intptr_t TypeArguments::HashForRange(intptr_t from_index, intptr_t len) const { + if (IsNull()) return kAllDynamicHash; + if (IsRaw(from_index, len)) return kAllDynamicHash; uint32_t result = 0; AbstractType& type = AbstractType::Handle(); - for (intptr_t i = 0; i < num_types; i++) { - type = TypeAt(i); + for (intptr_t i = 0; i < len; i++) { + type = TypeAt(from_index + i); // The hash may be calculated during type finalization (for debugging // purposes only) while a type argument is still temporarily null. if (type.IsNull() || type.IsNullTypeRef()) { @@ -5879,7 +5879,16 @@ intptr_t TypeArguments::ComputeHash() const { result = CombineHashes(result, type.Hash()); } result = FinalizeHash(result, kHashBits); - SetHash(result); + return result; +} + +intptr_t TypeArguments::ComputeHash() const { + if (IsNull()) return kAllDynamicHash; + const intptr_t num_types = Length(); + const uint32_t result = HashForRange(0, num_types); + if (result != 0) { + SetHash(result); + } return result; } @@ -20303,7 +20312,7 @@ void Type::EnumerateURIs(URIs* uris) const { intptr_t Type::ComputeHash() const { ASSERT(IsFinalized()); - uint32_t result = 1; + uint32_t result = 0; result = CombineHashes(result, type_class_id()); // A legacy type should have the same hash as its non-nullable version to be // consistent with the definition of type equality in Dart code. @@ -20312,7 +20321,20 @@ intptr_t Type::ComputeHash() const { type_nullability = Nullability::kNonNullable; } result = CombineHashes(result, static_cast(type_nullability)); - result = CombineHashes(result, TypeArguments::Handle(arguments()).Hash()); + uint32_t type_args_hash = TypeArguments::kAllDynamicHash; + if (arguments() != TypeArguments::null()) { + // Only include hashes of type arguments corresponding to type parameters. + // This prevents obtaining different hashes depending on the location of + // TypeRefs in the super class type argument vector. + const TypeArguments& type_args = TypeArguments::Handle(arguments()); + const Class& cls = Class::Handle(type_class()); + const intptr_t num_type_params = cls.NumTypeParameters(); + if (num_type_params > 0) { + const intptr_t from_index = cls.NumTypeArguments() - num_type_params; + type_args_hash = type_args.HashForRange(from_index, num_type_params); + } + } + result = CombineHashes(result, type_args_hash); if (IsFunctionType()) { AbstractType& type = AbstractType::Handle(); const Function& sig_fun = Function::Handle(signature()); diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 96eb9c5aa2a..2824547c346 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -7528,6 +7528,9 @@ class TypeArguments : public Instance { // architecture. static const intptr_t kHashBits = 30; + // Hash value for a type argument vector consisting solely of dynamic types. + static const intptr_t kAllDynamicHash = 1; + intptr_t Length() const; AbstractTypePtr TypeAt(intptr_t index) const; AbstractTypePtr TypeAtNullSafe(intptr_t index) const; @@ -7742,6 +7745,7 @@ class TypeArguments : public Instance { return 0; } intptr_t Hash() const; + intptr_t HashForRange(intptr_t from_index, intptr_t len) const; static TypeArgumentsPtr New(intptr_t len, Heap::Space space = Heap::kOld); @@ -11326,7 +11330,7 @@ inline void TypeParameter::SetHash(intptr_t value) const { } inline intptr_t TypeArguments::Hash() const { - if (IsNull()) return 0; + if (IsNull()) return kAllDynamicHash; intptr_t result = Smi::Value(raw_ptr()->hash_); if (result != 0) { return result; diff --git a/tests/language/regress/regress42954_test.dart b/tests/language/regress/regress42954_test.dart new file mode 100644 index 00000000000..93c5d2b62ec --- /dev/null +++ b/tests/language/regress/regress42954_test.dart @@ -0,0 +1,20 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +class A { + Type get type => T; +} + +class B extends A {} + +void main() { + var first = B; + var second = B().type; + + Expect.equals(first, second); + Expect.equals(second, first); + Expect.equals(first.hashCode, second.hashCode); +} diff --git a/tests/language_2/regress/regress42954_test.dart b/tests/language_2/regress/regress42954_test.dart new file mode 100644 index 00000000000..93c5d2b62ec --- /dev/null +++ b/tests/language_2/regress/regress42954_test.dart @@ -0,0 +1,20 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +class A { + Type get type => T; +} + +class B extends A {} + +void main() { + var first = B; + var second = B().type; + + Expect.equals(first, second); + Expect.equals(second, first); + Expect.equals(first.hashCode, second.hashCode); +}