mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:59:47 +00:00
Reland "[VM/runtime] Handle generic types in intrinsics for type equality and runtimeType comparison."
This is a reland of 09d2025685
Fixes https://github.com/dart-lang/sdk/issues/23746
Intrinsics for sameRuntimeType wrongly assumed that types with unequal type arguments are unequal, which is not true:
1) nullability of individual type arguments may be different
2) one vector may be a prefix of the other vector
Note that the intrinsic for type equality did not make this assumption.
Case 2 above was not handled properly in the runtime.
TEST=added regression test
Original change's description:
> [VM/runtime] Handle generic types in intrinsics for type equality and runtimeType comparison.
>
> Generic types with equal class ids and equal type arguments are now considered equal by the intrinsics and a runtime call is avoided.
>
> Fixes https://github.com/dart-lang/sdk/issues/23746
>
> TEST=existing ones
>
> Change-Id: I668db119ac6d2525eac3a4f17a44f36c53b9dbf5
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/203143
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
> Commit-Queue: Régis Crelier <regis@google.com>
Change-Id: I53fc00d856ecd9d9b8d66b8da95285e6e0bd508e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/204363
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Régis Crelier <regis@google.com>
This commit is contained in:
parent
902f149e2c
commit
8b7870bc62
|
@ -1272,15 +1272,19 @@ void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler,
|
|||
|
||||
// Compares cid1 and cid2 to see if they're syntactically equivalent. If this
|
||||
// can be determined by this fast path, it jumps to either equal or not_equal,
|
||||
// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
|
||||
// if equal but belonging to a generic class, it falls through with the scratch
|
||||
// register containing host_type_arguments_field_offset_in_words,
|
||||
// otherwise it jumps to normal_ir_body. May clobber scratch.
|
||||
static void EquivalentClassIds(Assembler* assembler,
|
||||
Label* normal_ir_body,
|
||||
Label* equal,
|
||||
Label* not_equal,
|
||||
Register cid1,
|
||||
Register cid2,
|
||||
Register scratch) {
|
||||
Label different_cids, not_integer, not_integer_or_string;
|
||||
Register scratch,
|
||||
bool testing_instance_cids) {
|
||||
Label different_cids, equal_cids_but_generic, not_integer,
|
||||
not_integer_or_string;
|
||||
|
||||
// Check if left hand side is a closure. Closures are handled in the runtime.
|
||||
__ CompareImmediate(cid1, kClosureCid);
|
||||
|
@ -1296,10 +1300,13 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
// Check if there are no type arguments. In this case we can return true.
|
||||
// Otherwise fall through into the runtime to handle comparison.
|
||||
__ LoadClassById(scratch, cid1);
|
||||
__ ldrh(scratch,
|
||||
FieldAddress(scratch, target::Class::num_type_arguments_offset()));
|
||||
__ CompareImmediate(scratch, 0);
|
||||
__ b(normal_ir_body, NE);
|
||||
__ ldr(
|
||||
scratch,
|
||||
FieldAddress(
|
||||
scratch,
|
||||
target::Class::host_type_arguments_field_offset_in_words_offset()));
|
||||
__ CompareImmediate(scratch, target::Class::kNoTypeArguments);
|
||||
__ b(&equal_cids_but_generic, NE);
|
||||
__ b(equal);
|
||||
|
||||
// Class ids are different. Check if we are comparing two string types (with
|
||||
|
@ -1318,35 +1325,50 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
|
||||
__ Bind(¬_integer);
|
||||
// Check if both are String types.
|
||||
JumpIfNotString(assembler, cid1, scratch, ¬_integer_or_string);
|
||||
JumpIfNotString(assembler, cid1, scratch,
|
||||
testing_instance_cids ? ¬_integer_or_string : not_equal);
|
||||
|
||||
// First type is String. Check if the second is a string too.
|
||||
JumpIfString(assembler, cid2, scratch, equal);
|
||||
// String types are only equivalent to other String types.
|
||||
__ b(not_equal);
|
||||
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
JumpIfNotType(assembler, cid1, scratch, not_equal);
|
||||
if (testing_instance_cids) {
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
JumpIfNotType(assembler, cid1, scratch, not_equal);
|
||||
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
JumpIfType(assembler, cid2, scratch, equal);
|
||||
// Type types are only equivalent to other Type types.
|
||||
__ b(not_equal);
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
JumpIfType(assembler, cid2, scratch, equal);
|
||||
// Type types are only equivalent to other Type types.
|
||||
__ b(not_equal);
|
||||
}
|
||||
|
||||
// The caller must compare the type arguments.
|
||||
__ Bind(&equal_cids_but_generic);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ ldr(R0, Address(SP, 0 * target::kWordSize));
|
||||
__ LoadClassIdMayBeSmi(R1, R0);
|
||||
|
||||
__ ldr(R0, Address(SP, 1 * target::kWordSize));
|
||||
__ LoadClassIdMayBeSmi(R2, R0);
|
||||
__ ldm(IA, SP, (1 << R1 | 1 << R2));
|
||||
__ LoadClassIdMayBeSmi(R1, R1);
|
||||
__ LoadClassIdMayBeSmi(R2, R2);
|
||||
|
||||
Label equal, not_equal;
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, R1, R2, R0);
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, R1, R2, R0,
|
||||
/* testing_instance_cids = */ true);
|
||||
|
||||
// Compare type arguments, host_type_arguments_field_offset_in_words in R0.
|
||||
__ ldm(IA, SP, (1 << R1 | 1 << R2));
|
||||
__ AddImmediate(R1, -kHeapObjectTag);
|
||||
__ ldr(R1, Address(R1, R0, LSL, target::kWordSizeLog2));
|
||||
__ AddImmediate(R2, -kHeapObjectTag);
|
||||
__ ldr(R2, Address(R2, R0, LSL, target::kWordSizeLog2));
|
||||
__ cmp(R1, Operand(R2));
|
||||
__ b(normal_ir_body, NE);
|
||||
// Fall through to equal case if type arguments are equal.
|
||||
|
||||
__ Bind(&equal);
|
||||
__ LoadObject(R0, CastHandle<Object>(TrueObject()));
|
||||
|
@ -1396,8 +1418,16 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ SmiUntag(R3);
|
||||
__ ldr(R4, FieldAddress(R2, target::Type::type_class_id_offset()));
|
||||
__ SmiUntag(R4);
|
||||
// We are not testing instance cids, but type class cids of Type instances.
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, ¬_equal, R3, R4,
|
||||
R0);
|
||||
R0, /* testing_instance_cids = */ false);
|
||||
|
||||
// Compare type arguments in Type instances.
|
||||
__ ldr(R3, FieldAddress(R1, target::Type::arguments_offset()));
|
||||
__ ldr(R4, FieldAddress(R2, target::Type::arguments_offset()));
|
||||
__ cmp(R3, Operand(R4));
|
||||
__ b(normal_ir_body, NE);
|
||||
// Fall through to check nullability if type arguments are equal.
|
||||
|
||||
// Check nullability.
|
||||
__ Bind(&equiv_cids);
|
||||
|
|
|
@ -1418,15 +1418,19 @@ void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler,
|
|||
|
||||
// Compares cid1 and cid2 to see if they're syntactically equivalent. If this
|
||||
// can be determined by this fast path, it jumps to either equal or not_equal,
|
||||
// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
|
||||
// if equal but belonging to a generic class, it falls through with the scratch
|
||||
// register containing host_type_arguments_field_offset_in_words,
|
||||
// otherwise it jumps to normal_ir_body. May clobber scratch.
|
||||
static void EquivalentClassIds(Assembler* assembler,
|
||||
Label* normal_ir_body,
|
||||
Label* equal,
|
||||
Label* not_equal,
|
||||
Register cid1,
|
||||
Register cid2,
|
||||
Register scratch) {
|
||||
Label different_cids, not_integer, not_integer_or_string;
|
||||
Register scratch,
|
||||
bool testing_instance_cids) {
|
||||
Label different_cids, equal_cids_but_generic, not_integer,
|
||||
not_integer_or_string;
|
||||
|
||||
// Check if left hand side is a closure. Closures are handled in the runtime.
|
||||
__ CompareImmediate(cid1, kClosureCid);
|
||||
|
@ -1443,10 +1447,12 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
// Otherwise fall through into the runtime to handle comparison.
|
||||
__ LoadClassById(scratch, cid1);
|
||||
__ ldr(scratch,
|
||||
FieldAddress(scratch, target::Class::num_type_arguments_offset(),
|
||||
kTwoBytes),
|
||||
kTwoBytes);
|
||||
__ cbnz(normal_ir_body, scratch);
|
||||
FieldAddress(
|
||||
scratch,
|
||||
target::Class::host_type_arguments_field_offset_in_words_offset()),
|
||||
kFourBytes);
|
||||
__ CompareImmediate(scratch, target::Class::kNoTypeArguments);
|
||||
__ b(&equal_cids_but_generic, NE);
|
||||
__ b(equal);
|
||||
|
||||
// Class ids are different. Check if we are comparing two string types (with
|
||||
|
@ -1465,35 +1471,50 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
|
||||
__ Bind(¬_integer);
|
||||
// Check if both are String types.
|
||||
JumpIfNotString(assembler, cid1, scratch, ¬_integer_or_string);
|
||||
JumpIfNotString(assembler, cid1, scratch,
|
||||
testing_instance_cids ? ¬_integer_or_string : not_equal);
|
||||
|
||||
// First type is String. Check if the second is a string too.
|
||||
JumpIfString(assembler, cid2, scratch, equal);
|
||||
// String types are only equivalent to other String types.
|
||||
__ b(not_equal);
|
||||
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
JumpIfNotType(assembler, cid1, scratch, not_equal);
|
||||
if (testing_instance_cids) {
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
JumpIfNotType(assembler, cid1, scratch, not_equal);
|
||||
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
JumpIfType(assembler, cid2, scratch, equal);
|
||||
// Type types are only equivalent to other Type types.
|
||||
__ b(not_equal);
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
JumpIfType(assembler, cid2, scratch, equal);
|
||||
// Type types are only equivalent to other Type types.
|
||||
__ b(not_equal);
|
||||
}
|
||||
|
||||
// The caller must compare the type arguments.
|
||||
__ Bind(&equal_cids_but_generic);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ ldr(R0, Address(SP, 0 * target::kWordSize));
|
||||
__ ldp(R0, R1, Address(SP, 0 * target::kWordSize, Address::PairOffset));
|
||||
__ LoadClassIdMayBeSmi(R2, R1);
|
||||
__ LoadClassIdMayBeSmi(R1, R0);
|
||||
|
||||
__ ldr(R0, Address(SP, 1 * target::kWordSize));
|
||||
__ LoadClassIdMayBeSmi(R2, R0);
|
||||
|
||||
Label equal, not_equal;
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, R1, R2, R0);
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, R1, R2, R0,
|
||||
/* testing_instance_cids = */ true);
|
||||
|
||||
// Compare type arguments, host_type_arguments_field_offset_in_words in R0.
|
||||
__ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
|
||||
__ AddImmediate(R1, -kHeapObjectTag);
|
||||
__ ldr(R1, Address(R1, R0, UXTX, Address::Scaled));
|
||||
__ AddImmediate(R2, -kHeapObjectTag);
|
||||
__ ldr(R2, Address(R2, R0, UXTX, Address::Scaled));
|
||||
__ CompareObjectRegisters(R1, R2);
|
||||
__ b(normal_ir_body, NE);
|
||||
// Fall through to equal case if type arguments are equal.
|
||||
|
||||
__ Bind(&equal);
|
||||
__ LoadObject(R0, CastHandle<Object>(TrueObject()));
|
||||
|
@ -1549,8 +1570,16 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ LoadCompressedSmi(R4,
|
||||
FieldAddress(R2, target::Type::type_class_id_offset()));
|
||||
__ SmiUntag(R4);
|
||||
// We are not testing instance cids, but type class cids of Type instances.
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, ¬_equal, R3, R4,
|
||||
R0);
|
||||
R0, /* testing_instance_cids = */ false);
|
||||
|
||||
// Compare type arguments in Type instances.
|
||||
__ LoadCompressed(R3, FieldAddress(R1, target::Type::arguments_offset()));
|
||||
__ LoadCompressed(R4, FieldAddress(R2, target::Type::arguments_offset()));
|
||||
__ CompareObjectRegisters(R3, R4);
|
||||
__ b(normal_ir_body, NE);
|
||||
// Fall through to check nullability if type arguments are equal.
|
||||
|
||||
// Check nullability.
|
||||
__ Bind(&equiv_cids);
|
||||
|
|
|
@ -1358,15 +1358,19 @@ void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler,
|
|||
|
||||
// Compares cid1 and cid2 to see if they're syntactically equivalent. If this
|
||||
// can be determined by this fast path, it jumps to either equal or not_equal,
|
||||
// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
|
||||
// if equal but belonging to a generic class, it falls through with the scratch
|
||||
// register containing host_type_arguments_field_offset_in_words,
|
||||
// otherwise it jumps to normal_ir_body. May clobber scratch.
|
||||
static void EquivalentClassIds(Assembler* assembler,
|
||||
Label* normal_ir_body,
|
||||
Label* equal,
|
||||
Label* not_equal,
|
||||
Register cid1,
|
||||
Register cid2,
|
||||
Register scratch) {
|
||||
Label different_cids, not_integer, not_integer_or_string;
|
||||
Register scratch,
|
||||
bool testing_instance_cids) {
|
||||
Label different_cids, equal_cids_but_generic, not_integer,
|
||||
not_integer_or_string;
|
||||
|
||||
// Check if left hand side is a closure. Closures are handled in the runtime.
|
||||
__ cmpl(cid1, Immediate(kClosureCid));
|
||||
|
@ -1382,10 +1386,13 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
// Check if there are no type arguments. In this case we can return true.
|
||||
// Otherwise fall through into the runtime to handle comparison.
|
||||
__ LoadClassById(scratch, cid1);
|
||||
__ movzxw(scratch,
|
||||
FieldAddress(scratch, target::Class::num_type_arguments_offset()));
|
||||
__ cmpl(scratch, Immediate(0));
|
||||
__ j(NOT_EQUAL, normal_ir_body);
|
||||
__ movl(
|
||||
scratch,
|
||||
FieldAddress(
|
||||
scratch,
|
||||
target::Class::host_type_arguments_field_offset_in_words_offset()));
|
||||
__ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
|
||||
__ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
|
||||
__ jmp(equal);
|
||||
|
||||
// Class ids are different. Check if we are comparing two string types (with
|
||||
|
@ -1405,23 +1412,29 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
|
||||
__ Bind(¬_integer);
|
||||
// Check if both are String types.
|
||||
JumpIfNotString(assembler, cid1, ¬_integer_or_string);
|
||||
JumpIfNotString(assembler, cid1,
|
||||
testing_instance_cids ? ¬_integer_or_string : not_equal);
|
||||
|
||||
// First type is a String. Check if the second is a String too.
|
||||
JumpIfString(assembler, cid2, equal);
|
||||
// String types are only equivalent to other String types.
|
||||
__ jmp(not_equal);
|
||||
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
JumpIfNotType(assembler, cid1, not_equal);
|
||||
if (testing_instance_cids) {
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
JumpIfNotType(assembler, cid1, not_equal);
|
||||
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
JumpIfType(assembler, cid2, equal);
|
||||
// Type types are only equivalent to other Type types.
|
||||
__ jmp(not_equal);
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
JumpIfType(assembler, cid2, equal);
|
||||
// Type types are only equivalent to other Type types.
|
||||
__ jmp(not_equal);
|
||||
}
|
||||
|
||||
// The caller must compare the type arguments.
|
||||
__ Bind(&equal_cids_but_generic);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
|
||||
|
@ -1434,7 +1447,16 @@ void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
|
|||
|
||||
Label equal, not_equal;
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, EDI, EBX,
|
||||
EAX);
|
||||
EAX, /* testing_instance_cids = */ true);
|
||||
|
||||
// Compare type arguments, host_type_arguments_field_offset_in_words in EAX.
|
||||
__ movl(EDI, Address(ESP, +1 * target::kWordSize));
|
||||
__ movl(EBX, Address(ESP, +2 * target::kWordSize));
|
||||
__ movl(EDI, FieldAddress(EDI, EAX, TIMES_4, 0));
|
||||
__ movl(EBX, FieldAddress(EBX, EAX, TIMES_4, 0));
|
||||
__ cmpl(EDI, EBX);
|
||||
__ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
|
||||
// Fall through to equal case if type arguments are equal.
|
||||
|
||||
__ Bind(&equal);
|
||||
__ LoadObject(EAX, CastHandle<Object>(TrueObject()));
|
||||
|
@ -1489,8 +1511,16 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ SmiUntag(ECX);
|
||||
__ movl(EDX, FieldAddress(EBX, target::Type::type_class_id_offset()));
|
||||
__ SmiUntag(EDX);
|
||||
// We are not testing instance cids, but type class cids of Type instances.
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, ¬_equal, ECX,
|
||||
EDX, EAX);
|
||||
EDX, EAX, /* testing_instance_cids = */ false);
|
||||
|
||||
// Compare type arguments in Type instances.
|
||||
__ movl(ECX, FieldAddress(EDI, target::Type::arguments_offset()));
|
||||
__ movl(EDX, FieldAddress(EBX, target::Type::arguments_offset()));
|
||||
__ cmpl(ECX, EDX);
|
||||
__ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
|
||||
// Fall through to check nullability if type arguments are equal.
|
||||
|
||||
// Check nullability.
|
||||
__ Bind(&equiv_cids);
|
||||
|
|
|
@ -1262,15 +1262,19 @@ void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler,
|
|||
|
||||
// Compares cid1 and cid2 to see if they're syntactically equivalent. If this
|
||||
// can be determined by this fast path, it jumps to either equal or not_equal,
|
||||
// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
|
||||
// if equal but belonging to a generic class, it falls through with the scratch
|
||||
// register containing host_type_arguments_field_offset_in_words,
|
||||
// otherwise it jumps to normal_ir_body. May clobber scratch.
|
||||
static void EquivalentClassIds(Assembler* assembler,
|
||||
Label* normal_ir_body,
|
||||
Label* equal,
|
||||
Label* not_equal,
|
||||
Register cid1,
|
||||
Register cid2,
|
||||
Register scratch) {
|
||||
Label different_cids, not_integer, not_integer_or_string;
|
||||
Register scratch,
|
||||
bool testing_instance_cids) {
|
||||
Label different_cids, equal_cids_but_generic, not_integer,
|
||||
not_integer_or_string;
|
||||
|
||||
// Check if left hand side is a closure. Closures are handled in the runtime.
|
||||
__ cmpq(cid1, Immediate(kClosureCid));
|
||||
|
@ -1286,10 +1290,13 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
// Check if there are no type arguments. In this case we can return true.
|
||||
// Otherwise fall through into the runtime to handle comparison.
|
||||
__ LoadClassById(scratch, cid1);
|
||||
__ movzxw(scratch,
|
||||
FieldAddress(scratch, target::Class::num_type_arguments_offset()));
|
||||
__ cmpq(scratch, Immediate(0));
|
||||
__ j(NOT_EQUAL, normal_ir_body);
|
||||
__ movl(
|
||||
scratch,
|
||||
FieldAddress(
|
||||
scratch,
|
||||
target::Class::host_type_arguments_field_offset_in_words_offset()));
|
||||
__ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
|
||||
__ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
|
||||
__ jmp(equal);
|
||||
|
||||
// Class ids are different. Check if we are comparing two string types (with
|
||||
|
@ -1309,23 +1316,29 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
|
||||
__ Bind(¬_integer);
|
||||
// Check if both are String types.
|
||||
JumpIfNotString(assembler, cid1, ¬_integer_or_string);
|
||||
JumpIfNotString(assembler, cid1,
|
||||
testing_instance_cids ? ¬_integer_or_string : not_equal);
|
||||
|
||||
// First type is a String. Check if the second is a String too.
|
||||
JumpIfString(assembler, cid2, equal);
|
||||
// String types are only equivalent to other String types.
|
||||
__ jmp(not_equal);
|
||||
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
JumpIfNotType(assembler, cid1, not_equal);
|
||||
if (testing_instance_cids) {
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
JumpIfNotType(assembler, cid1, not_equal);
|
||||
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
JumpIfType(assembler, cid2, equal);
|
||||
// Type types are only equivalent to other Type types.
|
||||
__ jmp(not_equal);
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
JumpIfType(assembler, cid2, equal);
|
||||
// Type types are only equivalent to other Type types.
|
||||
__ jmp(not_equal);
|
||||
}
|
||||
|
||||
// The caller must compare the type arguments.
|
||||
__ Bind(&equal_cids_but_generic);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
|
||||
|
@ -1338,7 +1351,16 @@ void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
|
|||
|
||||
Label equal, not_equal;
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equal, ¬_equal, RCX, RDX,
|
||||
RAX);
|
||||
RAX, /* testing_instance_cids = */ true);
|
||||
|
||||
// Compare type arguments, host_type_arguments_field_offset_in_words in RAX.
|
||||
__ movq(RCX, Address(RSP, +1 * target::kWordSize));
|
||||
__ movq(RDX, Address(RSP, +2 * target::kWordSize));
|
||||
__ movq(RCX, FieldAddress(RCX, RAX, TIMES_8, 0));
|
||||
__ movq(RDX, FieldAddress(RDX, RAX, TIMES_8, 0));
|
||||
__ cmpq(RCX, RDX);
|
||||
__ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
|
||||
// Fall through to equal case if type arguments are equal.
|
||||
|
||||
__ Bind(&equal);
|
||||
__ LoadObject(RAX, CastHandle<Object>(TrueObject()));
|
||||
|
@ -1399,8 +1421,16 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ LoadCompressedSmi(RSI,
|
||||
FieldAddress(RDX, target::Type::type_class_id_offset()));
|
||||
__ SmiUntag(RSI);
|
||||
// We are not testing instance cids, but type class cids of Type instances.
|
||||
EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, ¬_equal, RDI,
|
||||
RSI, RAX);
|
||||
RSI, RAX, /* testing_instance_cids = */ false);
|
||||
|
||||
// Compare type arguments in Type instances.
|
||||
__ LoadCompressed(RDI, FieldAddress(RCX, target::Type::arguments_offset()));
|
||||
__ LoadCompressed(RSI, FieldAddress(RDX, target::Type::arguments_offset()));
|
||||
__ cmpq(RDI, RSI);
|
||||
__ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
|
||||
// Fall through to check nullability if type arguments are equal.
|
||||
|
||||
// Check nullability.
|
||||
__ Bind(&equiv_cids);
|
||||
|
|
|
@ -6183,17 +6183,18 @@ void TypeParameters::Print(Thread* thread,
|
|||
printer->AddString(TypeParameter::CanonicalNameCString(
|
||||
are_class_type_parameters, base, base + i));
|
||||
}
|
||||
if (!AllDynamicBounds()) {
|
||||
if (FLAG_show_internal_names || !AllDynamicBounds()) {
|
||||
type = BoundAt(i);
|
||||
// Do not print default bound or non-nullable Object bound in weak mode.
|
||||
if (!type.IsNull() &&
|
||||
(!type.IsObjectType() ||
|
||||
(FLAG_show_internal_names || !type.IsObjectType() ||
|
||||
(thread->isolate_group()->null_safety() && type.IsNonNullable()))) {
|
||||
printer->AddString(" extends ");
|
||||
type.PrintName(name_visibility, printer);
|
||||
if (FLAG_show_internal_names && !AllDynamicDefaults()) {
|
||||
type = DefaultAt(i);
|
||||
if (!type.IsNull() && !type.IsDynamicType()) {
|
||||
if (!type.IsNull() &&
|
||||
(FLAG_show_internal_names || !type.IsDynamicType())) {
|
||||
printer->AddString(" defaults to ");
|
||||
type.PrintName(name_visibility, printer);
|
||||
}
|
||||
|
@ -6427,18 +6428,19 @@ bool TypeArguments::IsSubvectorEquivalent(const TypeArguments& other,
|
|||
if (this->ptr() == other.ptr()) {
|
||||
return true;
|
||||
}
|
||||
if (IsNull() || other.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
const intptr_t num_types = Length();
|
||||
if (num_types != other.Length()) {
|
||||
return false;
|
||||
if (kind == TypeEquality::kCanonical) {
|
||||
if (IsNull() || other.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
if (Length() != other.Length()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
AbstractType& type = AbstractType::Handle();
|
||||
AbstractType& other_type = AbstractType::Handle();
|
||||
for (intptr_t i = from_index; i < from_index + len; i++) {
|
||||
type = TypeAt(i);
|
||||
other_type = other.TypeAt(i);
|
||||
type = IsNull() ? Type::DynamicType() : TypeAt(i);
|
||||
other_type = other.IsNull() ? Type::DynamicType() : other.TypeAt(i);
|
||||
// Still unfinalized vectors should not be considered equivalent.
|
||||
if (type.IsNull() || !type.IsEquivalent(other_type, kind, trail)) {
|
||||
return false;
|
||||
|
|
|
@ -7735,8 +7735,9 @@ class TypeArguments : public Instance {
|
|||
bool IsEquivalent(const TypeArguments& other,
|
||||
TypeEquality kind,
|
||||
TrailPtr trail = nullptr) const {
|
||||
return IsSubvectorEquivalent(other, 0, IsNull() ? 0 : Length(), kind,
|
||||
trail);
|
||||
// Make a null vector a vector of dynamic as long as the other vector.
|
||||
return IsSubvectorEquivalent(other, 0, IsNull() ? other.Length() : Length(),
|
||||
kind, trail);
|
||||
}
|
||||
bool IsSubvectorEquivalent(const TypeArguments& other,
|
||||
intptr_t from_index,
|
||||
|
|
22
tests/language/regress/regress23746_test.dart
Normal file
22
tests/language/regress/regress23746_test.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2021, 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<T, U> {
|
||||
B<T> get b => B<T>();
|
||||
}
|
||||
|
||||
class B<T> {}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
bool test(Object a, Object b) {
|
||||
print(a.runtimeType);
|
||||
print(b.runtimeType);
|
||||
return a.runtimeType == b.runtimeType;
|
||||
}
|
||||
|
||||
void main() {
|
||||
Expect.isTrue(test(B<int>(), A<int, String>().b));
|
||||
}
|
22
tests/language_2/regress/regress23746_test.dart
Normal file
22
tests/language_2/regress/regress23746_test.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2021, 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<T, U> {
|
||||
B<T> get b => B<T>();
|
||||
}
|
||||
|
||||
class B<T> {}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
bool test(Object a, Object b) {
|
||||
print(a.runtimeType);
|
||||
print(b.runtimeType);
|
||||
return a.runtimeType == b.runtimeType;
|
||||
}
|
||||
|
||||
void main() {
|
||||
Expect.isTrue(test(B<int>(), A<int, String>().b));
|
||||
}
|
Loading…
Reference in a new issue