| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| // 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. |
| |
| import '../ast.dart'; |
| |
| import '../class_hierarchy.dart' show ClassHierarchyBase; |
| |
| import '../core_types.dart' show CoreTypes; |
| |
| import '../type_algebra.dart' |
| show Substitution, combineNullabilitiesForSubstitution; |
| |
| import '../type_environment.dart' show IsSubtypeOf, SubtypeCheckMode; |
| |
| import '../src/standard_bounds.dart'; |
| |
| class Types with StandardBounds { |
| @override |
| final ClassHierarchyBase hierarchy; |
| |
| Types(this.hierarchy); |
| |
| @override |
| CoreTypes get coreTypes => hierarchy.coreTypes; |
| |
| @override |
| bool areMutualSubtypes(DartType s, DartType t, SubtypeCheckMode mode) { |
| IsSubtypeOf result = performNullabilityAwareMutualSubtypesCheck(s, t); |
| switch (mode) { |
| case SubtypeCheckMode.ignoringNullabilities: |
| return result.isSubtypeWhenIgnoringNullabilities(); |
| case SubtypeCheckMode.withNullabilities: |
| return result.isSubtypeWhenUsingNullabilities(); |
| } |
| } |
| |
| bool _isSubtypeFromMode(IsSubtypeOf isSubtypeOf, SubtypeCheckMode mode) { |
| switch (mode) { |
| case SubtypeCheckMode.withNullabilities: |
| return isSubtypeOf.isSubtypeWhenUsingNullabilities(); |
| case SubtypeCheckMode.ignoringNullabilities: |
| return isSubtypeOf.isSubtypeWhenIgnoringNullabilities(); |
| default: |
| throw new StateError("Unhandled subtype checking mode '$mode'"); |
| } |
| } |
| |
| /// Returns true if [s] is a subtype of [t]. |
| @override |
| 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>[]) |
| .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 (t is DynamicType) { |
| return const IsSubtypeOf.always(); // Rule 2. |
| } |
| if (t is VoidType) { |
| return const IsSubtypeOf.always(); // Rule 2. |
| } |
| if (s is NeverType) { |
| return new IsSubtypeOf.basedSolelyOnNullabilities(s, t); |
| } |
| if (s is NullType) { |
| // Rule 4. |
| return new IsSubtypeOf.basedSolelyOnNullabilities(s, t); |
| } |
| |
| if (t is InterfaceType) { |
| Class cls = t.classNode; |
| if (cls == hierarchy.coreTypes.objectClass && s is! FutureOrType) { |
| return new IsSubtypeOf.basedSolelyOnNullabilities(s, t); |
| } |
| const IsInterfaceSubtypeOf relation = const IsInterfaceSubtypeOf(); |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } else if (t is FunctionType) { |
| const IsFunctionSubtypeOf relation = const IsFunctionSubtypeOf(); |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } else if (t is TypeParameterType) { |
| if (t.promotedBound == null) { |
| const IsTypeParameterSubtypeOf relation = |
| const IsTypeParameterSubtypeOf(); |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } else { |
| const IsIntersectionSubtypeOf relation = |
| const IsIntersectionSubtypeOf(); |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } |
| } else if (t is TypedefType) { |
| const IsTypedefSubtypeOf relation = const IsTypedefSubtypeOf(); |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } else if (t is FutureOrType) { |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } else if (t is NullType) { |
| const IsNullTypeSubtypeOf relation = const IsNullTypeSubtypeOf(); |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } else if (t is NeverType) { |
| const IsNeverTypeSubtypeOf relation = const IsNeverTypeSubtypeOf(); |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } else if (t is ExtensionType) { |
| const IsExtensionTypeSubtypeOf relation = |
| const IsExtensionTypeSubtypeOf(); |
| 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 relation.isInterfaceRelated(s, t, this); |
| } else if (s is FunctionType) { |
| return relation.isFunctionRelated(s, t, this); |
| } else if (s is TypeParameterType) { |
| return s.promotedBound == null |
| ? relation.isTypeParameterRelated(s, t, this) |
| : relation.isIntersectionRelated(s, t, this); |
| } else if (s is TypedefType) { |
| return relation.isTypedefRelated(s, t, this); |
| } else if (s is FutureOrType) { |
| return relation.isFutureOrRelated(s, t, this); |
| } else if (s is ExtensionType) { |
| return relation.isExtensionRelated(s, t, this); |
| } |
| } else { |
| throw "Unhandled type: ${t.runtimeType}"; |
| } |
| throw "Unhandled type combination: ${t.runtimeType} ${s.runtimeType}"; |
| } |
| |
| /// Returns true if all type arguments in [s] and [t] pairwise are subtypes |
| /// with respect to the variance of the corresponding [p] type parameter. |
| IsSubtypeOf areTypeArgumentsOfSubtypeKernel( |
| List<DartType> s, List<DartType> t, List<TypeParameter> p) { |
| if (s.length != t.length || s.length != p.length) { |
| throw "Numbers of type arguments don't match $s $t with parameters $p."; |
| } |
| IsSubtypeOf result = const IsSubtypeOf.always(); |
| for (int i = 0; i < s.length; i++) { |
| int variance = p[i].variance; |
| if (variance == Variance.contravariant) { |
| result = result.and(performNullabilityAwareSubtypeCheck(t[i], s[i])); |
| if (!result.isSubtypeWhenIgnoringNullabilities()) { |
| return const IsSubtypeOf.never(); |
| } |
| } else if (variance == Variance.invariant) { |
| result = |
| result.and(performNullabilityAwareMutualSubtypesCheck(s[i], t[i])); |
| if (!result.isSubtypeWhenIgnoringNullabilities()) { |
| return const IsSubtypeOf.never(); |
| } |
| } else { |
| result = result.and(performNullabilityAwareSubtypeCheck(s[i], t[i])); |
| if (!result.isSubtypeWhenIgnoringNullabilities()) { |
| return const IsSubtypeOf.never(); |
| } |
| } |
| } |
| return result; |
| } |
| |
| static List<Object>? typeChecksForTesting; |
| |
| InterfaceType? getTypeAsInstanceOf(InterfaceType type, Class superclass, |
| Library clientLibrary, CoreTypes coreTypes) { |
| return hierarchy.getTypeAsInstanceOf(type, superclass, clientLibrary); |
| } |
| |
| List<DartType>? getTypeArgumentsAsInstanceOf( |
| InterfaceType type, Class superclass) { |
| return hierarchy.getTypeArgumentsAsInstanceOf(type, superclass); |
| } |
| |
| bool isTop(DartType type) { |
| return type is DynamicType || |
| type is VoidType || |
| type == hierarchy.coreTypes.objectLegacyRawType || |
| type == hierarchy.coreTypes.objectNullableRawType; |
| } |
| |
| IsSubtypeOf performNullabilityAwareMutualSubtypesCheck( |
| DartType type1, DartType type2) { |
| return performNullabilityAwareSubtypeCheck(type1, type2) |
| .andSubtypeCheckFor(type2, type1, this); |
| } |
| } |
| |
| abstract class TypeRelation<T extends DartType> { |
| const TypeRelation(); |
| |
| IsSubtypeOf isDynamicRelated(DynamicType s, T t, Types types); |
| |
| IsSubtypeOf isVoidRelated(VoidType s, T t, Types types); |
| |
| IsSubtypeOf isInterfaceRelated(InterfaceType s, T t, Types types); |
| |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, T t, Types types); |
| |
| IsSubtypeOf isFunctionRelated(FunctionType s, T t, Types types); |
| |
| IsSubtypeOf isFutureOrRelated(FutureOrType s, T t, Types types); |
| |
| IsSubtypeOf isTypeParameterRelated(TypeParameterType s, T t, Types types); |
| |
| IsSubtypeOf isTypedefRelated(TypedefType s, T t, Types types); |
| |
| IsSubtypeOf isExtensionRelated(ExtensionType s, T t, Types types); |
| } |
| |
| class IsInterfaceSubtypeOf extends TypeRelation<InterfaceType> { |
| const IsInterfaceSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isInterfaceRelated( |
| InterfaceType s, InterfaceType t, Types types) { |
| List<DartType>? asSupertypeArguments = |
| types.hierarchy.getTypeArgumentsAsInstanceOf(s, t.classNode); |
| if (asSupertypeArguments == null) { |
| return const IsSubtypeOf.never(); |
| } |
| return types |
| .areTypeArgumentsOfSubtypeKernel( |
| asSupertypeArguments, t.typeArguments, t.classNode.typeParameters) |
| .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, InterfaceType t, Types types) { |
| return types |
| .performNullabilityAwareSubtypeCheck(s.parameter.bound, t) |
| .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated(FutureOrType s, InterfaceType t, Types types) { |
| // Rules 7.1 and 7.2. |
| return types |
| .performNullabilityAwareSubtypeCheck( |
| new InterfaceType(types.hierarchy.coreTypes.futureClass, |
| Nullability.nonNullable, [s.typeArgument]), |
| t) |
| .andSubtypeCheckFor(s.typeArgument, t, types) |
| .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, InterfaceType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck( |
| intersection.promotedBound!, t); // Rule 12. |
| } |
| |
| @override |
| IsSubtypeOf isDynamicRelated(DynamicType s, InterfaceType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isFunctionRelated(FunctionType s, InterfaceType t, Types types) { |
| return t.classNode == types.hierarchy.coreTypes.functionClass |
| ? new IsSubtypeOf.basedSolelyOnNullabilities(s, t) |
| : const IsSubtypeOf.never(); // Rule 14. |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated(TypedefType s, InterfaceType t, Types types) { |
| // Rule 5. |
| return types |
| .performNullabilityAwareSubtypeCheck(s.unalias, t) |
| .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated(VoidType s, InterfaceType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated( |
| ExtensionType s, InterfaceType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| } |
| |
| class IsFunctionSubtypeOf extends TypeRelation<FunctionType> { |
| const IsFunctionSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isFunctionRelated(FunctionType s, FunctionType t, Types types) { |
| List<TypeParameter> sTypeVariables = s.typeParameters; |
| List<TypeParameter> tTypeVariables = t.typeParameters; |
| if (sTypeVariables.length != tTypeVariables.length) { |
| return const IsSubtypeOf.never(); |
| } |
| IsSubtypeOf result = const IsSubtypeOf.always(); |
| if (sTypeVariables.isNotEmpty) { |
| // If the function types have type variables, we alpha-rename the type |
| // variables of [s] to use those of [t]. |
| |
| // As an optimization, we first check if the bounds of the type variables |
| // of the two types on the same positions are mutual subtypes without |
| // alpha-renaming them. |
| List<DartType> typeVariableSubstitution = <DartType>[]; |
| for (int i = 0; i < sTypeVariables.length; i++) { |
| TypeParameter sTypeVariable = sTypeVariables[i]; |
| TypeParameter tTypeVariable = tTypeVariables[i]; |
| result = result.and(types.performNullabilityAwareMutualSubtypesCheck( |
| sTypeVariable.bound, tTypeVariable.bound)); |
| typeVariableSubstitution.add(new TypeParameterType.forAlphaRenaming( |
| sTypeVariable, tTypeVariable)); |
| } |
| Substitution substitution = |
| Substitution.fromPairs(sTypeVariables, typeVariableSubstitution); |
| // If the bounds aren't the same, we need to try again after computing the |
| // substitution of type variables. |
| if (!result.isSubtypeWhenIgnoringNullabilities()) { |
| result = const IsSubtypeOf.always(); |
| for (int i = 0; i < sTypeVariables.length; i++) { |
| TypeParameter sTypeVariable = sTypeVariables[i]; |
| TypeParameter tTypeVariable = tTypeVariables[i]; |
| result = result.and(types.performNullabilityAwareMutualSubtypesCheck( |
| substitution.substituteType(sTypeVariable.bound), |
| tTypeVariable.bound)); |
| if (!result.isSubtypeWhenIgnoringNullabilities()) { |
| return const IsSubtypeOf.never(); |
| } |
| } |
| } |
| s = substitution.substituteType(s.withoutTypeParameters) as FunctionType; |
| } |
| result = result.and( |
| types.performNullabilityAwareSubtypeCheck(s.returnType, t.returnType)); |
| if (!result.isSubtypeWhenIgnoringNullabilities()) { |
| return const IsSubtypeOf.never(); |
| } |
| List<DartType> sPositional = s.positionalParameters; |
| List<DartType> tPositional = t.positionalParameters; |
| if (s.requiredParameterCount > t.requiredParameterCount) { |
| // Rule 15, n1 <= n2. |
| return const IsSubtypeOf.never(); |
| } |
| if (sPositional.length < tPositional.length) { |
| // Rule 15, n1 + k1 >= n2 + k2. |
| return const IsSubtypeOf.never(); |
| } |
| for (int i = 0; i < tPositional.length; i++) { |
| result = result.and(types.performNullabilityAwareSubtypeCheck( |
| tPositional[i], sPositional[i])); |
| if (!result.isSubtypeWhenIgnoringNullabilities()) { |
| // Rule 15, Tj <: Sj. |
| return const IsSubtypeOf.never(); |
| } |
| } |
| List<NamedType> sNamedParameters = s.namedParameters; |
| List<NamedType> tNamedParameters = t.namedParameters; |
| if (sNamedParameters.isNotEmpty || tNamedParameters.isNotEmpty) { |
| // Rule 16, the number of positional parameters must be the same. |
| if (sPositional.length != tPositional.length) { |
| return const IsSubtypeOf.never(); |
| } |
| if (s.requiredParameterCount != t.requiredParameterCount) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| // Rule 16, the parameter names of [t] must be a subset of those of |
| // [s]. Also, for the intersection, the type of the parameter of [t] must |
| // be a subtype of the type of the parameter of [s]. |
| int sCount = 0; |
| for (int tCount = 0; tCount < tNamedParameters.length; tCount++) { |
| NamedType tNamedParameter = tNamedParameters[tCount]; |
| String name = tNamedParameter.name; |
| NamedType? sNamedParameter; |
| for (; sCount < sNamedParameters.length; sCount++) { |
| sNamedParameter = sNamedParameters[sCount]; |
| if (sNamedParameter.name == name) { |
| break; |
| } else if (sNamedParameter.isRequired) { |
| /// From the NNBD spec: For each j such that r0j is required, then |
| /// there exists an i in n+1...q such that xj = yi, and r1i is |
| /// required |
| result = result.and(new IsSubtypeOf.onlyIfIgnoringNullabilities( |
| subtype: s, supertype: t)); |
| } |
| } |
| if (sCount == sNamedParameters.length) return const IsSubtypeOf.never(); |
| // Increment [sCount] so we don't check [sNamedParameter] again in the |
| // loop above or below and assume it is an extra (unmatched) parameter. |
| sCount++; |
| result = result.and(types.performNullabilityAwareSubtypeCheck( |
| tNamedParameter.type, sNamedParameter!.type)); |
| if (!result.isSubtypeWhenIgnoringNullabilities()) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| /// From the NNBD spec: For each j such that r0j is required, then there |
| /// exists an i in n+1...q such that xj = yi, and r1i is required |
| if (sNamedParameter.isRequired && !tNamedParameter.isRequired) { |
| result = result.and(new IsSubtypeOf.onlyIfIgnoringNullabilities( |
| subtype: s, supertype: t)); |
| } |
| } |
| for (; sCount < sNamedParameters.length; sCount++) { |
| NamedType sNamedParameter = sNamedParameters[sCount]; |
| if (sNamedParameter.isRequired) { |
| /// From the NNBD spec: For each j such that r0j is required, then |
| /// there exists an i in n+1...q such that xj = yi, and r1i is |
| /// required |
| result = result.and(new IsSubtypeOf.onlyIfIgnoringNullabilities( |
| subtype: s, supertype: t)); |
| } |
| } |
| } |
| return result.and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isInterfaceRelated(InterfaceType s, FunctionType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isDynamicRelated(DynamicType s, FunctionType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated(FutureOrType s, FunctionType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, FunctionType t, Types types) { |
| // Rule 12. |
| return types.performNullabilityAwareSubtypeCheck( |
| intersection.promotedBound!, t); |
| } |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, FunctionType t, Types types) { |
| // Rule 13. |
| return types |
| .performNullabilityAwareSubtypeCheck(s.parameter.bound, t) |
| .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated(TypedefType s, FunctionType t, Types types) { |
| // Rule 5. |
| return types.performNullabilityAwareSubtypeCheck(s.unalias, t); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated(VoidType s, FunctionType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated(ExtensionType s, FunctionType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| } |
| |
| class IsTypeParameterSubtypeOf extends TypeRelation<TypeParameterType> { |
| const IsTypeParameterSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, TypeParameterType t, Types types) { |
| IsSubtypeOf result = const IsSubtypeOf.always(); |
| if (s.parameter != t.parameter) { |
| result = types.performNullabilityAwareSubtypeCheck(s.bound, t); |
| } |
| if (s.nullability == Nullability.undetermined && |
| t.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(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, TypeParameterType t, Types types) { |
| // Nullable types aren't promoted to intersection types. |
| // TODO(dmitryas): Uncomment the following when the inference is updated. |
| //assert(intersection.typeParameterTypeNullability != Nullability.nullable); |
| |
| // Rule 8. |
| if (intersection.parameter == t.parameter) { |
| if (intersection.nullability == Nullability.undetermined && |
| t.nullability == Nullability.undetermined) { |
| // The two nullabilities are undetermined, but are connected via |
| // additional constraint, namely that they will be equal at run time. |
| return const IsSubtypeOf.always(); |
| } |
| return new IsSubtypeOf.basedSolelyOnNullabilities(intersection, t); |
| } |
| |
| // Rule 12. |
| return types.performNullabilityAwareSubtypeCheck( |
| intersection.promotedBound! |
| .withDeclaredNullability(intersection.nullability), |
| t); |
| } |
| |
| @override |
| IsSubtypeOf isInterfaceRelated( |
| InterfaceType s, TypeParameterType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isDynamicRelated( |
| DynamicType s, TypeParameterType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isFunctionRelated( |
| FunctionType s, TypeParameterType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated( |
| FutureOrType s, TypeParameterType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated( |
| TypedefType s, TypeParameterType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s.unalias, t); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated(VoidType s, TypeParameterType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated( |
| ExtensionType s, TypeParameterType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| } |
| |
| class IsTypedefSubtypeOf extends TypeRelation<TypedefType> { |
| const IsTypedefSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isInterfaceRelated(InterfaceType s, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.unalias); |
| } |
| |
| @override |
| IsSubtypeOf isDynamicRelated(DynamicType s, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.unalias); |
| } |
| |
| @override |
| IsSubtypeOf isFunctionRelated(FunctionType s, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.unalias); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated(FutureOrType s, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.unalias); |
| } |
| |
| @override |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(intersection, t.unalias); |
| } |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.unalias); |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated(TypedefType s, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s.unalias, t.unalias); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated(VoidType s, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.unalias); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated(ExtensionType s, TypedefType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.unalias); |
| } |
| } |
| |
| class IsFutureOrSubtypeOf extends TypeRelation<FutureOrType> { |
| const IsFutureOrSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isInterfaceRelated(InterfaceType s, FutureOrType t, Types types) { |
| return types |
| // Rule 11. |
| .performNullabilityAwareSubtypeCheck( |
| s, t.typeArgument.withDeclaredNullability(t.nullability)) |
| // Rule 10. |
| .orSubtypeCheckFor( |
| s, |
| new InterfaceType(types.hierarchy.coreTypes.futureClass, |
| t.nullability, [t.typeArgument]), |
| types); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated(FutureOrType s, FutureOrType t, Types types) { |
| // This follows from combining rules 7, 10, and 11. |
| DartType sArgument = s.typeArgument; |
| DartType tArgument = t.typeArgument; |
| DartType sFutureOfArgument = new InterfaceType( |
| types.hierarchy.coreTypes.futureClass, |
| Nullability.nonNullable, |
| [sArgument]); |
| DartType tFutureOfArgument = new InterfaceType( |
| types.hierarchy.coreTypes.futureClass, |
| Nullability.nonNullable, |
| [tArgument]); |
| // 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> <: 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 |
| .performNullabilityAwareSubtypeCheck(sArgument, tFutureOfArgument) |
| .andSubtypeCheckFor(sFutureOfArgument, tArgument, types)) |
| .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isDynamicRelated(DynamicType s, FutureOrType t, Types types) { |
| // Rule 11. |
| return types.performNullabilityAwareSubtypeCheck( |
| s, t.typeArgument.withDeclaredNullability(t.nullability)); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated(VoidType s, FutureOrType t, Types types) { |
| // Rule 11. |
| return types.performNullabilityAwareSubtypeCheck( |
| s, t.typeArgument.withDeclaredNullability(t.nullability)); |
| } |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, FutureOrType t, Types types) { |
| // TODO(dmitryas): Revise the original optimization. |
| return types |
| // Rule 11. |
| .performNullabilityAwareSubtypeCheck( |
| s, t.typeArgument.withDeclaredNullability(t.nullability)) |
| // Rule 13. |
| .orSubtypeCheckFor( |
| s.parameter.bound.withDeclaredNullability( |
| combineNullabilitiesForSubstitution( |
| s.parameter.bound.nullability, s.nullability)), |
| t, |
| types) |
| // Rule 10. |
| .orSubtypeCheckFor( |
| s, |
| new InterfaceType(types.hierarchy.coreTypes.futureClass, |
| t.nullability, [t.typeArgument]), |
| types); |
| } |
| |
| @override |
| IsSubtypeOf isFunctionRelated(FunctionType s, FutureOrType t, Types types) { |
| // Rule 11. |
| return types.performNullabilityAwareSubtypeCheck( |
| s, t.typeArgument.withDeclaredNullability(t.nullability)); |
| } |
| |
| @override |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, FutureOrType t, Types types) { |
| return isTypeParameterRelated(intersection, t, types) // Rule 8. |
| .orSubtypeCheckFor(intersection.promotedBound!, t, types); // Rule 12. |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated(TypedefType s, FutureOrType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s.unalias, t); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated(ExtensionType s, FutureOrType t, Types types) { |
| // Rule 11. |
| return types.performNullabilityAwareSubtypeCheck( |
| s, t.typeArgument.withDeclaredNullability(t.nullability)); |
| } |
| } |
| |
| class IsIntersectionSubtypeOf extends TypeRelation<TypeParameterType> { |
| const IsIntersectionSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isIntersectionRelated(TypeParameterType sIntersection, |
| TypeParameterType tIntersection, Types types) { |
| // Rule 9. |
| return const IsTypeParameterSubtypeOf() |
| .isIntersectionRelated(sIntersection, tIntersection, types) |
| .andSubtypeCheckFor(sIntersection, tIntersection.promotedBound!, types); |
| } |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, TypeParameterType intersection, Types types) { |
| // Rule 9. |
| return const IsTypeParameterSubtypeOf() |
| .isTypeParameterRelated(s, intersection, types) |
| .andSubtypeCheckFor(s, intersection.promotedBound!, types); |
| } |
| |
| @override |
| IsSubtypeOf isInterfaceRelated( |
| InterfaceType s, TypeParameterType intersection, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isDynamicRelated( |
| DynamicType s, TypeParameterType intersection, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isFunctionRelated( |
| FunctionType s, TypeParameterType intersection, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated( |
| FutureOrType s, TypeParameterType intersection, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated( |
| TypedefType s, TypeParameterType intersection, Types types) { |
| // Rule 5. |
| return types.performNullabilityAwareSubtypeCheck(s.unalias, intersection); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated( |
| VoidType s, TypeParameterType intersection, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated( |
| ExtensionType s, TypeParameterType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| } |
| |
| class IsNullTypeSubtypeOf implements TypeRelation<NullType> { |
| const IsNullTypeSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isDynamicRelated(DynamicType s, NullType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated(VoidType s, NullType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isInterfaceRelated(InterfaceType s, NullType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, NullType t, Types types) { |
| return types.performNullabilityAwareMutualSubtypesCheck( |
| intersection.promotedBound!, t); |
| } |
| |
| @override |
| IsSubtypeOf isFunctionRelated(FunctionType s, NullType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated(FutureOrType s, NullType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, NullType t, Types types) { |
| // We don't need to combine the check of the bound against [t] with the |
| // check of the nullability of [s] against the nullability of [t] because |
| // [t] is always nullable. |
| return types.performNullabilityAwareSubtypeCheck(s.bound, t); |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated(TypedefType s, NullType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s.unalias, t); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated(ExtensionType s, NullType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| } |
| |
| class IsNeverTypeSubtypeOf implements TypeRelation<NeverType> { |
| const IsNeverTypeSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isDynamicRelated(DynamicType s, NeverType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated(VoidType s, NeverType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isInterfaceRelated(InterfaceType s, NeverType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, NeverType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck( |
| intersection.promotedBound!, t); |
| } |
| |
| @override |
| IsSubtypeOf isFunctionRelated(FunctionType s, NeverType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated(FutureOrType s, NeverType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, NeverType t, Types types) { |
| return types |
| .performNullabilityAwareSubtypeCheck(s.bound, t) |
| .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated(TypedefType s, NeverType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s.unalias, t); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated(ExtensionType s, NeverType t, Types types) { |
| return const IsSubtypeOf.never(); |
| } |
| } |
| |
| class IsExtensionTypeSubtypeOf implements TypeRelation<ExtensionType> { |
| const IsExtensionTypeSubtypeOf(); |
| |
| @override |
| IsSubtypeOf isDynamicRelated(DynamicType s, ExtensionType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.onType); |
| } |
| |
| @override |
| IsSubtypeOf isVoidRelated(VoidType s, ExtensionType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.onType); |
| } |
| |
| @override |
| IsSubtypeOf isInterfaceRelated( |
| InterfaceType s, ExtensionType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.onType); |
| } |
| |
| @override |
| IsSubtypeOf isIntersectionRelated( |
| TypeParameterType intersection, ExtensionType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(intersection, t.onType); |
| } |
| |
| @override |
| IsSubtypeOf isFunctionRelated(FunctionType s, ExtensionType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.onType); |
| } |
| |
| @override |
| IsSubtypeOf isFutureOrRelated(FutureOrType s, ExtensionType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.onType); |
| } |
| |
| @override |
| IsSubtypeOf isTypeParameterRelated( |
| TypeParameterType s, ExtensionType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.onType); |
| } |
| |
| @override |
| IsSubtypeOf isTypedefRelated(TypedefType s, ExtensionType t, Types types) { |
| return types.performNullabilityAwareSubtypeCheck(s, t.onType); |
| } |
| |
| @override |
| IsSubtypeOf isExtensionRelated( |
| ExtensionType s, ExtensionType t, Types types) { |
| if (s.extension != t.extension) { |
| return const IsSubtypeOf.never(); |
| } |
| // TODO(dmitryas): Check if subtyping or mutual subtyping should be used. |
| return types |
| .areTypeArgumentsOfSubtypeKernel( |
| s.typeArguments, t.typeArguments, t.extension.typeParameters) |
| .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t)); |
| } |
| } |