[dart2js] Implement new subtype algorithm structure for DartTypes.

Change-Id: Ib5144c7960dda82a82a16da7cef0b302e34a4b18
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/130464
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Mayank Patke <fishythefish@google.com>
This commit is contained in:
Mayank Patke 2020-01-10 01:20:06 +00:00 committed by commit-bot@chromium.org
parent 9860a07ef7
commit 7ad7e9aa55
6 changed files with 412 additions and 508 deletions

View file

@ -303,6 +303,8 @@ abstract class CommonElements {
ClassEntity get jsJavaScriptFunctionClass;
InterfaceType get jsJavaScriptFunctionType;
ClassEntity get jsJavaScriptObjectClass;
ClassEntity get jsIndexableClass;
@ -1328,6 +1330,10 @@ class CommonElementsImpl
ClassEntity get jsJavaScriptFunctionClass => _jsJavaScriptFunctionClass ??=
_findInterceptorsClass('JavaScriptFunction');
@override
InterfaceType get jsJavaScriptFunctionType =>
_getRawType(jsJavaScriptFunctionClass);
ClassEntity _jsJavaScriptObjectClass;
@override
ClassEntity get jsJavaScriptObjectClass =>

View file

@ -52,9 +52,12 @@ abstract class DartType {
bool get containsFreeTypeVariables =>
_ContainsFreeTypeVariablesVisitor().run(this);
/// Is `true` if this type is the 'Object' type defined in 'dart:core'.
/// Is `true` if this type is the `Object` type defined in `dart:core`.
bool get isObject => false;
/// Is `true` if this type is the `Null` type defined in `dart:core`.
bool get isNull => false;
/// Applies [f] to each occurrence of a [TypeVariableType] within this
/// type. This excludes function type variables, whether free or bound.
void forEachTypeVariable(f(TypeVariableType variable)) {}
@ -80,6 +83,21 @@ abstract class DartType {
bool _equals(DartType other, _Assumptions assumptions);
/// Returns `true` if `this` is structurally equal to [other] up to renaming
/// of bound type variables (trivially true) and equating all top types.
///
/// This method handles the common cases of identity and top types. Types
/// should add any additional logic by implementing
/// [_equalsModuloTopInternal].
bool _equalsModuloTop(DartType other) {
other = other.unaliased;
if (identical(this, other)) return true;
if (isTop) return other.isTop;
return _equalsModuloTopInternal(other);
}
bool _equalsModuloTopInternal(DartType other);
@override
String toString() => _DartTypeToStringVisitor().run(this);
}
@ -177,6 +195,14 @@ class LegacyType extends DartType {
bool _equalsInternal(LegacyType other, _Assumptions assumptions) =>
baseType._equals(other.baseType, assumptions);
@override
bool _equalsModuloTopInternal(DartType other) {
if (other is LegacyType) {
return baseType._equalsModuloTop(other.baseType);
}
return false;
}
}
class NullableType extends DartType {
@ -215,6 +241,14 @@ class NullableType extends DartType {
bool _equalsInternal(NullableType other, _Assumptions assumptions) =>
baseType._equals(other.baseType, assumptions);
@override
bool _equalsModuloTopInternal(DartType other) {
if (other is NullableType) {
return baseType._equalsModuloTop(other.baseType);
}
return false;
}
}
class InterfaceType extends DartType {
@ -228,10 +262,13 @@ class InterfaceType extends DartType {
bool get isTop => isObject;
@override
bool get isObject {
return element.name == 'Object' &&
element.library.canonicalUri == Uris.dart_core;
}
bool get isObject =>
element.name == 'Object' &&
element.library.canonicalUri == Uris.dart_core;
@override
bool get isNull =>
element.name == 'Null' && element.library.canonicalUri == Uris.dart_core;
@override
bool get containsTypeVariables =>
@ -282,6 +319,15 @@ class InterfaceType extends DartType {
return identical(element, other.element) &&
_equalTypes(typeArguments, other.typeArguments, assumptions);
}
@override
bool _equalsModuloTopInternal(DartType other) {
if (other is InterfaceType) {
return identical(element, other.element) &&
_equalTypesModuloTop(typeArguments, other.typeArguments);
}
return false;
}
}
class TypedefType extends DartType {
@ -344,6 +390,12 @@ class TypedefType extends DartType {
return identical(element, other.element) &&
_equalTypes(typeArguments, other.typeArguments, assumptions);
}
@override
bool _equalsModuloTop(DartType other) => unaliased._equalsModuloTop(other);
@override
bool _equalsModuloTopInternal(DartType other) => false;
}
class TypeVariableType extends DartType {
@ -379,6 +431,14 @@ class TypeVariableType extends DartType {
}
return false;
}
@override
bool _equalsModuloTopInternal(DartType other) {
if (other is TypeVariableType) {
return identical(other.element, element);
}
return false;
}
}
/// A type variable declared on a function type.
@ -428,6 +488,14 @@ class FunctionTypeVariable extends DartType {
return false;
}
@override
bool _equalsModuloTopInternal(DartType other) {
if (other is FunctionTypeVariable) {
return index == other.index;
}
return false;
}
@override
R accept<R, A>(DartTypeVisitor<R, A> visitor, A argument) =>
visitor.visitFunctionTypeVariable(this, argument);
@ -448,6 +516,9 @@ class NeverType extends DartType {
@override
bool _equals(DartType other, _Assumptions assumptions) =>
identical(this, other);
@override
bool _equalsModuloTopInternal(DartType other) => false;
}
class VoidType extends DartType {
@ -469,6 +540,9 @@ class VoidType extends DartType {
bool _equals(DartType other, _Assumptions assumptions) {
return identical(this, other);
}
@override
bool _equalsModuloTopInternal(DartType other) => false;
}
class DynamicType extends DartType {
@ -490,6 +564,9 @@ class DynamicType extends DartType {
bool _equals(DartType other, _Assumptions assumptions) {
return identical(this, other);
}
@override
bool _equalsModuloTopInternal(DartType other) => false;
}
class ErasedType extends DartType {
@ -510,6 +587,9 @@ class ErasedType extends DartType {
@override
bool _equals(DartType other, _Assumptions assumptions) =>
identical(this, other);
@override
bool _equalsModuloTopInternal(DartType other) => false;
}
/// Represents a type which is simultaneously top and bottom.
@ -541,6 +621,9 @@ class AnyType extends DartType {
@override
bool _equals(DartType other, _Assumptions assumptions) =>
identical(this, other);
@override
bool _equalsModuloTopInternal(DartType other) => false;
}
class FunctionType extends DartType {
@ -575,6 +658,11 @@ class FunctionType extends DartType {
assert(!typeVariables.contains(null), "Invalid type variables in $this.");
}
bool get isGeneric => typeVariables.isNotEmpty;
List<DartType> get typeVariableBounds =>
typeVariables.map((FunctionTypeVariable v) => v.bound).toList();
@override
bool get containsTypeVariables {
return typeVariables.any((type) => type.bound.containsTypeVariables) ||
@ -662,6 +750,21 @@ class FunctionType extends DartType {
}
return result;
}
@override
bool _equalsModuloTopInternal(DartType other) {
if (other is FunctionType) {
return returnType._equalsModuloTop(other.returnType) &&
_equalTypesModuloTop(parameterTypes, other.parameterTypes) &&
_equalTypesModuloTop(
optionalParameterTypes, other.optionalParameterTypes) &&
equalElements(namedParameters, other.namedParameters) &&
_equalTypesModuloTop(
namedParameterTypes, other.namedParameterTypes) &&
_equalTypesModuloTop(typeVariableBounds, other.typeVariableBounds);
}
return false;
}
}
class FutureOrType extends DartType {
@ -704,6 +807,14 @@ class FutureOrType extends DartType {
bool _equalsInternal(FutureOrType other, _Assumptions assumptions) {
return typeArgument._equals(other.typeArgument, assumptions);
}
@override
bool _equalsModuloTopInternal(DartType other) {
if (other is FutureOrType) {
return typeArgument._equalsModuloTop(other.typeArgument);
}
return false;
}
}
bool _equalTypes(List<DartType> a, List<DartType> b, _Assumptions assumptions) {
@ -716,6 +827,14 @@ bool _equalTypes(List<DartType> a, List<DartType> b, _Assumptions assumptions) {
return true;
}
bool _equalTypesModuloTop(List<DartType> a, List<DartType> b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++) {
if (!a[i]._equalsModuloTop(b[i])) return false;
}
return true;
}
abstract class DartTypeVisitor<R, A> {
const DartTypeVisitor();
@ -1466,447 +1585,272 @@ class _DartTypeToStringVisitor extends DartTypeVisitor<void, void> {
}
}
/// Abstract visitor for determining relations between types.
// TODO(fishythefish): Rewrite type relations to support NNBD types and new
// subtyping algorithm structure.
abstract class AbstractTypeRelation<T extends DartType>
extends BaseDartTypeVisitor<bool, T> {
CommonElements get commonElements;
final _Assumptions assumptions = new _Assumptions();
/// Ensures that the super hierarchy of [type] is computed.
void ensureResolved(InterfaceType type) {}
/// Returns the unaliased version of [type].
T getUnaliased(T type) => type.unaliased;
/// Returns [type] as an instance of [cls], or `null` if [type] is not subtype
/// if [cls].
InterfaceType asInstanceOf(InterfaceType type, ClassEntity cls);
/// Returns the type of the `call` method on [type], or `null` if the class
/// of [type] does not have a `call` method.
FunctionType getCallType(InterfaceType type);
/// Returns the declared bound of [element].
DartType getTypeVariableBound(TypeVariableEntity element);
/// Returns the variances for each type parameter in [cls].
List<Variance> getTypeVariableVariances(ClassEntity cls);
@override
bool visitType(T t, T s) {
throw 'internal error: unknown type ${t}';
}
@override
bool visitVoidType(VoidType t, T s) {
assert(s is! VoidType);
return false;
}
bool invalidTypeArguments(T t, T s);
bool invalidFunctionReturnTypes(T t, T s);
bool invalidFunctionParameterTypes(T t, T s);
bool invalidTypeVariableBounds(T bound, T s);
bool invalidCallableType(covariant DartType callType, covariant DartType s);
@override
bool visitInterfaceType(InterfaceType t, covariant DartType s) {
ensureResolved(t);
bool checkTypeArguments(InterfaceType instance, InterfaceType other) {
List<T> tTypeArgs = instance.typeArguments;
List<T> sTypeArgs = other.typeArguments;
List<Variance> tVariances = getTypeVariableVariances(instance.element);
assert(tTypeArgs.length == sTypeArgs.length);
assert(tTypeArgs.length == tVariances.length);
for (int i = 0; i < tTypeArgs.length; i++) {
switch (tVariances[i]) {
case Variance.legacyCovariant:
case Variance.covariant:
if (invalidTypeArguments(tTypeArgs[i], sTypeArgs[i])) return false;
break;
case Variance.contravariant:
if (invalidTypeArguments(sTypeArgs[i], tTypeArgs[i])) return false;
break;
case Variance.invariant:
if (invalidTypeArguments(tTypeArgs[i], sTypeArgs[i]) ||
invalidTypeArguments(sTypeArgs[i], tTypeArgs[i])) return false;
break;
default:
throw StateError(
"Invalid variance ${tVariances[i]} used for subtype check.");
}
}
return true;
}
if (s is InterfaceType) {
InterfaceType instance = asInstanceOf(t, s.element);
if (instance != null && checkTypeArguments(instance, s)) {
return true;
}
}
FunctionType callType = getCallType(t);
if (s == commonElements.functionType && callType != null) {
return true;
} else if (s is FunctionType) {
return callType != null && !invalidCallableType(callType, s);
}
return false;
}
@override
bool visitFunctionType(FunctionType t, DartType s) {
if (s == commonElements.functionType) {
return true;
}
if (s is! FunctionType) return false;
FunctionType tf = t;
FunctionType sf = s;
int typeVariablesCount = getCommonTypeVariablesCount(tf, sf);
if (typeVariablesCount == null) {
return false;
}
for (int i = 0; i < typeVariablesCount; i++) {
assumptions.assume(tf.typeVariables[i], sf.typeVariables[i]);
}
for (int i = 0; i < typeVariablesCount; i++) {
if (!tf.typeVariables[i].bound
._equals(sf.typeVariables[i].bound, assumptions)) {
return false;
}
}
if (invalidFunctionReturnTypes(tf.returnType, sf.returnType)) {
return false;
}
bool result = visitFunctionTypeInternal(tf, sf);
for (int i = 0; i < typeVariablesCount; i++) {
assumptions.forget(tf.typeVariables[i], sf.typeVariables[i]);
}
return result;
}
int getCommonTypeVariablesCount(FunctionType t, FunctionType s) {
if (t.typeVariables.length == s.typeVariables.length) {
return t.typeVariables.length;
}
return null;
}
bool visitFunctionTypeInternal(FunctionType tf, FunctionType sf) {
// TODO(johnniwinther): Rewrite the function subtyping to be more readable
// but still as efficient.
// For the comments we use the following abbreviations:
// x.p : parameterTypes on [:x:],
// x.o : optionalParameterTypes on [:x:], and
// len(xs) : length of list [:xs:].
Iterator<T> tps = tf.parameterTypes.iterator;
Iterator<T> sps = sf.parameterTypes.iterator;
bool sNotEmpty = sps.moveNext();
bool tNotEmpty = tps.moveNext();
tNext() => (tNotEmpty = tps.moveNext());
sNext() => (sNotEmpty = sps.moveNext());
bool incompatibleParameters() {
while (tNotEmpty && sNotEmpty) {
if (invalidFunctionParameterTypes(tps.current, sps.current)) {
return true;
}
tNext();
sNext();
}
return false;
}
if (incompatibleParameters()) return false;
if (tNotEmpty) {
// We must have [: len(t.p) <= len(s.p) :].
return false;
}
if (!sf.namedParameters.isEmpty) {
// We must have [: len(t.p) == len(s.p) :].
if (sNotEmpty) {
return false;
}
// Since named parameters are globally ordered we can determine the
// subset relation with a linear search for [:sf.namedParameters:]
// within [:tf.namedParameters:].
List<String> tNames = tf.namedParameters;
List<T> tTypes = tf.namedParameterTypes;
List<String> sNames = sf.namedParameters;
List<T> sTypes = sf.namedParameterTypes;
int tIndex = 0;
int sIndex = 0;
while (tIndex < tNames.length && sIndex < sNames.length) {
if (tNames[tIndex] == sNames[sIndex]) {
if (invalidFunctionParameterTypes(tTypes[tIndex], sTypes[sIndex])) {
return false;
}
sIndex++;
}
tIndex++;
}
if (sIndex < sNames.length) {
// We didn't find all names.
return false;
}
} else {
// Check the remaining [: s.p :] against [: t.o :].
tps = tf.optionalParameterTypes.iterator;
tNext();
if (incompatibleParameters()) return false;
if (sNotEmpty) {
// We must have [: len(t.p) + len(t.o) >= len(s.p) :].
return false;
}
if (!sf.optionalParameterTypes.isEmpty) {
// Check the remaining [: s.o :] against the remaining [: t.o :].
sps = sf.optionalParameterTypes.iterator;
sNext();
if (incompatibleParameters()) return false;
if (sNotEmpty) {
// We didn't find enough parameters:
// We must have [: len(t.p) + len(t.o) <= len(s.p) + len(s.o) :].
return false;
}
} else {
if (sNotEmpty) {
// We must have [: len(t.p) + len(t.o) >= len(s.p) :].
return false;
}
}
}
return true;
}
@override
bool visitTypeVariableType(TypeVariableType t, T s) {
// Identity check is handled in [isSubtype].
DartType bound = getTypeVariableBound(t.element);
if (bound is TypeVariableType) {
// The bound is potentially cyclic so we need to be extra careful.
Set<TypeVariableEntity> seenTypeVariables = new Set<TypeVariableEntity>();
seenTypeVariables.add(t.element);
while (bound is TypeVariableType) {
TypeVariableType typeVariable = bound;
if (bound == s) {
// [t] extends [s].
return true;
}
if (seenTypeVariables.contains(typeVariable.element)) {
// We have a cycle and have already checked all bounds in the cycle
// against [s] and can therefore conclude that [t] is not a subtype
// of [s].
return false;
}
seenTypeVariables.add(typeVariable.element);
bound = getTypeVariableBound(typeVariable.element);
}
}
if (invalidTypeVariableBounds(bound, s)) return false;
return true;
}
@override
bool visitFunctionTypeVariable(FunctionTypeVariable t, DartType s) {
if (s is! FunctionTypeVariable) return false;
return assumptions.isAssumed(t, s);
}
}
abstract class MoreSpecificVisitor<T extends DartType>
extends AbstractTypeRelation<T> {
bool isMoreSpecific(T t, T s) {
if (identical(t, s) ||
t is AnyType ||
s is AnyType ||
s.isTop ||
s is VoidType ||
s == commonElements.objectType ||
t == commonElements.nullType) {
return true;
}
if (t.isTop) {
return false;
}
t = getUnaliased(t);
s = getUnaliased(s);
return t.accept(this, s);
}
@override
bool invalidTypeArguments(T t, T s) {
return !isMoreSpecific(t, s);
}
@override
bool invalidFunctionReturnTypes(T t, T s) {
if (s.isTop && t is VoidType) return true;
return s is! VoidType && !isMoreSpecific(t, s);
}
@override
bool invalidFunctionParameterTypes(T t, T s) {
return !isMoreSpecific(t, s);
}
@override
bool invalidTypeVariableBounds(T bound, T s) {
return !isMoreSpecific(bound, s);
}
@override
bool invalidCallableType(covariant DartType callType, covariant DartType s) {
return !isMoreSpecific(callType, s);
}
@override
bool visitFutureOrType(FutureOrType t, covariant DartType s) {
return false;
}
}
/// Type visitor that determines the subtype relation two types.
abstract class SubtypeVisitor<T extends DartType>
extends MoreSpecificVisitor<T> {
bool isSubtype(DartType t, DartType s) {
if (t is AnyType || s is AnyType) return true;
if (s is FutureOrType) {
FutureOrType sFutureOr = s;
if (isSubtype(t, sFutureOr.typeArgument)) {
return true;
} else if (t is InterfaceType) {
InterfaceType tInterface = t;
if (tInterface.element == commonElements.futureClass &&
isSubtype(
tInterface.typeArguments.single, sFutureOr.typeArgument)) {
return true;
}
}
}
return isMoreSpecific(t, s);
}
bool isAssignable(T t, T s) {
return isSubtype(t, s) || isSubtype(s, t);
}
@override
bool invalidTypeArguments(T t, T s) {
return !isSubtype(t, s);
}
@override
bool invalidFunctionReturnTypes(T t, T s) {
return !isSubtype(t, s);
}
@override
bool invalidFunctionParameterTypes(T t, T s) {
return !isSubtype(s, t);
}
@override
bool invalidTypeVariableBounds(T bound, T s) {
return !isSubtype(bound, s);
}
@override
bool invalidCallableType(covariant DartType callType, covariant DartType s) {
return !isSubtype(callType, s);
}
@override
bool visitFutureOrType(FutureOrType t, covariant DartType s) {
if (s is FutureOrType) {
FutureOrType sFutureOr = s;
return isSubtype(t.typeArgument, sFutureOr.typeArgument);
}
return false;
}
}
/// Type visitor that determines one type could a subtype of another given the
/// right type variable substitution. The computation is approximate and returns
/// `false` only if we are sure no such substitution exists.
abstract class PotentialSubtypeVisitor<T extends DartType>
extends SubtypeVisitor<T> {
bool _assumeInstantiations = true;
@override
bool isSubtype(DartType t, DartType s) {
if (t is AnyType || s is AnyType) return true;
if (t is TypeVariableType || s is TypeVariableType) {
return true;
}
if ((t is FunctionTypeVariable || s is FunctionTypeVariable) &&
_assumeInstantiations) {
return true;
}
return super.isSubtype(t, s);
}
@override
int getCommonTypeVariablesCount(FunctionType t, FunctionType s) {
if (t.typeVariables.length == s.typeVariables.length) {
return t.typeVariables.length;
}
if (_assumeInstantiations && s.typeVariables.length == 0) {
return 0;
}
return null;
}
bool isPotentialSubtype(DartType t, DartType s,
{bool assumeInstantiations: true}) {
_assumeInstantiations = assumeInstantiations;
return isSubtype(t, s);
}
}
/// Basic interface for the Dart type system.
abstract class DartTypes {
/// The types defined in 'dart:core'.
CommonElements get commonElements;
/// Returns `true` if [t] is a subtype of [s].
bool isSubtype(DartType t, DartType s);
/// Returns `true` if [s] is a subtype of [t].
bool isSubtype(DartType s, DartType t) => _subtypeHelper(s, t);
/// Returns `true` if [t] is assignable to [s].
bool isAssignable(DartType t, DartType s);
/// Returns `true` if [s] is assignable to [t].
bool isAssignable(DartType s, DartType t) =>
isSubtype(s, t) || isSubtype(t, s);
/// Returns `true` if [t] might be a subtype of [s] for some values of
/// Returns `true` if [s] might be a subtype of [t] for some values of
/// type variables in [s] and [t].
///
/// If [assumeInstantiations], generic function types are assumed to be
/// potentially instantiated.
bool isPotentialSubtype(DartType t, DartType s,
{bool assumeInstantiations: true});
bool isPotentialSubtype(DartType s, DartType t,
{bool assumeInstantiations: true}) =>
_subtypeHelper(s, t,
allowPotentialSubtypes: true,
assumeInstantiations: assumeInstantiations);
static const int IS_SUBTYPE = 1;
static const int MAYBE_SUBTYPE = 0;
static const int NOT_SUBTYPE = -1;
bool _subtypeHelper(DartType s, DartType t,
{bool allowPotentialSubtypes: false, bool assumeInstantiations: false}) {
/// Based on
/// https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md.
/// See also [_isSubtype] in `dart:_rti`.
bool _isSubtype(DartType s, Set<FunctionTypeVariable> sEnv, DartType t,
Set<FunctionTypeVariable> tEnv) {
s = s.unaliased;
t = t.unaliased;
/// Returns [IS_SUBTYPE], [MAYBE_SUBTYPE], or [NOT_SUBTYPE] if [t] is a
/// (potential) subtype of [s]
int computeSubtypeRelation(DartType t, DartType s) {
// TODO(johnniwinther): Compute this directly in [isPotentialSubtype].
if (isSubtype(t, s)) return IS_SUBTYPE;
return isPotentialSubtype(t, s) ? MAYBE_SUBTYPE : NOT_SUBTYPE;
// Reflexivity:
if (s == t) return true;
if (s is FunctionTypeVariable &&
t is FunctionTypeVariable &&
sEnv.contains(s) &&
tEnv.contains(t) &&
s.index == t.index) return true;
// Right Top:
if (t.isTop) return true;
if (s is AnyType) return true;
if (allowPotentialSubtypes &&
(s is TypeVariableType || t is TypeVariableType)) return true;
if (assumeInstantiations &&
(s is FunctionTypeVariable || t is FunctionTypeVariable)) return true;
// Left Top:
if (s.isTop) return false;
// Left Bottom:
// TODO(fishythefish): Update for NNBD - check for `Never` instead of
// `Null`.
if (s.isNull) return true;
// Left Type Variable Bound 1:
if (s is TypeVariableType) {
if (_isSubtype(getTypeVariableBound(s.element), sEnv, t, tEnv))
return true;
}
if (s is FunctionTypeVariable) {
if (_isSubtype(s._bound, sEnv, t, tEnv)) return true;
}
// Left Null:
// Note: Interchanging the Left Null and Right Object rules allows us to
// reduce casework.
if (s.isNull) {
if (t is FutureOrType) {
return _isSubtype(s, sEnv, t.typeArgument, tEnv);
}
return t.isNull || t is NullableType || t is LegacyType;
}
// Right Object:
if (t.isObject) {
if (s is FutureOrType) {
return _isSubtype(s.typeArgument, sEnv, t, tEnv);
}
if (s is LegacyType) {
return _isSubtype(s.baseType, sEnv, t, tEnv);
}
return s is! NullableType;
}
// Left Legacy:
if (s is LegacyType) {
return _isSubtype(s.baseType, sEnv, t, tEnv);
}
// Right Legacy:
if (t is LegacyType) {
return _isSubtype(s, sEnv, NullableType(t.baseType), tEnv);
}
// Left FutureOr:
if (s is FutureOrType) {
DartType typeArgument = s.typeArgument;
return _isSubtype(typeArgument, sEnv, t, tEnv) &&
_isSubtype(commonElements.futureType(typeArgument), sEnv, t, tEnv);
}
// Left Nullable:
if (s is NullableType) {
return _isSubtype(commonElements.nullType, sEnv, t, tEnv) &&
_isSubtype(s.baseType, sEnv, t, tEnv);
}
// Type Variable Reflexivity 1 is subsumed by Reflexivity and therefore
// elided.
// Type Variable Reflexivity 2 does not apply because we do not represent
// promoted type variables.
// Right Promoted Variable does not apply because we do not represent
// promoted type variables.
// Right FutureOr:
if (t is FutureOrType) {
DartType typeArgument = t.typeArgument;
return _isSubtype(s, sEnv, typeArgument, tEnv) ||
_isSubtype(s, sEnv, commonElements.futureType(typeArgument), tEnv);
}
// Right Nullable:
if (t is NullableType) {
return _isSubtype(s, sEnv, commonElements.nullType, tEnv) ||
_isSubtype(s, sEnv, t.baseType, tEnv);
}
// Left Promoted Variable does not apply because we do not represent
// promoted type variables.
// Left Type Variable Bound 2:
if (s is TypeVariableType) return false;
if (s is FunctionTypeVariable) return false;
// Function Type/Function:
if (s is FunctionType && t == commonElements.functionType) {
return true;
}
// Positional Function Types + Named Function Types:
// TODO(fishythefish): Disallow JavaScriptFunction as a subtype of
// function types using features inaccessible from JavaScript.
if (t is FunctionType) {
if (s == commonElements.jsJavaScriptFunctionType) return true;
if (s is FunctionType) {
if (t.isGeneric) {
if (!s.isGeneric) return false;
if (!_equalTypesModuloTop(
s.typeVariableBounds, t.typeVariableBounds)) {
return false;
}
sEnv = sEnv.toSet()..addAll(s.typeVariables);
tEnv = tEnv.toSet()..addAll(t.typeVariables);
}
if (!_isSubtype(s.returnType, sEnv, t.returnType, tEnv)) return false;
// TODO(fishythefish): Support required named parameters.
List<DartType> sRequiredPositional = s.parameterTypes;
List<DartType> tRequiredPositional = t.parameterTypes;
int sRequiredPositionalLength = sRequiredPositional.length;
int tRequiredPositionalLength = tRequiredPositional.length;
if (sRequiredPositionalLength > tRequiredPositionalLength) {
return false;
}
int requiredPositionalDelta =
tRequiredPositionalLength - sRequiredPositionalLength;
List<DartType> sOptionalPositional = s.optionalParameterTypes;
List<DartType> tOptionalPositional = t.optionalParameterTypes;
int sOptionalPositionalLength = sOptionalPositional.length;
int tOptionalPositionalLength = tOptionalPositional.length;
if (sRequiredPositionalLength + sOptionalPositionalLength <
tRequiredPositionalLength + tOptionalPositionalLength) {
return false;
}
for (int i = 0; i < sRequiredPositionalLength; i++) {
if (!_isSubtype(
tRequiredPositional[i], tEnv, sRequiredPositional[i], sEnv)) {
return false;
}
}
for (int i = 0; i < requiredPositionalDelta; i++) {
if (!_isSubtype(tRequiredPositional[sRequiredPositionalLength + i],
tEnv, sOptionalPositional[i], sEnv)) {
return false;
}
}
for (int i = 0; i < tOptionalPositionalLength; i++) {
if (!_isSubtype(tOptionalPositional[i], tEnv,
sOptionalPositional[requiredPositionalDelta + i], sEnv)) {
return false;
}
}
List<String> sOptionalNamed = s.namedParameters;
List<String> tOptionalNamed = t.namedParameters;
List<DartType> sOptionalNamedTypes = s.namedParameterTypes;
List<DartType> tOptionalNamedTypes = t.namedParameterTypes;
int sOptionalNamedLength = sOptionalNamed.length;
int tOptionalNamedLength = tOptionalNamed.length;
for (int i = 0, j = 0; j < tOptionalNamedLength; j++) {
String sName;
String tName = tOptionalNamed[j];
int comparison;
do {
if (i >= sOptionalNamedLength) return false;
sName = sOptionalNamed[i++];
comparison = sName.compareTo(tName);
} while (comparison < 0);
if (comparison > 0) return false;
if (!_isSubtype(
tOptionalNamedTypes[j], tEnv, sOptionalNamedTypes[i - 1], sEnv))
return false;
}
return true;
}
return false;
}
// Interface Compositionality + Super-Interface:
if (s is InterfaceType) {
if (t is InterfaceType) {
InterfaceType instance =
s.element == t.element ? s : asInstanceOf(s, t.element);
if (instance == null) return false;
List<DartType> sArgs = instance.typeArguments;
List<DartType> tArgs = t.typeArguments;
List<Variance> variances = getTypeVariableVariances(t.element);
assert(sArgs.length == tArgs.length);
assert(tArgs.length == variances.length);
for (int i = 0; i < variances.length; i++) {
switch (variances[i]) {
case Variance.legacyCovariant:
case Variance.covariant:
if (!_isSubtype(sArgs[i], sEnv, tArgs[i], tEnv)) return false;
break;
case Variance.contravariant:
if (!_isSubtype(tArgs[i], tEnv, sArgs[i], sEnv)) return false;
break;
case Variance.invariant:
if (!_isSubtype(sArgs[i], sEnv, tArgs[i], tEnv) ||
!_isSubtype(tArgs[i], tEnv, sArgs[i], sEnv)) return false;
break;
default:
throw StateError(
"Invalid variance ${variances[i]} used for subtype check.");
}
}
return true;
}
return false;
}
return false;
}
return _isSubtype(s, {}, t, {});
}
/// Returns [type] as an instance of [cls] or `null` if [type] is not a
@ -1972,4 +1916,8 @@ abstract class DartTypes {
// returning in the next statement.
return contextClass;
}
DartType getTypeVariableBound(TypeVariableEntity element);
List<Variance> getTypeVariableVariances(ClassEntity cls);
}

View file

@ -11,30 +11,8 @@ import 'element_map.dart';
/// Support for subtype checks of kernel based [DartType]s.
class KernelDartTypes extends DartTypes {
final IrToElementMap elementMap;
final SubtypeVisitor<DartType> subtypeVisitor;
final PotentialSubtypeVisitor<DartType> potentialSubtypeVisitor;
KernelDartTypes(this.elementMap)
: this.subtypeVisitor = new KernelSubtypeVisitor(elementMap),
this.potentialSubtypeVisitor =
new _KernelPotentialSubtypeVisitor(elementMap);
@override
bool isPotentialSubtype(DartType t, DartType s,
{bool assumeInstantiations: true}) {
return potentialSubtypeVisitor.isPotentialSubtype(t, s,
assumeInstantiations: assumeInstantiations);
}
@override
bool isAssignable(DartType t, DartType s) {
return isSubtype(t, s) || isSubtype(s, t);
}
@override
bool isSubtype(DartType t, DartType s) {
return subtypeVisitor.isSubtype(t, s);
}
KernelDartTypes(this.elementMap);
@override
InterfaceType getThisType(ClassEntity cls) {
@ -92,6 +70,15 @@ class KernelDartTypes extends DartTypes {
@override
CommonElements get commonElements => elementMap.commonElements;
@override
DartType getTypeVariableBound(TypeVariableEntity element) {
return elementMap.getTypeVariableBound(element);
}
@override
List<Variance> getTypeVariableVariances(ClassEntity cls) =>
elementMap.getTypeVariableVariances(cls);
}
class KernelOrderedTypeSetBuilder extends OrderedTypeSetBuilderBase {
@ -119,47 +106,3 @@ class KernelOrderedTypeSetBuilder extends OrderedTypeSetBuilderBase {
return elementMap.getOrderedTypeSet(cls);
}
}
abstract class AbstractTypeRelationMixin
implements AbstractTypeRelation<DartType> {
IrToElementMap get elementMap;
@override
CommonElements get commonElements => elementMap.commonElements;
@override
DartType getTypeVariableBound(TypeVariableEntity element) {
return elementMap.getTypeVariableBound(element);
}
@override
List<Variance> getTypeVariableVariances(ClassEntity cls) {
return elementMap.getTypeVariableVariances(cls);
}
@override
FunctionType getCallType(InterfaceType type) {
return elementMap.getCallType(type);
}
@override
InterfaceType asInstanceOf(InterfaceType type, ClassEntity cls) {
return elementMap.asInstanceOf(type, cls);
}
}
class KernelSubtypeVisitor extends SubtypeVisitor<DartType>
with AbstractTypeRelationMixin {
@override
final IrToElementMap elementMap;
KernelSubtypeVisitor(this.elementMap);
}
class _KernelPotentialSubtypeVisitor extends PotentialSubtypeVisitor<DartType>
with AbstractTypeRelationMixin {
@override
final IrToElementMap elementMap;
_KernelPotentialSubtypeVisitor(this.elementMap);
}

View file

@ -157,15 +157,22 @@ main() {
futureOrT,
],
futureListNum: [futureOrListNum],
futureT: [futureOrT],
futureFutureNum: [futureOrFutureOrNum],
futureOrNum: [futureOrFutureOrNum],
futureOrInt: [futureOrNum, futureOrFutureOrNum],
futureOrNull: [
futureOrT,
futureNull,
futureNum,
futureOrNum,
futureInt,
futureOrInt,
futureListNum,
futureOrListNum,
futureFutureNum,
futureOrFutureOrNum,
futureT,
futureOrT,
],
returnFutureNull: [returnVoid],
};

View file

@ -39,13 +39,13 @@ class Class4 {}
method10<T extends Class4>() => null;
main() {
/*strong.needsArgs,selectors=[Selector(call, call, arity=0, types=1)]*/
method7<T extends Class1a>() => null;
/*strong.needsArgs,needsSignature,selectors=[Selector(call, call, arity=0, types=1)]*/
/*omit.needsSignature*/method7<T extends Class1a>() => null;
/*strong.needsArgs,selectors=[Selector(call, call, arity=0, types=1)]*/
method8<T extends Class2a<num>>() => null;
/*strong.needsArgs,needsSignature,selectors=[Selector(call, call, arity=0, types=1)]*/
/*omit.needsSignature*/method8<T extends Class2a<num>>() => null;
/**/
/*needsSignature*/
method9<T>() => null;
dynamic f1 = method1;

View file

@ -6,7 +6,7 @@ import 'package:expect/expect.dart';
class Class1 {
method1() {
/**/
/*needsSignature*/
num local<T>(num n) => null;
return local;
}
@ -18,7 +18,7 @@ class Class1 {
}
method3() {
/**/
/*needsSignature*/
int local<T>(num n) => null;
return local;
}