| Index: pkg/analyzer/lib/src/generated/type_system.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
|
| index 84f2eb7c2168c68532b02a388a9061296c734233..5d87ae1168988c3e6052338400ac0f29ea8f87dc 100644
|
| --- a/pkg/analyzer/lib/src/generated/type_system.dart
|
| +++ b/pkg/analyzer/lib/src/generated/type_system.dart
|
| @@ -22,8 +22,6 @@ typedef bool _GuardedSubtypeChecker<T>(T t1, T t2, Set<Element> visited);
|
| * https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md
|
| */
|
| class StrongTypeSystemImpl extends TypeSystem {
|
| - final _specTypeSystem = new TypeSystemImpl();
|
| -
|
| bool anyParameterType(FunctionType ft, bool predicate(DartType t)) {
|
| return ft.parameters.any((p) => predicate(p.type));
|
| }
|
| @@ -43,13 +41,6 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| return null;
|
| }
|
|
|
| - @override
|
| - DartType getLeastUpperBound(
|
| - TypeProvider typeProvider, DartType type1, DartType type2) {
|
| - // TODO(leafp): Implement a strong mode version of this.
|
| - return _specTypeSystem.getLeastUpperBound(typeProvider, type1, type2);
|
| - }
|
| -
|
| /**
|
| * Given a generic function type `F<T0, T1, ... Tn>` and a context type C,
|
| * infer an instantiation of F, such that `F<S0, S1, ..., Sn>` <: C.
|
| @@ -222,8 +213,7 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| // TODO(leafp): Emit warnings and hints for these in some way.
|
| // TODO(leafp): Consider adding a flag to disable these? Or just rely on
|
| // --warnings-as-errors?
|
| - if (isSubtypeOf(toType, fromType) ||
|
| - _specTypeSystem.isAssignableTo(toType, fromType)) {
|
| + if (isSubtypeOf(toType, fromType) || toType.isAssignableTo(fromType)) {
|
| // TODO(leafp): error if type is known to be exact (literal,
|
| // instance creation).
|
| // TODO(leafp): Warn on composite downcast.
|
| @@ -377,6 +367,11 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| _GuardedSubtypeChecker<DartType> guardedSubtype = _guard(_isSubtypeOf);
|
| _GuardedSubtypeChecker<DartType> guardedInferTypeParameter =
|
| _guard(_inferTypeParameterSubtypeOf);
|
| + // Void only appears as the return type of a function, any we handle it
|
| + // directly in the function subtype rules. We should not get to a point
|
| + // where we're doing a subtype test on a "bare" void.
|
| + assert(!t1.isVoid);
|
| + assert(!t2.isVoid);
|
|
|
| if (t1 == t2) {
|
| return true;
|
| @@ -448,6 +443,174 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
|
| return (t.isDynamic && !dynamicIsBottom) || t.isObject;
|
| }
|
| +
|
| + /// Computes the greatest lower bound of [type1] and [type2].
|
| + @override
|
| + DartType getGreatestLowerBound(
|
| + TypeProvider provider, DartType type1, DartType type2) {
|
| + // The greatest lower bound relation is reflexive.
|
| + if (identical(type1, type2)) {
|
| + return type1;
|
| + }
|
| +
|
| + // Treat dynamic as top. The GLB of dynamic and any type is just that type
|
| + // since dynamic permits all values.
|
| + if (type1.isDynamic) {
|
| + return type2;
|
| + }
|
| + if (type2.isDynamic) {
|
| + return type1;
|
| + }
|
| +
|
| + // You can't get any lower than bottom.
|
| + if (type1.isBottom || type2.isBottom) {
|
| + return provider.bottomType;
|
| + }
|
| +
|
| + // Treat void as top-like for GLB. This only comes into play with the
|
| + // return types of two functions whose GLB is being taken. We allow a
|
| + // non-void-returning function to subtype a void-returning one, so match
|
| + // that logic here by treating the non-void arm as the subtype for GLB.
|
| + if (type1.isVoid) {
|
| + return type2;
|
| + }
|
| + if (type2.isVoid) {
|
| + return type1;
|
| + }
|
| +
|
| + // Function types have structural GLB.
|
| + if (type1 is FunctionType && type2 is FunctionType) {
|
| + return _functionGreatestLowerBound(provider, type1, type2);
|
| + }
|
| +
|
| + // Otherwise, the GLB of two types is one of them it if it is a subtype of
|
| + // the other.
|
| + if (isSubtypeOf(type1, type2)) {
|
| + return type1;
|
| + }
|
| +
|
| + if (isSubtypeOf(type2, type1)) {
|
| + return type2;
|
| + }
|
| +
|
| + // No subtype relation, so no known GLB.
|
| + return provider.bottomType;
|
| + }
|
| +
|
| + /**
|
| + * Compute the greatest lower bound of function types [f] and [g].
|
| + *
|
| + * The spec rules for GLB on function types, informally, are pretty simple:
|
| + *
|
| + * - If a parameter is required in both, it stays required.
|
| + *
|
| + * - If a positional parameter is optional or missing in one, it becomes
|
| + * optional.
|
| + *
|
| + * - Named parameters are unioned together.
|
| + *
|
| + * - For any parameter that exists in both functions, use the LUB of them as
|
| + * the resulting parameter type.
|
| + *
|
| + * - Use the GLB of their return types.
|
| + */
|
| + DartType _functionGreatestLowerBound(
|
| + TypeProvider provider, FunctionType f, FunctionType g) {
|
| + // Calculate the LUB of each corresponding pair of parameters.
|
| + List<ParameterElement> parameters = [];
|
| +
|
| + bool hasPositional = false;
|
| + bool hasNamed = false;
|
| + addParameter(
|
| + String name, DartType fType, DartType gType, ParameterKind kind) {
|
| + DartType paramType;
|
| + if (fType != null && gType != null) {
|
| + // If both functions have this parameter, include both of their types.
|
| + paramType = getLeastUpperBound(provider, fType, gType);
|
| + } else {
|
| + paramType = fType ?? gType;
|
| + }
|
| +
|
| + parameters.add(new ParameterElementImpl.synthetic(name, paramType, kind));
|
| + }
|
| +
|
| + // TODO(rnystrom): Right now, this assumes f and g do not have any type
|
| + // parameters. Revisit that in the presence of generic methods.
|
| + List<DartType> fRequired = f.normalParameterTypes;
|
| + List<DartType> gRequired = g.normalParameterTypes;
|
| +
|
| + // We need some parameter names for in the synthesized function type.
|
| + List<String> fRequiredNames = f.normalParameterNames;
|
| + List<String> gRequiredNames = g.normalParameterNames;
|
| +
|
| + // Parameters that are required in both functions are required in the
|
| + // result.
|
| + int requiredCount = math.min(fRequired.length, gRequired.length);
|
| + for (int i = 0; i < requiredCount; i++) {
|
| + addParameter(fRequiredNames[i], fRequired[i], gRequired[i],
|
| + ParameterKind.REQUIRED);
|
| + }
|
| +
|
| + // Parameters that are optional or missing in either end up optional.
|
| + List<DartType> fPositional = f.optionalParameterTypes;
|
| + List<DartType> gPositional = g.optionalParameterTypes;
|
| + List<String> fPositionalNames = f.optionalParameterNames;
|
| + List<String> gPositionalNames = g.optionalParameterNames;
|
| +
|
| + int totalPositional = math.max(fRequired.length + fPositional.length,
|
| + gRequired.length + gPositional.length);
|
| + for (int i = requiredCount; i < totalPositional; i++) {
|
| + // Find the corresponding positional parameters (required or optional) at
|
| + // this index, if there is one.
|
| + DartType fType;
|
| + String fName;
|
| + if (i < fRequired.length) {
|
| + fType = fRequired[i];
|
| + fName = fRequiredNames[i];
|
| + } else if (i < fRequired.length + fPositional.length) {
|
| + fType = fPositional[i - fRequired.length];
|
| + fName = fPositionalNames[i - fRequired.length];
|
| + }
|
| +
|
| + DartType gType;
|
| + String gName;
|
| + if (i < gRequired.length) {
|
| + gType = gRequired[i];
|
| + gName = gRequiredNames[i];
|
| + } else if (i < gRequired.length + gPositional.length) {
|
| + gType = gPositional[i - gRequired.length];
|
| + gName = gPositionalNames[i - gRequired.length];
|
| + }
|
| +
|
| + // The loop should not let us go past both f and g's positional params.
|
| + assert(fType != null || gType != null);
|
| +
|
| + addParameter(fName ?? gName, fType, gType, ParameterKind.POSITIONAL);
|
| + hasPositional = true;
|
| + }
|
| +
|
| + // Union the named parameters together.
|
| + Map<String, DartType> fNamed = f.namedParameterTypes;
|
| + Map<String, DartType> gNamed = g.namedParameterTypes;
|
| + for (String name in fNamed.keys.toSet()..addAll(gNamed.keys)) {
|
| + addParameter(name, fNamed[name], gNamed[name], ParameterKind.NAMED);
|
| + hasNamed = true;
|
| + }
|
| +
|
| + // Edge case. Dart does not support functions with both optional positional
|
| + // and named parameters. If we would synthesize that, give up.
|
| + if (hasPositional && hasNamed) return provider.bottomType;
|
| +
|
| + // Calculate the GLB of the return type.
|
| + DartType returnType =
|
| + getGreatestLowerBound(provider, f.returnType, g.returnType);
|
| + return new FunctionElementImpl.synthetic(parameters, returnType).type;
|
| + }
|
| +
|
| + @override
|
| + DartType _functionParameterBound(
|
| + TypeProvider provider, DartType f, DartType g) =>
|
| + getGreatestLowerBound(provider, f, g);
|
| }
|
|
|
| /**
|
| @@ -473,7 +636,82 @@ abstract class TypeSystem {
|
| * Compute the least upper bound of two types.
|
| */
|
| DartType getLeastUpperBound(
|
| - TypeProvider typeProvider, DartType type1, DartType type2);
|
| + TypeProvider typeProvider, DartType type1, DartType type2) {
|
| + // The least upper bound relation is reflexive.
|
| + if (identical(type1, type2)) {
|
| + return type1;
|
| + }
|
| + // The least upper bound of dynamic and any type T is dynamic.
|
| + if (type1.isDynamic) {
|
| + return type1;
|
| + }
|
| + if (type2.isDynamic) {
|
| + return type2;
|
| + }
|
| + // The least upper bound of void and any type T != dynamic is void.
|
| + if (type1.isVoid) {
|
| + return type1;
|
| + }
|
| + if (type2.isVoid) {
|
| + return type2;
|
| + }
|
| + // The least upper bound of bottom and any type T is T.
|
| + if (type1.isBottom) {
|
| + return type2;
|
| + }
|
| + if (type2.isBottom) {
|
| + return type1;
|
| + }
|
| +
|
| + // TODO(rnystrom): This isn't correct. See: https://dartbug.com/26054.
|
| + // Leaf says:
|
| + // It does at least give *an* upper bound, but it won't be the least, and in
|
| + // many common cases won't be the useful one (e.g. T, S extends T).
|
| + //
|
| + // The spec definition is pretty unhelpful.
|
| + //
|
| + // I suspect that a reasonable algorithm is to expand the innermost type
|
| + // variable first. Alternatively, you could probably choose to treat it as
|
| + // just an instance of the interface type upper bound problem, with the
|
| + // "inheritance" chain extended by the bounds placed on the variables.
|
| + //
|
| + // In the short term, in the case that both are type variables you could
|
| + // check if either is a subtype of the other, and if so use the supertype,
|
| + // and otherwise fall back to expanding both. This should catch many of the
|
| + // interesting cases without getting too involved.
|
| + // Let U be a type variable with upper bound B. The least upper bound of U
|
| + // and a type T is the least upper bound of B and T.
|
| + type1 = type1.resolveToBound(typeProvider.objectType);
|
| + type2 = type2.resolveToBound(typeProvider.objectType);
|
| +
|
| + // The least upper bound of a function type and an interface type T is the
|
| + // least upper bound of Function and T.
|
| + if (type1 is FunctionType && type2 is InterfaceType) {
|
| + type1 = typeProvider.functionType;
|
| + }
|
| + if (type2 is FunctionType && type1 is InterfaceType) {
|
| + type2 = typeProvider.functionType;
|
| + }
|
| +
|
| + // At this point type1 and type2 should both either be interface types or
|
| + // function types.
|
| + if (type1 is InterfaceType && type2 is InterfaceType) {
|
| + InterfaceType result =
|
| + InterfaceTypeImpl.computeLeastUpperBound(type1, type2);
|
| + if (result == null) {
|
| + return typeProvider.dynamicType;
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + if (type1 is FunctionType && type2 is FunctionType) {
|
| + return _functionLeastUpperBound(typeProvider, type1, type2);
|
| + }
|
| +
|
| + // Should never happen. As a defensive measure, return the dynamic type.
|
| + assert(false);
|
| + return typeProvider.dynamicType;
|
| + }
|
|
|
| /**
|
| * Given a [DartType] [type], instantiate it with its bounds.
|
| @@ -594,111 +832,6 @@ abstract class TypeSystem {
|
| ? new StrongTypeSystemImpl()
|
| : new TypeSystemImpl();
|
| }
|
| -}
|
| -
|
| -/**
|
| - * Implementation of [TypeSystem] using the rules in the Dart specification.
|
| - */
|
| -class TypeSystemImpl extends TypeSystem {
|
| - TypeSystemImpl();
|
| -
|
| - @override
|
| - bool canPromoteToType(DartType to, DartType from) {
|
| - // Declared type should not be "dynamic".
|
| - // Promoted type should not be "dynamic".
|
| - // Promoted type should be more specific than declared.
|
| - return !from.isDynamic && !to.isDynamic && to.isMoreSpecificThan(from);
|
| - }
|
| -
|
| - @override
|
| - DartType getLeastUpperBound(
|
| - TypeProvider typeProvider, DartType type1, DartType type2) {
|
| - // The least upper bound relation is reflexive.
|
| - if (identical(type1, type2)) {
|
| - return type1;
|
| - }
|
| - // The least upper bound of dynamic and any type T is dynamic.
|
| - if (type1.isDynamic) {
|
| - return type1;
|
| - }
|
| - if (type2.isDynamic) {
|
| - return type2;
|
| - }
|
| - // The least upper bound of void and any type T != dynamic is void.
|
| - if (type1.isVoid) {
|
| - return type1;
|
| - }
|
| - if (type2.isVoid) {
|
| - return type2;
|
| - }
|
| - // The least upper bound of bottom and any type T is T.
|
| - if (type1.isBottom) {
|
| - return type2;
|
| - }
|
| - if (type2.isBottom) {
|
| - return type1;
|
| - }
|
| -
|
| - // Let U be a type variable with upper bound B. The least upper bound of U
|
| - // and a type T is the least upper bound of B and T.
|
| - type1 = type1.resolveToBound(typeProvider.objectType);
|
| - type2 = type2.resolveToBound(typeProvider.objectType);
|
| -
|
| - // The least upper bound of a function type and an interface type T is the
|
| - // least upper bound of Function and T.
|
| - if (type1 is FunctionType && type2 is InterfaceType) {
|
| - type1 = typeProvider.functionType;
|
| - }
|
| - if (type2 is FunctionType && type1 is InterfaceType) {
|
| - type2 = typeProvider.functionType;
|
| - }
|
| -
|
| - // At this point type1 and type2 should both either be interface types or
|
| - // function types.
|
| - if (type1 is InterfaceType && type2 is InterfaceType) {
|
| - InterfaceType result =
|
| - InterfaceTypeImpl.computeLeastUpperBound(type1, type2);
|
| - if (result == null) {
|
| - return typeProvider.dynamicType;
|
| - }
|
| - return result;
|
| - } else if (type1 is FunctionType && type2 is FunctionType) {
|
| - return _functionLeastUpperBound(typeProvider, type1, type2);
|
| - } else {
|
| - // Should never happen. As a defensive measure, return the dynamic type.
|
| - assert(false);
|
| - return typeProvider.dynamicType;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Instantiate a parameterized type using `dynamic` for all generic
|
| - * parameters. Returns the type unchanged if there are no parameters.
|
| - */
|
| - DartType instantiateToBounds(DartType type) {
|
| - List<DartType> typeFormals = typeFormalsAsTypes(type);
|
| - int count = typeFormals.length;
|
| - if (count > 0) {
|
| - List<DartType> typeArguments =
|
| - new List<DartType>.filled(count, DynamicTypeImpl.instance);
|
| - return instantiateType(type, typeArguments);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - @override
|
| - bool isAssignableTo(DartType leftType, DartType rightType) {
|
| - return leftType.isAssignableTo(rightType);
|
| - }
|
| -
|
| - @override
|
| - bool isMoreSpecificThan(DartType t1, DartType t2) =>
|
| - t1.isMoreSpecificThan(t2);
|
| -
|
| - @override
|
| - bool isSubtypeOf(DartType leftType, DartType rightType) {
|
| - return leftType.isSubtypeOf(rightType);
|
| - }
|
|
|
| /**
|
| * Compute the least upper bound of function types [f] and [g].
|
| @@ -739,7 +872,7 @@ class TypeSystemImpl extends TypeSystem {
|
| for (int i = 0; i < fRequired.length; i++) {
|
| parameters.add(new ParameterElementImpl.synthetic(
|
| fRequiredNames[i],
|
| - getLeastUpperBound(provider, fRequired[i], gRequired[i]),
|
| + _functionParameterBound(provider, fRequired[i], gRequired[i]),
|
| ParameterKind.REQUIRED));
|
| }
|
|
|
| @@ -752,7 +885,7 @@ class TypeSystemImpl extends TypeSystem {
|
| for (int i = 0; i < length; i++) {
|
| parameters.add(new ParameterElementImpl.synthetic(
|
| fPositionalNames[i],
|
| - getLeastUpperBound(provider, fPositional[i], gPositional[i]),
|
| + _functionParameterBound(provider, fPositional[i], gPositional[i]),
|
| ParameterKind.POSITIONAL));
|
| }
|
|
|
| @@ -761,21 +894,69 @@ class TypeSystemImpl extends TypeSystem {
|
| for (String name in fNamed.keys.toSet()..retainAll(gNamed.keys)) {
|
| parameters.add(new ParameterElementImpl.synthetic(
|
| name,
|
| - getLeastUpperBound(provider, fNamed[name], gNamed[name]),
|
| + _functionParameterBound(provider, fNamed[name], gNamed[name]),
|
| ParameterKind.NAMED));
|
| }
|
|
|
| // Calculate the LUB of the return type.
|
| DartType returnType =
|
| getLeastUpperBound(provider, f.returnType, g.returnType);
|
| + return new FunctionElementImpl.synthetic(parameters, returnType).type;
|
| + }
|
| +
|
| + /**
|
| + * Calculates the appropriate upper or lower bound of a pair of parameters
|
| + * for two function types whose least upper bound is being calculated.
|
| + *
|
| + * In spec mode, this uses least upper bound, which... doesn't really make
|
| + * much sense. Strong mode overrides this to use greatest lower bound.
|
| + */
|
| + DartType _functionParameterBound(
|
| + TypeProvider provider, DartType f, DartType g) =>
|
| + getLeastUpperBound(provider, f, g);
|
| +}
|
| +
|
| +/**
|
| + * Implementation of [TypeSystem] using the rules in the Dart specification.
|
| + */
|
| +class TypeSystemImpl extends TypeSystem {
|
| + TypeSystemImpl();
|
| +
|
| + @override
|
| + bool canPromoteToType(DartType to, DartType from) {
|
| + // Declared type should not be "dynamic".
|
| + // Promoted type should not be "dynamic".
|
| + // Promoted type should be more specific than declared.
|
| + return !from.isDynamic && !to.isDynamic && to.isMoreSpecificThan(from);
|
| + }
|
| +
|
| + /**
|
| + * Instantiate a parameterized type using `dynamic` for all generic
|
| + * parameters. Returns the type unchanged if there are no parameters.
|
| + */
|
| + DartType instantiateToBounds(DartType type) {
|
| + List<DartType> typeFormals = typeFormalsAsTypes(type);
|
| + int count = typeFormals.length;
|
| + if (count > 0) {
|
| + List<DartType> typeArguments =
|
| + new List<DartType>.filled(count, DynamicTypeImpl.instance);
|
| + return instantiateType(type, typeArguments);
|
| + }
|
| + return type;
|
| + }
|
| +
|
| + @override
|
| + bool isAssignableTo(DartType leftType, DartType rightType) {
|
| + return leftType.isAssignableTo(rightType);
|
| + }
|
|
|
| - FunctionElementImpl function = new FunctionElementImpl("", -1);
|
| - function.synthetic = true;
|
| - function.returnType = returnType;
|
| - function.parameters = parameters;
|
| + @override
|
| + bool isMoreSpecificThan(DartType t1, DartType t2) =>
|
| + t1.isMoreSpecificThan(t2);
|
|
|
| - function.type = new FunctionTypeImpl(function);
|
| - return function.type;
|
| + @override
|
| + bool isSubtypeOf(DartType leftType, DartType rightType) {
|
| + return leftType.isSubtypeOf(rightType);
|
| }
|
| }
|
|
|
|
|