diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc index 5f948e262c8..872713f496f 100644 --- a/runtime/lib/object.cc +++ b/runtime/lib/object.cc @@ -81,8 +81,7 @@ DEFINE_NATIVE_ENTRY(Object_runtimeType, 0, 1) { return Type::IntType(); } else if (instance.IsDouble()) { return Type::Double(); - } else if (instance.IsType() || instance.IsFunctionType() || - instance.IsRecordType()) { + } else if (instance.IsAbstractType()) { return Type::DartTypeType(); } else if (IsArrayClassId(instance.GetClassId())) { const auto& cls = Class::Handle( @@ -147,8 +146,23 @@ static bool HaveSameRuntimeTypeHelper(Zone* zone, } if (left_cid == kRecordCid) { - // TODO(dartbug.com/49719) - UNIMPLEMENTED(); + const auto& left_record = Record::Cast(left); + const auto& right_record = Record::Cast(right); + const intptr_t num_fields = left_record.num_fields(); + if ((num_fields != right_record.num_fields()) || + (left_record.field_names() != right_record.field_names())) { + return false; + } + Instance& left_field = Instance::Handle(zone); + Instance& right_field = Instance::Handle(zone); + for (intptr_t i = 0; i < num_fields; ++i) { + left_field ^= left_record.FieldAt(i); + right_field ^= right_record.FieldAt(i); + if (!HaveSameRuntimeTypeHelper(zone, left_field, right_field)) { + return false; + } + } + return true; } const Class& cls = Class::Handle(zone, left.clazz()); @@ -224,6 +238,26 @@ DEFINE_NATIVE_ENTRY(AbstractType_toString, 0, 1) { return type.UserVisibleName(); } +DEFINE_NATIVE_ENTRY(AbstractType_getHashCode, 0, 1) { + const AbstractType& type = + AbstractType::CheckedHandle(zone, arguments->NativeArgAt(0)); + intptr_t hash_val = type.Hash(); + ASSERT(hash_val > 0); + ASSERT(Smi::IsValid(hash_val)); + return Smi::New(hash_val); +} + +DEFINE_NATIVE_ENTRY(AbstractType_equality, 0, 2) { + const AbstractType& type = + AbstractType::CheckedHandle(zone, arguments->NativeArgAt(0)); + const Instance& other = + Instance::CheckedHandle(zone, arguments->NativeArgAt(1)); + if (type.ptr() == other.ptr()) { + return Bool::True().ptr(); + } + return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).ptr(); +} + DEFINE_NATIVE_ENTRY(Type_getHashCode, 0, 1) { const Type& type = Type::CheckedHandle(zone, arguments->NativeArgAt(0)); intptr_t hash_val = type.Hash(); @@ -242,26 +276,6 @@ DEFINE_NATIVE_ENTRY(Type_equality, 0, 2) { return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).ptr(); } -DEFINE_NATIVE_ENTRY(FunctionType_getHashCode, 0, 1) { - const FunctionType& type = - FunctionType::CheckedHandle(zone, arguments->NativeArgAt(0)); - intptr_t hash_val = type.Hash(); - ASSERT(hash_val > 0); - ASSERT(Smi::IsValid(hash_val)); - return Smi::New(hash_val); -} - -DEFINE_NATIVE_ENTRY(FunctionType_equality, 0, 2) { - const FunctionType& type = - FunctionType::CheckedHandle(zone, arguments->NativeArgAt(0)); - const Instance& other = - Instance::CheckedHandle(zone, arguments->NativeArgAt(1)); - if (type.ptr() == other.ptr()) { - return Bool::True().ptr(); - } - return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).ptr(); -} - DEFINE_NATIVE_ENTRY(LibraryPrefix_isLoaded, 0, 1) { const LibraryPrefix& prefix = LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0)); diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index a901058a21a..e3d6a6eb9ca 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -26,11 +26,11 @@ namespace dart { V(Function_apply, 2) \ V(Closure_equals, 2) \ V(Closure_computeHash, 1) \ + V(AbstractType_equality, 2) \ + V(AbstractType_getHashCode, 1) \ V(AbstractType_toString, 1) \ - V(Type_getHashCode, 1) \ V(Type_equality, 2) \ - V(FunctionType_getHashCode, 1) \ - V(FunctionType_equality, 2) \ + V(Type_getHashCode, 1) \ V(LibraryPrefix_isLoaded, 1) \ V(LibraryPrefix_setLoaded, 1) \ V(LibraryPrefix_loadingUnit, 1) \ diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc index 16edcd8197b..78907610a27 100644 --- a/runtime/vm/compiler/asm_intrinsifier_arm.cc +++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc @@ -1324,7 +1324,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler, __ Bind(normal_ir_body); } -void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, +void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler, Label* normal_ir_body) { __ ldr(R0, Address(SP, 0 * target::kWordSize)); __ ldr(R0, FieldAddress(R0, target::FunctionType::hash_offset())); @@ -1333,7 +1333,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, __ Bind(normal_ir_body); // Hash not yet computed. } -void AsmIntrinsifier::FunctionType_equality(Assembler* assembler, +void AsmIntrinsifier::AbstractType_equality(Assembler* assembler, Label* normal_ir_body) { __ ldm(IA, SP, (1 << R1 | 1 << R2)); __ cmp(R1, Operand(R2)); diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc index 1f305c90b89..d9ad060a817 100644 --- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc +++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc @@ -1495,7 +1495,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler, __ Bind(normal_ir_body); } -void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, +void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler, Label* normal_ir_body) { __ ldr(R0, Address(SP, 0 * target::kWordSize)); __ LoadCompressed(R0, FieldAddress(R0, target::FunctionType::hash_offset())); @@ -1505,7 +1505,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, __ Bind(normal_ir_body); } -void AsmIntrinsifier::FunctionType_equality(Assembler* assembler, +void AsmIntrinsifier::AbstractType_equality(Assembler* assembler, Label* normal_ir_body) { __ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset)); __ CompareObjectRegisters(R1, R2); diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc index 2f0181b6b87..fa431d92100 100644 --- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc +++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc @@ -1460,7 +1460,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler, __ Bind(normal_ir_body); } -void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, +void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler, Label* normal_ir_body) { __ movl(EAX, Address(ESP, +1 * target::kWordSize)); // FunctionType object. __ movl(EAX, FieldAddress(EAX, target::FunctionType::hash_offset())); @@ -1471,7 +1471,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, // Hash not yet computed. } -void AsmIntrinsifier::FunctionType_equality(Assembler* assembler, +void AsmIntrinsifier::AbstractType_equality(Assembler* assembler, Label* normal_ir_body) { __ movl(EDI, Address(ESP, +1 * target::kWordSize)); __ movl(EBX, Address(ESP, +2 * target::kWordSize)); diff --git a/runtime/vm/compiler/asm_intrinsifier_riscv.cc b/runtime/vm/compiler/asm_intrinsifier_riscv.cc index 2f4d1527abb..e14eb6df7c2 100644 --- a/runtime/vm/compiler/asm_intrinsifier_riscv.cc +++ b/runtime/vm/compiler/asm_intrinsifier_riscv.cc @@ -1514,7 +1514,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler, __ Bind(normal_ir_body); } -void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, +void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler, Label* normal_ir_body) { __ lx(A0, Address(SP, 0 * target::kWordSize)); __ LoadCompressed(A0, FieldAddress(A0, target::FunctionType::hash_offset())); @@ -1524,7 +1524,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, __ Bind(normal_ir_body); } -void AsmIntrinsifier::FunctionType_equality(Assembler* assembler, +void AsmIntrinsifier::AbstractType_equality(Assembler* assembler, Label* normal_ir_body) { __ lx(A0, Address(SP, 1 * target::kWordSize)); __ lx(A1, Address(SP, 0 * target::kWordSize)); diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc index e93886b18ad..24928d64586 100644 --- a/runtime/vm/compiler/asm_intrinsifier_x64.cc +++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc @@ -1369,7 +1369,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler, __ Bind(normal_ir_body); } -void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, +void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler, Label* normal_ir_body) { __ movq(RAX, Address(RSP, +1 * target::kWordSize)); // FunctionType object. __ LoadCompressed(RAX, @@ -1383,7 +1383,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, // Hash not yet computed. } -void AsmIntrinsifier::FunctionType_equality(Assembler* assembler, +void AsmIntrinsifier::AbstractType_equality(Assembler* assembler, Label* normal_ir_body) { __ movq(RCX, Address(RSP, +1 * target::kWordSize)); __ movq(RDX, Address(RSP, +2 * target::kWordSize)); diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h index 186ce59dcb1..d3241af6b9c 100644 --- a/runtime/vm/compiler/recognized_methods_list.h +++ b/runtime/vm/compiler/recognized_methods_list.h @@ -362,10 +362,10 @@ namespace dart { OneByteString_substringUnchecked, 0x9b18195e) \ V(_OneByteString, ==, OneByteString_equality, 0xb5003d69) \ V(_TwoByteString, ==, TwoByteString_equality, 0xb5003d69) \ + V(_AbstractType, get:hashCode, AbstractType_getHashCode, 0x75e0d454) \ + V(_AbstractType, ==, AbstractType_equality, 0x465868ae) \ V(_Type, get:hashCode, Type_getHashCode, 0x75e0d454) \ V(_Type, ==, Type_equality, 0x465868ae) \ - V(_FunctionType, get:hashCode, FunctionType_getHashCode, 0x75e0d454) \ - V(_FunctionType, ==, FunctionType_equality, 0x465868ae) \ V(::, _getHash, Object_getHash, 0xc60ff758) \ #define CORE_INTEGER_LIB_INTRINSIC_LIST(V) \ diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 9dd27fe02fc..0326b1ccec1 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -21284,6 +21284,23 @@ AbstractTypePtr Type::InstantiateFrom( return instantiated_type.NormalizeFutureOrType(space); } +// Certain built-in classes are treated as syntactically equivalent. +static classid_t NormalizeClassIdForSyntacticalTypeEquality(classid_t cid) { + if (IsIntegerClassId(cid)) { + return Type::Handle(Type::IntType()).type_class_id(); + } else if (IsStringClassId(cid)) { + return Type::Handle(Type::StringType()).type_class_id(); + } else if (cid == kDoubleCid) { + return Type::Handle(Type::Double()).type_class_id(); + } else if (IsTypeClassId(cid)) { + return Type::Handle(Type::DartTypeType()).type_class_id(); + } else if (IsArrayClassId(cid)) { + return Class::Handle(IsolateGroup::Current()->object_store()->list_class()) + .id(); + } + return cid; +} + bool Type::IsEquivalent(const Instance& other, TypeEquality kind, TrailPtr trail) const { @@ -21302,8 +21319,14 @@ bool Type::IsEquivalent(const Instance& other, return false; } const Type& other_type = Type::Cast(other); - if (type_class_id() != other_type.type_class_id()) { - return false; + const classid_t type_cid = type_class_id(); + const classid_t other_type_cid = other_type.type_class_id(); + if (type_cid != other_type_cid) { + if ((kind != TypeEquality::kSyntactical) || + (NormalizeClassIdForSyntacticalTypeEquality(type_cid) != + NormalizeClassIdForSyntacticalTypeEquality(other_type_cid))) { + return false; + } } Thread* thread = Thread::Current(); Zone* zone = thread->zone(); diff --git a/sdk/lib/_internal/vm/lib/type_patch.dart b/sdk/lib/_internal/vm/lib/type_patch.dart index 68247e88d5f..66cffa6f412 100644 --- a/sdk/lib/_internal/vm/lib/type_patch.dart +++ b/sdk/lib/_internal/vm/lib/type_patch.dart @@ -9,6 +9,16 @@ abstract class _AbstractType implements Type { @pragma("vm:external-name", "AbstractType_toString") external String toString(); + + @pragma("vm:recognized", "asm-intrinsic") + @pragma("vm:exact-result-type", "dart:core#_Smi") + @pragma("vm:external-name", "AbstractType_getHashCode") + external int get hashCode; + + @pragma("vm:recognized", "asm-intrinsic") + @pragma("vm:exact-result-type", bool) + @pragma("vm:external-name", "AbstractType_equality") + external bool operator ==(other); } @pragma("vm:entry-point") @@ -33,16 +43,6 @@ class _FunctionType extends _AbstractType { factory _FunctionType._uninstantiable() { throw "Unreachable"; } - - @pragma("vm:recognized", "asm-intrinsic") - @pragma("vm:exact-result-type", "dart:core#_Smi") - @pragma("vm:external-name", "FunctionType_getHashCode") - external int get hashCode; - - @pragma("vm:recognized", "asm-intrinsic") - @pragma("vm:exact-result-type", bool) - @pragma("vm:external-name", "FunctionType_equality") - external bool operator ==(other); } @pragma("vm:entry-point") diff --git a/tests/language/records/simple/runtime_type_test.dart b/tests/language/records/simple/runtime_type_test.dart new file mode 100644 index 00000000000..d9aa7157eac --- /dev/null +++ b/tests/language/records/simple/runtime_type_test.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code as governed by a +// BSD-style license that can be found in the LICENSE file. + +// SharedOptions=--enable-experiment=records + +import "package:expect/expect.dart"; + +@pragma('vm:never-inline') +Object getType1(Object obj) => obj.runtimeType; + +@pragma('vm:never-inline') +Object getType2() => T; + +@pragma('vm:never-inline') +void testRuntimeTypeEquality(bool expected, Object a, Object b) { + bool result1 = getType1(a) == getType1(b); + Expect.equals(expected, result1); + // Test optimized 'a.runtimeType == b.runtimeType' pattern. + bool result2 = a.runtimeType == b.runtimeType; + Expect.equals(expected, result2); +} + +main() { + Expect.equals(getType1(true), bool); + Expect.equals(getType1(false), getType2()); + Expect.equals(getType1(1), getType2()); + Expect.equals(getType1((true, 3)), getType2<(bool, int)>()); + Expect.equals(getType1((foo: true, bar: 2)), getType2<({int bar, bool foo})>()); + Expect.equals(getType1((1, foo: true, false, bar: 2)), getType2<(int, bool, {int bar, bool foo})>()); + + testRuntimeTypeEquality(true, (1, 2), (3, 4)); + testRuntimeTypeEquality(false, (1, 2), (1, 2, 3)); + testRuntimeTypeEquality(false, (1, 2), (1, false)); + testRuntimeTypeEquality(true, (1, 2, foo: true), (foo: false, 5, 4)); + testRuntimeTypeEquality(false, (1, 2, foo: true), (bar: false, 5, 4)); + testRuntimeTypeEquality(true, (foo: 1, bar: 2), (bar: 3, foo: 4)); + testRuntimeTypeEquality(false, (foo: 1, bar: 2), (1, foo: 2)); + testRuntimeTypeEquality(false, (1, 2), 3); + testRuntimeTypeEquality(false, (1, 2), 'hey'); + testRuntimeTypeEquality(false, (1, 2), [1, 2]); +}