[vm] Ensure _Closure <: FutureOr<Function>.

Instead of checking in AbstractType::IsSubtypeOf, check in
Class::IsSubtypeOf, after any FutureOr layers have been unwrapped.

TEST=vm/cc/ClosureType

Change-Id: I5f39bba1660e795442a7a489cf6922a38c449932
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210683
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
Tess Strickland
2021-08-20 12:56:15 +00:00
committed by commit-bot@chromium.org
parent f4ca1ed08b
commit d95e6337dd
2 changed files with 107 additions and 11 deletions
+11 -10
View File
@@ -5497,6 +5497,12 @@ bool Class::IsSubtypeOf(const Class& cls,
}
return true;
}
// _Closure <: Function
if (this_class.IsClosureClass() && other_class.IsDartFunctionClass()) {
return true;
}
// Check for 'direct super type' specified in the implements clause
// and check for transitivity at the same time.
Array& interfaces = Array::Handle(zone, this_class.interfaces());
@@ -20453,17 +20459,12 @@ bool AbstractType::IsSubtypeOf(const AbstractType& other,
return false;
}
// Function types cannot be handled by Class::IsSubtypeOf().
if (other.IsDartFunctionType()) {
// Any type that can be the type of a closure is a subtype of Function.
if (IsDartFunctionType() || IsDartClosureType() || IsFunctionType()) {
if (isolate_group->use_strict_null_safety_checks() && IsNullable()) {
return !other.IsNonNullable();
}
return true;
}
// Fall through.
}
if (IsFunctionType()) {
// Any type that can be the type of a closure is a subtype of Function.
if (other.IsDartFunctionType()) {
return !isolate_group->use_strict_null_safety_checks() || !IsNullable() ||
!other.IsNonNullable();
}
if (other.IsFunctionType()) {
// Check for two function types.
if (isolate_group->use_strict_null_safety_checks() && IsNullable() &&
+96 -1
View File
@@ -5305,13 +5305,108 @@ TEST_CASE(TypeParameterTypeRef) {
}
ISOLATE_UNIT_TEST_CASE(ClosureType_SubtypeOfFunctionType) {
auto check_subtype_relation = [](const Expect& expect, const Type& sub,
const Type& super, bool is_subtype) {
if (sub.IsSubtypeOf(super, Heap::kNew) != is_subtype) {
TextBuffer buffer(128);
buffer.AddString("Expected ");
sub.PrintName(Object::kScrubbedName, &buffer);
buffer.Printf(" to %s a subtype of ", is_subtype ? "be" : "not be");
super.PrintName(Object::kScrubbedName, &buffer);
expect.Fail("%s", buffer.buffer());
}
};
#define EXPECT_SUBTYPE(sub, super) \
check_subtype_relation(Expect(__FILE__, __LINE__), sub, super, true);
#define EXPECT_NOT_SUBTYPE(sub, super) \
check_subtype_relation(Expect(__FILE__, __LINE__), sub, super, false);
auto finalize_and_canonicalize = [](Type* type) {
*type ^= ClassFinalizer::FinalizeType(*type);
ASSERT(type->IsCanonical());
};
const auto& closure_class =
Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
const auto& closure_type = Type::Handle(closure_class.DeclarationType());
auto& closure_type_nullable = Type::Handle(
closure_type.ToNullability(Nullability::kNullable, Heap::kNew));
finalize_and_canonicalize(&closure_type_nullable);
auto& closure_type_legacy = Type::Handle(
closure_type.ToNullability(Nullability::kLegacy, Heap::kNew));
finalize_and_canonicalize(&closure_type_legacy);
auto& closure_type_nonnullable = Type::Handle(
closure_type.ToNullability(Nullability::kNonNullable, Heap::kNew));
finalize_and_canonicalize(&closure_type_nonnullable);
const auto& function_type =
Type::Handle(IsolateGroup::Current()->object_store()->function_type());
auto& function_type_nullable = Type::Handle(
function_type.ToNullability(Nullability::kNullable, Heap::kNew));
finalize_and_canonicalize(&function_type_nullable);
auto& function_type_legacy = Type::Handle(
function_type.ToNullability(Nullability::kLegacy, Heap::kNew));
finalize_and_canonicalize(&function_type_legacy);
auto& function_type_nonnullable = Type::Handle(
function_type.ToNullability(Nullability::kNonNullable, Heap::kNew));
finalize_and_canonicalize(&function_type_nonnullable);
EXPECT(closure_type.IsSubtypeOf(function_type, Heap::kNew));
EXPECT_SUBTYPE(closure_type_nonnullable, function_type_nullable);
EXPECT_SUBTYPE(closure_type_nonnullable, function_type_legacy);
EXPECT_SUBTYPE(closure_type_nonnullable, function_type_nonnullable);
EXPECT_SUBTYPE(closure_type_legacy, function_type_nullable);
EXPECT_SUBTYPE(closure_type_legacy, function_type_legacy);
EXPECT_SUBTYPE(closure_type_legacy, function_type_nonnullable);
EXPECT_SUBTYPE(closure_type_nullable, function_type_nullable);
EXPECT_SUBTYPE(closure_type_nullable, function_type_legacy);
// Nullable types are not a subtype of non-nullable types in strict mode.
if (IsolateGroup::Current()->use_strict_null_safety_checks()) {
EXPECT_NOT_SUBTYPE(closure_type_nullable, function_type_nonnullable);
} else {
EXPECT_SUBTYPE(closure_type_nullable, function_type_nonnullable);
}
const auto& async_lib = Library::Handle(Library::AsyncLibrary());
const auto& future_or_class =
Class::Handle(async_lib.LookupClass(Symbols::FutureOr()));
auto& tav_function_nullable = TypeArguments::Handle(TypeArguments::New(1));
tav_function_nullable.SetTypeAt(0, function_type_nullable);
tav_function_nullable = tav_function_nullable.Canonicalize(thread, nullptr);
auto& tav_function_legacy = TypeArguments::Handle(TypeArguments::New(1));
tav_function_legacy.SetTypeAt(0, function_type_legacy);
tav_function_legacy = tav_function_legacy.Canonicalize(thread, nullptr);
auto& tav_function_nonnullable = TypeArguments::Handle(TypeArguments::New(1));
tav_function_nonnullable.SetTypeAt(0, function_type_nonnullable);
tav_function_nonnullable =
tav_function_nonnullable.Canonicalize(thread, nullptr);
auto& future_or_function_type_nullable =
Type::Handle(Type::New(future_or_class, tav_function_nullable));
finalize_and_canonicalize(&future_or_function_type_nullable);
auto& future_or_function_type_legacy =
Type::Handle(Type::New(future_or_class, tav_function_legacy));
finalize_and_canonicalize(&future_or_function_type_legacy);
auto& future_or_function_type_nonnullable =
Type::Handle(Type::New(future_or_class, tav_function_nonnullable));
finalize_and_canonicalize(&future_or_function_type_nonnullable);
EXPECT_SUBTYPE(closure_type_nonnullable, future_or_function_type_nullable);
EXPECT_SUBTYPE(closure_type_nonnullable, future_or_function_type_legacy);
EXPECT_SUBTYPE(closure_type_nonnullable, future_or_function_type_nonnullable);
EXPECT_SUBTYPE(closure_type_legacy, future_or_function_type_nullable);
EXPECT_SUBTYPE(closure_type_legacy, future_or_function_type_legacy);
EXPECT_SUBTYPE(closure_type_legacy, future_or_function_type_nonnullable);
EXPECT_SUBTYPE(closure_type_nullable, future_or_function_type_nullable);
EXPECT_SUBTYPE(closure_type_nullable, future_or_function_type_legacy);
// Nullable types are not a subtype of non-nullable types in strict mode.
if (IsolateGroup::Current()->use_strict_null_safety_checks()) {
EXPECT_NOT_SUBTYPE(closure_type_nullable,
future_or_function_type_nonnullable);
} else {
EXPECT_SUBTYPE(closure_type_nullable, future_or_function_type_nonnullable);
}
#undef EXPECT_NOT_SUBTYPE
#undef EXPECT_SUBTYPE
}
TEST_CASE(Class_GetInstantiationOf) {