Skip to content

Commit 432fa1b

Browse files
creliercommit-bot@chromium.org
authored andcommitted
[VM/nnbd] Implement pre-nnbd language changes (see lang/#807).
Normalize FutureOr<T> and Never? after instantiation. Use mutual subtyping of bounds in generic function type parameters instead of structural equality. Change-Id: I78b836d8bd2f091bd56e5a0f9c2089a411fa6f6a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135787 Commit-Queue: Régis Crelier <[email protected]> Reviewed-by: Alexander Markov <[email protected]> Reviewed-by: Liam Appelbe <[email protected]>
1 parent c9884cc commit 432fa1b

File tree

5 files changed

+95
-18
lines changed

5 files changed

+95
-18
lines changed

runtime/vm/object.cc

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7476,7 +7476,7 @@ bool Function::AreValidArguments(NNBDMode mode,
74767476
num_named_arguments, error_message)) {
74777477
return false;
74787478
}
7479-
if (mode != NNBDMode::kLegacyLib) {
7479+
if (FLAG_null_safety) {
74807480
// TODO(regis): Check required named arguments.
74817481
}
74827482
// Verify that all argument names are valid parameter names.
@@ -7844,6 +7844,7 @@ bool Function::HasSameTypeParametersAndBounds(const Function& other,
78447844
return false;
78457845
}
78467846
if (num_type_params > 0) {
7847+
const NNBDMode mode = nnbd_mode(); // TODO(regis): Remove unused mode.
78477848
const TypeArguments& type_params =
78487849
TypeArguments::Handle(zone, type_parameters());
78497850
ASSERT(!type_params.IsNull());
@@ -7861,8 +7862,16 @@ bool Function::HasSameTypeParametersAndBounds(const Function& other,
78617862
ASSERT(bound.IsFinalized());
78627863
other_bound = other_type_param.bound();
78637864
ASSERT(other_bound.IsFinalized());
7864-
if (!bound.IsEquivalent(other_bound, kind)) {
7865-
return false;
7865+
if (Dart::non_nullable_flag() && kind == TypeEquality::kInSubtypeTest) {
7866+
// Bounds that are mutual subtypes are considered equal.
7867+
if (!bound.IsSubtypeOf(mode, other_bound, Heap::kOld) ||
7868+
!other_bound.IsSubtypeOf(mode, bound, Heap::kOld)) {
7869+
return false;
7870+
}
7871+
} else {
7872+
if (!bound.IsEquivalent(other_bound, kind)) {
7873+
return false;
7874+
}
78667875
}
78677876
}
78687877
}
@@ -17575,6 +17584,7 @@ bool Instance::NullIsInstanceOf(
1757517584
ASSERT(!other.IsTypeRef()); // Must be dereferenced at compile time.
1757617585
// TODO(regis): Verify that the nullability of an instantiated FutureOr
1757717586
// always matches the nullability of its type argument. For now, be safe.
17587+
// Also see issue #40123.
1757817588
AbstractType& type = AbstractType::Handle(other.UnwrapFutureOr());
1757917589
Nullability nullability = type.nullability();
1758017590
if (nullability == Nullability::kNullable) {
@@ -17983,6 +17993,55 @@ RawAbstractType* AbstractType::SetInstantiatedNullability(
1798317993
.SetInstantiatedNullability(type_param, space);
1798417994
}
1798517995

17996+
RawAbstractType* AbstractType::NormalizeInstantiatedType() const {
17997+
if (Dart::non_nullable_flag()) {
17998+
// Normalize FutureOr<T>.
17999+
if (IsFutureOrType()) {
18000+
const AbstractType& unwrapped_type =
18001+
AbstractType::Handle(UnwrapFutureOr());
18002+
const classid_t cid = unwrapped_type.type_class_id();
18003+
if (cid == kDynamicCid || cid == kVoidCid || cid == kInstanceCid) {
18004+
return unwrapped_type.raw();
18005+
}
18006+
if (cid == kNeverCid && IsNonNullable()) {
18007+
ObjectStore* object_store = Isolate::Current()->object_store();
18008+
if (object_store->non_nullable_future_never_type() == Type::null()) {
18009+
const Class& cls = Class::Handle(object_store->future_class());
18010+
ASSERT(!cls.IsNull());
18011+
const TypeArguments& type_args =
18012+
TypeArguments::Handle(TypeArguments::New(1));
18013+
type_args.SetTypeAt(0, never_type());
18014+
Type& type =
18015+
Type::Handle(Type::New(cls, type_args, TokenPosition::kNoSource,
18016+
Nullability::kNonNullable));
18017+
type.SetIsFinalized();
18018+
type ^= type.Canonicalize();
18019+
object_store->set_non_nullable_future_never_type(type);
18020+
}
18021+
return object_store->non_nullable_future_never_type();
18022+
}
18023+
if (cid == kNullCid) {
18024+
ObjectStore* object_store = Isolate::Current()->object_store();
18025+
if (object_store->nullable_future_null_type() == Type::null()) {
18026+
const Class& cls = Class::Handle(object_store->future_class());
18027+
ASSERT(!cls.IsNull());
18028+
const TypeArguments& type_args =
18029+
TypeArguments::Handle(TypeArguments::New(1));
18030+
Type& type = Type::Handle(object_store->null_type());
18031+
type_args.SetTypeAt(0, type);
18032+
type = Type::New(cls, type_args, TokenPosition::kNoSource,
18033+
Nullability::kNullable);
18034+
type.SetIsFinalized();
18035+
type ^= type.Canonicalize();
18036+
object_store->set_nullable_future_null_type(type);
18037+
}
18038+
return object_store->nullable_future_null_type();
18039+
}
18040+
}
18041+
}
18042+
return raw();
18043+
}
18044+
1798618045
bool AbstractType::IsInstantiated(Genericity genericity,
1798718046
intptr_t num_free_fun_type_params,
1798818047
TrailPtr trail) const {
@@ -18395,20 +18454,29 @@ bool AbstractType::IsSubtypeOf(NNBDMode mode,
1839518454
Heap::Space space) const {
1839618455
ASSERT(IsFinalized());
1839718456
ASSERT(other.IsFinalized());
18398-
// Right top type or left bottom type.
18457+
// Reflexivity.
18458+
if (raw() == other.raw()) {
18459+
return true;
18460+
}
18461+
// Right top type.
18462+
if (other.IsTopType()) {
18463+
return true;
18464+
}
18465+
// Left bottom type.
1839918466
// Any form of Never in weak mode maps to Null and Null is a bottom type in
1840018467
// weak mode. In strong mode, Never and Never* are bottom types. Therefore,
1840118468
// Never and Never* are bottom types regardless of weak/strong mode.
18402-
if (other.IsTopType() || (IsNeverType() && !IsNullable())) {
18469+
// Note that we cannot encounter Never?, as it is normalized to Null.
18470+
if (IsNeverType()) {
18471+
ASSERT(!IsNullable());
1840318472
return true;
1840418473
}
18474+
// Left top type.
1840518475
if (IsDynamicType() || IsVoidType()) {
1840618476
return false;
1840718477
}
18408-
// Left Null type, including left Never type mapped to Null.
18409-
// Note that we already handled Never and Never* above.
18410-
// Only Never? remains, which maps to Null regardless of weak/strong mode.
18411-
if (IsNullType() || IsNeverType()) {
18478+
// Left Null type.
18479+
if (IsNullType()) {
1841218480
// In weak mode, Null is a bottom type.
1841318481
if (!FLAG_null_safety) {
1841418482
return true;
@@ -18679,6 +18747,10 @@ RawType* Type::ToNullability(Nullability value, Heap::Space space) const {
1867918747
if (cid == kDynamicCid || cid == kVoidCid || cid == kNullCid) {
1868018748
return raw();
1868118749
}
18750+
if (cid == kNeverCid && value == Nullability::kNullable) {
18751+
// Normalize Never? to Null.
18752+
return Type::NullType();
18753+
}
1868218754
// Clone type and set new nullability.
1868318755
Type& type = Type::Handle();
1868418756
// Always cloning in old space and removing space parameter would not satisfy
@@ -18828,7 +18900,7 @@ RawAbstractType* Type::InstantiateFrom(
1882818900
}
1882918901
}
1883018902
// Canonicalization is not part of instantiation.
18831-
return instantiated_type.raw();
18903+
return instantiated_type.NormalizeInstantiatedType();
1883218904
}
1883318905

1883418906
bool Type::IsEquivalent(const Instance& other,
@@ -19694,9 +19766,10 @@ RawAbstractType* TypeParameter::InstantiateFrom(
1969419766
if (function_type_arguments.IsNull()) {
1969519767
return Type::DynamicType();
1969619768
}
19697-
const AbstractType& result =
19769+
AbstractType& result =
1969819770
AbstractType::Handle(function_type_arguments.TypeAt(index()));
19699-
return result.SetInstantiatedNullability(*this, space);
19771+
result = result.SetInstantiatedNullability(*this, space);
19772+
return result.NormalizeInstantiatedType();
1970019773
}
1970119774
ASSERT(IsClassTypeParameter());
1970219775
if (instantiator_type_arguments.IsNull()) {
@@ -19711,9 +19784,10 @@ RawAbstractType* TypeParameter::InstantiateFrom(
1971119784
// (see AssertAssignableInstr::Canonicalize).
1971219785
return AbstractType::null();
1971319786
}
19714-
const AbstractType& result =
19787+
AbstractType& result =
1971519788
AbstractType::Handle(instantiator_type_arguments.TypeAt(index()));
19716-
return result.SetInstantiatedNullability(*this, space);
19789+
result = result.SetInstantiatedNullability(*this, space);
19790+
return result.NormalizeInstantiatedType();
1971719791
// There is no need to canonicalize the instantiated type parameter, since all
1971819792
// type arguments are canonicalized at type finalization time. It would be too
1971919793
// early to canonicalize the returned type argument here, since instantiation

runtime/vm/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7299,6 +7299,7 @@ class AbstractType : public Instance {
72997299
virtual RawAbstractType* SetInstantiatedNullability(
73007300
const TypeParameter& type_param,
73017301
Heap::Space space) const;
7302+
virtual RawAbstractType* NormalizeInstantiatedType() const;
73027303

73037304
virtual bool HasTypeClass() const { return type_class_id() != kIllegalCid; }
73047305
virtual classid_t type_class_id() const;

runtime/vm/object_store.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class ObjectPointerVisitor;
9696
RW(Field, pragma_name) \
9797
RW(Field, pragma_options) \
9898
RW(Class, future_class) \
99+
RW(Type, non_nullable_future_never_type) /* maybe be null, lazily built */ \
100+
RW(Type, nullable_future_null_type) /* maybe be null, lazily built */ \
99101
RW(Class, completer_class) \
100102
RW(Class, symbol_class) \
101103
RW(Class, one_byte_string_class) \

tests/corelib/type_tostring_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ void main() {
5555

5656
// Generic non-class, non-function type.
5757
expect<FutureOr<int>>("FutureOr<int>");
58-
expect<FutureOr<Object>>("FutureOr<Object>");
58+
expect<FutureOr<Object>>("Object");
5959
expect<FutureOr<FutureOr<Future<Object>>>>(
6060
"FutureOr<FutureOr<Future<Object>>>");
61-
expect<FutureOr<Null>>("FutureOr<Null>");
61+
expect<FutureOr<Null>>("Future<Null>?");
6262
// TODO: Add nullable types with NNBD.
6363

6464
// Private names may be mangled.

tests/corelib_2/type_tostring_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ void main() {
5555

5656
// Generic non-class, non-function type.
5757
expect<FutureOr<int>>("FutureOr<int>");
58-
expect<FutureOr<Object>>("FutureOr<Object>");
58+
expect<FutureOr<Object>>("Object");
5959
expect<FutureOr<FutureOr<Future<Object>>>>(
6060
"FutureOr<FutureOr<Future<Object>>>");
61-
expect<FutureOr<Null>>("FutureOr<Null>");
61+
expect<FutureOr<Null>>("Future<Null>?");
6262
// TODO: Add nullable types with NNBD.
6363

6464
// Private names may be mangled.

0 commit comments

Comments
 (0)