[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:
committed by
Commit Queue
parent
ed943203b9
commit
6f4bed30cc
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user