[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 <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Liam Appelbe <liama@google.com>
This commit is contained in:
Régis Crelier 2020-02-26 21:32:51 +00:00 committed by commit-bot@chromium.org
parent c9884ccf52
commit 432fa1b4e8
5 changed files with 95 additions and 18 deletions

View file

@ -7476,7 +7476,7 @@ bool Function::AreValidArguments(NNBDMode mode,
num_named_arguments, error_message)) {
return false;
}
if (mode != NNBDMode::kLegacyLib) {
if (FLAG_null_safety) {
// TODO(regis): Check required named arguments.
}
// Verify that all argument names are valid parameter names.
@ -7844,6 +7844,7 @@ bool Function::HasSameTypeParametersAndBounds(const Function& other,
return false;
}
if (num_type_params > 0) {
const NNBDMode mode = nnbd_mode(); // TODO(regis): Remove unused mode.
const TypeArguments& type_params =
TypeArguments::Handle(zone, type_parameters());
ASSERT(!type_params.IsNull());
@ -7861,8 +7862,16 @@ bool Function::HasSameTypeParametersAndBounds(const Function& other,
ASSERT(bound.IsFinalized());
other_bound = other_type_param.bound();
ASSERT(other_bound.IsFinalized());
if (!bound.IsEquivalent(other_bound, kind)) {
return false;
if (Dart::non_nullable_flag() && kind == TypeEquality::kInSubtypeTest) {
// Bounds that are mutual subtypes are considered equal.
if (!bound.IsSubtypeOf(mode, other_bound, Heap::kOld) ||
!other_bound.IsSubtypeOf(mode, bound, Heap::kOld)) {
return false;
}
} else {
if (!bound.IsEquivalent(other_bound, kind)) {
return false;
}
}
}
}
@ -17575,6 +17584,7 @@ bool Instance::NullIsInstanceOf(
ASSERT(!other.IsTypeRef()); // Must be dereferenced at compile time.
// TODO(regis): Verify that the nullability of an instantiated FutureOr
// always matches the nullability of its type argument. For now, be safe.
// Also see issue #40123.
AbstractType& type = AbstractType::Handle(other.UnwrapFutureOr());
Nullability nullability = type.nullability();
if (nullability == Nullability::kNullable) {
@ -17983,6 +17993,55 @@ RawAbstractType* AbstractType::SetInstantiatedNullability(
.SetInstantiatedNullability(type_param, space);
}
RawAbstractType* AbstractType::NormalizeInstantiatedType() const {
if (Dart::non_nullable_flag()) {
// Normalize FutureOr<T>.
if (IsFutureOrType()) {
const AbstractType& unwrapped_type =
AbstractType::Handle(UnwrapFutureOr());
const classid_t cid = unwrapped_type.type_class_id();
if (cid == kDynamicCid || cid == kVoidCid || cid == kInstanceCid) {
return unwrapped_type.raw();
}
if (cid == kNeverCid && IsNonNullable()) {
ObjectStore* object_store = Isolate::Current()->object_store();
if (object_store->non_nullable_future_never_type() == Type::null()) {
const Class& cls = Class::Handle(object_store->future_class());
ASSERT(!cls.IsNull());
const TypeArguments& type_args =
TypeArguments::Handle(TypeArguments::New(1));
type_args.SetTypeAt(0, never_type());
Type& type =
Type::Handle(Type::New(cls, type_args, TokenPosition::kNoSource,
Nullability::kNonNullable));
type.SetIsFinalized();
type ^= type.Canonicalize();
object_store->set_non_nullable_future_never_type(type);
}
return object_store->non_nullable_future_never_type();
}
if (cid == kNullCid) {
ObjectStore* object_store = Isolate::Current()->object_store();
if (object_store->nullable_future_null_type() == Type::null()) {
const Class& cls = Class::Handle(object_store->future_class());
ASSERT(!cls.IsNull());
const TypeArguments& type_args =
TypeArguments::Handle(TypeArguments::New(1));
Type& type = Type::Handle(object_store->null_type());
type_args.SetTypeAt(0, type);
type = Type::New(cls, type_args, TokenPosition::kNoSource,
Nullability::kNullable);
type.SetIsFinalized();
type ^= type.Canonicalize();
object_store->set_nullable_future_null_type(type);
}
return object_store->nullable_future_null_type();
}
}
}
return raw();
}
bool AbstractType::IsInstantiated(Genericity genericity,
intptr_t num_free_fun_type_params,
TrailPtr trail) const {
@ -18395,20 +18454,29 @@ bool AbstractType::IsSubtypeOf(NNBDMode mode,
Heap::Space space) const {
ASSERT(IsFinalized());
ASSERT(other.IsFinalized());
// Right top type or left bottom type.
// Reflexivity.
if (raw() == other.raw()) {
return true;
}
// Right top type.
if (other.IsTopType()) {
return true;
}
// Left bottom type.
// Any form of Never in weak mode maps to Null and Null is a bottom type in
// weak mode. In strong mode, Never and Never* are bottom types. Therefore,
// Never and Never* are bottom types regardless of weak/strong mode.
if (other.IsTopType() || (IsNeverType() && !IsNullable())) {
// Note that we cannot encounter Never?, as it is normalized to Null.
if (IsNeverType()) {
ASSERT(!IsNullable());
return true;
}
// Left top type.
if (IsDynamicType() || IsVoidType()) {
return false;
}
// Left Null type, including left Never type mapped to Null.
// Note that we already handled Never and Never* above.
// Only Never? remains, which maps to Null regardless of weak/strong mode.
if (IsNullType() || IsNeverType()) {
// Left Null type.
if (IsNullType()) {
// In weak mode, Null is a bottom type.
if (!FLAG_null_safety) {
return true;
@ -18679,6 +18747,10 @@ RawType* Type::ToNullability(Nullability value, Heap::Space space) const {
if (cid == kDynamicCid || cid == kVoidCid || cid == kNullCid) {
return raw();
}
if (cid == kNeverCid && value == Nullability::kNullable) {
// Normalize Never? to Null.
return Type::NullType();
}
// Clone type and set new nullability.
Type& type = Type::Handle();
// Always cloning in old space and removing space parameter would not satisfy
@ -18828,7 +18900,7 @@ RawAbstractType* Type::InstantiateFrom(
}
}
// Canonicalization is not part of instantiation.
return instantiated_type.raw();
return instantiated_type.NormalizeInstantiatedType();
}
bool Type::IsEquivalent(const Instance& other,
@ -19694,9 +19766,10 @@ RawAbstractType* TypeParameter::InstantiateFrom(
if (function_type_arguments.IsNull()) {
return Type::DynamicType();
}
const AbstractType& result =
AbstractType& result =
AbstractType::Handle(function_type_arguments.TypeAt(index()));
return result.SetInstantiatedNullability(*this, space);
result = result.SetInstantiatedNullability(*this, space);
return result.NormalizeInstantiatedType();
}
ASSERT(IsClassTypeParameter());
if (instantiator_type_arguments.IsNull()) {
@ -19711,9 +19784,10 @@ RawAbstractType* TypeParameter::InstantiateFrom(
// (see AssertAssignableInstr::Canonicalize).
return AbstractType::null();
}
const AbstractType& result =
AbstractType& result =
AbstractType::Handle(instantiator_type_arguments.TypeAt(index()));
return result.SetInstantiatedNullability(*this, space);
result = result.SetInstantiatedNullability(*this, space);
return result.NormalizeInstantiatedType();
// There is no need to canonicalize the instantiated type parameter, since all
// type arguments are canonicalized at type finalization time. It would be too
// early to canonicalize the returned type argument here, since instantiation

View file

@ -7299,6 +7299,7 @@ class AbstractType : public Instance {
virtual RawAbstractType* SetInstantiatedNullability(
const TypeParameter& type_param,
Heap::Space space) const;
virtual RawAbstractType* NormalizeInstantiatedType() const;
virtual bool HasTypeClass() const { return type_class_id() != kIllegalCid; }
virtual classid_t type_class_id() const;

View file

@ -96,6 +96,8 @@ class ObjectPointerVisitor;
RW(Field, pragma_name) \
RW(Field, pragma_options) \
RW(Class, future_class) \
RW(Type, non_nullable_future_never_type) /* maybe be null, lazily built */ \
RW(Type, nullable_future_null_type) /* maybe be null, lazily built */ \
RW(Class, completer_class) \
RW(Class, symbol_class) \
RW(Class, one_byte_string_class) \

View file

@ -55,10 +55,10 @@ void main() {
// Generic non-class, non-function type.
expect<FutureOr<int>>("FutureOr<int>");
expect<FutureOr<Object>>("FutureOr<Object>");
expect<FutureOr<Object>>("Object");
expect<FutureOr<FutureOr<Future<Object>>>>(
"FutureOr<FutureOr<Future<Object>>>");
expect<FutureOr<Null>>("FutureOr<Null>");
expect<FutureOr<Null>>("Future<Null>?");
// TODO: Add nullable types with NNBD.
// Private names may be mangled.

View file

@ -55,10 +55,10 @@ void main() {
// Generic non-class, non-function type.
expect<FutureOr<int>>("FutureOr<int>");
expect<FutureOr<Object>>("FutureOr<Object>");
expect<FutureOr<Object>>("Object");
expect<FutureOr<FutureOr<Future<Object>>>>(
"FutureOr<FutureOr<Future<Object>>>");
expect<FutureOr<Null>>("FutureOr<Null>");
expect<FutureOr<Null>>("Future<Null>?");
// TODO: Add nullable types with NNBD.
// Private names may be mangled.