[vm] Fix CompileType::CanBeFuture()

In certain cases compiler can omit a type check in 'await e' if it can
prove that expression 'e' cannot be evaluated to Future.

Previously, if actual type of 'e' is unknown, CompileType::CanBeFuture()
tested if static type of 'e' is a subtype of Future.

This is not correct, as static type could have a subtype which is also
a subtype of Future, but static type itself is not a subtype of Future:

class A {}
class B implements A, Future<A> {}

A e = confuse(B()); // 'e' is B, but has a static type A.
await e;            // A is not a subtype of Future, but 'e' is Future
                    // and should be awaited.

TEST=language/async/await_flatten_test
Fixes https://github.com/dart-lang/sdk/issues/52585

Change-Id: I270a8260224246e1f8c16eff57231363a0f25ae6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/309380
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Alexander Markov
2023-06-14 19:18:26 +00:00
committed by Commit Queue
parent ed943203b9
commit 6f4bed30cc
10 changed files with 581 additions and 546 deletions
+28 -3
View File
@@ -571,6 +571,7 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
bool has_isolate_unsendable_pragma =
cls.is_isolate_unsendable_due_to_pragma();
bool is_future_subtype = cls.IsFutureClass();
// Finalize super class.
Class& super_class = Class::Handle(zone, cls.SuperClass());
@@ -588,7 +589,8 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
super_type ^= FinalizeType(super_type);
cls.set_super_type(super_type);
has_isolate_unsendable_pragma |=
Class::IsIsolateUnsendableDueToPragma(super_type.type_class());
super_class.is_isolate_unsendable_due_to_pragma();
is_future_subtype |= super_class.is_future_subtype();
}
// Finalize interface types (but not necessarily interface classes).
const auto& interface_types = Array::Handle(zone, cls.interfaces());
@@ -602,12 +604,17 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
FinalizeTypesInClass(interface_class);
interface_types.SetAt(i, interface_type);
has_isolate_unsendable_pragma |=
Class::IsIsolateUnsendableDueToPragma(interface_type.type_class());
interface_class.is_isolate_unsendable_due_to_pragma();
is_future_subtype |= interface_class.is_future_subtype();
}
cls.set_is_type_finalized();
cls.set_is_isolate_unsendable_due_to_pragma(has_isolate_unsendable_pragma);
cls.set_is_future_subtype(is_future_subtype);
if (is_future_subtype && !cls.is_abstract()) {
MarkClassCanBeFuture(zone, cls);
}
RegisterClassInHierarchy(thread->zone(), cls);
RegisterClassInHierarchy(zone, cls);
#endif // defined(DART_PRECOMPILED_RUNTIME)
}
@@ -654,6 +661,24 @@ void ClassFinalizer::RegisterClassInHierarchy(Zone* zone, const Class& cls) {
}
}
}
void ClassFinalizer::MarkClassCanBeFuture(Zone* zone, const Class& cls) {
if (cls.can_be_future()) return;
cls.set_can_be_future(true);
Class& base = Class::Handle(zone, cls.SuperClass());
if (!base.IsNull()) {
MarkClassCanBeFuture(zone, base);
}
auto& interfaces = Array::Handle(zone, cls.interfaces());
auto& type = AbstractType::Handle(zone);
for (intptr_t i = 0; i < interfaces.Length(); ++i) {
type ^= interfaces.At(i);
base = type.type_class();
MarkClassCanBeFuture(zone, base);
}
}
#endif // defined(DART_PRECOMPILED_RUNTIME)
void ClassFinalizer::FinalizeClass(const Class& cls) {
+3
View File
@@ -54,6 +54,9 @@ class ClassFinalizer : public AllStatic {
#if !defined(DART_PRECOMPILED_RUNTIME)
// Register class in the lists of direct subclasses and direct implementors.
static void RegisterClassInHierarchy(Zone* zone, const Class& cls);
// Mark [cls], its superclass and superinterfaces as can_be_future().
static void MarkClassCanBeFuture(Zone* zone, const Class& cls);
#endif // !defined(DART_PRECOMPILED_RUNTIME)
// Ensures members of the class are loaded, class layout is finalized and size
+7 -13
View File
@@ -949,22 +949,19 @@ bool CompileType::CanBeSmi() {
}
bool CompileType::CanBeFuture() {
IsolateGroup* isolate_group = IsolateGroup::Current();
ObjectStore* object_store = isolate_group->object_store();
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
if (cid_ != kIllegalCid && cid_ != kDynamicCid) {
if ((cid_ == kNullCid) || (cid_ == kNeverCid) ||
IsInternalOnlyClassId(cid_) || cid_ == kTypeArgumentsCid) {
return false;
}
const Class& cls = Class::Handle(isolate_group->class_table()->At(cid_));
return Class::IsSubtypeOf(
cls, TypeArguments::null_type_arguments(), Nullability::kNonNullable,
Type::Handle(object_store->non_nullable_future_rare_type()),
Heap::kNew);
const Class& cls =
Class::Handle(zone, thread->isolate_group()->class_table()->At(cid_));
return cls.is_future_subtype();
}
AbstractType& type = AbstractType::Handle(ToAbstractType()->ptr());
AbstractType& type = AbstractType::Handle(zone, ToAbstractType()->ptr());
if (type.IsTypeParameter()) {
type = TypeParameter::Cast(type).bound();
}
@@ -980,10 +977,7 @@ bool CompileType::CanBeFuture() {
if ((type_class_id == kNullCid) || (type_class_id == kNeverCid)) {
return false;
}
Type& future_type =
Type::Handle(object_store->non_nullable_future_rare_type());
future_type = future_type.ToNullability(Nullability::kNullable, Heap::kNew);
return type.IsSubtypeOf(future_type, Heap::kNew);
return Class::Handle(zone, type.type_class()).can_be_future();
}
void CompileType::PrintTo(BaseTextBuffer* f) const {
File diff suppressed because it is too large Load Diff
+1 -8
View File
@@ -2539,15 +2539,8 @@ DART_EXPORT bool Dart_IsFuture(Dart_Handle handle) {
API_TIMELINE_DURATION(T);
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle));
if (obj.IsInstance()) {
ObjectStore* object_store = T->isolate_group()->object_store();
const Type& future_rare_type =
Type::Handle(Z, object_store->non_nullable_future_rare_type());
ASSERT(!future_rare_type.IsNull());
const Class& obj_class = Class::Handle(Z, obj.clazz());
bool is_future = Class::IsSubtypeOf(
obj_class, Object::null_type_arguments(), Nullability::kNonNullable,
future_rare_type, Heap::kNew);
return is_future;
return obj_class.is_future_subtype();
}
return false;
}
+10
View File
@@ -3172,6 +3172,16 @@ void Class::set_is_isolate_unsendable_due_to_pragma(bool value) const {
IsIsolateUnsendableDueToPragmaBit::update(value, state_bits()));
}
void Class::set_is_future_subtype(bool value) const {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
set_state_bits(IsFutureSubtypeBit::update(value, state_bits()));
}
void Class::set_can_be_future(bool value) const {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
set_state_bits(CanBeFutureBit::update(value, state_bits()));
}
// Initialize class fields of type Array with empty array.
void Class::InitEmptyFields() {
if (Object::empty_array().ptr() == Array::null()) {
+19 -6
View File
@@ -1687,10 +1687,6 @@ class Class : public Object {
static bool IsIsolateUnsendable(ClassPtr clazz) {
return IsIsolateUnsendableBit::decode(clazz->untag()->state_bits_);
}
static bool IsIsolateUnsendableDueToPragma(ClassPtr clazz) {
return IsIsolateUnsendableDueToPragmaBit::decode(
clazz->untag()->state_bits_);
}
#if !defined(DART_PRECOMPILED_RUNTIME)
CodePtr allocation_stub() const { return untag()->allocation_stub(); }
@@ -1941,13 +1937,19 @@ class Class : public Object {
// Whether instances of the class cannot be sent across ports.
//
// Will be true iff
// - class is marked with `@pramga('vm:isolate-unsendable')
// - class is marked with `@pragma('vm:isolate-unsendable')
// - super class / super interface classes are marked as unsendable.
// - class has native fields.
kIsIsolateUnsendableBit,
// True if this class has `@pramga('vm:isolate-unsendable') annotation or
// True if this class has `@pragma('vm:isolate-unsendable') annotation or
// base class or implemented interfaces has this bit.
kIsIsolateUnsendableDueToPragmaBit,
// This class is a subtype of Future.
kIsFutureSubtypeBit,
// This class has a non-abstract subtype which is a subtype of Future.
// It means that variable of static type based on this class may hold
// a Future instance.
kCanBeFutureBit,
};
class ConstBit : public BitField<uint32_t, bool, kConstBit, 1> {};
class ImplementedBit : public BitField<uint32_t, bool, kImplementedBit, 1> {};
@@ -1981,6 +1983,9 @@ class Class : public Object {
class IsIsolateUnsendableDueToPragmaBit
: public BitField<uint32_t, bool, kIsIsolateUnsendableDueToPragmaBit, 1> {
};
class IsFutureSubtypeBit
: public BitField<uint32_t, bool, kIsFutureSubtypeBit, 1> {};
class CanBeFutureBit : public BitField<uint32_t, bool, kCanBeFutureBit, 1> {};
void set_name(const String& value) const;
void set_user_name(const String& value) const;
@@ -2033,6 +2038,14 @@ class Class : public Object {
return IsIsolateUnsendableDueToPragmaBit::decode(state_bits());
}
void set_is_future_subtype(bool value) const;
bool is_future_subtype() const {
return IsFutureSubtypeBit::decode(state_bits());
}
void set_can_be_future(bool value) const;
bool can_be_future() const { return CanBeFutureBit::decode(state_bits()); }
private:
void set_functions(const Array& value) const;
void set_fields(const Array& value) const;
+1 -5
View File
@@ -494,9 +494,8 @@ void ObjectStore::LazyInitAsyncMembers() {
auto* const thread = Thread::Current();
SafepointWriteRwLocker locker(thread,
thread->isolate_group()->program_lock());
if (non_nullable_future_rare_type_.load() == Type::null()) {
if (nullable_future_null_type_.load() == Type::null()) {
ASSERT(non_nullable_future_never_type_.load() == Type::null());
ASSERT(nullable_future_null_type_.load() == Type::null());
auto* const zone = thread->zone();
const auto& cls = Class::Handle(zone, future_class());
@@ -521,9 +520,6 @@ void ObjectStore::LazyInitAsyncMembers() {
type.SetIsFinalized();
type ^= type.Canonicalize(thread);
nullable_future_null_type_.store(type.ptr());
type = cls.RareType();
non_nullable_future_rare_type_.store(type.ptr());
}
}
-1
View File
@@ -60,7 +60,6 @@ class ObjectPointerVisitor;
LAZY_FFI(Class, varargs_class) \
LAZY_FFI(Function, handle_finalizer_message_function) \
LAZY_FFI(Function, handle_native_finalizer_message_function) \
LAZY_ASYNC(Type, non_nullable_future_rare_type) \
LAZY_ASYNC(Type, non_nullable_future_never_type) \
LAZY_ASYNC(Type, nullable_future_null_type) \
LAZY_ISOLATE(Class, send_port_class) \
@@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Requirements=nnbd-strong
import 'dart:async';
import 'package:expect/expect.dart';
import '../static_type_helper.dart';