[cfe] Handle A extends FutureOr<B> <: FutureOr<B>

Change-Id: Ib5580328f006b033b2c004083b575a2cf5aed551
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/141541
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther 2020-04-05 06:31:19 +00:00 committed by commit-bot@chromium.org
parent 80ae6ed91d
commit a7f1a5e677
66 changed files with 298 additions and 685 deletions

View file

@ -56,6 +56,8 @@ import 'package:kernel/type_algebra.dart' show Substitution, substitute;
import 'package:kernel/type_environment.dart'
show SubtypeCheckMode, TypeEnvironment;
import 'package:kernel/src/types.dart' show Types;
import '../dill/dill_member_builder.dart' show DillMemberBuilder;
import '../fasta_codes.dart';
@ -64,8 +66,6 @@ import '../kernel/redirecting_factory_body.dart' show getRedirectingFactoryBody;
import '../kernel/kernel_target.dart' show KernelTarget;
import '../kernel/types.dart' show Types;
import '../loader.dart';
import '../modifier.dart';
@ -1025,8 +1025,9 @@ abstract class ClassBuilderImpl extends DeclarationBuilderImpl
// When a parameter is covariant we have to check that we also
// override the same member in all parents.
for (Supertype supertype in interfaceMember.enclosingClass.supers) {
Member m = types.hierarchy.getInterfaceMemberKernel(
supertype.classNode, interfaceMember.name, isSetter);
Member m = types.hierarchy.getInterfaceMember(
supertype.classNode, interfaceMember.name,
setter: isSetter);
if (m != null) {
callback(declaredMember, m, isSetter);
}
@ -1160,7 +1161,7 @@ abstract class ClassBuilderImpl extends DeclarationBuilderImpl
interfaceSubstitution = Substitution.fromPairs(
enclosingClass.typeParameters,
types.hierarchy
.getKernelTypeArgumentsAsInstanceOf(thisType, enclosingClass));
.getTypeArgumentsAsInstanceOf(thisType, enclosingClass));
}
if (declaredFunction?.typeParameters?.length !=
@ -1204,7 +1205,8 @@ abstract class ClassBuilderImpl extends DeclarationBuilderImpl
}
DartType computedBound = substitution.substituteType(interfaceBound);
if (!types
.isSameTypeKernel(declaredBound, computedBound)
.performNullabilityAwareMutualSubtypesCheck(
declaredBound, computedBound)
.isSubtypeWhenUsingNullabilities()) {
reportInvalidOverride(
isInterfaceCheck,
@ -1243,7 +1245,7 @@ abstract class ClassBuilderImpl extends DeclarationBuilderImpl
declaredSubstitution = Substitution.fromPairs(
enclosingClass.typeParameters,
types.hierarchy
.getKernelTypeArgumentsAsInstanceOf(thisType, enclosingClass));
.getTypeArgumentsAsInstanceOf(thisType, enclosingClass));
}
return declaredSubstitution;
}
@ -1276,11 +1278,11 @@ abstract class ClassBuilderImpl extends DeclarationBuilderImpl
DartType subtype = inParameter ? interfaceType : declaredType;
DartType supertype = inParameter ? declaredType : interfaceType;
if (types.isSubtypeOfKernel(
if (types.isSubtypeOf(
subtype, supertype, SubtypeCheckMode.withNullabilities)) {
// No problem--the proper subtyping relation is satisfied.
} else if (isCovariant &&
types.isSubtypeOfKernel(
types.isSubtypeOf(
supertype, subtype, SubtypeCheckMode.withNullabilities)) {
// No problem--the overriding parameter is marked "covariant" and has
// a type which is a subtype of the parameter it overrides.
@ -1289,10 +1291,10 @@ abstract class ClassBuilderImpl extends DeclarationBuilderImpl
// been reported.
} else {
// Report an error.
bool isErrorInNnbdOptedOutMode = !types.isSubtypeOfKernel(
bool isErrorInNnbdOptedOutMode = !types.isSubtypeOf(
subtype, supertype, SubtypeCheckMode.ignoringNullabilities) &&
(!isCovariant ||
!types.isSubtypeOfKernel(
!types.isSubtypeOf(
supertype, subtype, SubtypeCheckMode.ignoringNullabilities));
if (isErrorInNnbdOptedOutMode || library.isNonNullableByDefault) {
String declaredMemberName = '${declaredMember.enclosingClass.name}'

View file

@ -6,7 +6,8 @@ library fasta.class_hierarchy_builder;
import 'package:kernel/ast.dart' hide MapEntry;
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClassHierarchyBase;
import 'package:kernel/core_types.dart' show CoreTypes;
@ -17,6 +18,7 @@ import 'package:kernel/src/future_or.dart';
import 'package:kernel/src/legacy_erasure.dart';
import 'package:kernel/src/nnbd_top_merge.dart';
import 'package:kernel/src/norm.dart';
import 'package:kernel/src/types.dart' show Types;
import '../../testing/id_testing_utils.dart' show typeToText;
@ -81,8 +83,6 @@ import 'forwarding_node.dart' show ForwardingNode;
import 'kernel_builder.dart' show ImplicitFieldType;
import 'types.dart' show Types;
const bool useConsolidated = true;
const DebugLogger debug =
@ -309,7 +309,7 @@ bool hasSameSignature(FunctionNode a, FunctionNode b) {
return aReturnType == bReturnType;
}
class ClassHierarchyBuilder {
class ClassHierarchyBuilder implements ClassHierarchyBase {
final Map<Class, ClassHierarchyNode> nodes = <Class, ClassHierarchyNode>{};
final Map<ClassBuilder, Map<Class, Substitution>> substitutions =
@ -473,8 +473,8 @@ class ClassHierarchyBuilder {
return null;
}
InterfaceType getKernelTypeAsInstanceOf(
InterfaceType type, Class superclass, Library clientLibrary) {
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass,
Library clientLibrary, CoreTypes coreTypes) {
Class kernelClass = type.classNode;
if (kernelClass == superclass) return type;
if (kernelClass == nullClass) {
@ -495,7 +495,7 @@ class ClassHierarchyBuilder {
.withNullability(type.nullability);
}
List<DartType> getKernelTypeArgumentsAsInstanceOf(
List<DartType> getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass) {
Class kernelClass = type.classNode;
if (kernelClass == superclass) return type.typeArguments;
@ -534,10 +534,10 @@ class ClassHierarchyBuilder {
ClassHierarchyNode node = nodes2[i];
if (node == null) continue;
if (nodes1.contains(node)) {
DartType candidate1 = getKernelTypeAsInstanceOf(
type1, node.classBuilder.cls, clientLibrary);
DartType candidate2 = getKernelTypeAsInstanceOf(
type2, node.classBuilder.cls, clientLibrary);
DartType candidate1 = getTypeAsInstanceOf(
type1, node.classBuilder.cls, clientLibrary, coreTypes);
DartType candidate2 = getTypeAsInstanceOf(
type2, node.classBuilder.cls, clientLibrary, coreTypes);
if (candidate1 == candidate2) {
common.add(node);
}
@ -553,8 +553,8 @@ class ClassHierarchyBuilder {
for (int i = 0; i < common.length - 1; i++) {
ClassHierarchyNode node = common[i];
if (node.maxInheritancePath != common[i + 1].maxInheritancePath) {
return getKernelTypeAsInstanceOf(
type1, node.classBuilder.cls, clientLibrary)
return getTypeAsInstanceOf(
type1, node.classBuilder.cls, clientLibrary, coreTypes)
.withNullability(
uniteNullabilities(type1.nullability, type2.nullability));
} else {
@ -567,9 +567,9 @@ class ClassHierarchyBuilder {
uniteNullabilities(type1.nullability, type2.nullability));
}
Member getInterfaceMemberKernel(Class cls, Name name, bool isSetter) {
Member getInterfaceMember(Class cls, Name name, {bool setter: false}) {
return getNodeFromClass(cls)
.getInterfaceMember(name, isSetter)
.getInterfaceMember(name, setter)
?.getMember(this);
}
@ -744,7 +744,8 @@ class ClassHierarchyNodeBuilder {
DartType overriddenBound = methodSubstitution
.substituteType(overriddenTypeParameters[i].bound);
if (!hierarchy.types
.isSameTypeKernel(declaredBound, overriddenBound)
.performNullabilityAwareMutualSubtypesCheck(
declaredBound, overriddenBound)
.isSubtypeWhenUsingNullabilities()) {
debug?.log("Giving up 3");
continue;
@ -2565,13 +2566,14 @@ class TypeBuilderConstraintGatherer extends TypeConstraintGatherer
@override
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass,
Library clientLibrary, CoreTypes coreTypes) {
return hierarchy.getKernelTypeAsInstanceOf(type, superclass, clientLibrary);
return hierarchy.getTypeAsInstanceOf(
type, superclass, clientLibrary, coreTypes);
}
@override
List<DartType> getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass) {
return hierarchy.getKernelTypeArgumentsAsInstanceOf(type, superclass);
return hierarchy.getTypeArgumentsAsInstanceOf(type, superclass);
}
@override
@ -2583,7 +2585,7 @@ class TypeBuilderConstraintGatherer extends TypeConstraintGatherer
@override
bool isSubtypeOf(
DartType subtype, DartType supertype, SubtypeCheckMode mode) {
return hierarchy.types.isSubtypeOfKernel(subtype, supertype, mode);
return hierarchy.types.isSubtypeOf(subtype, supertype, mode);
}
@override
@ -2945,8 +2947,11 @@ class InterfaceConflict extends DelayedMember {
unhandled("${member.runtimeType}", "$member", classBuilder.charOffset,
classBuilder.fileUri);
}
InterfaceType instance = hierarchy.getKernelTypeAsInstanceOf(
thisType, member.enclosingClass, classBuilder.library.library);
InterfaceType instance = hierarchy.getTypeAsInstanceOf(
thisType,
member.enclosingClass,
classBuilder.library.library,
hierarchy.coreTypes);
assert(
instance != null,
"No instance of $thisType as ${member.enclosingClass} found for "
@ -2957,10 +2962,10 @@ class InterfaceConflict extends DelayedMember {
bool isMoreSpecific(ClassHierarchyBuilder hierarchy, DartType a, DartType b) {
if (isSetter) {
return hierarchy.types
.isSubtypeOfKernel(b, a, SubtypeCheckMode.withNullabilities);
.isSubtypeOf(b, a, SubtypeCheckMode.withNullabilities);
} else {
return hierarchy.types
.isSubtypeOfKernel(a, b, SubtypeCheckMode.withNullabilities);
.isSubtypeOf(a, b, SubtypeCheckMode.withNullabilities);
}
}

View file

@ -537,11 +537,12 @@ class ForwardingNode {
Substitution _substitutionFor(
List<TypeParameter> stubTypeParameters, Member candidate, Class class_) {
Substitution substitution = Substitution.fromInterfaceType(
hierarchy.getKernelTypeAsInstanceOf(
hierarchy.getTypeAsInstanceOf(
hierarchy.coreTypes
.thisInterfaceType(class_, class_.enclosingLibrary.nonNullable),
candidate.enclosingClass,
class_.enclosingLibrary));
class_.enclosingLibrary,
hierarchy.coreTypes));
if (stubTypeParameters != null) {
// If the stub is generic ensure that type parameters are alpha renamed
// to the [stubTypeParameters].

View file

@ -94,7 +94,7 @@ class UnknownType extends DartType {
@override
String toStringInternal() {
return "";
return "<unknown-type>";
}
}

View file

@ -102,7 +102,9 @@ class TypeConstraint {
class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
with StandardBounds {
TypeSchemaEnvironment(CoreTypes coreTypes, ClassHierarchy hierarchy)
final ClassHierarchy hierarchy;
TypeSchemaEnvironment(CoreTypes coreTypes, this.hierarchy)
: super(coreTypes, hierarchy);
Class get functionClass => coreTypes.functionClass;
@ -364,7 +366,7 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
unwrappedSupertype =
(unwrappedSupertype as InterfaceType).typeArguments.single;
}
if (subtype == coreTypes.nullType && unwrappedSupertype is UnknownType) {
if (unwrappedSupertype is UnknownType) {
return const IsSubtypeOf.always();
}
return super.performNullabilityAwareSubtypeCheck(subtype, supertype);

View file

@ -248,7 +248,6 @@ abstract class SubtypeTest<T, E> {
isSubtype('Future<int*>*', 'FutureOr<num*>*');
isSubtype('Future<int*>*', 'FutureOr<Object*>*');
isSubtype('FutureOr<int*>*', 'FutureOr<int*>*');
isSubtype('FutureOr<int*>*', 'FutureOr<num*>*');
isSubtype('FutureOr<int*>*', 'Object*');
isSubtype('Null', 'FutureOr<num?>');
isSubtype('Null', 'FutureOr<num>?');
@ -265,6 +264,14 @@ abstract class SubtypeTest<T, E> {
isSubtype('FutureOr<X>', 'FutureOr<Future<X>>',
typeParameters: 'X extends Future<Future<X>>');
isSubtype('FutureOr<int*>*', 'FutureOr<num*>*');
isSubtype('FutureOr<A>', 'FutureOr<B>', typeParameters: 'B,A extends B');
isSubtype('X', 'FutureOr<int>',
typeParameters: 'X extends FutureOr<int*>*');
isSubtype('X*', 'FutureOr<int>',
typeParameters: 'X extends FutureOr<int*>*');
isSubtype('num?', 'FutureOr<FutureOr<FutureOr<num>>?>');
isSubtype('Future<num>?', 'FutureOr<FutureOr<FutureOr<num>>?>');
isSubtype('Future<Future<num>>?', 'FutureOr<FutureOr<FutureOr<num>>?>');
@ -282,6 +289,16 @@ abstract class SubtypeTest<T, E> {
isSubtype('FutureOr<num>?', 'FutureOr<num?>');
isObliviousSubtype('FutureOr<num?>', 'FutureOr<num>?');
isSubtype('List<FutureOr<List<dynamic>>>',
'List<FutureOr<List<FutureOr<dynamic>>>>');
isSubtype('List<FutureOr<List<FutureOr<dynamic>>>>',
'List<FutureOr<List<dynamic>>>');
isSubtype('X', 'FutureOr<List<X>>',
typeParameters: 'X extends FutureOr<List<X>>');
isNotSubtype('X', 'FutureOr<List<X>>',
typeParameters: 'X extends List<FutureOr<X>>');
isSubtype('dynamic', 'FutureOr<Object?>');
isSubtype('dynamic', 'FutureOr<Object>?');
isSubtype('void', 'FutureOr<Object?>');

View file

@ -108,8 +108,8 @@ void performFastaChecks(
List<SubtypeCheck> checks, ClassHierarchyBuilder hierarchy) {
for (int i = 0; i < checks.length; i++) {
SubtypeCheck check = checks[i];
bool isSubtype = hierarchy.types.isSubtypeOfKernel(
check.s, check.t, SubtypeCheckMode.ignoringNullabilities);
bool isSubtype = hierarchy.types
.isSubtypeOf(check.s, check.t, SubtypeCheckMode.ignoringNullabilities);
if (isSubtype != check.isSubtype) {
throw "Check failed: $check";
}

View file

@ -300,7 +300,6 @@ disjoint
dispatched
divided
dmitryas
dupdate
doc
docs
dom
@ -313,6 +312,7 @@ dq
dquote
dst
dummy
dupdate
e
easy
ecma
@ -343,6 +343,7 @@ eq
equation
erasure
es
established
estimate
eval
excludes

View file

@ -11,6 +11,6 @@ import self as self;
import "dart:core";
static method testDynamic() → invalid-type
return 0 as{TypeError} invalid-type;
return 0;
static method testVoid() → void {}
static method main() → dynamic {}

View file

@ -11,6 +11,6 @@ import self as self;
import "dart:core";
static method testDynamic() → invalid-type
return 0 as{TypeError} invalid-type;
return 0;
static method testVoid() → void {}
static method main() → dynamic {}

View file

@ -213,11 +213,11 @@ class C extends core::Object implements self::A<core::int*>, self::B<core::int*,
field core::int* field1;
field invalid-type field2;
field core::int* field3 = 0;
field invalid-type field4 = 0 as{TypeError} invalid-type;
field invalid-type field4 = 0;
field core::int* field5;
field invalid-type field6;
field core::int* field7 = 0;
field invalid-type field8 = 0 as{TypeError} invalid-type;
field invalid-type field8 = 0;
field dynamic field9;
generic-covariant-impl field core::int* field10;
generic-covariant-impl field invalid-type field11;
@ -236,11 +236,11 @@ class D<T extends core::Object* = dynamic> extends core::Object implements self:
field core::int* field1;
field invalid-type field2;
field core::int* field3 = 0;
field invalid-type field4 = 0 as{TypeError} invalid-type;
field invalid-type field4 = 0;
field core::int* field5;
field invalid-type field6;
field core::int* field7 = 0;
field invalid-type field8 = 0 as{TypeError} invalid-type;
field invalid-type field8 = 0;
field dynamic field9;
generic-covariant-impl field self::D::T* field10;
generic-covariant-impl field self::D::T* field11;

View file

@ -33,6 +33,6 @@ class Operators7 extends core::Object {
;
operator <<() → dynamic {}
operator >(dynamic a) → invalid-type
return true as{TypeError} invalid-type;
return true;
}
static method main() → dynamic {}

View file

@ -33,6 +33,6 @@ class Operators7 extends core::Object {
;
operator <<() → dynamic {}
operator >(dynamic a) → invalid-type
return true as{TypeError} invalid-type;
return true;
}
static method main() → dynamic {}

View file

@ -16,15 +16,6 @@ library;
// foo(List<A> a) {}
// ^
//
// pkg/front_end/testcases/general/issue39421.dart:16:15: Error: The parameter 'a' of the method 'C.foo' has type 'List<invalid-type>', which does not match the corresponding type, 'List<Null>', in the overridden method, 'B.foo'.
// - 'List' is from 'dart:core'.
// Change to a supertype of 'List<Null>', or, for a covariant parameter, a subtype.
// foo(List<A> a) {}
// ^
// pkg/front_end/testcases/general/issue39421.dart:12:3: Context: This is the overridden method ('foo').
// foo(List<Null> a) {}
// ^
//
import self as self;
import "dart:core" as core;

View file

@ -16,15 +16,6 @@ library;
// foo(List<A> a) {}
// ^
//
// pkg/front_end/testcases/general/issue39421.dart:16:15: Error: The parameter 'a' of the method 'C.foo' has type 'List<invalid-type>', which does not match the corresponding type, 'List<Null>', in the overridden method, 'B.foo'.
// - 'List' is from 'dart:core'.
// Change to a supertype of 'List<Null>', or, for a covariant parameter, a subtype.
// foo(List<A> a) {}
// ^
// pkg/front_end/testcases/general/issue39421.dart:12:3: Context: This is the overridden method ('foo').
// foo(List<Null> a) {}
// ^
//
// pkg/front_end/testcases/general/issue39421.dart:16:12: Error: Can't use 'A' because it is declared more than once.
// foo(List<A> a) {}
// ^

View file

@ -16,15 +16,6 @@ library;
// foo(List<A> a) {}
// ^
//
// pkg/front_end/testcases/general/issue39421.dart:16:15: Error: The parameter 'a' of the method 'C.foo' has type 'List<invalid-type>', which does not match the corresponding type, 'List<Null>', in the overridden method, 'B.foo'.
// - 'List' is from 'dart:core'.
// Change to a supertype of 'List<Null>', or, for a covariant parameter, a subtype.
// foo(List<A> a) {}
// ^
// pkg/front_end/testcases/general/issue39421.dart:12:3: Context: This is the overridden method ('foo').
// foo(List<Null> a) {}
// ^
//
// pkg/front_end/testcases/general/issue39421.dart:16:12: Error: Can't use 'A' because it is declared more than once.
// foo(List<A> a) {}
// ^

View file

@ -16,9 +16,9 @@ class C<T extends core::Object* = dynamic> extends core::Object {
: super core::Object::•()
;
method method() → invalid-type
return "Hello, World!" as{TypeError} invalid-type;
return "Hello, World!";
}
static method main() → dynamic {
core::String* s = new self::C::•<dynamic>().{self::C::method}() as{TypeError} core::String*;
core::String* s = new self::C::•<dynamic>().{self::C::method}();
core::print(s);
}

View file

@ -16,9 +16,9 @@ class C<T extends core::Object* = dynamic> extends core::Object {
: super core::Object::•()
;
method method() → invalid-type
return "Hello, World!" as{TypeError} invalid-type;
return "Hello, World!";
}
static method main() → dynamic {
core::String* s = new self::C::•<dynamic>().{self::C::method}() as{TypeError} core::String*;
core::String* s = new self::C::•<dynamic>().{self::C::method}();
core::print(s);
}

View file

@ -11,6 +11,6 @@ import self as self;
import "dart:core";
static method testDynamic() → invalid-type
return 0 as{TypeError} invalid-type;
return 0;
static method testVoid() → void {}
static method main() → dynamic {}

View file

@ -11,6 +11,6 @@ import self as self;
import "dart:core";
static method testDynamic() → invalid-type
return 0 as{TypeError} invalid-type;
return 0;
static method testVoid() → void {}
static method main() → dynamic {}

View file

@ -11,6 +11,6 @@ import self as self;
import "dart:core";
static method testDynamic() → invalid-type
return 0 as{TypeError} invalid-type;
return 0;
static method testVoid() → void {}
static method main() → dynamic {}

View file

@ -11,6 +11,6 @@ import self as self;
import "dart:core";
static method testDynamic() → invalid-type
return 0 as{TypeError} invalid-type;
return 0;
static method testVoid() → void {}
static method main() → dynamic {}

View file

@ -16,15 +16,6 @@ library;
// foo(List<A> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:15: Error: The parameter 'a' of the method 'C.foo' has type 'List<invalid-type>', which does not match the corresponding type, 'List<Null>', in the overridden method, 'B.foo'.
// - 'List' is from 'dart:core'.
// Change to a supertype of 'List<Null>', or, for a covariant parameter, a subtype.
// foo(List<A> a) {}
// ^
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:14:3: Context: This is the overridden method ('foo').
// foo(List<Null> a) {}
// ^
//
import self as self;
import "dart:core" as core;

View file

@ -16,15 +16,6 @@ library;
// foo(List<A> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:15: Error: The parameter 'a' of the method 'C.foo' has type 'List<invalid-type>', which does not match the corresponding type, 'List<Null>', in the overridden method, 'B.foo'.
// - 'List' is from 'dart:core'.
// Change to a supertype of 'List<Null>', or, for a covariant parameter, a subtype.
// foo(List<A> a) {}
// ^
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:14:3: Context: This is the overridden method ('foo').
// foo(List<Null> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:12: Error: Can't use 'A' because it is declared more than once.
// foo(List<A> a) {}
// ^

View file

@ -16,15 +16,6 @@ library;
// foo(List<A> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:15: Error: The parameter 'a' of the method 'C.foo' has type 'List<invalid-type>', which does not match the corresponding type, 'List<Null>', in the overridden method, 'B.foo'.
// - 'List' is from 'dart:core'.
// Change to a supertype of 'List<Null>', or, for a covariant parameter, a subtype.
// foo(List<A> a) {}
// ^
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:14:3: Context: This is the overridden method ('foo').
// foo(List<Null> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:12: Error: Can't use 'A' because it is declared more than once.
// foo(List<A> a) {}
// ^

View file

@ -16,15 +16,6 @@ library;
// foo(List<A> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:15: Error: The parameter 'a' of the method 'C.foo' has type 'List<invalid-type>', which does not match the corresponding type, 'List<Null>', in the overridden method, 'B.foo'.
// - 'List' is from 'dart:core'.
// Change to a supertype of 'List<Null>', or, for a covariant parameter, a subtype.
// foo(List<A> a) {}
// ^
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:14:3: Context: This is the overridden method ('foo').
// foo(List<Null> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:12: Error: Can't use 'A' because it is declared more than once.
// foo(List<A> a) {}
// ^

View file

@ -16,15 +16,6 @@ library;
// foo(List<A> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:15: Error: The parameter 'a' of the method 'C.foo' has type 'List<invalid-type>', which does not match the corresponding type, 'List<Null>', in the overridden method, 'B.foo'.
// - 'List' is from 'dart:core'.
// Change to a supertype of 'List<Null>', or, for a covariant parameter, a subtype.
// foo(List<A> a) {}
// ^
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:14:3: Context: This is the overridden method ('foo').
// foo(List<Null> a) {}
// ^
//
// pkg/front_end/testcases/general_nnbd_opt_out/issue39421.dart:18:12: Error: Can't use 'A' because it is declared more than once.
// foo(List<A> a) {}
// ^

View file

@ -16,9 +16,9 @@ class C<T extends core::Object* = dynamic> extends core::Object {
: super core::Object::•()
;
method method() → invalid-type
return "Hello, World!" as{TypeError} invalid-type;
return "Hello, World!";
}
static method main() → dynamic {
core::String* s = new self::C::•<dynamic>().{self::C::method}() as{TypeError} core::String*;
core::String* s = new self::C::•<dynamic>().{self::C::method}();
core::print(s);
}

View file

@ -16,9 +16,9 @@ class C<T extends core::Object* = dynamic> extends core::Object {
: super core::Object::•()
;
method method() → invalid-type
return "Hello, World!" as{TypeError} invalid-type;
return "Hello, World!";
}
static method main() → dynamic {
core::String* s = new self::C::•<dynamic>().{self::C::method}() as{TypeError} core::String*;
core::String* s = new self::C::•<dynamic>().{self::C::method}();
core::print(s);
}

View file

@ -16,9 +16,9 @@ class C<T extends core::Object* = dynamic> extends core::Object {
: super core::Object::•()
;
method method() → invalid-type
return "Hello, World!" as{TypeError} invalid-type;
return "Hello, World!";
}
static method main() → dynamic {
core::String* s = new self::C::•<dynamic>().{self::C::method}() as{TypeError} core::String*;
core::String* s = new self::C::•<dynamic>().{self::C::method}();
core::print(s);
}

View file

@ -16,9 +16,9 @@ class C<T extends core::Object* = dynamic> extends core::Object {
: super core::Object::•()
;
method method() → invalid-type
return "Hello, World!" as{TypeError} invalid-type;
return "Hello, World!";
}
static method main() → dynamic {
core::String* s = new self::C::•<dynamic>().{self::C::method}() as{TypeError} core::String*;
core::String* s = new self::C::•<dynamic>().{self::C::method}();
core::print(s);
}

View file

@ -9,6 +9,6 @@ library test;
//
import self as self;
static field invalid-type x = (() → () →* invalid-type => self::y) as{TypeError} invalid-type;
static field invalid-type x = () → () →* invalid-type => self::y;
static field () →* invalid-type y = () → invalid-type => self::x;
static method main() → dynamic {}

View file

@ -9,6 +9,6 @@ library test;
//
import self as self;
static field invalid-type x = (() → () →* invalid-type => self::y) as{TypeError} invalid-type;
static field invalid-type x = () → () →* invalid-type => self::y;
static field () →* invalid-type y = () → invalid-type => self::x;
static method main() → dynamic {}

View file

@ -9,6 +9,6 @@ library test;
//
import self as self;
static field invalid-type x = (() → () →* invalid-type => self::y) as{TypeError} invalid-type;
static field invalid-type x = () → () →* invalid-type => self::y;
static field () →* invalid-type y = () → invalid-type => self::x;
static method main() → dynamic {}

View file

@ -9,6 +9,6 @@ library test;
//
import self as self;
static field invalid-type x = (() → () →* invalid-type => self::y) as{TypeError} invalid-type;
static field invalid-type x = () → () →* invalid-type => self::y;
static field () →* invalid-type y = () → invalid-type => self::x;
static method main() → dynamic {}

View file

@ -39,8 +39,8 @@ class D extends self::C {
return x;
}
static method main() → dynamic {
core::int* y = (let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/inference/generic_methods_do_not_infer_invalid_override_of_generic_method.dart:18:74: Error: Expected 0 type arguments.
core::int* y = let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/inference/generic_methods_do_not_infer_invalid_override_of_generic_method.dart:18:74: Error: Expected 0 type arguments.
. /*error:WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD*/ /*@target=D::m*/ m<int>(
^" in new self::D::•().{self::D::m}<core::int*>(42)) as{TypeError} core::int*;
^" in new self::D::•().{self::D::m}<core::int*>(42);
core::print(y);
}

View file

@ -20,7 +20,7 @@ class A extends core::Object {
return 0;
}
static field self::A* a = new self::A::•();
static field invalid-type b = (() → () →* invalid-type => self::a.{self::A::f}<() →* invalid-type>(self::c)) as{TypeError} invalid-type;
static field invalid-type b = () → () →* invalid-type => self::a.{self::A::f}<() →* invalid-type>(self::c);
static field () →* invalid-type c = () → invalid-type => self::a.{self::A::f}<invalid-type>(self::b);
static field () →* () →* core::int* d = () → () →* core::int* => self::a.{self::A::f}<() →* core::int*>(self::e);
static field () →* core::int* e = () → core::int* => self::a.{self::A::g}(self::d);

View file

@ -20,7 +20,7 @@ class A extends core::Object {
return 0;
}
static field self::A* a = new self::A::•();
static field invalid-type b = (() → () →* invalid-type => self::a.{self::A::f}<() →* invalid-type>(self::c)) as{TypeError} invalid-type;
static field invalid-type b = () → () →* invalid-type => self::a.{self::A::f}<() →* invalid-type>(self::c);
static field () →* invalid-type c = () → invalid-type => self::a.{self::A::f}<invalid-type>(self::b);
static field () →* () →* core::int* d = () → () →* core::int* => self::a.{self::A::f}<() →* core::int*>(self::e);
static field () →* core::int* e = () → core::int* => self::a.{self::A::g}(self::d);

View file

@ -13,7 +13,7 @@ import "dart:core" as core;
static field core::int* intValue = 0;
static field core::num* numValue = 0;
static field core::double* doubleValue = 0.0;
static field invalid-type a = (() → core::num* => self::intValue.{core::num::+}(self::b as{TypeError,ForDynamic} core::num*)) as{TypeError} invalid-type;
static field invalid-type a = () → core::num* => self::intValue.{core::num::+}(self::b as{TypeError,ForDynamic} core::num*);
static field dynamic b = self::a.call();
static field () →* core::num* c = () → core::num* => self::numValue.{core::num::+}(self::d);
static field core::num* d = self::c.call();

View file

@ -13,7 +13,7 @@ import "dart:core" as core;
static field core::int* intValue = 0;
static field core::num* numValue = 0;
static field core::double* doubleValue = 0.0;
static field invalid-type a = (() → core::num* => self::intValue.{core::num::+}(self::b as{TypeError,ForDynamic} core::num*)) as{TypeError} invalid-type;
static field invalid-type a = () → core::num* => self::intValue.{core::num::+}(self::b as{TypeError,ForDynamic} core::num*);
static field dynamic b = self::a.call();
static field () →* core::num* c = () → core::num* => self::numValue.{core::num::+}(self::d);
static field core::num* d = self::c.call();

View file

@ -11,7 +11,7 @@ import self as self;
import "dart:core" as core;
class A extends core::Object {
field invalid-type x = (() → invalid-type => new self::B::•().{self::B::x}) as{TypeError} invalid-type;
field invalid-type x = () → invalid-type => new self::B::•().{self::B::x};
field () →* invalid-type y = () → invalid-type => new self::B::•().{self::B::x};
synthetic constructor •() → self::A*
: super core::Object::•()

View file

@ -11,7 +11,7 @@ import self as self;
import "dart:core" as core;
class A extends core::Object {
field invalid-type x = (() → invalid-type => new self::B::•().{self::B::x}) as{TypeError} invalid-type;
field invalid-type x = () → invalid-type => new self::B::•().{self::B::x};
field () →* invalid-type y = () → invalid-type => new self::B::•().{self::B::x};
synthetic constructor •() → self::A*
: super core::Object::•()

View file

@ -11,7 +11,7 @@ import self as self;
import "dart:core" as core;
class A extends core::Object {
field invalid-type b = (() → () →* invalid-type => self::x) as{TypeError} invalid-type;
field invalid-type b = () → () →* invalid-type => self::x;
field () →* () →* invalid-type c = () → () →* invalid-type => self::x;
synthetic constructor •() → self::A*
: super core::Object::•()

View file

@ -11,7 +11,7 @@ import self as self;
import "dart:core" as core;
class A extends core::Object {
field invalid-type b = (() → () →* invalid-type => self::x) as{TypeError} invalid-type;
field invalid-type b = () → () →* invalid-type => self::x;
field () →* () →* invalid-type c = () → () →* invalid-type => self::x;
synthetic constructor •() → self::A*
: super core::Object::•()

View file

@ -10,7 +10,7 @@ library test;
import self as self;
import "dart:core" as core;
static field invalid-type x = (() → () →* invalid-type => self::f() ?{() →* invalid-type} self::y : self::z) as{TypeError} invalid-type;
static field invalid-type x = () → () →* invalid-type => self::f() ?{() →* invalid-type} self::y : self::z;
static field () →* invalid-type y = () → invalid-type => self::x;
static field () →* invalid-type z = () → invalid-type => self::x;
static method f() → core::bool*

View file

@ -10,7 +10,7 @@ library test;
import self as self;
import "dart:core" as core;
static field invalid-type x = (() → () →* invalid-type => self::f() ?{() →* invalid-type} self::y : self::z) as{TypeError} invalid-type;
static field invalid-type x = () → () →* invalid-type => self::f() ?{() →* invalid-type} self::y : self::z;
static field () →* invalid-type y = () → invalid-type => self::x;
static field () →* invalid-type z = () → invalid-type => self::x;
static method f() → core::bool*

View file

@ -10,9 +10,9 @@ import self as self;
typedef F = invalid-type;
static method foo(() → void x) → void {
self::bar(x as{TypeError,ForNonNullableByDefault} invalid-type);
self::bar(x);
self::bar(null);
self::baz(x as{TypeError,ForNonNullableByDefault} invalid-type);
self::baz(x);
self::baz(null);
}
static method bar(invalid-type x) → void {}

View file

@ -10,9 +10,9 @@ import self as self;
typedef F = invalid-type;
static method foo(() → void x) → void {
self::bar(x as{TypeError,ForNonNullableByDefault} invalid-type);
self::bar(x);
self::bar(null);
self::baz(x as{TypeError,ForNonNullableByDefault} invalid-type);
self::baz(x);
self::baz(null);
}
static method bar(invalid-type x) → void {}

View file

@ -10,9 +10,9 @@ import self as self;
typedef F = invalid-type;
static method foo(() → void x) → void {
self::bar(x as{TypeError,ForNonNullableByDefault} invalid-type);
self::bar(x);
self::bar(null);
self::baz(x as{TypeError,ForNonNullableByDefault} invalid-type);
self::baz(x);
self::baz(null);
}
static method bar(invalid-type x) → void {}

View file

@ -10,9 +10,9 @@ import self as self;
typedef F = invalid-type;
static method foo(() → void x) → void {
self::bar(x as{TypeError,ForNonNullableByDefault} invalid-type);
self::bar(x);
self::bar(null);
self::baz(x as{TypeError,ForNonNullableByDefault} invalid-type);
self::baz(x);
self::baz(null);
}
static method bar(invalid-type x) → void {}

View file

@ -85,7 +85,7 @@ Try correcting the name to the name of an existing getter, or defining a getter
static method b(dynamic c) → invalid-type
return invalid-expression "pkg/front_end/testcases/rasta/issue_000044.dart:7:10: Error: Getter not found: 'd'.
a b(c) = d;
^" as{TypeError,ForDynamic} invalid-type;
^";
static method main() → dynamic {
self::C* c = null;
core::print(#C1);

View file

@ -85,7 +85,7 @@ Try correcting the name to the name of an existing getter, or defining a getter
static method b(dynamic c) → invalid-type
return invalid-expression "pkg/front_end/testcases/rasta/issue_000044.dart:7:10: Error: Getter not found: 'd'.
a b(c) = d;
^" as{TypeError,ForDynamic} invalid-type;
^";
static method main() → dynamic {
self::C* c = null;
core::print(#C1);

View file

@ -34,6 +34,6 @@ static field invalid-type T = invalid-expression "pkg/front_end/testcases/regres
- 'Type' is from 'dart:core'.
Try correcting the operator to an existing operator, or defining a '<' operator.
type T = Map<A, B>
^" as{TypeError,ForDynamic} invalid-type;
^";
static field invalid-type B;
static method main() → dynamic {}

View file

@ -34,6 +34,6 @@ static field invalid-type T = invalid-expression "pkg/front_end/testcases/regres
- 'Type' is from 'dart:core'.
Try correcting the operator to an existing operator, or defining a '<' operator.
type T = Map<A, B>
^" as{TypeError,ForDynamic} invalid-type;
^";
static field invalid-type B;
static method main() → dynamic {}

View file

@ -19,5 +19,5 @@ class Foo extends core::Object {
}
static method main() → dynamic {
self::Foo* instance = new self::Foo::•();
instance.{self::Foo::self} = instance as{TypeError} invalid-type;
instance.{self::Foo::self} = instance;
}

View file

@ -19,5 +19,5 @@ class Foo extends core::Object {
}
static method main() → dynamic {
self::Foo* instance = new self::Foo::•();
instance.{self::Foo::self} = instance as{TypeError} invalid-type;
instance.{self::Foo::self} = instance;
}

View file

@ -50,10 +50,6 @@ library;
// Future<List<>> f3() async {
// ^
//
// pkg/front_end/testcases/regress/issue_34850.dart:14:16: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Future<List<>> f3() async {
// ^^
//
import self as self;
import "dart:core" as core;

View file

@ -50,10 +50,6 @@ library;
// Future<List<>> f3() async {
// ^
//
// pkg/front_end/testcases/regress/issue_34850.dart:14:16: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Future<List<>> f3() async {
// ^^
//
import self as self;
import "dart:core" as core;
import "dart:async" as asy;

View file

@ -18,7 +18,7 @@ import 'package:kernel/kernel.dart'
import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
import 'package:kernel/type_environment.dart' show SubtypeTester;
import 'package:kernel/src/types.dart' show Types;
import 'package:vm/bytecode/gen_bytecode.dart'
show createFreshComponentWithBytecode, generateBytecode;
@ -78,7 +78,7 @@ compileEntryPoint(List<String> arguments) async {
stopwatch.stop();
elapsedTimes.add(stopwatch.elapsedMilliseconds.toDouble());
List<Object> typeChecks = SubtypeTester.typeChecks;
List<Object> typeChecks = Types.typeChecksForTesting;
if (typeChecks?.isNotEmpty ?? false) {
BenchMaker.writeTypeChecks("type_checks.json", typeChecks);
}

View file

@ -23,8 +23,36 @@ abstract class MixinInferrer {
void infer(ClassHierarchy hierarchy, Class classNode);
}
/// Core interface for answering queries needed to compute the subtyping
/// relation.
abstract class ClassHierarchyBase {
CoreTypes get coreTypes;
/// Returns the instantiation of [superclass] that is implemented by [type],
/// or `null` if [type] does not implement [superclass] at all.
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass,
Library clientLibrary, CoreTypes coreTypes);
/// Returns the type arguments of the instantiation of [superclass] that is
/// implemented by [type], or `null` if [type] does not implement [superclass]
/// at all.
List<DartType> getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass);
/// Returns the possibly abstract interface member of [class_] with the given
/// [name].
///
/// If [setter] is `false`, only fields, methods, and getters with that name
/// will be found. If [setter] is `true`, only non-final fields and setters
/// will be found.
///
/// If multiple members with that name are inherited and not overridden, the
/// member from the first declared supertype is returned.
Member getInterfaceMember(Class class_, Name name, {bool setter: false});
}
/// Interface for answering various subclassing queries.
abstract class ClassHierarchy {
abstract class ClassHierarchy implements ClassHierarchyBase {
factory ClassHierarchy(Component component, CoreTypes coreTypes,
{HandleAmbiguousSupertypes onAmbiguousSupertypes,
MixinInferrer mixinInferrer}) {
@ -76,17 +104,6 @@ abstract class ClassHierarchy {
/// or `null` if [class_] does not implement [superclass] at all.
Supertype getClassAsInstanceOf(Class class_, Class superclass);
/// Returns the instantiation of [superclass] that is implemented by [type],
/// or `null` if [type] does not implement [superclass] at all.
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass,
Library clientLibrary, CoreTypes coreTypes);
/// Returns the type arguments of the instantiation of [superclass] that is
/// implemented by [type], or `null` if [type] does not implement [superclass]
/// at all.
List<DartType> getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass);
/// Returns the instantiation of [superclass] that is implemented by [type],
/// or `null` if [type] does not implement [superclass]. [superclass] must
/// be a generic class.
@ -119,17 +136,6 @@ abstract class ClassHierarchy {
/// The returned list should not be modified.
List<Member> getDispatchTargets(Class class_, {bool setters: false});
/// Returns the possibly abstract interface member of [class_] with the given
/// [name].
///
/// If [setter] is `false`, only fields, methods, and getters with that name
/// will be found. If [setter] is `true`, only non-final fields and setters
/// will be found.
///
/// If multiple members with that name are inherited and not overridden, the
/// member from the first declared supertype is returned.
Member getInterfaceMember(Class class_, Name name, {bool setter: false});
/// Returns the list of members denoting the interface for [class_], which
/// may include abstract members.
///

View file

@ -6,17 +6,17 @@ library kernel.hierarchy_based_type_environment;
import '../ast.dart' show Class, DartType, InterfaceType, Library, Member, Name;
import '../class_hierarchy.dart' show ClassHierarchy;
import '../class_hierarchy.dart' show ClassHierarchyBase;
import '../core_types.dart' show CoreTypes;
import '../type_environment.dart' show TypeEnvironment;
class HierarchyBasedTypeEnvironment extends TypeEnvironment {
final ClassHierarchy hierarchy;
final ClassHierarchyBase hierarchy;
HierarchyBasedTypeEnvironment(CoreTypes coreTypes, this.hierarchy)
: super.fromSubclass(coreTypes);
: super.fromSubclass(coreTypes, hierarchy);
@override
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass,

View file

@ -2,9 +2,7 @@
// 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.
library fasta.types;
import 'package:kernel/ast.dart'
import '../ast.dart'
show
BottomType,
Class,
@ -23,37 +21,63 @@ import 'package:kernel/ast.dart'
Variance,
VoidType;
import 'package:kernel/core_types.dart';
import '../class_hierarchy.dart' show ClassHierarchyBase;
import 'package:kernel/type_algebra.dart'
import '../core_types.dart' show CoreTypes;
import '../type_algebra.dart'
show Substitution, combineNullabilitiesForSubstitution;
import 'package:kernel/type_environment.dart';
import '../type_environment.dart' show IsSubtypeOf, SubtypeCheckMode;
import 'package:kernel/src/future_or.dart';
import 'future_or.dart';
import 'kernel_builder.dart' show ClassHierarchyBuilder;
class Types implements SubtypeTester {
final ClassHierarchyBuilder hierarchy;
class Types {
final ClassHierarchyBase hierarchy;
Types(this.hierarchy);
/// Returns true if [s] is a subtype of [t].
bool isSubtypeOfKernel(DartType s, DartType t, SubtypeCheckMode mode) {
IsSubtypeOf result = performNullabilityAwareSubtypeCheck(s, t);
bool _isSubtypeFromMode(IsSubtypeOf isSubtypeOf, SubtypeCheckMode mode) {
switch (mode) {
case SubtypeCheckMode.withNullabilities:
return result.isSubtypeWhenUsingNullabilities();
return isSubtypeOf.isSubtypeWhenUsingNullabilities();
case SubtypeCheckMode.ignoringNullabilities:
return result.isSubtypeWhenIgnoringNullabilities();
return isSubtypeOf.isSubtypeWhenIgnoringNullabilities();
default:
throw new StateError("Unhandled subtype checking mode '$mode'");
}
}
@override
/// Returns true if [s] is a subtype of [t].
bool isSubtypeOf(DartType s, DartType t, SubtypeCheckMode mode) {
IsSubtypeOf result = performNullabilityAwareSubtypeCheck(s, t);
return _isSubtypeFromMode(result, mode);
}
/// Can be use to collect type checks. To use:
/// 1. Rename `performNullabilityAwareSubtypeCheck` to
/// `_performNullabilityAwareSubtypeCheck`.
/// 2. Rename `_collect_performNullabilityAwareSubtypeCheck` to
/// `performNullabilityAwareSubtypeCheck`.
/// 3. Comment out the call to `_performNullabilityAwareSubtypeCheck` below.
// ignore:unused_element
bool _collect_performNullabilityAwareSubtypeCheck(
DartType subtype, DartType supertype, SubtypeCheckMode mode) {
IsSubtypeOf result = const IsSubtypeOf.always();
//result = _performNullabilityAwareSubtypeCheck(subtype, supertype, mode);
bool booleanResult = _isSubtypeFromMode(result, mode);
typeChecksForTesting ??= <Object>[];
typeChecksForTesting.add([subtype, supertype, booleanResult]);
return booleanResult;
}
IsSubtypeOf performNullabilityAwareSubtypeCheck(DartType s, DartType t) {
// TODO(johnniwinther,dmitryas): Ensure complete handling of InvalidType in
// the subtype relation.
if (s is InvalidType || t is InvalidType) {
return const IsSubtypeOf.always();
}
if (s is BottomType) {
return const IsSubtypeOf.always(); // Rule 3.
}
@ -68,33 +92,25 @@ class Types implements SubtypeTester {
}
if (s is NeverType) {
return new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, hierarchy.futureOrClass);
}
// TODO(dmitryas): Remove InvalidType from subtype relation.
if (s is InvalidType) {
// InvalidType is a bottom type.
return const IsSubtypeOf.always();
}
if (t is InvalidType) {
return const IsSubtypeOf.never();
s, t, hierarchy.coreTypes.futureOrClass);
}
if (t is InterfaceType) {
Class cls = t.classNode;
if (cls == hierarchy.objectClass &&
!(s is InterfaceType && s.classNode == hierarchy.futureOrClass)) {
if (cls == hierarchy.coreTypes.objectClass &&
!(s is InterfaceType &&
s.classNode == hierarchy.coreTypes.futureOrClass)) {
return new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, hierarchy.futureOrClass);
s, t, hierarchy.coreTypes.futureOrClass);
}
if (cls == hierarchy.futureOrClass) {
if (cls == hierarchy.coreTypes.futureOrClass) {
const IsFutureOrSubtypeOf relation = const IsFutureOrSubtypeOf();
if (s is DynamicType) {
return relation.isDynamicRelated(s, t, this);
} else if (s is VoidType) {
return relation.isVoidRelated(s, t, this);
} else if (s is InterfaceType) {
return s.classNode == hierarchy.futureOrClass
return s.classNode == hierarchy.coreTypes.futureOrClass
? relation.isFutureOrRelated(s, t, this)
: relation.isInterfaceRelated(s, t, this);
} else if (s is FunctionType) {
@ -113,7 +129,7 @@ class Types implements SubtypeTester {
} else if (s is VoidType) {
return relation.isVoidRelated(s, t, this);
} else if (s is InterfaceType) {
return s.classNode == hierarchy.futureOrClass
return s.classNode == hierarchy.coreTypes.futureOrClass
? relation.isFutureOrRelated(s, t, this)
: relation.isInterfaceRelated(s, t, this);
} else if (s is FunctionType) {
@ -133,7 +149,7 @@ class Types implements SubtypeTester {
} else if (s is VoidType) {
return relation.isVoidRelated(s, t, this);
} else if (s is InterfaceType) {
return s.classNode == hierarchy.futureOrClass
return s.classNode == hierarchy.coreTypes.futureOrClass
? relation.isFutureOrRelated(s, t, this)
: relation.isInterfaceRelated(s, t, this);
} else if (s is FunctionType) {
@ -154,7 +170,7 @@ class Types implements SubtypeTester {
} else if (s is VoidType) {
return relation.isVoidRelated(s, t, this);
} else if (s is InterfaceType) {
return s.classNode == hierarchy.futureOrClass
return s.classNode == hierarchy.coreTypes.futureOrClass
? relation.isFutureOrRelated(s, t, this)
: relation.isInterfaceRelated(s, t, this);
} else if (s is FunctionType) {
@ -174,7 +190,7 @@ class Types implements SubtypeTester {
} else if (s is VoidType) {
return relation.isVoidRelated(s, t, this);
} else if (s is InterfaceType) {
return s.classNode == hierarchy.futureOrClass
return s.classNode == hierarchy.coreTypes.futureOrClass
? relation.isFutureOrRelated(s, t, this)
: relation.isInterfaceRelated(s, t, this);
} else if (s is FunctionType) {
@ -194,7 +210,7 @@ class Types implements SubtypeTester {
} else if (s is VoidType) {
return relation.isVoidRelated(s, t, this);
} else if (s is InterfaceType) {
return s.classNode == hierarchy.futureOrClass
return s.classNode == hierarchy.coreTypes.futureOrClass
? relation.isFutureOrRelated(s, t, this)
: relation.isInterfaceRelated(s, t, this);
} else if (s is FunctionType) {
@ -245,7 +261,8 @@ class Types implements SubtypeTester {
return const IsSubtypeOf.never();
}
} else if (variance == Variance.invariant) {
result = result.and(isSameTypeKernel(s[i], t[i]));
result =
result.and(performNullabilityAwareMutualSubtypesCheck(s[i], t[i]));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
return const IsSubtypeOf.never();
}
@ -259,73 +276,32 @@ class Types implements SubtypeTester {
return result;
}
IsSubtypeOf isSameTypeKernel(DartType s, DartType t) {
return performNullabilityAwareSubtypeCheck(s, t)
.andSubtypeCheckFor(t, s, this);
}
static List<Object> typeChecksForTesting;
@override
bool isSubtypeOf(
DartType subtype, DartType supertype, SubtypeCheckMode mode) {
return isSubtypeOfKernel(subtype, supertype, mode);
}
@override
Class get futureOrClass => hierarchy.coreTypes.futureOrClass;
@override
Class get functionClass => hierarchy.coreTypes.functionClass;
@override
InterfaceType get functionLegacyRawType =>
hierarchy.coreTypes.functionLegacyRawType;
@override
InterfaceType futureType(DartType type, Nullability nullability) {
return new InterfaceType(
hierarchy.coreTypes.futureClass, nullability, <DartType>[type]);
}
@override
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass,
Library clientLibrary, CoreTypes coreTypes) {
return hierarchy.getKernelTypeAsInstanceOf(type, superclass, clientLibrary);
return hierarchy.getTypeAsInstanceOf(
type, superclass, clientLibrary, coreTypes);
}
@override
List<DartType> getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass) {
return hierarchy.getKernelTypeArgumentsAsInstanceOf(type, superclass);
return hierarchy.getTypeArgumentsAsInstanceOf(type, superclass);
}
@override
bool isTop(DartType type) {
return type is DynamicType ||
type is VoidType ||
type == objectLegacyRawType ||
type == objectNullableRawType;
type == hierarchy.coreTypes.objectLegacyRawType ||
type == hierarchy.coreTypes.objectNullableRawType;
}
@override
InterfaceType get nullType => hierarchy.coreTypes.nullType;
@override
Class get objectClass => hierarchy.coreTypes.objectClass;
@override
InterfaceType get objectLegacyRawType {
return hierarchy.coreTypes.objectLegacyRawType;
}
@override
InterfaceType get objectNullableRawType {
return hierarchy.coreTypes.objectNullableRawType;
}
@override
IsSubtypeOf performNullabilityAwareMutualSubtypesCheck(
DartType type1, DartType type2) {
return isSameTypeKernel(type1, type2);
return performNullabilityAwareSubtypeCheck(type1, type2)
.andSubtypeCheckFor(type2, type1, this);
}
}
@ -356,14 +332,14 @@ class IsInterfaceSubtypeOf extends TypeRelation<InterfaceType> {
@override
IsSubtypeOf isInterfaceRelated(
InterfaceType s, InterfaceType t, Types types) {
if (s.classNode == types.hierarchy.nullClass) {
if (s.classNode == types.hierarchy.coreTypes.nullClass) {
// This is an optimization, to avoid instantiating unnecessary type
// arguments in getKernelTypeAsInstanceOf.
return new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, types.futureOrClass);
s, t, types.hierarchy.coreTypes.futureOrClass);
}
List<DartType> asSupertypeArguments =
types.hierarchy.getKernelTypeArgumentsAsInstanceOf(s, t.classNode);
types.hierarchy.getTypeArgumentsAsInstanceOf(s, t.classNode);
if (asSupertypeArguments == null) {
return const IsSubtypeOf.never();
}
@ -371,14 +347,15 @@ class IsInterfaceSubtypeOf extends TypeRelation<InterfaceType> {
.areTypeArgumentsOfSubtypeKernel(
asSupertypeArguments, t.typeArguments, t.classNode.typeParameters)
.and(new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, types.futureOrClass));
s, t, types.hierarchy.coreTypes.futureOrClass));
}
@override
IsSubtypeOf isTypeParameterRelated(
TypeParameterType s, InterfaceType t, Types types) {
return types.performNullabilityAwareSubtypeCheck(s.parameter.bound, t).and(
new IsSubtypeOf.basedSolelyOnNullabilities(s, t, types.futureOrClass));
new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, types.hierarchy.coreTypes.futureOrClass));
}
@override
@ -389,12 +366,12 @@ class IsInterfaceSubtypeOf extends TypeRelation<InterfaceType> {
return types
.performNullabilityAwareSubtypeCheck(arguments.single, t)
.andSubtypeCheckFor(
new InterfaceType(types.hierarchy.futureClass,
new InterfaceType(types.hierarchy.coreTypes.futureClass,
Nullability.nonNullable, arguments),
t,
types)
.and(new IsSubtypeOf.basedSolelyOnNullabilities(
futureOr, t, types.futureOrClass));
futureOr, t, types.hierarchy.coreTypes.futureOrClass));
}
@override
@ -411,8 +388,9 @@ class IsInterfaceSubtypeOf extends TypeRelation<InterfaceType> {
@override
IsSubtypeOf isFunctionRelated(FunctionType s, InterfaceType t, Types types) {
return t.classNode == types.hierarchy.functionClass
? new IsSubtypeOf.basedSolelyOnNullabilities(s, t, types.futureOrClass)
return t.classNode == types.hierarchy.coreTypes.functionClass
? new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, types.hierarchy.coreTypes.futureOrClass)
: const IsSubtypeOf.never(); // Rule 14.
}
@ -420,7 +398,8 @@ class IsInterfaceSubtypeOf extends TypeRelation<InterfaceType> {
IsSubtypeOf isTypedefRelated(TypedefType s, InterfaceType t, Types types) {
// Rule 5.
return types.performNullabilityAwareSubtypeCheck(s.unalias, t).and(
new IsSubtypeOf.basedSolelyOnNullabilities(s, t, types.futureOrClass));
new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, types.hierarchy.coreTypes.futureOrClass));
}
@override
@ -451,8 +430,8 @@ class IsFunctionSubtypeOf extends TypeRelation<FunctionType> {
for (int i = 0; i < sTypeVariables.length; i++) {
TypeParameter sTypeVariable = sTypeVariables[i];
TypeParameter tTypeVariable = tTypeVariables[i];
result = result.and(
types.isSameTypeKernel(sTypeVariable.bound, tTypeVariable.bound));
result = result.and(types.performNullabilityAwareMutualSubtypesCheck(
sTypeVariable.bound, tTypeVariable.bound));
typeVariableSubstitution.add(new TypeParameterType.forAlphaRenaming(
sTypeVariable, tTypeVariable));
}
@ -465,7 +444,7 @@ class IsFunctionSubtypeOf extends TypeRelation<FunctionType> {
for (int i = 0; i < sTypeVariables.length; i++) {
TypeParameter sTypeVariable = sTypeVariables[i];
TypeParameter tTypeVariable = tTypeVariables[i];
result = result.and(types.isSameTypeKernel(
result = result.and(types.performNullabilityAwareMutualSubtypesCheck(
substitution.substituteType(sTypeVariable.bound),
tTypeVariable.bound));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
@ -532,7 +511,7 @@ class IsFunctionSubtypeOf extends TypeRelation<FunctionType> {
@override
IsSubtypeOf isInterfaceRelated(InterfaceType s, FunctionType t, Types types) {
if (s.classNode == types.hierarchy.nullClass) {
if (s.classNode == types.hierarchy.coreTypes.nullClass) {
// Rule 4.
return new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, types.futureOrClass);
@ -627,7 +606,7 @@ class IsTypeParameterSubtypeOf extends TypeRelation<TypeParameterType> {
@override
IsSubtypeOf isInterfaceRelated(
InterfaceType s, TypeParameterType t, Types types) {
if (s.classNode == types.hierarchy.nullClass) {
if (s.classNode == types.hierarchy.coreTypes.nullClass) {
// Rule 4.
return new IsSubtypeOf.basedSolelyOnNullabilities(
s, t, types.futureOrClass);
@ -720,8 +699,8 @@ class IsFutureOrSubtypeOf extends TypeRelation<InterfaceType> {
InterfaceType s, InterfaceType futureOr, Types types) {
List<DartType> arguments = futureOr.typeArguments;
Nullability unitedNullability =
computeNullabilityOfFutureOr(futureOr, types.hierarchy.futureOrClass);
Nullability unitedNullability = computeNullabilityOfFutureOr(
futureOr, types.hierarchy.coreTypes.futureOrClass);
return types
// Rule 11.
@ -730,8 +709,8 @@ class IsFutureOrSubtypeOf extends TypeRelation<InterfaceType> {
// Rule 10.
.orSubtypeCheckFor(
s,
new InterfaceType(
types.hierarchy.futureClass, unitedNullability, arguments),
new InterfaceType(types.hierarchy.coreTypes.futureClass,
unitedNullability, arguments),
types);
}
@ -741,30 +720,50 @@ class IsFutureOrSubtypeOf extends TypeRelation<InterfaceType> {
// This follows from combining rules 7, 10, and 11.
DartType sArgument = sFutureOr.typeArguments.single;
DartType tArgument = tFutureOr.typeArguments.single;
DartType sFutureOfArgument = new InterfaceType(types.hierarchy.futureClass,
Nullability.nonNullable, sFutureOr.typeArguments);
DartType tFutureOfArgument = new InterfaceType(types.hierarchy.futureClass,
Nullability.nonNullable, tFutureOr.typeArguments);
Nullability sNullability =
computeNullabilityOfFutureOr(sFutureOr, types.hierarchy.futureOrClass);
Nullability tNullability =
computeNullabilityOfFutureOr(tFutureOr, types.hierarchy.futureOrClass);
DartType sFutureOfArgument = new InterfaceType(
types.hierarchy.coreTypes.futureClass,
Nullability.nonNullable,
sFutureOr.typeArguments);
DartType tFutureOfArgument = new InterfaceType(
types.hierarchy.coreTypes.futureClass,
Nullability.nonNullable,
tFutureOr.typeArguments);
Nullability sNullability = computeNullabilityOfFutureOr(
sFutureOr, types.hierarchy.coreTypes.futureOrClass);
Nullability tNullability = computeNullabilityOfFutureOr(
tFutureOr, types.hierarchy.coreTypes.futureOrClass);
// The following is an optimized is-subtype-of test for the case where
// both LHS and RHS are FutureOrs. It's based on the following:
// FutureOr<X> <: FutureOr<Y> iff X <: Y OR (X <: Future<Y> AND
// Future<X> <: Y).
//
// The correctness of that can be shown as follows:
// 1. FutureOr<X> <: Y iff X <: Y AND Future<X> <: Y
// 2. X <: FutureOr<Y> iff X <: Y OR X <: Future<Y>
// 3. 1,2 => FutureOr<X> <: FutureOr<Y> iff
// 1. FutureOr<X> <: FutureOr<Y> iff
//
// X <: FutureOr<Y> AND Future<X> <: FutureOr<Y>
//
// 2a. X <: FutureOr<Y> iff
//
// X <: Y OR X <: Future<Y>
//
// 2b. Future<X> <: FutureOr<Y> iff
//
// Future<X> <: Y OR Future<X> <: Future<Y>
//
// 3. 1,2a,2b => FutureOr<X> <: FutureOr<Y> iff
//
// (X <: Y OR X <: Future<Y>) AND
// (Future<X> <: Y OR Future<X> <: Future<Y>)
//
// 4. X <: Y iff Future<X> <: Future<Y>
//
// 5. 3,4 => FutureOr<X> <: FutureOr<Y> iff
//
// (X <: Y OR X <: Future<Y>) AND
// (X <: Y OR Future<X> <: Y) iff
//
// X <: Y OR (X <: Future<Y> AND Future<X> <: Y)
//
return types
.performNullabilityAwareSubtypeCheck(sArgument, tArgument)
.or(types
@ -784,7 +783,7 @@ class IsFutureOrSubtypeOf extends TypeRelation<InterfaceType> {
return types.performNullabilityAwareSubtypeCheck(
s,
argument.withNullability(computeNullabilityOfFutureOr(
futureOr, types.hierarchy.futureOrClass)));
futureOr, types.hierarchy.coreTypes.futureOrClass)));
}
@override
@ -794,15 +793,15 @@ class IsFutureOrSubtypeOf extends TypeRelation<InterfaceType> {
return types.performNullabilityAwareSubtypeCheck(
s,
argument.withNullability(computeNullabilityOfFutureOr(
futureOr, types.hierarchy.futureOrClass)));
futureOr, types.hierarchy.coreTypes.futureOrClass)));
}
@override
IsSubtypeOf isTypeParameterRelated(
TypeParameterType s, InterfaceType futureOr, Types types) {
List<DartType> arguments = futureOr.typeArguments;
Nullability unitedNullability =
computeNullabilityOfFutureOr(futureOr, types.hierarchy.futureOrClass);
Nullability unitedNullability = computeNullabilityOfFutureOr(
futureOr, types.hierarchy.coreTypes.futureOrClass);
// TODO(dmitryas): Revise the original optimization.
return types
// Rule 11.
@ -818,8 +817,8 @@ class IsFutureOrSubtypeOf extends TypeRelation<InterfaceType> {
// Rule 10.
.orSubtypeCheckFor(
s,
new InterfaceType(
types.hierarchy.futureClass, unitedNullability, arguments),
new InterfaceType(types.hierarchy.coreTypes.futureClass,
unitedNullability, arguments),
types);
}
@ -831,7 +830,7 @@ class IsFutureOrSubtypeOf extends TypeRelation<InterfaceType> {
return types.performNullabilityAwareSubtypeCheck(
s,
argument.withNullability(computeNullabilityOfFutureOr(
futureOr, types.hierarchy.futureOrClass)));
futureOr, types.hierarchy.coreTypes.futureOrClass)));
}
@override
@ -873,7 +872,7 @@ class IsIntersectionSubtypeOf extends TypeRelation<TypeParameterType> {
@override
IsSubtypeOf isInterfaceRelated(
InterfaceType s, TypeParameterType intersection, Types types) {
if (s.classNode == types.hierarchy.nullClass) {
if (s.classNode == types.hierarchy.coreTypes.nullClass) {
// Rule 4.
return new IsSubtypeOf.basedSolelyOnNullabilities(
s, intersection, types.futureOrClass);
@ -925,7 +924,7 @@ class IsNeverTypeSubtypeOf implements TypeRelation<NeverType> {
}
IsSubtypeOf isInterfaceRelated(InterfaceType s, NeverType t, Types types) {
if (s.classNode == types.hierarchy.nullClass) {
if (s.classNode == types.hierarchy.coreTypes.nullClass) {
if (t.nullability == Nullability.nullable ||
t.nullability == Nullability.legacy) {
return const IsSubtypeOf.always();

View file

@ -6,22 +6,23 @@ library kernel.type_environment;
import 'ast.dart';
import 'class_hierarchy.dart';
import 'core_types.dart';
import 'type_algebra.dart';
import 'src/future_or.dart';
import 'src/hierarchy_based_type_environment.dart'
show HierarchyBasedTypeEnvironment;
import 'src/types.dart';
typedef void ErrorHandler(TreeNode node, String message);
abstract class TypeEnvironment extends SubtypeTester {
abstract class TypeEnvironment extends Types {
final CoreTypes coreTypes;
/// An error handler for use in debugging, or `null` if type errors should not
/// be tolerated. See [typeError].
ErrorHandler errorHandler;
TypeEnvironment.fromSubclass(this.coreTypes);
TypeEnvironment.fromSubclass(this.coreTypes, ClassHierarchyBase base)
: super(base);
factory TypeEnvironment(CoreTypes coreTypes, ClassHierarchy hierarchy) {
return new HierarchyBasedTypeEnvironment(coreTypes, hierarchy);
@ -309,11 +310,12 @@ class IsSubtypeOf {
unwrappedSupertype =
(unwrappedSupertype as InterfaceType).typeArguments.single;
}
if (unwrappedSubtype is TypeParameterType &&
unwrappedSubtype.promotedBound == null &&
unwrappedSupertype is TypeParameterType &&
unwrappedSupertype.promotedBound == null &&
unwrappedSubtype.parameter == unwrappedSupertype.parameter) {
Nullability unwrappedSubtypeNullability =
computeNullability(unwrappedSubtype, futureOrClass);
Nullability unwrappedSupertypeNullability =
computeNullability(unwrappedSupertype, futureOrClass);
if (unwrappedSubtypeNullability == unwrappedSupertypeNullability) {
// The relationship between the types must be established elsewhere.
return const IsSubtypeOf.always();
}
}
@ -343,7 +345,7 @@ class IsSubtypeOf {
/// [IsSubtypeOf.never] because the right-hand side will not change the
/// overall result anyway.
IsSubtypeOf andSubtypeCheckFor(
DartType subtype, DartType supertype, SubtypeTester tester) {
DartType subtype, DartType supertype, Types tester) {
if (_value == _valueNever) return this;
return this
.and(tester.performNullabilityAwareSubtypeCheck(subtype, supertype));
@ -370,7 +372,7 @@ class IsSubtypeOf {
/// as [IsSubtypeOf.always] because the right-hand side will not change the
/// overall result anyway.
IsSubtypeOf orSubtypeCheckFor(
DartType subtype, DartType supertype, SubtypeTester tester) {
DartType subtype, DartType supertype, Types tester) {
if (_value == _valueAlways) return this;
return this
.or(tester.performNullabilityAwareSubtypeCheck(subtype, supertype));
@ -402,340 +404,6 @@ enum SubtypeCheckMode {
ignoringNullabilities,
}
/// The part of [TypeEnvironment] that deals with subtype tests.
///
/// This lives in a separate class so it can be tested independently of the SDK.
abstract class SubtypeTester {
InterfaceType get objectLegacyRawType;
InterfaceType get objectNullableRawType;
InterfaceType get nullType;
InterfaceType get functionLegacyRawType;
Class get objectClass;
Class get functionClass;
Class get futureOrClass;
InterfaceType futureType(DartType type, Nullability nullability);
static List<Object> typeChecks;
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass,
Library clientLibrary, CoreTypes coreTypes);
List<DartType> getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass);
/// Determines if the given type is at the top of the type hierarchy. May be
/// overridden in subclasses.
bool isTop(DartType type) {
return type is DynamicType ||
type is VoidType ||
type == objectLegacyRawType ||
type == objectNullableRawType;
}
/// Can be use to collect type checks. To use:
/// 1. Rename `isSubtypeOf` to `_isSubtypeOf`.
/// 2. Rename `_collect_isSubtypeOf` to `isSubtypeOf`.
/// 3. Comment out the call to `_isSubtypeOf` below.
// ignore:unused_element
bool _collect_isSubtypeOf(
DartType subtype, DartType supertype, SubtypeCheckMode mode) {
bool result = true;
//result = _isSubtypeOf(subtype, supertype, mode);
typeChecks ??= <Object>[];
typeChecks.add([subtype, supertype, result]);
return result;
}
/// Returns true if [subtype] is a subtype of [supertype].
bool isSubtypeOf(
DartType subtype, DartType supertype, SubtypeCheckMode mode) {
IsSubtypeOf result =
performNullabilityAwareSubtypeCheck(subtype, supertype);
switch (mode) {
case SubtypeCheckMode.ignoringNullabilities:
return result.isSubtypeWhenIgnoringNullabilities();
case SubtypeCheckMode.withNullabilities:
return result.isSubtypeWhenUsingNullabilities();
default:
throw new StateError("Unhandled subtype checking mode '$mode'");
}
}
/// Performs a nullability-aware subtype check.
///
/// The outcome is described in the comments to [IsSubtypeOf].
IsSubtypeOf performNullabilityAwareSubtypeCheck(
DartType subtype, DartType supertype) {
subtype = subtype.unalias;
supertype = supertype.unalias;
if (identical(subtype, supertype)) return const IsSubtypeOf.always();
if (subtype is BottomType) return const IsSubtypeOf.always();
if (subtype is NeverType) {
return supertype is BottomType
? const IsSubtypeOf.never()
: new IsSubtypeOf.basedSolelyOnNullabilities(
subtype, supertype, futureOrClass);
}
if (subtype == nullType) {
// TODO(dmitryas): Remove InvalidType from subtype relation.
if (supertype is InvalidType) {
// The return value is supposed to keep the backward compatibility.
return const IsSubtypeOf.always();
}
Nullability supertypeNullability =
computeNullability(supertype, futureOrClass);
if (supertypeNullability == Nullability.nullable ||
supertypeNullability == Nullability.legacy) {
return const IsSubtypeOf.always();
}
// See rule 4 of the subtype rules from the Dart Language Specification.
return supertype is BottomType || supertype is NeverType
? const IsSubtypeOf.never()
: const IsSubtypeOf.onlyIfIgnoringNullabilities();
}
if (isTop(supertype)) return const IsSubtypeOf.always();
// Handle FutureOr<T> union type.
if (subtype is InterfaceType &&
identical(subtype.classNode, futureOrClass)) {
var subtypeArg = subtype.typeArguments[0];
if (supertype is InterfaceType &&
identical(supertype.classNode, futureOrClass)) {
DartType supertypeArg = supertype.typeArguments[0];
Nullability subtypeNullability =
computeNullabilityOfFutureOr(subtype, futureOrClass);
Nullability supertypeNullability =
computeNullabilityOfFutureOr(supertype, futureOrClass);
// The following is an optimized is-subtype-of test for the case where
// both LHS and RHS are FutureOrs. It's based on the following:
// FutureOr<X> <: FutureOr<Y> iff X <: Y OR (X <: Future<Y> AND
// Future<X> <: Y).
//
// The correctness of that can be shown as follows:
// 1. FutureOr<X> <: Y iff X <: Y AND Future<X> <: Y
// 2. X <: FutureOr<Y> iff X <: Y OR X <: Future<Y>
// 3. 1,2 => FutureOr<X> <: FutureOr<Y> iff
// (X <: Y OR X <: Future<Y>) AND
// (Future<X> <: Y OR Future<X> <: Future<Y>)
// 4. X <: Y iff Future<X> <: Future<Y>
// 5. 3,4 => FutureOr<X> <: FutureOr<Y> iff
// (X <: Y OR X <: Future<Y>) AND
// (X <: Y OR Future<X> <: Y) iff
// X <: Y OR (X <: Future<Y> AND Future<X> <: Y)
return performNullabilityAwareSubtypeCheck(subtypeArg, supertypeArg)
.or(performNullabilityAwareSubtypeCheck(subtypeArg,
futureType(supertypeArg, Nullability.nonNullable))
.andSubtypeCheckFor(
futureType(subtypeArg, Nullability.nonNullable),
supertypeArg,
this))
.and(new IsSubtypeOf.basedSolelyOnNullabilities(
subtype.withNullability(subtypeNullability),
supertype.withNullability(supertypeNullability),
futureOrClass));
}
// given t1 is Future<A> | A, then:
// (Future<A> | A) <: t2 iff Future<A> <: t2 and A <: t2.
return performNullabilityAwareSubtypeCheck(subtypeArg, supertype)
.andSubtypeCheckFor(
futureType(subtypeArg, Nullability.nonNullable), supertype, this)
.and(new IsSubtypeOf.basedSolelyOnNullabilities(
subtype, supertype, futureOrClass));
}
if (supertype is InterfaceType && supertype.classNode == objectClass) {
assert(supertype.nullability == Nullability.nonNullable);
return new IsSubtypeOf.basedSolelyOnNullabilities(
subtype, supertype, futureOrClass);
}
if (supertype is InterfaceType &&
identical(supertype.classNode, futureOrClass)) {
// given t2 is Future<A> | A, then:
// t1 <: (Future<A> | A) iff t1 <: Future<A> or t1 <: A
Nullability unitedNullability =
computeNullabilityOfFutureOr(supertype, futureOrClass);
DartType supertypeArg = supertype.typeArguments[0];
DartType supertypeFuture = futureType(supertypeArg, unitedNullability);
return performNullabilityAwareSubtypeCheck(subtype, supertypeFuture)
.orSubtypeCheckFor(
subtype, supertypeArg.withNullability(unitedNullability), this);
}
if (subtype is InterfaceType && supertype is InterfaceType) {
Class supertypeClass = supertype.classNode;
List<DartType> upcastTypeArguments =
getTypeArgumentsAsInstanceOf(subtype, supertypeClass);
if (upcastTypeArguments == null) return const IsSubtypeOf.never();
IsSubtypeOf result = const IsSubtypeOf.always();
for (int i = 0; i < upcastTypeArguments.length; ++i) {
// Termination: the 'supertype' parameter decreases in size.
int variance = supertypeClass.typeParameters[i].variance;
DartType leftType = upcastTypeArguments[i];
DartType rightType = supertype.typeArguments[i];
if (variance == Variance.contravariant) {
result = result
.and(performNullabilityAwareSubtypeCheck(rightType, leftType));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
return const IsSubtypeOf.never();
}
} else if (variance == Variance.invariant) {
result = result.and(
performNullabilityAwareMutualSubtypesCheck(leftType, rightType));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
return const IsSubtypeOf.never();
}
} else {
result = result
.and(performNullabilityAwareSubtypeCheck(leftType, rightType));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
return const IsSubtypeOf.never();
}
}
}
return result.and(new IsSubtypeOf.basedSolelyOnNullabilities(
subtype, supertype, futureOrClass));
}
if (subtype is TypeParameterType) {
if (supertype is TypeParameterType) {
IsSubtypeOf result = const IsSubtypeOf.always();
if (subtype.parameter == supertype.parameter) {
if (supertype.promotedBound != null) {
return performNullabilityAwareSubtypeCheck(
subtype,
new TypeParameterType(supertype.parameter,
supertype.typeParameterTypeNullability))
.andSubtypeCheckFor(subtype, supertype.bound, this);
} else {
// Promoted bound should always be a subtype of the declared bound.
// TODO(dmitryas): Use the following assertion when type promotion
// is updated.
// assert(subtype.promotedBound == null ||
// performNullabilityAwareSubtypeCheck(
// subtype.bound, supertype.bound)
// .isSubtypeWhenUsingNullabilities());
assert(subtype.promotedBound == null ||
performNullabilityAwareSubtypeCheck(
subtype.bound, supertype.bound)
.isSubtypeWhenIgnoringNullabilities());
result = const IsSubtypeOf.always();
}
} else {
result =
performNullabilityAwareSubtypeCheck(subtype.bound, supertype);
}
if (subtype.nullability == Nullability.undetermined &&
supertype.nullability == Nullability.undetermined) {
// The two nullabilities are undetermined, but are connected via
// additional constraint, namely that they will be equal at run time.
return result;
}
return result.and(new IsSubtypeOf.basedSolelyOnNullabilities(
subtype, supertype, futureOrClass));
}
// Termination: if there are no cyclically bound type parameters, this
// recursive call can only occur a finite number of times, before reaching
// a shrinking recursive call (or terminating).
return performNullabilityAwareSubtypeCheck(subtype.bound, supertype).and(
new IsSubtypeOf.basedSolelyOnNullabilities(
subtype, supertype, futureOrClass));
}
if (subtype is FunctionType) {
if (supertype is InterfaceType && supertype.classNode == functionClass) {
return new IsSubtypeOf.basedSolelyOnNullabilities(
subtype, supertype, futureOrClass);
}
if (supertype is FunctionType) {
return _performNullabilityAwareFunctionSubtypeCheck(subtype, supertype);
}
}
return const IsSubtypeOf.never();
}
IsSubtypeOf performNullabilityAwareMutualSubtypesCheck(
DartType type1, DartType type2) {
// TODO(dmitryas): Replace it with one recursive descent instead of two.
return performNullabilityAwareSubtypeCheck(type1, type2)
.andSubtypeCheckFor(type2, type1, this);
}
IsSubtypeOf _performNullabilityAwareFunctionSubtypeCheck(
FunctionType subtype, FunctionType supertype) {
if (subtype.requiredParameterCount > supertype.requiredParameterCount) {
return const IsSubtypeOf.never();
}
if (subtype.positionalParameters.length <
supertype.positionalParameters.length) {
return const IsSubtypeOf.never();
}
if (subtype.typeParameters.length != supertype.typeParameters.length) {
return const IsSubtypeOf.never();
}
IsSubtypeOf result = const IsSubtypeOf.always();
if (subtype.typeParameters.isNotEmpty) {
var substitution = <TypeParameter, DartType>{};
for (int i = 0; i < subtype.typeParameters.length; ++i) {
var subParameter = subtype.typeParameters[i];
var superParameter = supertype.typeParameters[i];
substitution[subParameter] = new TypeParameterType.forAlphaRenaming(
subParameter, superParameter);
}
for (int i = 0; i < subtype.typeParameters.length; ++i) {
var subParameter = subtype.typeParameters[i];
var superParameter = supertype.typeParameters[i];
var subBound = substitute(subParameter.bound, substitution);
// Termination: if there are no cyclically bound type parameters, this
// recursive call can only occur a finite number of times before
// reaching a shrinking recursive call (or terminating).
result = result.and(performNullabilityAwareMutualSubtypesCheck(
superParameter.bound, subBound));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
return const IsSubtypeOf.never();
}
}
subtype = substitute(subtype.withoutTypeParameters, substitution);
}
result = result.and(performNullabilityAwareSubtypeCheck(
subtype.returnType, supertype.returnType));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
return const IsSubtypeOf.never();
}
for (int i = 0; i < supertype.positionalParameters.length; ++i) {
var supertypeParameter = supertype.positionalParameters[i];
var subtypeParameter = subtype.positionalParameters[i];
// Termination: Both types shrink in size.
result = result.and(performNullabilityAwareSubtypeCheck(
supertypeParameter, subtypeParameter));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
return const IsSubtypeOf.never();
}
}
int subtypeNameIndex = 0;
for (NamedType supertypeParameter in supertype.namedParameters) {
while (subtypeNameIndex < subtype.namedParameters.length &&
subtype.namedParameters[subtypeNameIndex].name !=
supertypeParameter.name) {
++subtypeNameIndex;
}
if (subtypeNameIndex == subtype.namedParameters.length) {
return const IsSubtypeOf.never();
}
NamedType subtypeParameter = subtype.namedParameters[subtypeNameIndex];
// Termination: Both types shrink in size.
result = result.and(performNullabilityAwareSubtypeCheck(
supertypeParameter.type, subtypeParameter.type));
if (!result.isSubtypeWhenIgnoringNullabilities()) {
return const IsSubtypeOf.never();
}
}
return result.and(new IsSubtypeOf.basedSolelyOnNullabilities(
subtype, supertype, futureOrClass));
}
}
/// Context object needed for computing `Expression.getStaticType`.
///
/// The [StaticTypeContext] provides access to the [TypeEnvironment] and the

View file

@ -27,18 +27,14 @@ Future<int, String>
// [analyzer] STATIC_TYPE_WARNING.WRONG_NUMBER_OF_TYPE_ARGUMENTS
// [cfe] Expected 1 type arguments.
foo4() async {
// [error line 29, column 1]
// [cfe] Functions marked 'async' must have a return type assignable to 'Future'.
return "String";
// ^
// [cfe] A value of type 'String' can't be assigned to a variable of type 'FutureOr<invalid-type>'.
}
int
// [error line 37, column 1, length 3]
// [error line 33, column 1, length 3]
// [analyzer] STATIC_TYPE_WARNING.ILLEGAL_ASYNC_RETURN_TYPE
foo5() async {
// [error line 40, column 1]
// [error line 36, column 1]
// [cfe] Functions marked 'async' must have a return type assignable to 'Future'.
return 3;
// ^

View file

@ -14,7 +14,5 @@ typedef void G(List<F> l);
main() {
F foo(G g) => g;
// ^
// [cfe] A value of type 'void Function(List<invalid-type>)' can't be assigned to a variable of type 'void Function(List<void Function(List<invalid-type>)>)'.
foo(null);
}

View file

@ -25,8 +25,6 @@ class Foo<T> implements I<T> {
// [cfe] Type variables can't be used in static members.
// ^^^^^^^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.INVALID_ASSIGNMENT
// ^
// [cfe] A value of type 'Foo<String>' can't be assigned to a variable of type 'Foo<invalid-type>'.
return new Foo<String>();
// ^^^^^^^^^^^^^^^^^
// [analyzer] STATIC_TYPE_WARNING.RETURN_OF_INVALID_TYPE