Reland "[vm/compiler] Add more checks in TTSes for uninstantiated types."

This is a reland of 1016bbadde

The broken benchmarks causing the revert were not due to this CL.

TEST=vm/cc/TTS

Original change's description:
> [vm/compiler] Add more checks in TTSes for uninstantiated types.
>
> In certain cases, optimized type testing stubs for uninstantiated types
> compare the instantiation of the type arguments for the type against the
> corresponding instance type arguments. Previously, only an identity
> check was generated, so the instantiation had to match the instance type
> argument exactly.
>
> This CL adds checks for the following cases:
> * The instantiated type argument is dynamic, void, or Object.
> * The instance type argument is Null or Never.
>
> When strong null safety is enabled, we also check that the the instance
> type argument is legacy or non-nullable when the instantiated type
> argument is non-nullable Object or that the instantiated type argument
> is nullable or legacy when the instance type argument is Null.
>
> This CL also adds handling for the case where the instantiated or
> instance type argument is not a Type, which is necessary for safely
> retrieving the type class id and nullability, and allocates some
> additional registers in TTSInternalRegs for storing type arguments. On
> ARM7, this requires pushes/pops around type argument checking, because
> we've run out of registers there.
>
> Fixes https://github.com/dart-lang/sdk/issues/40736
>
> TEST=vm/cc/TTS
>
> Bug: https://github.com/dart-lang/sdk/issues/46920
> Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try
> Change-Id: Ib8498fd2b9593b4abb92111f062ed2fc95a45c16
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210681
> Commit-Queue: Tess Strickland <sstrickl@google.com>
> Reviewed-by: Alexander Markov <alexmarkov@google.com>

Bug: https://github.com/dart-lang/sdk/issues/46920
Change-Id: Id84730b3bb292ea7fdfe9ebd8b1fc6e71c64cf22
Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211901
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Tess Strickland 2021-09-27 10:57:18 +00:00 committed by commit-bot@chromium.org
parent 11f497f6ab
commit bb1640eec2
14 changed files with 1024 additions and 236 deletions

View file

@ -2303,6 +2303,16 @@ void Assembler::Bind(Label* label) {
BindARMv7(label);
}
void Assembler::LoadCompressedSmi(Register dest, const Address& slot) {
ldr(dest, slot);
#if defined(DEBUG)
Label done;
BranchIfSmi(dest, &done);
Stop("Expected Smi");
Bind(&done);
#endif
}
OperandSize Address::OperandSizeFor(intptr_t cid) {
switch (cid) {
case kArrayCid:

View file

@ -395,6 +395,7 @@ class Assembler : public AssemblerBase {
LoadFromOffset(dst, base, offset);
}
void LoadCompressed(Register dest, const Address& slot) { ldr(dest, slot); }
void LoadCompressedSmi(Register dest, const Address& slot);
void StoreMemoryValue(Register src, Register base, int32_t offset) {
StoreToOffset(src, base, offset);
}
@ -429,7 +430,15 @@ class Assembler : public AssemblerBase {
cmp(value, Operand(TMP));
}
void CompareTypeNullabilityWith(Register type, int8_t value) {
void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) override {
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
ldrb(TMP, FieldAddress(
type, compiler::target::FunctionType::nullability_offset()));
cmp(TMP, Operand(value));
}
void CompareTypeNullabilityWith(Register type, int8_t value) override {
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
ldrb(TMP, FieldAddress(type, compiler::target::Type::nullability_offset()));
cmp(TMP, Operand(value));
}

View file

@ -646,7 +646,17 @@ class Assembler : public AssemblerBase {
cmp(value, Operand(TMP), sz);
}
void CompareTypeNullabilityWith(Register type, int8_t value) {
void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) override {
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
ldr(TMP,
FieldAddress(type, compiler::target::FunctionType::nullability_offset(),
kByte),
kUnsignedByte);
cmp(TMP, Operand(value));
}
void CompareTypeNullabilityWith(Register type, int8_t value) override {
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
ldr(TMP,
FieldAddress(type, compiler::target::Type::nullability_offset(), kByte),
kUnsignedByte);

View file

@ -718,6 +718,20 @@ class AssemblerBase : public StackResource {
Register address,
int32_t offset = 0) = 0;
// Retrieves nullability from a FunctionTypePtr in [type] and compares it
// to [value].
//
// TODO(dartbug.com/47034): Change how nullability is stored so that it
// can be accessed without checking the class id first.
virtual void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) = 0;
// Retrieves nullability from a TypePtr in [type] and compares it to [value].
//
// TODO(dartbug.com/47034): Change how nullability is stored so that it
// can be accessed without checking the class id first.
virtual void CompareTypeNullabilityWith(Register type, int8_t value) = 0;
virtual void EnsureHasClassIdInDEBUG(intptr_t cid,
Register src,
Register scratch,

View file

@ -782,7 +782,13 @@ class Assembler : public AssemblerBase {
cmpxchgl(address, reg);
}
void CompareTypeNullabilityWith(Register type, int8_t value) {
void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) override {
cmpb(FieldAddress(type,
compiler::target::FunctionType::nullability_offset()),
Immediate(value));
}
void CompareTypeNullabilityWith(Register type, int8_t value) override {
cmpb(FieldAddress(type, compiler::target::Type::nullability_offset()),
Immediate(value));
}

View file

@ -1102,7 +1102,15 @@ class Assembler : public AssemblerBase {
OBJ(cmp)(value, FieldAddress(base, offset));
}
void CompareTypeNullabilityWith(Register type, int8_t value) {
void CompareFunctionTypeNullabilityWith(Register type,
int8_t value) override {
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
cmpb(FieldAddress(type,
compiler::target::FunctionType::nullability_offset()),
Immediate(value));
}
void CompareTypeNullabilityWith(Register type, int8_t value) override {
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
cmpb(FieldAddress(type, compiler::target::Type::nullability_offset()),
Immediate(value));
}

View file

@ -425,14 +425,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 12;
static constexpr dart::compiler::target::word Type_type_state_offset = 24;
static constexpr dart::compiler::target::word Type_nullability_offset = 25;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 28;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
39;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 32;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 36;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 20;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 12;
static constexpr dart::compiler::target::word
@ -981,14 +983,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
static constexpr dart::compiler::target::word Type_type_state_offset = 48;
static constexpr dart::compiler::target::word Type_nullability_offset = 49;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 56;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
71;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 64;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 68;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 40;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -1530,14 +1534,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 12;
static constexpr dart::compiler::target::word Type_type_state_offset = 24;
static constexpr dart::compiler::target::word Type_nullability_offset = 25;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 28;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
39;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 32;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 36;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 20;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 12;
static constexpr dart::compiler::target::word
@ -2083,14 +2089,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
static constexpr dart::compiler::target::word Type_type_state_offset = 48;
static constexpr dart::compiler::target::word Type_nullability_offset = 49;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 56;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
71;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 64;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 68;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 40;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -2639,14 +2647,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
static constexpr dart::compiler::target::word Type_type_state_offset = 36;
static constexpr dart::compiler::target::word Type_nullability_offset = 37;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 40;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
51;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 44;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 48;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 32;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -3194,14 +3204,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
static constexpr dart::compiler::target::word Type_type_state_offset = 36;
static constexpr dart::compiler::target::word Type_nullability_offset = 37;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 40;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
51;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 44;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 48;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 32;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -3740,14 +3752,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 12;
static constexpr dart::compiler::target::word Type_type_state_offset = 24;
static constexpr dart::compiler::target::word Type_nullability_offset = 25;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 28;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
39;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 32;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 36;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 20;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 12;
static constexpr dart::compiler::target::word
@ -4290,14 +4304,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
static constexpr dart::compiler::target::word Type_type_state_offset = 48;
static constexpr dart::compiler::target::word Type_nullability_offset = 49;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 56;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
71;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 64;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 68;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 40;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -4833,14 +4849,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 12;
static constexpr dart::compiler::target::word Type_type_state_offset = 24;
static constexpr dart::compiler::target::word Type_nullability_offset = 25;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 28;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
39;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 32;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 36;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 20;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 12;
static constexpr dart::compiler::target::word
@ -5380,14 +5398,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
static constexpr dart::compiler::target::word Type_type_state_offset = 48;
static constexpr dart::compiler::target::word Type_nullability_offset = 49;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 56;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
71;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 64;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 68;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 40;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -5930,14 +5950,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
static constexpr dart::compiler::target::word Type_type_state_offset = 36;
static constexpr dart::compiler::target::word Type_nullability_offset = 37;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 40;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
51;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 44;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 48;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 32;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -6479,14 +6501,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
static constexpr dart::compiler::target::word Type_type_state_offset = 36;
static constexpr dart::compiler::target::word Type_nullability_offset = 37;
static constexpr dart::compiler::target::word FunctionType_hash_offset = 40;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
51;
static constexpr dart::compiler::target::word
FunctionType_packed_parameter_counts_offset = 44;
static constexpr dart::compiler::target::word
FunctionType_packed_type_parameter_counts_offset = 48;
static constexpr dart::compiler::target::word
FunctionType_parameter_types_offset = 32;
static constexpr dart::compiler::target::word
FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -7075,14 +7099,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 24;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 25;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 28;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 39;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 32;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 20;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 12;
static constexpr dart::compiler::target::word
@ -7691,14 +7717,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 48;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 49;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 71;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 64;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 68;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 40;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -8312,14 +8340,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 48;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 49;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 71;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 64;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 68;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 40;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -8930,14 +8960,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 36;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 37;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 51;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 44;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 32;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -9547,14 +9579,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 36;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 37;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 51;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 44;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 32;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -10159,14 +10193,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 24;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 25;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 28;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 39;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 32;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 20;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 24;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 12;
static constexpr dart::compiler::target::word
@ -10768,14 +10804,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 48;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 49;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 71;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 64;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 68;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 40;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -11382,14 +11420,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 48;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 49;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 71;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 64;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 68;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 40;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -11993,14 +12033,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 36;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 37;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 51;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 44;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 32;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word
@ -12603,14 +12645,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 36;
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 37;
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_nullability_offset = 51;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_parameter_counts_offset = 44;
static constexpr dart::compiler::target::word
AOT_FunctionType_packed_type_parameter_counts_offset = 48;
static constexpr dart::compiler::target::word
AOT_FunctionType_parameter_types_offset = 32;
static constexpr dart::compiler::target::word
AOT_FunctionType_named_parameter_names_offset = 36;
static constexpr dart::compiler::target::word
AOT_FunctionType_type_parameters_offset = 24;
static constexpr dart::compiler::target::word

View file

@ -294,10 +294,11 @@
FIELD(Type, type_state_offset) \
FIELD(Type, nullability_offset) \
FIELD(FunctionType, hash_offset) \
FIELD(FunctionType, named_parameter_names_offset) \
FIELD(FunctionType, nullability_offset) \
FIELD(FunctionType, packed_parameter_counts_offset) \
FIELD(FunctionType, packed_type_parameter_counts_offset) \
FIELD(FunctionType, parameter_types_offset) \
FIELD(FunctionType, named_parameter_names_offset) \
FIELD(FunctionType, type_parameters_offset) \
FIELD(TypeParameter, parameterized_class_id_offset) \
FIELD(TypeParameter, index_offset) \

View file

@ -339,9 +339,18 @@ struct InstantiationABI {
struct TTSInternalRegs {
static const Register kInstanceTypeArgumentsReg = R4;
static const Register kScratchReg = R9;
static const Register kSubTypeArgumentReg = R3;
static const Register kSuperTypeArgumentReg = R8;
// Must be pushed/popped whenever generic type arguments are being checked as
// they overlap with registers in TypeTestABI.
static const intptr_t kSavedTypeArgumentRegisters =
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg);
static const intptr_t kInternalRegisters =
(1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg);
((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) |
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) &
~kSavedTypeArgumentRegisters;
};
// Registers in addition to those listed in TypeTestABI used inside the

View file

@ -172,9 +172,17 @@ struct InstantiationABI {
struct TTSInternalRegs {
static const Register kInstanceTypeArgumentsReg = R7;
static const Register kScratchReg = R9;
static const Register kSubTypeArgumentReg = R5;
static const Register kSuperTypeArgumentReg = R6;
// Must be pushed/popped whenever generic type arguments are being checked as
// they overlap with registers in TypeTestABI.
static const intptr_t kSavedTypeArgumentRegisters = 0;
static const intptr_t kInternalRegisters =
(1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg);
((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) |
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) &
~kSavedTypeArgumentRegisters;
};
// Registers in addition to those listed in TypeTestABI used inside the

View file

@ -148,9 +148,17 @@ struct InstantiationABI {
struct TTSInternalRegs {
static const Register kInstanceTypeArgumentsReg = RSI;
static const Register kScratchReg = R8;
static const Register kSubTypeArgumentReg = R10;
static const Register kSuperTypeArgumentReg = R13;
// Must be pushed/popped whenever generic type arguments are being checked as
// they overlap with registers in TypeTestABI.
static const intptr_t kSavedTypeArgumentRegisters = 0;
static const intptr_t kInternalRegisters =
(1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg);
((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) |
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) &
~kSavedTypeArgumentRegisters;
};
// Registers in addition to those listed in TypeTestABI used inside the

View file

@ -4,7 +4,10 @@
#include <functional>
#include "platform/globals.h"
#include "vm/class_id.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/compiler/runtime_api.h"
#include "vm/hash_map.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
@ -329,7 +332,7 @@ void TypeTestingStubGenerator::BuildOptimizedTypeTestStubFastCases(
if (type.IsObjectType()) {
ASSERT(type.IsNonNullable() &&
IsolateGroup::Current()->use_strict_null_safety_checks());
hi->thread()->isolate_group()->use_strict_null_safety_checks());
compiler::Label is_null;
__ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
__ BranchIf(EQUAL, &is_null, compiler::Assembler::kNearJump);
@ -621,6 +624,11 @@ void TypeTestingStubGenerator::
}
// c) Then we'll check each value of the type argument.
compiler::Label pop_saved_registers_on_failure;
const RegisterSet saved_registers(
TTSInternalRegs::kSavedTypeArgumentRegisters);
__ PushRegisters(saved_registers);
AbstractType& type_arg = AbstractType::Handle();
const TypeArguments& ta = TypeArguments::Handle(type.arguments());
const intptr_t num_type_parameters = type_class.NumTypeParameters();
@ -634,10 +642,20 @@ void TypeTestingStubGenerator::
ASSERT(type_arg.IsTypeParameter() ||
hi->CanUseSubtypeRangeCheckFor(type_arg));
BuildOptimizedTypeArgumentValueCheck(
assembler, hi, type_arg, type_param_value_offset_i, &check_failed);
if (type_arg.IsTypeParameter()) {
BuildOptimizedTypeParameterArgumentValueCheck(
assembler, hi, TypeParameter::Cast(type_arg),
type_param_value_offset_i, &pop_saved_registers_on_failure);
} else {
BuildOptimizedTypeArgumentValueCheck(
assembler, hi, Type::Cast(type_arg), type_param_value_offset_i,
&pop_saved_registers_on_failure);
}
}
__ PopRegisters(saved_registers);
__ Ret();
__ Bind(&pop_saved_registers_on_failure);
__ PopRegisters(saved_registers);
}
// If anything fails.
@ -928,110 +946,259 @@ bool TypeTestingStubGenerator::BuildLoadInstanceTypeArguments(
return !type_argument_checks.is_empty();
}
// Unwraps TypeRefs, jumping to the appropriate label for the unwrapped type
// if that label is not nullptr and otherwise falling through.
//
// [type_reg] must contain an AbstractTypePtr, and [scratch] must be distinct
// from [type_reg]. Clobbers [type_reg] with the unwrapped type.
static void UnwrapAbstractType(compiler::Assembler* assembler,
Register type_reg,
Register scratch,
compiler::Label* is_type = nullptr,
compiler::Label* is_function_type = nullptr,
compiler::Label* is_type_parameter = nullptr) {
ASSERT(scratch != type_reg);
compiler::Label cid_checks, fall_through;
// TypeRefs never wrap other TypeRefs, so we only need to unwrap once.
__ LoadClassId(scratch, type_reg);
__ CompareImmediate(scratch, kTypeRefCid);
__ BranchIf(NOT_EQUAL, &cid_checks, compiler::Assembler::kNearJump);
__ LoadCompressedFieldFromOffset(type_reg, type_reg,
compiler::target::TypeRef::type_offset());
// Only load the class id of the unwrapped type if it will be checked below.
if (is_type != nullptr || is_function_type != nullptr ||
is_type_parameter != nullptr) {
__ LoadClassId(scratch, type_reg);
}
__ Bind(&cid_checks);
if (is_type != nullptr) {
__ CompareImmediate(scratch, kTypeCid);
__ BranchIf(EQUAL, is_type);
}
if (is_function_type != nullptr) {
__ CompareImmediate(scratch, kFunctionTypeCid);
__ BranchIf(EQUAL, is_function_type);
}
if (is_type_parameter != nullptr) {
__ CompareImmediate(scratch, kTypeParameterCid);
__ BranchIf(EQUAL, is_type_parameter);
}
}
// src must contain a TypePtr. Assumes dst != src. May affect flags.
static void LoadTypeClassId(compiler::Assembler* assembler,
Register dst,
Register src) {
ASSERT(src != dst);
__ EnsureHasClassIdInDEBUG(kTypeCid, src, dst);
__ LoadCompressedSmi(
dst, compiler::FieldAddress(
src, compiler::target::Type::type_class_id_offset()));
__ SmiUntag(dst);
}
void TypeTestingStubGenerator::BuildOptimizedTypeParameterArgumentValueCheck(
compiler::Assembler* assembler,
HierarchyInfo* hi,
const TypeParameter& type_param,
intptr_t type_param_value_offset_i,
compiler::Label* check_failed) {
if (assembler->EmittingComments()) {
TextBuffer buffer(128);
buffer.Printf("Generating check for type argument %" Pd ": ",
type_param_value_offset_i);
type_param.PrintName(Object::kScrubbedName, &buffer);
__ Comment("%s", buffer.buffer());
}
const Register kTypeArgumentsReg =
type_param.IsClassTypeParameter()
? TypeTestABI::kInstantiatorTypeArgumentsReg
: TypeTestABI::kFunctionTypeArgumentsReg;
const bool strict_null_safety =
hi->thread()->isolate_group()->use_strict_null_safety_checks();
compiler::Label is_subtype;
// TODO(dartbug.com/46920): Currently only canonical equality (identity)
// and some top and bottom types are checked.
__ CompareObject(kTypeArgumentsReg, Object::null_object());
__ BranchIf(EQUAL, &is_subtype);
__ LoadCompressedFieldFromOffset(
TTSInternalRegs::kSuperTypeArgumentReg, kTypeArgumentsReg,
compiler::target::TypeArguments::type_at_offset(type_param.index()));
__ LoadCompressedFieldFromOffset(
TTSInternalRegs::kSubTypeArgumentReg,
TTSInternalRegs::kInstanceTypeArgumentsReg,
compiler::target::TypeArguments::type_at_offset(
type_param_value_offset_i));
__ CompareRegisters(TTSInternalRegs::kSuperTypeArgumentReg,
TTSInternalRegs::kSubTypeArgumentReg);
__ BranchIf(EQUAL, &is_subtype);
__ Comment("Checking instantiated type parameter for possible top types");
compiler::Label check_subtype_type_class_ids;
UnwrapAbstractType(assembler, TTSInternalRegs::kSuperTypeArgumentReg,
TTSInternalRegs::kScratchReg, /*is_type=*/nullptr,
&check_subtype_type_class_ids);
LoadTypeClassId(assembler, TTSInternalRegs::kScratchReg,
TTSInternalRegs::kSuperTypeArgumentReg);
__ CompareImmediate(TTSInternalRegs::kScratchReg, kDynamicCid);
__ BranchIf(EQUAL, &is_subtype);
__ CompareImmediate(TTSInternalRegs::kScratchReg, kVoidCid);
__ BranchIf(EQUAL, &is_subtype);
__ CompareImmediate(TTSInternalRegs::kScratchReg, kInstanceCid);
if (strict_null_safety) {
__ BranchIf(NOT_EQUAL, &check_subtype_type_class_ids);
// If non-nullable Object, then the subtype must be legacy or non-nullable.
__ CompareTypeNullabilityWith(
TTSInternalRegs::kSuperTypeArgumentReg,
static_cast<int8_t>(Nullability::kNonNullable));
__ BranchIf(NOT_EQUAL, &is_subtype);
__ Comment("Checking for legacy or non-nullable instance type argument");
compiler::Label subtype_is_type;
UnwrapAbstractType(assembler, TTSInternalRegs::kSubTypeArgumentReg,
TTSInternalRegs::kScratchReg, &subtype_is_type);
__ CompareFunctionTypeNullabilityWith(
TTSInternalRegs::kSubTypeArgumentReg,
static_cast<int8_t>(Nullability::kNullable));
__ BranchIf(EQUAL, check_failed);
__ Jump(&is_subtype);
__ Bind(&subtype_is_type);
__ CompareTypeNullabilityWith(TTSInternalRegs::kSubTypeArgumentReg,
static_cast<int8_t>(Nullability::kNullable));
__ BranchIf(EQUAL, check_failed);
__ Jump(&is_subtype);
} else {
__ BranchIf(EQUAL, &is_subtype, compiler::Assembler::kNearJump);
}
__ Bind(&check_subtype_type_class_ids);
__ Comment("Checking instance type argument for possible bottom types");
// Nothing else to check for non-Types, so fall back to the slow stub.
UnwrapAbstractType(assembler, TTSInternalRegs::kSubTypeArgumentReg,
TTSInternalRegs::kScratchReg, /*is_type=*/nullptr,
check_failed);
LoadTypeClassId(assembler, TTSInternalRegs::kScratchReg,
TTSInternalRegs::kSubTypeArgumentReg);
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNeverCid);
__ BranchIf(EQUAL, &is_subtype);
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNullCid);
// Last possible check, so fall back to slow stub on failure.
__ BranchIf(NOT_EQUAL, check_failed);
if (strict_null_safety) {
// Only nullable or legacy types can be a supertype of Null.
__ Comment("Checking for legacy or nullable instantiated type parameter");
compiler::Label supertype_is_type;
UnwrapAbstractType(assembler, TTSInternalRegs::kSuperTypeArgumentReg,
TTSInternalRegs::kScratchReg, &supertype_is_type);
__ CompareFunctionTypeNullabilityWith(
TTSInternalRegs::kSuperTypeArgumentReg,
static_cast<int8_t>(Nullability::kNonNullable));
__ BranchIf(EQUAL, check_failed);
__ Jump(&is_subtype, compiler::Assembler::kNearJump);
__ Bind(&supertype_is_type);
__ CompareTypeNullabilityWith(
TTSInternalRegs::kSuperTypeArgumentReg,
static_cast<int8_t>(Nullability::kNonNullable));
__ BranchIf(EQUAL, check_failed);
}
__ Bind(&is_subtype);
}
// Generate code to verify that instance's type argument is a subtype of
// 'type_arg'.
void TypeTestingStubGenerator::BuildOptimizedTypeArgumentValueCheck(
compiler::Assembler* assembler,
HierarchyInfo* hi,
const AbstractType& type_arg,
const Type& type,
intptr_t type_param_value_offset_i,
compiler::Label* check_failed) {
if (type_arg.IsTopTypeForSubtyping()) {
ASSERT(type.IsInstantiated());
if (type.IsTopTypeForSubtyping()) {
return;
}
const bool strict_null_safety =
hi->thread()->isolate_group()->use_strict_null_safety_checks();
ASSERT(!type.IsObjectType() || (strict_null_safety && type.IsNonNullable()));
if (assembler->EmittingComments()) {
TextBuffer buffer(128);
buffer.Printf("Generating check for type argument %" Pd ": ",
type_param_value_offset_i);
type_arg.PrintName(Object::kScrubbedName, &buffer);
type.PrintName(Object::kScrubbedName, &buffer);
__ Comment("%s", buffer.buffer());
}
if (type_arg.IsTypeParameter()) {
const TypeParameter& type_param = TypeParameter::Cast(type_arg);
const Register kTypeArgumentsReg =
type_param.IsClassTypeParameter()
? TypeTestABI::kInstantiatorTypeArgumentsReg
: TypeTestABI::kFunctionTypeArgumentsReg;
compiler::Label is_dynamic;
__ CompareObject(kTypeArgumentsReg, Object::null_object());
__ BranchIf(EQUAL, &is_dynamic, compiler::Assembler::kNearJump);
// TODO(dartbug.com/46920): Currently only canonical equality (identity)
// is checked.
__ LoadCompressedFieldFromOffset(
TTSInternalRegs::kScratchReg, kTypeArgumentsReg,
compiler::target::TypeArguments::type_at_offset(type_param.index()));
__ CompareWithCompressedFieldFromOffset(
TTSInternalRegs::kScratchReg,
TTSInternalRegs::kInstanceTypeArgumentsReg,
compiler::target::TypeArguments::type_at_offset(
type_param_value_offset_i));
__ BranchIf(NOT_EQUAL, check_failed);
__ Bind(&is_dynamic);
} else {
__ LoadCompressedFieldFromOffset(
TTSInternalRegs::kScratchReg,
TTSInternalRegs::kInstanceTypeArgumentsReg,
compiler::target::TypeArguments::type_at_offset(
type_param_value_offset_i));
if (type_arg.IsObjectType()) {
// Just check the nullability, since this must be non-nullable Object
// and we must be in null safe mode.
ASSERT(IsolateGroup::Current()->use_strict_null_safety_checks() &&
type_arg.IsNonNullable());
__ CompareTypeNullabilityWith(TTSInternalRegs::kScratchReg,
compiler::target::Nullability::kNullable);
compiler::Label is_subtype, check_subtype_cid, sub_is_function_type,
sub_is_type;
__ LoadCompressedFieldFromOffset(
TTSInternalRegs::kSubTypeArgumentReg,
TTSInternalRegs::kInstanceTypeArgumentsReg,
compiler::target::TypeArguments::type_at_offset(
type_param_value_offset_i));
__ Bind(&check_subtype_cid);
UnwrapAbstractType(assembler, TTSInternalRegs::kSubTypeArgumentReg,
TTSInternalRegs::kScratchReg, &sub_is_type);
__ Comment("Checks for FunctionType");
__ EnsureHasClassIdInDEBUG(kFunctionTypeCid,
TTSInternalRegs::kSubTypeArgumentReg,
TTSInternalRegs::kScratchReg);
if (type.IsObjectType() || type.IsDartFunctionType()) {
if (strict_null_safety && type.IsNonNullable()) {
// Nullable types cannot be a subtype of a non-nullable type.
__ CompareFunctionTypeNullabilityWith(
TTSInternalRegs::kSubTypeArgumentReg,
compiler::target::Nullability::kNullable);
__ BranchIf(EQUAL, check_failed);
} else {
__ LoadCompressedFieldFromOffset(
TTSInternalRegs::kScratchReg, TTSInternalRegs::kScratchReg,
compiler::target::Type::type_class_id_offset());
const Class& type_class = Class::Handle(type_arg.type_class());
const bool null_is_assignable = Instance::NullIsAssignableTo(type_arg);
const CidRangeVector& ranges =
hi->SubtypeRangesForClass(type_class,
/*include_abstract=*/true,
/*exclude_null=*/!null_is_assignable);
compiler::Label is_subtype;
__ SmiUntag(TTSInternalRegs::kScratchReg);
// Never is a bottom type.
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNeverCid);
__ BranchIf(EQUAL, &is_subtype);
if (null_is_assignable) {
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNullCid);
__ BranchIf(EQUAL, &is_subtype);
}
compiler::Label check_nullability;
BuildOptimizedSubtypeRangeCheck(assembler, ranges,
TTSInternalRegs::kScratchReg,
&check_nullability, check_failed);
__ Bind(&check_nullability);
// Weak NNBD mode uses LEGACY_SUBTYPE which ignores nullability.
// We don't need to check nullability of LHS for nullable and legacy RHS
// ("Right Legacy", "Right Nullable" rules).
if (IsolateGroup::Current()->use_strict_null_safety_checks() &&
type_arg.IsNonNullable()) {
// Nullable type is not a subtype of non-nullable type.
// TODO(dartbug.com/40736): Allocate a register for instance type
// argument and avoid reloading it.
__ LoadCompressedFieldFromOffset(
TTSInternalRegs::kScratchReg,
TTSInternalRegs::kInstanceTypeArgumentsReg,
compiler::target::TypeArguments::type_at_offset(
type_param_value_offset_i));
__ CompareTypeNullabilityWith(TTSInternalRegs::kScratchReg,
compiler::target::Nullability::kNullable);
__ BranchIf(EQUAL, check_failed);
}
__ Bind(&is_subtype);
}
// No further checks needed for non-nullable Object or Function.
__ Jump(&is_subtype, compiler::Assembler::kNearJump);
} else {
// _Closure <: Function, and T <: Function for any FunctionType T, but
// T </: _Closure, so we _don't_ want to fall back to cid tests. Instead,
// just let the STC/runtime handle any possible false negatives here.
__ Jump(check_failed);
}
__ Comment("Checks for Type");
__ Bind(&sub_is_type);
if (strict_null_safety && type.IsNonNullable()) {
// Nullable types cannot be a subtype of a non-nullable type in strict mode.
__ CompareTypeNullabilityWith(TTSInternalRegs::kSubTypeArgumentReg,
compiler::target::Nullability::kNullable);
__ BranchIf(EQUAL, check_failed);
// Fall through to bottom type checks.
}
// No further checks needed for non-nullable object.
if (!type.IsObjectType()) {
LoadTypeClassId(assembler, TTSInternalRegs::kScratchReg,
TTSInternalRegs::kSubTypeArgumentReg);
const bool null_is_assignable = Instance::NullIsAssignableTo(type);
// Check bottom types.
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNeverCid);
__ BranchIf(EQUAL, &is_subtype);
if (null_is_assignable) {
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNullCid);
__ BranchIf(EQUAL, &is_subtype);
}
// Not a bottom type, so check cid ranges.
const Class& type_class = Class::Handle(type.type_class());
const CidRangeVector& ranges =
hi->SubtypeRangesForClass(type_class,
/*include_abstract=*/true,
/*exclude_null=*/!null_is_assignable);
BuildOptimizedSubtypeRangeCheck(assembler, ranges,
TTSInternalRegs::kScratchReg, &is_subtype,
check_failed);
}
__ Bind(&is_subtype);
}
void RegisterTypeArgumentsUse(const Function& function,

View file

@ -103,10 +103,17 @@ class TypeTestingStubGenerator {
compiler::Label* load_succeeded,
compiler::Label* load_failed);
static void BuildOptimizedTypeParameterArgumentValueCheck(
compiler::Assembler* assembler,
HierarchyInfo* hi,
const TypeParameter& type_param,
intptr_t type_param_value_offset_i,
compiler::Label* check_failed);
static void BuildOptimizedTypeArgumentValueCheck(
compiler::Assembler* assembler,
HierarchyInfo* hi,
const AbstractType& type_arg,
const Type& type,
intptr_t type_param_value_offset_i,
compiler::Label* check_failed);

View file

@ -20,16 +20,16 @@
namespace dart {
// FLAG_trace_type_checks is only a non-constant in DEBUG mode, so we only
// allow tracing of type testing stub tests there.
#if defined(DEBUG)
// Note that flags that this affects may only mutable in some modes, e.g.,
// tracing type checks can only be done in DEBUG mode.
DEFINE_FLAG(bool,
trace_type_testing_stub_tests,
false,
"Trace type checks performed in type testing stub tests");
#else
const bool FLAG_trace_type_testing_stub_tests = false;
#endif
"Trace type testing stub tests");
DEFINE_FLAG(bool,
print_type_testing_stub_test_headers,
true,
"Print headers for executed type testing stub tests");
class TraceStubInvocationScope : public ValueObject {
public:
@ -351,7 +351,9 @@ class TTSTestState : public ValueObject {
new_tts_stub_(Code::Handle(zone())),
last_stc_(SubtypeTestCache::Handle(zone())),
last_result_(Object::Handle(zone())) {
THR_Print("Creating test state for type %s\n", type.ToCString());
if (FLAG_print_type_testing_stub_test_headers) {
THR_Print("Creating test state for type %s\n", type.ToCString());
}
}
Zone* zone() const { return thread_->zone(); }
@ -379,17 +381,20 @@ class TTSTestState : public ValueObject {
const auto& default_stub =
Code::Handle(zone(), TypeTestingStubGenerator::DefaultCodeForType(
last_tested_type_, /*lazy_specialize=*/false));
previous_tts_stub_ =
TypeTestingStubGenerator::SpecializeStubFor(thread_, last_tested_type_);
{
// To make sure we output the disassembled stub if desired.
TraceStubInvocationScope scope;
previous_tts_stub_ = TypeTestingStubGenerator::SpecializeStubFor(
thread_, last_tested_type_);
}
EXPECT_EQ(test_case.should_specialize,
previous_tts_stub_.ptr() != default_stub.ptr());
last_tested_type_.SetTypeTestingStub(previous_tts_stub_);
PrintInvocationHeader(test_case);
PrintInvocationHeader("eagerly specialized", test_case);
InvokeStubHelper(test_case);
// Treat it as a failure if the stub respecializes, since we're attempting
// to simulate AOT mode.
EXPECT(previous_tts_stub_.ptr() == new_tts_stub_.ptr());
ReportUnexpectedSTCChanges(test_case);
}
void InvokeLazilySpecializedStub(const TTSTestCase& test_case) {
@ -401,8 +406,8 @@ class TTSTestState : public ValueObject {
const auto& specializing_stub =
Code::Handle(zone(), TypeTestingStubGenerator::DefaultCodeForType(
last_tested_type_, /*lazy_specialize=*/true));
PrintInvocationHeader(test_case);
last_tested_type_.SetTypeTestingStub(specializing_stub);
PrintInvocationHeader("lazy specialized", test_case);
InvokeStubHelper(test_case,
/*is_lazy_specialization=*/test_case.should_specialize);
if (test_case.should_fail || test_case.instance.IsNull()) {
@ -417,18 +422,15 @@ class TTSTestState : public ValueObject {
// Non-specializing test cases should result in a default TTS.
EXPECT(new_tts_stub_.ptr() == default_stub.ptr());
}
ReportUnexpectedSTCChanges(
test_case, /*is_lazy_specialization=*/test_case.should_specialize);
}
void InvokeExistingStub(const TTSTestCase& test_case) {
last_tested_type_ = TypeToTest(test_case);
PrintInvocationHeader(test_case);
PrintInvocationHeader("existing", test_case);
InvokeStubHelper(test_case);
// Only respecialization should result in a new stub.
EXPECT_EQ(test_case.should_respecialize,
previous_tts_stub_.ptr() != new_tts_stub_.ptr());
ReportUnexpectedSTCChanges(test_case);
}
private:
@ -443,12 +445,14 @@ class TTSTestState : public ValueObject {
return Smi::RawCast(modified_rest_regs_box_.At(0));
}
void PrintInvocationHeader(const TTSTestCase& test_case) {
void PrintInvocationHeader(const char* stub_type,
const TTSTestCase& test_case) {
if (!FLAG_print_type_testing_stub_test_headers) return;
LogBlock lb;
const auto& tts = Code::Handle(zone(), last_tested_type_.type_test_stub());
auto* const stub_name = StubCode::NameOfStub(tts.EntryPoint());
THR_Print("Testing %s stub for type %s\n",
stub_name == nullptr ? "optimized" : stub_name,
THR_Print("Testing %s %s stub for type %s\n",
stub_name == nullptr ? "optimized" : stub_name, stub_type,
last_tested_type_.ToCString());
if (last_tested_type_.ptr() != type_.ptr()) {
THR_Print(" Original type: %s\n", type_.ToCString());
@ -523,8 +527,8 @@ class TTSTestState : public ValueObject {
}
new_tts_stub_ = last_tested_type_.type_test_stub();
last_stc_ = current_stc();
EXPECT_EQ(test_case.should_fail, !last_result_.IsNull());
if (test_case.should_fail) {
EXPECT(!last_result_.IsNull());
EXPECT(last_result_.IsError());
EXPECT(last_result_.IsUnhandledException());
if (last_result_.IsUnhandledException()) {
@ -533,16 +537,28 @@ class TTSTestState : public ValueObject {
EXPECT(strstr(error.ToCString(), "_TypeError"));
}
} else {
EXPECT(new_tts_stub_.ptr() != StubCode::LazySpecializeTypeTest().ptr());
ReportModifiedRegisters(modified_abi_regs());
// If we shouldn't go to the runtime, report any unexpected changes in
// non-ABI registers.
if (!is_lazy_specialization && !test_case.should_respecialize &&
(!test_case.should_be_false_negative ||
test_case.HasSTCEntry(previous_stc_, type_))) {
ReportModifiedRegisters(modified_rest_regs());
EXPECT(last_result_.IsNull());
if (!last_result_.IsNull()) {
EXPECT(last_result_.IsError());
EXPECT(last_result_.IsUnhandledException());
if (last_result_.IsUnhandledException()) {
const auto& exception = UnhandledException::Cast(last_result_);
dart::Expect(__FILE__, __LINE__)
.Fail("%s", exception.ToErrorCString());
}
} else {
EXPECT(new_tts_stub_.ptr() != StubCode::LazySpecializeTypeTest().ptr());
ReportModifiedRegisters(modified_abi_regs());
// If we shouldn't go to the runtime, report any unexpected changes in
// non-ABI registers.
if (!is_lazy_specialization && !test_case.should_respecialize &&
(!test_case.should_be_false_negative ||
test_case.HasSTCEntry(previous_stc_, type_))) {
ReportModifiedRegisters(modified_rest_regs());
}
}
}
ReportUnexpectedSTCChanges(test_case, is_lazy_specialization);
}
static void ReportModifiedRegisters(SmiPtr encoded_reg_mask) {
@ -595,6 +611,7 @@ class TTSTestState : public ValueObject {
void ReportUnexpectedSTCChanges(const TTSTestCase& test_case,
bool is_lazy_specialization = false) {
// Make sure should_be_false_negative is not set if respecialization is.
ASSERT(!test_case.should_be_false_negative ||
!test_case.should_respecialize);
const bool had_stc_entry = test_case.HasSTCEntry(previous_stc_, type_);
@ -872,20 +889,22 @@ ISOLATE_UNIT_TEST_CASE(TTS_SubtypeRangeCheck) {
RunTTSTest(type_i_object_dynamic, Failure({obj_b1, tav_null, tav_null}));
RunTTSTest(type_i_object_dynamic, {obj_b2, tav_null, tav_null});
// We do not generate TTS for uninstantiated types if we would need to use
// subtype range checks for the class of the interface type.
// We do generate TTSes for uninstantiated types when we need to use
// subtype range checks for the class of the interface type, but the TTS
// may be partial (returns a false negative in some cases that means going
// to the STC/runtime).
//
// obj as I<dynamic, T>
//
auto& type_dynamic_t =
AbstractType::Handle(Type::New(class_i, tav_dynamic_t));
FinalizeAndCanonicalize(&type_dynamic_t);
RunTTSTest(type_dynamic_t, FalseNegative({obj_i, tav_object, tav_null}));
RunTTSTest(type_dynamic_t, {obj_i, tav_object, tav_null});
RunTTSTest(type_dynamic_t, Failure({obj_i2, tav_object, tav_null}));
RunTTSTest(type_dynamic_t, Failure({obj_base_int, tav_object, tav_null}));
RunTTSTest(type_dynamic_t, Failure({obj_a, tav_object, tav_null}));
RunTTSTest(type_dynamic_t, Failure({obj_a1, tav_object, tav_null}));
RunTTSTest(type_dynamic_t, FalseNegative({obj_a2, tav_object, tav_null}));
RunTTSTest(type_dynamic_t, {obj_a2, tav_object, tav_null});
RunTTSTest(type_dynamic_t, Failure({obj_b, tav_object, tav_null}));
RunTTSTest(type_dynamic_t, Failure({obj_b1, tav_object, tav_null}));
RunTTSTest(type_dynamic_t, FalseNegative({obj_b2, tav_object, tav_null}));
@ -1116,24 +1135,52 @@ ISOLATE_UNIT_TEST_CASE(TTS_Future) {
R"(
import "dart:async";
createFutureInt() => (() async => 3)();
Future<int> createFutureInt() async => 3;
Future<int Function()> createFutureFunction() async => () => 3;
Future<int Function()?> createFutureNullableFunction() async =>
(() => 3) as int Function()?;
)";
const auto& class_future =
Class::Handle(IsolateGroup::Current()->object_store()->future_class());
const auto& root_library = Library::Handle(LoadTestScript(kScript));
const auto& class_future = Class::Handle(GetClass(root_library, "Future"));
const auto& class_closure =
Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
const auto& obj_futureint =
Object::Handle(Invoke(root_library, "createFutureInt"));
const auto& type_nullable_object = Type::Handle(
IsolateGroup::Current()->object_store()->nullable_object_type());
const auto& type_int = Type::Handle(Type::IntType());
const auto& type_string = Type::Handle(Type::StringType());
const auto& type_num = Type::Handle(Type::Number());
const auto& obj_futurefunction =
Object::Handle(Invoke(root_library, "createFutureFunction"));
const auto& obj_futurenullablefunction =
Object::Handle(Invoke(root_library, "createFutureNullableFunction"));
const auto& tav_null = Object::null_type_arguments();
const auto& type_object = Type::Handle(
IsolateGroup::Current()->object_store()->non_nullable_object_type());
const auto& type_legacy_object = Type::Handle(
IsolateGroup::Current()->object_store()->legacy_object_type());
const auto& type_nullable_object = Type::Handle(
IsolateGroup::Current()->object_store()->nullable_object_type());
const auto& type_int = Type::Handle(
IsolateGroup::Current()->object_store()->non_nullable_int_type());
auto& type_string = Type::Handle(Type::StringType());
type_string =
type_string.ToNullability(Nullability::kNonNullable, Heap::kNew);
FinalizeAndCanonicalize(&type_string);
auto& type_num = Type::Handle(Type::Number());
type_num = type_num.ToNullability(Nullability::kNonNullable, Heap::kNew);
FinalizeAndCanonicalize(&type_num);
auto& tav_dynamic = TypeArguments::Handle(TypeArguments::New(1));
tav_dynamic.SetTypeAt(0, Object::dynamic_type());
CanonicalizeTAV(&tav_dynamic);
auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
tav_object.SetTypeAt(0, type_object);
CanonicalizeTAV(&tav_object);
auto& tav_legacy_object = TypeArguments::Handle(TypeArguments::New(1));
tav_legacy_object.SetTypeAt(0, type_legacy_object);
CanonicalizeTAV(&tav_legacy_object);
auto& tav_nullable_object = TypeArguments::Handle(TypeArguments::New(1));
tav_nullable_object.SetTypeAt(0, type_nullable_object);
CanonicalizeTAV(&tav_nullable_object);
@ -1147,45 +1194,422 @@ ISOLATE_UNIT_TEST_CASE(TTS_Future) {
tav_string.SetTypeAt(0, type_string);
CanonicalizeTAV(&tav_string);
auto& type_future = Type::Handle(Type::New(class_future, tav_null));
auto& type_future = Type::Handle(
Type::New(class_future, tav_null, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future);
auto& type_future_dynamic =
Type::Handle(Type::New(class_future, tav_dynamic));
auto& type_future_dynamic = Type::Handle(
Type::New(class_future, tav_dynamic, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_dynamic);
auto& type_future_nullable_object =
Type::Handle(Type::New(class_future, tav_nullable_object));
auto& type_future_object = Type::Handle(
Type::New(class_future, tav_object, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_object);
auto& type_future_legacy_object = Type::Handle(
Type::New(class_future, tav_legacy_object, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_legacy_object);
auto& type_future_nullable_object = Type::Handle(
Type::New(class_future, tav_nullable_object, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_nullable_object);
auto& type_future_int = Type::Handle(Type::New(class_future, tav_int));
auto& type_future_int =
Type::Handle(Type::New(class_future, tav_int, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_int);
auto& type_future_string = Type::Handle(Type::New(class_future, tav_string));
auto& type_future_string = Type::Handle(
Type::New(class_future, tav_string, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_string);
auto& type_future_num = Type::Handle(Type::New(class_future, tav_num));
auto& type_future_num =
Type::Handle(Type::New(class_future, tav_num, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_num);
auto& type_future_t = Type::Handle(class_future.DeclarationType());
const auto& type_future_t = Type::Handle(class_future.DeclarationType());
THR_Print("********************************************************\n");
THR_Print(" Testing Future<int>\n");
THR_Print("********************************************************\n\n");
// Some more tests of generic implemented classes, using Future. Here,
// obj is an object of type Future<int>.
//
// obj as Future : Null type args (caught by TTS)
// True positives from TTS:
// obj as Future : Null type args
// obj as Future<dynamic> : Canonicalized to same as previous case.
// obj as Future<Object?> : Type arg is top type (caught by TTS)
// obj as Future<int> : Type arg is the same type (caught by TTS)
// obj as Future<String> : Type arg is not a subtype (error via runtime)
// obj as Future<Object?> : Type arg is top type
// obj as Future<Object*> : Type arg is top type
// obj as Future<Object> : Type arg is certain supertype
// obj as Future<int> : Type arg is the same type
// obj as Future<num> : Type arg is a supertype that can be matched
// with cid range (caught by TTS)
// with cid range
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = int : ... the same type (caught by TTS)
// X = String : ... an unrelated type (error via runtime)
// X = num : ... a supertype (caught by STC/runtime)
// X = int : ... the same type
//
RunTTSTest(type_future, {obj_futureint, tav_null, tav_null});
RunTTSTest(type_future_dynamic, {obj_futureint, tav_null, tav_null});
RunTTSTest(type_future_object, {obj_futureint, tav_null, tav_null});
RunTTSTest(type_future_legacy_object, {obj_futureint, tav_null, tav_null});
RunTTSTest(type_future_nullable_object, {obj_futureint, tav_null, tav_null});
RunTTSTest(type_future_int, {obj_futureint, tav_null, tav_null});
RunTTSTest(type_future_string, Failure({obj_futureint, tav_null, tav_null}));
RunTTSTest(type_future_num, {obj_futureint, tav_null, tav_null});
RunTTSTest(type_future_t, {obj_futureint, tav_int, tav_null});
RunTTSTest(type_future_t, Failure({obj_futureint, tav_string, tav_null}));
// False negatives from TTS (caught by STC/runtime):
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = num : ... a supertype
RunTTSTest(type_future_t, FalseNegative({obj_futureint, tav_num, tav_null}));
// Errors:
// obj as Future<String> : Type arg is not a supertype
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = String : ... an unrelated type
//
RunTTSTest(type_future_string, Failure({obj_futureint, tav_null, tav_null}));
RunTTSTest(type_future_t, Failure({obj_futureint, tav_string, tav_null}));
auto& type_function = Type::Handle(Type::DartFunctionType());
type_function =
type_function.ToNullability(Nullability::kNonNullable, Heap::kNew);
FinalizeAndCanonicalize(&type_function);
auto& type_legacy_function = Type::Handle(
type_function.ToNullability(Nullability::kLegacy, Heap::kNew));
FinalizeAndCanonicalize(&type_legacy_function);
auto& type_nullable_function = Type::Handle(
type_function.ToNullability(Nullability::kNullable, Heap::kOld));
FinalizeAndCanonicalize(&type_nullable_function);
auto& type_closure = Type::Handle(
Type::New(class_closure, tav_null, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_closure);
auto& type_legacy_closure = Type::Handle(
type_closure.ToNullability(Nullability::kLegacy, Heap::kOld));
FinalizeAndCanonicalize(&type_legacy_closure);
auto& type_nullable_closure = Type::Handle(
type_closure.ToNullability(Nullability::kNullable, Heap::kOld));
FinalizeAndCanonicalize(&type_nullable_closure);
auto& type_function_int_nullary =
FunctionType::Handle(FunctionType::New(0, Nullability::kNonNullable));
// Testing with a closure, so it has an implicit parameter, and we want a
// type that is canonically equal to the type of the closure.
type_function_int_nullary.set_num_implicit_parameters(1);
type_function_int_nullary.set_num_fixed_parameters(1);
type_function_int_nullary.set_parameter_types(Array::Handle(Array::New(1)));
type_function_int_nullary.SetParameterTypeAt(0, Type::dynamic_type());
type_function_int_nullary.set_result_type(type_int);
FinalizeAndCanonicalize(&type_function_int_nullary);
auto& type_legacy_function_int_nullary =
FunctionType::Handle(type_function_int_nullary.ToNullability(
Nullability::kLegacy, Heap::kOld));
FinalizeAndCanonicalize(&type_legacy_function_int_nullary);
auto& type_nullable_function_int_nullary =
FunctionType::Handle(type_function_int_nullary.ToNullability(
Nullability::kNullable, Heap::kOld));
FinalizeAndCanonicalize(&type_nullable_function_int_nullary);
auto& tav_function = TypeArguments::Handle(TypeArguments::New(1));
tav_function.SetTypeAt(0, type_function);
CanonicalizeTAV(&tav_function);
auto& tav_legacy_function = TypeArguments::Handle(TypeArguments::New(1));
tav_legacy_function.SetTypeAt(0, type_legacy_function);
CanonicalizeTAV(&tav_legacy_function);
auto& tav_nullable_function = TypeArguments::Handle(TypeArguments::New(1));
tav_nullable_function.SetTypeAt(0, type_nullable_function);
CanonicalizeTAV(&tav_nullable_function);
auto& tav_closure = TypeArguments::Handle(TypeArguments::New(1));
tav_closure.SetTypeAt(0, type_closure);
CanonicalizeTAV(&tav_closure);
auto& tav_legacy_closure = TypeArguments::Handle(TypeArguments::New(1));
tav_legacy_closure.SetTypeAt(0, type_legacy_closure);
CanonicalizeTAV(&tav_legacy_closure);
auto& tav_nullable_closure = TypeArguments::Handle(TypeArguments::New(1));
tav_nullable_closure.SetTypeAt(0, type_nullable_closure);
CanonicalizeTAV(&tav_nullable_closure);
auto& tav_function_int_nullary = TypeArguments::Handle(TypeArguments::New(1));
tav_function_int_nullary.SetTypeAt(0, type_function_int_nullary);
CanonicalizeTAV(&tav_function_int_nullary);
auto& tav_legacy_function_int_nullary =
TypeArguments::Handle(TypeArguments::New(1));
tav_legacy_function_int_nullary.SetTypeAt(0,
type_legacy_function_int_nullary);
CanonicalizeTAV(&tav_legacy_function_int_nullary);
auto& tav_nullable_function_int_nullary =
TypeArguments::Handle(TypeArguments::New(1));
tav_nullable_function_int_nullary.SetTypeAt(
0, type_nullable_function_int_nullary);
CanonicalizeTAV(&tav_nullable_function_int_nullary);
auto& type_future_function = Type::Handle(
Type::New(class_future, tav_function, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_function);
auto& type_future_legacy_function = Type::Handle(
Type::New(class_future, tav_legacy_function, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_legacy_function);
auto& type_future_nullable_function = Type::Handle(Type::New(
class_future, tav_nullable_function, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_nullable_function);
auto& type_future_closure = Type::Handle(
Type::New(class_future, tav_closure, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_closure);
auto& type_future_legacy_closure = Type::Handle(
Type::New(class_future, tav_legacy_closure, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_legacy_closure);
auto& type_future_nullable_closure = Type::Handle(
Type::New(class_future, tav_nullable_closure, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_future_nullable_closure);
auto& type_future_function_int_nullary =
Type::Handle(Type::New(class_future, tav_function_int_nullary));
FinalizeAndCanonicalize(&type_future_function_int_nullary);
auto& type_future_legacy_function_int_nullary =
Type::Handle(Type::New(class_future, tav_legacy_function_int_nullary));
FinalizeAndCanonicalize(&type_future_legacy_function_int_nullary);
auto& type_future_nullable_function_int_nullary =
Type::Handle(Type::New(class_future, tav_nullable_function_int_nullary));
FinalizeAndCanonicalize(&type_future_nullable_function_int_nullary);
THR_Print("\n********************************************************\n");
THR_Print(" Testing Future<int Function()>\n");
THR_Print("********************************************************\n\n");
// And here, obj is an object of type Future<int Function()>. Note that
// int Function() <: Function, but int Function() </: _Closure. That is,
// _Closure is a separate subtype of Function from FunctionTypes.
//
// True positive from TTS:
// obj as Future : Null type args
// obj as Future<dynamic> : Canonicalized to same as previous case.
// obj as Future<Object?> : Type arg is top type
// obj as Future<Object*> : Type arg is top typ
// obj as Future<Object> : Type arg is certain supertype
// obj as Future<Function?> : Type arg is certain supertype
// obj as Future<Function*> : Type arg is certain supertype
// obj as Future<Function> : Type arg is certain supertype
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = dynamic : ... a top type
// X = Object? : ... a top type
// X = Object* : ... a top type
// X = Object : ... a certain supertype
// X = int Function() : ... the same type.
//
RunTTSTest(type_future, {obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_dynamic, {obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_nullable_object,
{obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_legacy_object,
{obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_object, {obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_nullable_object,
{obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_legacy_object,
{obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_object, {obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_nullable_function,
{obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_legacy_function,
{obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_function, {obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_t, {obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_t,
{obj_futurefunction, tav_nullable_object, tav_null});
RunTTSTest(type_future_t, {obj_futurefunction, tav_legacy_object, tav_null});
RunTTSTest(type_future_t, {obj_futurefunction, tav_object, tav_null});
RunTTSTest(type_future_t,
{obj_futurefunction, tav_function_int_nullary, tav_null});
// False negative from TTS (caught by runtime or STC):
// obj as Future<int Function()?> : No specialization.
// obj as Future<int Function()*> : No specialization.
// obj as Future<int Function()> : No specialization.
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = Function? : ... a certain supertype (but not checked)
// X = Function* : ... a certain supertype (but not checked)
// X = Function : ... a certain supertype (but not checked)
// X = int Function()? : ... a canonically different type.
// X = int Function()* : ... a canonically different type.
//
RunTTSTest(type_future_nullable_function_int_nullary,
FalseNegative({obj_futurefunction, tav_null, tav_null,
/*should_specialize=*/false}));
RunTTSTest(type_future_legacy_function_int_nullary,
FalseNegative({obj_futurefunction, tav_null, tav_null,
/*should_specialize=*/false}));
RunTTSTest(type_future_function_int_nullary,
FalseNegative({obj_futurefunction, tav_null, tav_null,
/*should_specialize=*/false}));
RunTTSTest(type_future_t, FalseNegative({obj_futurefunction,
tav_nullable_function, tav_null}));
RunTTSTest(type_future_t, FalseNegative({obj_futurefunction,
tav_legacy_function, tav_null}));
RunTTSTest(type_future_t,
FalseNegative({obj_futurefunction, tav_function, tav_null}));
RunTTSTest(type_future_t,
FalseNegative({obj_futurefunction,
tav_nullable_function_int_nullary, tav_null}));
RunTTSTest(type_future_t,
FalseNegative({obj_futurefunction, tav_legacy_function_int_nullary,
tav_null}));
// Errors:
// obj as Future<_Closure?> : Type arg is not a supertype
// obj as Future<_Closure*> : Type arg is not a supertype
// obj as Future<_Closure> : Type arg is not a supertype
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = _Closure? : ... an unrelated type.
// X = _Closure* : ... an unrelated type.
// X = _Closure : ... an unrelated type.
//
RunTTSTest(type_future_nullable_closure,
Failure({obj_futurefunction, tav_null, tav_null}));
RunTTSTest(type_future_legacy_closure,
Failure({obj_futurefunction, tav_null, tav_null}));
RunTTSTest(type_future_closure,
Failure({obj_futurefunction, tav_null, tav_null}));
RunTTSTest(type_future_t,
Failure({obj_futurefunction, tav_nullable_closure, tav_null}));
RunTTSTest(type_future_t,
Failure({obj_futurefunction, tav_legacy_closure, tav_null}));
RunTTSTest(type_future_t,
Failure({obj_futurefunction, tav_closure, tav_null}));
THR_Print("\n********************************************************\n");
THR_Print(" Testing Future<int Function()?>\n");
THR_Print("********************************************************\n\n");
const bool strict_null_safety =
thread->isolate_group()->use_strict_null_safety_checks();
// And here, obj is an object of type Future<int Function()?>.
//
// True positive from TTS:
// obj as Future : Null type args
// obj as Future<dynamic> : Canonicalized to same as previous case.
// obj as Future<Object?> : Type arg is top type
// obj as Future<Object*> : Type arg is top typ
// obj as Future<Function?> : Type arg is certain supertype
// obj as Future<Function*> : Type arg is certain supertype
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = dynamic : ... a top type
// X = Object? : ... a top type
// X = Object* : ... a top type
// X = int Function()? : ... the same type.
//
// If not null safe:
// obj as Future<Object> : Type arg is certain supertype
// obj as Future<Function> : Type arg is certain supertype
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = Object : ... a certain supertype
RunTTSTest(type_future, {obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_dynamic,
{obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_nullable_object,
{obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_legacy_object,
{obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_nullable_object,
{obj_futurefunction, tav_null, tav_null});
RunTTSTest(type_future_legacy_object,
{obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_nullable_function,
{obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_legacy_function,
{obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_t, {obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_t,
{obj_futurenullablefunction, tav_nullable_object, tav_null});
RunTTSTest(type_future_t,
{obj_futurenullablefunction, tav_legacy_object, tav_null});
RunTTSTest(type_future_t, {obj_futurenullablefunction,
tav_nullable_function_int_nullary, tav_null});
if (!strict_null_safety) {
RunTTSTest(type_future_object,
{obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_function,
{obj_futurenullablefunction, tav_null, tav_null});
RunTTSTest(type_future_t,
{obj_futurenullablefunction, tav_object, tav_null});
}
// False negative from TTS (caught by runtime or STC):
// obj as Future<int Function()?> : No specialization.
// obj as Future<int Function()*> : No specialization.
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = Function? : ... a certain supertype (but not checked)
// X = Function* : ... a certain supertype (but not checked)
// X = int Function()* : ... a canonically different type.
//
// If not null safe:
// obj as Future<int Function()> : No specialization.
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = Function : ... a certain supertype (but not checked)
// X = int Function() : ... a canonically different type.
RunTTSTest(type_future_nullable_function_int_nullary,
FalseNegative({obj_futurenullablefunction, tav_null, tav_null,
/*should_specialize=*/false}));
RunTTSTest(type_future_legacy_function_int_nullary,
FalseNegative({obj_futurenullablefunction, tav_null, tav_null,
/*should_specialize=*/false}));
RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
tav_nullable_function, tav_null}));
RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
tav_legacy_function, tav_null}));
RunTTSTest(type_future_t,
FalseNegative({obj_futurenullablefunction,
tav_legacy_function_int_nullary, tav_null}));
if (!strict_null_safety) {
RunTTSTest(type_future_function_int_nullary,
FalseNegative({obj_futurenullablefunction, tav_null, tav_null,
/*should_specialize=*/false}));
RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
tav_function, tav_null}));
RunTTSTest(type_future_t,
FalseNegative({obj_futurenullablefunction,
tav_function_int_nullary, tav_null}));
}
// Errors:
// obj as Future<_Closure?> : Type arg is not a supertype
// obj as Future<_Closure*> : Type arg is not a supertype
// obj as Future<_Closure> : Type arg is not a supertype
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = _Closure? : ... an unrelated type.
// X = _Closure* : ... an unrelated type.
// X = _Closure : ... an unrelated type.
//
// If null safe:
// obj as Future<int Function()> : Nullable type cannot be subtype of a
// non-nullable type.
// obj as Future<Object> : Nullable type cannot be subtype of a
// non-nullable type.
// obj as Future<Function> : Nullable type cannot be subtype of a
// non-nullable type.
// obj as Future<X>, : Type arg is a type parameter instantiated with
// X = Object : ... a non-nullable type.
// X = Function : ... a non-nullable type.
// X = int Function() : ... a non-nullable type.
RunTTSTest(type_future_nullable_closure,
Failure({obj_futurenullablefunction, tav_null, tav_null}));
RunTTSTest(type_future_legacy_closure,
Failure({obj_futurenullablefunction, tav_null, tav_null}));
RunTTSTest(type_future_closure,
Failure({obj_futurenullablefunction, tav_null, tav_null}));
RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
tav_nullable_closure, tav_null}));
RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
tav_legacy_closure, tav_null}));
RunTTSTest(type_future_t,
Failure({obj_futurenullablefunction, tav_closure, tav_null}));
if (strict_null_safety) {
RunTTSTest(type_future_function_int_nullary,
Failure({obj_futurenullablefunction, tav_null, tav_null,
/*should_specialize=*/false}));
RunTTSTest(type_future_object,
Failure({obj_futurenullablefunction, tav_null, tav_null}));
RunTTSTest(type_future_function,
Failure({obj_futurenullablefunction, tav_null, tav_null}));
RunTTSTest(type_future_t,
Failure({obj_futurenullablefunction, tav_object, tav_null}));
RunTTSTest(type_future_t,
Failure({obj_futurenullablefunction, tav_function, tav_null}));
RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
tav_function_int_nullary, tav_null}));
}
}
ISOLATE_UNIT_TEST_CASE(TTS_Regress40964) {
@ -1408,25 +1832,78 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial) {
const char* kScript =
R"(
class B<T> {}
class C {}
class D extends C {}
class E extends D {}
F<A>() {}
createB() => B<int>();
createBE() => B<E>();
createBENullable() => B<E?>();
createBNull() => B<Null>();
createBNever() => B<Never>();
)";
const auto& root_library = Library::Handle(LoadTestScript(kScript));
const auto& class_b = Class::Handle(GetClass(root_library, "B"));
const auto& class_c = Class::Handle(GetClass(root_library, "C"));
const auto& class_d = Class::Handle(GetClass(root_library, "D"));
const auto& class_e = Class::Handle(GetClass(root_library, "E"));
const auto& fun_f = Function::Handle(GetFunction(root_library, "F"));
const auto& obj_b = Object::Handle(Invoke(root_library, "createB"));
const auto& obj_b_e = Object::Handle(Invoke(root_library, "createBE"));
const auto& obj_b_e_nullable =
Object::Handle(Invoke(root_library, "createBENullable"));
const auto& obj_b_null = Object::Handle(Invoke(root_library, "createBNull"));
const auto& obj_b_never =
Object::Handle(Invoke(root_library, "createBNever"));
const auto& tav_null = Object::null_type_arguments();
auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
tav_int.SetTypeAt(0, Type::Handle(Type::IntType()));
CanonicalizeTAV(&tav_int);
auto& tav_nullable_object = TypeArguments::Handle(TypeArguments::New(1));
tav_nullable_object.SetTypeAt(
0, Type::Handle(
IsolateGroup::Current()->object_store()->nullable_object_type()));
CanonicalizeTAV(&tav_nullable_object);
auto& tav_legacy_object = TypeArguments::Handle(TypeArguments::New(1));
tav_legacy_object.SetTypeAt(
0, Type::Handle(
IsolateGroup::Current()->object_store()->legacy_object_type()));
CanonicalizeTAV(&tav_legacy_object);
auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
tav_object.SetTypeAt(0, Type::Handle(Type::ObjectType()));
tav_object.SetTypeAt(
0, Type::Handle(IsolateGroup::Current()->object_store()->object_type()));
CanonicalizeTAV(&tav_object);
auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
tav_num.SetTypeAt(0, Type::Handle(Type::Number()));
CanonicalizeTAV(&tav_num);
auto& type_e =
Type::Handle(Type::New(class_e, tav_null, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_e);
auto& type_d =
Type::Handle(Type::New(class_d, tav_null, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_d);
auto& type_c =
Type::Handle(Type::New(class_c, tav_null, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_c);
auto& type_c_nullable =
Type::Handle(Type::New(class_c, tav_null, Nullability::kNullable));
FinalizeAndCanonicalize(&type_c_nullable);
auto& type_c_legacy =
Type::Handle(Type::New(class_c, tav_null, Nullability::kLegacy));
FinalizeAndCanonicalize(&type_c_legacy);
auto& tav_e = TypeArguments::Handle(TypeArguments::New(1));
tav_e.SetTypeAt(0, type_e);
CanonicalizeTAV(&tav_e);
auto& tav_d = TypeArguments::Handle(TypeArguments::New(1));
tav_d.SetTypeAt(0, type_d);
CanonicalizeTAV(&tav_d);
auto& tav_c = TypeArguments::Handle(TypeArguments::New(1));
tav_c.SetTypeAt(0, type_c);
CanonicalizeTAV(&tav_c);
auto& tav_nullable_c = TypeArguments::Handle(TypeArguments::New(1));
tav_nullable_c.SetTypeAt(0, type_c_nullable);
CanonicalizeTAV(&tav_nullable_c);
auto& tav_legacy_c = TypeArguments::Handle(TypeArguments::New(1));
tav_legacy_c.SetTypeAt(0, type_c_legacy);
CanonicalizeTAV(&tav_legacy_c);
// One case where optimized TTSes can be partial is if the type is
// uninstantiated with a type parameter at the same position as one of the
@ -1438,41 +1915,51 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial) {
TypeParameter::Handle(GetFunctionTypeParameter(fun_f, 0));
auto& tav_a = TypeArguments::Handle(TypeArguments::New(1));
tav_a.SetTypeAt(0, type_a);
auto& type_b2_a = AbstractType::Handle(Type::New(class_b, tav_a));
FinalizeAndCanonicalize(&type_b2_a);
TTSTestState state(thread, type_b2_a);
CanonicalizeTAV(&tav_a);
auto& type_b_a = AbstractType::Handle(
Type::New(class_b, tav_a, Nullability::kNonNullable));
FinalizeAndCanonicalize(&type_b_a);
TTSTestState state(thread, type_b_a);
TTSTestCase positive_test_case{obj_b, tav_null, tav_int};
TTSTestCase first_false_negative_test_case =
FalseNegative({obj_b, tav_null, tav_object});
TTSTestCase second_false_negative_test_case =
FalseNegative({obj_b, tav_null, tav_num});
// No test case should possibly hit the same STC entry as another.
ASSERT(!first_false_negative_test_case.HasSameSTCEntry(positive_test_case));
ASSERT(!second_false_negative_test_case.HasSameSTCEntry(positive_test_case));
ASSERT(!second_false_negative_test_case.HasSameSTCEntry(
first_false_negative_test_case));
// The type with the tested stub must be the same in all test cases.
ASSERT(state.TypeToTest(positive_test_case) ==
state.TypeToTest(first_false_negative_test_case));
ASSERT(state.TypeToTest(positive_test_case) ==
state.TypeToTest(second_false_negative_test_case));
TTSTestCase b_e_testcase{obj_b_e, tav_null, tav_e};
TTSTestCase b_d_testcase = FalseNegative({obj_b_e, tav_null, tav_d});
TTSTestCase b_c_testcase = FalseNegative({obj_b_e, tav_null, tav_c});
// First, test that the positive test case is handled by the TTS.
state.InvokeLazilySpecializedStub(positive_test_case);
state.InvokeExistingStub(positive_test_case);
state.InvokeLazilySpecializedStub(b_e_testcase);
state.InvokeExistingStub(b_e_testcase);
// Now restart, using the false negative test cases.
state.ClearCache();
state.InvokeLazilySpecializedStub(first_false_negative_test_case);
state.InvokeExistingStub(first_false_negative_test_case);
state.InvokeEagerlySpecializedStub(first_false_negative_test_case);
state.InvokeLazilySpecializedStub(b_d_testcase);
state.InvokeExistingStub(b_d_testcase);
state.InvokeEagerlySpecializedStub(b_d_testcase);
state.InvokeExistingStub(positive_test_case);
state.InvokeExistingStub(second_false_negative_test_case);
state.InvokeExistingStub(first_false_negative_test_case);
state.InvokeExistingStub(positive_test_case);
state.InvokeExistingStub(b_e_testcase);
state.InvokeExistingStub(b_c_testcase);
state.InvokeExistingStub(b_d_testcase);
state.InvokeExistingStub(b_e_testcase);
state.InvokeExistingStub({obj_b_never, tav_null, tav_d});
state.InvokeExistingStub({obj_b_null, tav_null, tav_nullable_c});
state.InvokeExistingStub({obj_b_null, tav_null, tav_legacy_c});
if (IsolateGroup::Current()->use_strict_null_safety_checks()) {
state.InvokeExistingStub(Failure({obj_b_null, tav_null, tav_c}));
} else {
state.InvokeExistingStub({obj_b_null, tav_null, tav_c});
}
state.InvokeExistingStub({obj_b_e, tav_null, tav_nullable_object});
state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_nullable_object});
state.InvokeExistingStub({obj_b_e, tav_null, tav_legacy_object});
state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_legacy_object});
state.InvokeExistingStub({obj_b_e, tav_null, tav_object});
if (IsolateGroup::Current()->use_strict_null_safety_checks()) {
state.InvokeExistingStub(Failure({obj_b_e_nullable, tav_null, tav_object}));
} else {
state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_object});
}
}
ISOLATE_UNIT_TEST_CASE(TTS_Partial_Incremental) {