[VM/nnbd] Nullability of FutureOr and of its type arg do not necessarily match.

Change-Id: Ie1cb6287cdfa852c8ac73868b571b422fbc9dc0a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/137460
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Régis Crelier 2020-02-27 21:08:24 +00:00 committed by commit-bot@chromium.org
parent 38508be344
commit acd7b8367e
9 changed files with 50 additions and 33 deletions

View file

@ -2270,7 +2270,9 @@ FlowGraphCompiler::GetTypeTestStubKindForTypeParameter(
// never have a value of a function type, then we can safely do a 4-type
// test instead of a 6-type test.
AbstractType& bound = AbstractType::Handle(zone(), type_param.bound());
bound = bound.UnwrapFutureOr();
if (bound.IsFutureOrType()) {
bound = bound.UnwrapFutureOr();
}
return !bound.IsTopType() && !bound.IsObjectType() &&
!bound.IsFunctionType() && !bound.IsDartFunctionType() &&
bound.IsType()

View file

@ -628,8 +628,10 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
// 'null' is an instance of Null, Object*, Never*, void, and dynamic.
// In addition, 'null' is an instance of any nullable type.
// It is also an instance of FutureOr<T> if it is an instance of T.
const AbstractType& unwrapped_type =
AbstractType::Handle(type.UnwrapFutureOr());
AbstractType& unwrapped_type = AbstractType::Handle(type.raw());
if (unwrapped_type.IsFutureOrType()) {
unwrapped_type = unwrapped_type.UnwrapFutureOr();
}
if (!unwrapped_type.IsTypeParameter() || unwrapped_type.IsNullable()) {
// Only nullable type parameter remains nullable after instantiation.
// See NullIsInstanceOf().

View file

@ -604,8 +604,10 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
// 'null' is an instance of Null, Object*, Never*, void, and dynamic.
// In addition, 'null' is an instance of any nullable type.
// It is also an instance of FutureOr<T> if it is an instance of T.
const AbstractType& unwrapped_type =
AbstractType::Handle(type.UnwrapFutureOr());
AbstractType& unwrapped_type = AbstractType::Handle(type.raw());
if (unwrapped_type.IsFutureOrType()) {
unwrapped_type = unwrapped_type.UnwrapFutureOr();
}
if (!unwrapped_type.IsTypeParameter() || unwrapped_type.IsNullable()) {
// Only nullable type parameter remains nullable after instantiation.
// See NullIsInstanceOf().

View file

@ -608,8 +608,10 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
// 'null' is an instance of Null, Object*, Never*, void, and dynamic.
// In addition, 'null' is an instance of any nullable type.
// It is also an instance of FutureOr<T> if it is an instance of T.
const AbstractType& unwrapped_type =
AbstractType::Handle(type.UnwrapFutureOr());
AbstractType& unwrapped_type = AbstractType::Handle(type.raw());
if (unwrapped_type.IsFutureOrType()) {
unwrapped_type = unwrapped_type.UnwrapFutureOr();
}
if (!unwrapped_type.IsTypeParameter() || unwrapped_type.IsNullable()) {
// Only nullable type parameter remains nullable after instantiation.
// See NullIsInstanceOf().

View file

@ -626,8 +626,10 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
// 'null' is an instance of Null, Object*, Never*, void, and dynamic.
// In addition, 'null' is an instance of any nullable type.
// It is also an instance of FutureOr<T> if it is an instance of T.
const AbstractType& unwrapped_type =
AbstractType::Handle(type.UnwrapFutureOr());
AbstractType& unwrapped_type = AbstractType::Handle(type.raw());
if (unwrapped_type.IsFutureOrType()) {
unwrapped_type = unwrapped_type.UnwrapFutureOr();
}
if (!unwrapped_type.IsTypeParameter() || unwrapped_type.IsNullable()) {
// Only nullable type parameter remains nullable after instantiation.
// See NullIsInstanceOf().

View file

@ -3235,8 +3235,11 @@ static bool MayBeNumber(CompileType* type) {
if (type->IsNone()) {
return false;
}
const AbstractType& unwrapped_type =
AbstractType::Handle(type->ToAbstractType()->UnwrapFutureOr());
AbstractType& unwrapped_type =
AbstractType::Handle(type->ToAbstractType()->raw());
if (unwrapped_type.IsFutureOrType()) {
unwrapped_type = unwrapped_type.UnwrapFutureOr();
}
// Note that type 'Number' is a subtype of itself.
return unwrapped_type.IsTopType() || unwrapped_type.IsObjectType() ||
unwrapped_type.IsTypeParameter() ||

View file

@ -1104,8 +1104,10 @@ RawBool* CallSpecializer::InstanceOfAsBool(
// 'null' is an instance of Null, Object*, Never*, void, and dynamic.
// In addition, 'null' is an instance of any nullable type.
// It is also an instance of FutureOr<T> if it is an instance of T.
const AbstractType& unwrapped_type =
AbstractType::Handle(type.UnwrapFutureOr());
AbstractType& unwrapped_type = AbstractType::Handle(type.raw());
if (unwrapped_type.IsFutureOrType()) {
unwrapped_type = unwrapped_type.UnwrapFutureOr();
}
ASSERT(unwrapped_type.IsInstantiated() &&
!unwrapped_type.IsUndetermined());
is_subtype = unwrapped_type.IsTopType() || unwrapped_type.IsNullable() ||
@ -1204,8 +1206,10 @@ bool CallSpecializer::TryOptimizeInstanceOfUsingStaticTypes(
// 'null' is an instance of Null, Object*, Never*, void, and dynamic.
// In addition, 'null' is an instance of any nullable type.
// It is also an instance of FutureOr<T> if it is an instance of T.
const AbstractType& unwrapped_type =
AbstractType::Handle(type.UnwrapFutureOr());
AbstractType& unwrapped_type = AbstractType::Handle(type.raw());
if (unwrapped_type.IsFutureOrType()) {
unwrapped_type = unwrapped_type.UnwrapFutureOr();
}
if (unwrapped_type.IsTopType() || !unwrapped_type.IsInstantiated()) {
return false; // Always true or cannot tell.
}

View file

@ -17584,12 +17584,15 @@ bool Instance::NullIsInstanceOf(
const TypeArguments& other_function_type_arguments) {
ASSERT(other.IsFinalized());
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) {
if (other.IsNullable()) {
// The type will remain nullable after instantiation.
return true;
}
AbstractType& type = AbstractType::Handle(other.raw());
if (type.IsFutureOrType()) {
type = type.UnwrapFutureOr();
}
if (type.IsNullable()) {
// The type will remain nullable after instantiation.
return true;
}
@ -17602,13 +17605,13 @@ bool Instance::NullIsInstanceOf(
if (type.IsTypeRef()) {
type = TypeRef::Cast(type).type();
}
type = type.UnwrapFutureOr();
return Instance::NullIsInstanceOf(mode, type, Object::null_type_arguments(),
Object::null_type_arguments());
}
nullability = type.nullability();
if (nullability == Nullability::kLegacy) {
return type.IsNullType() || type.IsTopType() || type.IsNeverType();
if (type.IsLegacy()) {
return type.IsTopType() || type.IsNeverType();
}
return nullability == Nullability::kNullable;
return type.IsNullable();
}
bool Instance::NullIsAssignableTo(const AbstractType& other) {
@ -17726,7 +17729,7 @@ bool Instance::RuntimeTypeIsSubtypeOf(
bool Instance::IsFutureOrInstanceOf(Zone* zone,
NNBDMode mode,
const AbstractType& other) const {
if (other.IsType() && other.IsFutureOrType()) {
if (other.IsFutureOrType()) {
const TypeArguments& other_type_arguments =
TypeArguments::Handle(zone, other.arguments());
const AbstractType& other_type_arg =
@ -18457,9 +18460,7 @@ bool AbstractType::IsFfiPointerType() const {
}
RawAbstractType* AbstractType::UnwrapFutureOr() const {
if (!IsType() || !IsFutureOrType()) {
return raw();
}
ASSERT(IsFutureOrType());
if (arguments() == TypeArguments::null()) {
return Type::dynamic_type().raw();
}
@ -18470,7 +18471,7 @@ RawAbstractType* AbstractType::UnwrapFutureOr() const {
REUSABLE_ABSTRACT_TYPE_HANDLESCOPE(thread);
AbstractType& type_arg = thread->AbstractTypeHandle();
type_arg = type_args.TypeAt(0);
while (type_arg.IsType() && type_arg.IsFutureOrType()) {
while (type_arg.IsFutureOrType()) {
if (type_arg.arguments() == TypeArguments::null()) {
return Type::dynamic_type().raw();
}
@ -18594,7 +18595,7 @@ bool AbstractType::IsSubtypeOfFutureOr(Zone* zone,
NNBDMode mode,
const AbstractType& other,
Heap::Space space) const {
if (other.IsType() && other.IsFutureOrType()) {
if (other.IsFutureOrType()) {
// This function is only called with a receiver that is either a function
// type or an uninstantiated type parameter, therefore, it cannot be of
// class Future and we can spare the check.

View file

@ -7484,7 +7484,6 @@ class AbstractType : public Instance {
bool IsFutureOrType() const { return type_class_id() == kFutureOrCid; }
// Returns the type argument of this (possibly nested) 'FutureOr' type.
// Returns unmodified type if this type is not a 'FutureOr' type.
RawAbstractType* UnwrapFutureOr() const;
// Check the subtype relationship.