| 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 9caffef9643c06c118961057ad1e653d31875e2f..650397e53c0e91364f91519c442a40bd7628ad2e 100644
|
| --- a/pkg/analyzer/lib/src/generated/type_system.dart
|
| +++ b/pkg/analyzer/lib/src/generated/type_system.dart
|
| @@ -29,6 +29,36 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| @override
|
| bool canPromoteToType(DartType to, DartType from) => isSubtypeOf(to, from);
|
|
|
| + @override
|
| + FunctionType functionTypeToConcreteType(
|
| + TypeProvider typeProvider, FunctionType t) {
|
| + // TODO(jmesserly): should we use a real "fuzzyArrow" bit on the function
|
| + // type? That would allow us to implement this in the subtype relation.
|
| + // TODO(jmesserly): we'll need to factor this differently if we want to
|
| + // move CodeChecker's functionality into existing analyzer. Likely we can
|
| + // let the Expression have a strict arrow, then in places were we do
|
| + // inference, convert back to a fuzzy arrow.
|
| +
|
| + if (!t.parameters.any((p) => p.type.isDynamic)) {
|
| + return t;
|
| + }
|
| + ParameterElement shave(ParameterElement p) {
|
| + if (p.type.isDynamic) {
|
| + return new ParameterElementImpl.synthetic(
|
| + p.name, typeProvider.objectType, p.parameterKind);
|
| + }
|
| + return p;
|
| + }
|
| +
|
| + List<ParameterElement> parameters = t.parameters.map(shave).toList();
|
| + FunctionElementImpl function = new FunctionElementImpl("", -1);
|
| + function.synthetic = true;
|
| + function.returnType = t.returnType;
|
| + function.shareTypeParameters(t.typeFormals);
|
| + function.shareParameters(parameters);
|
| + return function.type = new FunctionTypeImpl(function);
|
| + }
|
| +
|
| /**
|
| * Given a type t, if t is an interface type with a call method
|
| * defined, return the function type for the call method, otherwise
|
| @@ -95,111 +125,6 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| }
|
|
|
| /**
|
| - * This currently does not implement a very complete least upper bound
|
| - * algorithm, but handles a couple of the very common cases that are
|
| - * causing pain in real code. The current algorithm is:
|
| - * 1. If either of the types is a supertype of the other, return it.
|
| - * This is in fact the best result in this case.
|
| - * 2. If the two types have the same class element, then take the
|
| - * pointwise least upper bound of the type arguments. This is again
|
| - * the best result, except that the recursive calls may not return
|
| - * the true least uppper bounds. The result is guaranteed to be a
|
| - * well-formed type under the assumption that the input types were
|
| - * well-formed (and assuming that the recursive calls return
|
| - * well-formed types).
|
| - * 3. Otherwise return the spec-defined least upper bound. This will
|
| - * be an upper bound, might (or might not) be least, and might
|
| - * (or might not) be a well-formed type.
|
| - *
|
| - * TODO(leafp): Use matchTypes or something similar here to handle the
|
| - * case where one of the types is a superclass (but not supertype) of
|
| - * the other, e.g. LUB(Iterable<double>, List<int>) = Iterable<num>
|
| - * TODO(leafp): Figure out the right final algorithm and implement it.
|
| - */
|
| - @override
|
| - DartType _interfaceLeastUpperBound(
|
| - TypeProvider provider, InterfaceType type1, InterfaceType type2) {
|
| - if (isSubtypeOf(type1, type2)) {
|
| - return type2;
|
| - }
|
| - if (isSubtypeOf(type2, type1)) {
|
| - return type1;
|
| - }
|
| - if (type1.element == type2.element) {
|
| - List<DartType> tArgs1 = type1.typeArguments;
|
| - List<DartType> tArgs2 = type2.typeArguments;
|
| -
|
| - assert(tArgs1.length == tArgs2.length);
|
| - List<DartType> tArgs = new List(tArgs1.length);
|
| - for (int i = 0; i < tArgs1.length; i++) {
|
| - tArgs[i] = getLeastUpperBound(provider, tArgs1[i], tArgs2[i]);
|
| - }
|
| - InterfaceTypeImpl lub = new InterfaceTypeImpl(type1.element);
|
| - lub.typeArguments = tArgs;
|
| - return lub;
|
| - }
|
| - return InterfaceTypeImpl.computeLeastUpperBound(type1, type2) ??
|
| - provider.dynamicType;
|
| - }
|
| -
|
| - /**
|
| - * This currently just implements a simple least upper bound to
|
| - * handle some common cases. It also avoids some termination issues
|
| - * with the naive spec algorithm. The least upper bound of two types
|
| - * (at least one of which is a type parameter) is computed here as:
|
| - * 1. If either type is a supertype of the other, return it.
|
| - * 2. If the first type is a type parameter, replace it with its bound,
|
| - * with recursive occurrences of itself replaced with Object.
|
| - * The second part of this should ensure termination. Informally,
|
| - * each type variable instantiation in one of the arguments to the
|
| - * least upper bound algorithm now strictly reduces the number
|
| - * of bound variables in scope in that argument position.
|
| - * 3. If the second type is a type parameter, do the symmetric operation
|
| - * to #2.
|
| - *
|
| - * It's not immediately obvious why this is symmetric in the case that both
|
| - * of the them are type parameters. For #1, symmetry holds since subtype
|
| - * is antisymmetric. For #2, it's clearly not symmetric if upper bounds of
|
| - * bottom are allowed. Ignoring this (for various reasons, not least
|
| - * of which that there's no way to write it), there's an informal
|
| - * argument (that might even be right) that you will always either
|
| - * end up expanding both of them or else returning the same result no matter
|
| - * which order you expand them in. A key observation is that
|
| - * identical(expand(type1), type2) => subtype(type1, type2)
|
| - * and hence the contra-positive.
|
| - *
|
| - * TODO(leafp): Think this through and figure out what's the right
|
| - * definition. Be careful about termination.
|
| - *
|
| - * I suspect in general 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.
|
| - */
|
| - @override
|
| - DartType _typeParameterLeastUpperBound(
|
| - TypeProvider provider, DartType type1, DartType type2) {
|
| - if (isSubtypeOf(type1, type2)) {
|
| - return type2;
|
| - }
|
| - if (isSubtypeOf(type2, type1)) {
|
| - return type1;
|
| - }
|
| - if (type1 is TypeParameterType) {
|
| - type1 = type1
|
| - .resolveToBound(provider.objectType)
|
| - .substitute2([provider.objectType], [type1]);
|
| - return getLeastUpperBound(provider, type1, type2);
|
| - }
|
| - // We should only be called when at least one of the types is a
|
| - // TypeParameterType
|
| - type2 = type2
|
| - .resolveToBound(provider.objectType)
|
| - .substitute2([provider.objectType], [type2]);
|
| - return getLeastUpperBound(provider, 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.
|
| *
|
| @@ -417,6 +342,14 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| return _isSubtypeOf(leftType, rightType, null);
|
| }
|
|
|
| + @override
|
| + DartType typeToConcreteType(TypeProvider typeProvider, DartType t) {
|
| + if (t is FunctionType) {
|
| + return functionTypeToConcreteType(typeProvider, t);
|
| + }
|
| + return t;
|
| + }
|
| +
|
| /**
|
| * Compute the greatest lower bound of function types [f] and [g].
|
| *
|
| @@ -561,6 +494,54 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| return false;
|
| }
|
|
|
| + /**
|
| + * This currently does not implement a very complete least upper bound
|
| + * algorithm, but handles a couple of the very common cases that are
|
| + * causing pain in real code. The current algorithm is:
|
| + * 1. If either of the types is a supertype of the other, return it.
|
| + * This is in fact the best result in this case.
|
| + * 2. If the two types have the same class element, then take the
|
| + * pointwise least upper bound of the type arguments. This is again
|
| + * the best result, except that the recursive calls may not return
|
| + * the true least uppper bounds. The result is guaranteed to be a
|
| + * well-formed type under the assumption that the input types were
|
| + * well-formed (and assuming that the recursive calls return
|
| + * well-formed types).
|
| + * 3. Otherwise return the spec-defined least upper bound. This will
|
| + * be an upper bound, might (or might not) be least, and might
|
| + * (or might not) be a well-formed type.
|
| + *
|
| + * TODO(leafp): Use matchTypes or something similar here to handle the
|
| + * case where one of the types is a superclass (but not supertype) of
|
| + * the other, e.g. LUB(Iterable<double>, List<int>) = Iterable<num>
|
| + * TODO(leafp): Figure out the right final algorithm and implement it.
|
| + */
|
| + @override
|
| + DartType _interfaceLeastUpperBound(
|
| + TypeProvider provider, InterfaceType type1, InterfaceType type2) {
|
| + if (isSubtypeOf(type1, type2)) {
|
| + return type2;
|
| + }
|
| + if (isSubtypeOf(type2, type1)) {
|
| + return type1;
|
| + }
|
| + if (type1.element == type2.element) {
|
| + List<DartType> tArgs1 = type1.typeArguments;
|
| + List<DartType> tArgs2 = type2.typeArguments;
|
| +
|
| + assert(tArgs1.length == tArgs2.length);
|
| + List<DartType> tArgs = new List(tArgs1.length);
|
| + for (int i = 0; i < tArgs1.length; i++) {
|
| + tArgs[i] = getLeastUpperBound(provider, tArgs1[i], tArgs2[i]);
|
| + }
|
| + InterfaceTypeImpl lub = new InterfaceTypeImpl(type1.element);
|
| + lub.typeArguments = tArgs;
|
| + return lub;
|
| + }
|
| + return InterfaceTypeImpl.computeLeastUpperBound(type1, type2) ??
|
| + provider.dynamicType;
|
| + }
|
| +
|
| bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
|
| return (t.isDynamic && dynamicIsBottom) || t.isBottom;
|
| }
|
| @@ -716,6 +697,63 @@ class StrongTypeSystemImpl extends TypeSystem {
|
| // TODO(leafp): Document the rules in play here
|
| return (t.isDynamic && !dynamicIsBottom) || t.isObject;
|
| }
|
| +
|
| + /**
|
| + * This currently just implements a simple least upper bound to
|
| + * handle some common cases. It also avoids some termination issues
|
| + * with the naive spec algorithm. The least upper bound of two types
|
| + * (at least one of which is a type parameter) is computed here as:
|
| + * 1. If either type is a supertype of the other, return it.
|
| + * 2. If the first type is a type parameter, replace it with its bound,
|
| + * with recursive occurrences of itself replaced with Object.
|
| + * The second part of this should ensure termination. Informally,
|
| + * each type variable instantiation in one of the arguments to the
|
| + * least upper bound algorithm now strictly reduces the number
|
| + * of bound variables in scope in that argument position.
|
| + * 3. If the second type is a type parameter, do the symmetric operation
|
| + * to #2.
|
| + *
|
| + * It's not immediately obvious why this is symmetric in the case that both
|
| + * of the them are type parameters. For #1, symmetry holds since subtype
|
| + * is antisymmetric. For #2, it's clearly not symmetric if upper bounds of
|
| + * bottom are allowed. Ignoring this (for various reasons, not least
|
| + * of which that there's no way to write it), there's an informal
|
| + * argument (that might even be right) that you will always either
|
| + * end up expanding both of them or else returning the same result no matter
|
| + * which order you expand them in. A key observation is that
|
| + * identical(expand(type1), type2) => subtype(type1, type2)
|
| + * and hence the contra-positive.
|
| + *
|
| + * TODO(leafp): Think this through and figure out what's the right
|
| + * definition. Be careful about termination.
|
| + *
|
| + * I suspect in general 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.
|
| + */
|
| + @override
|
| + DartType _typeParameterLeastUpperBound(
|
| + TypeProvider provider, DartType type1, DartType type2) {
|
| + if (isSubtypeOf(type1, type2)) {
|
| + return type2;
|
| + }
|
| + if (isSubtypeOf(type2, type1)) {
|
| + return type1;
|
| + }
|
| + if (type1 is TypeParameterType) {
|
| + type1 = type1
|
| + .resolveToBound(provider.objectType)
|
| + .substitute2([provider.objectType], [type1]);
|
| + return getLeastUpperBound(provider, type1, type2);
|
| + }
|
| + // We should only be called when at least one of the types is a
|
| + // TypeParameterType
|
| + type2 = type2
|
| + .resolveToBound(provider.objectType)
|
| + .substitute2([provider.objectType], [type2]);
|
| + return getLeastUpperBound(provider, type1, type2);
|
| + }
|
| }
|
|
|
| /**
|
| @@ -738,6 +776,25 @@ abstract class TypeSystem {
|
| bool canPromoteToType(DartType to, DartType from);
|
|
|
| /**
|
| + * Make a function type concrete.
|
| + *
|
| + * Normally we treat dynamically typed parameters as bottom for function
|
| + * types. This allows type tests such as `if (f is SingleArgFunction)`.
|
| + * It also requires a dynamic check on the parameter type to call these
|
| + * functions.
|
| + *
|
| + * When we convert to a strict arrow, dynamically typed parameters become
|
| + * top. This is safe to do for known functions, like top-level or local
|
| + * functions and static methods. Those functions must already be essentially
|
| + * treating dynamic as top.
|
| + *
|
| + * Only the outer-most arrow can be strict. Any others must be fuzzy, because
|
| + * we don't know what function value will be passed there.
|
| + */
|
| + FunctionType functionTypeToConcreteType(
|
| + TypeProvider typeProvider, FunctionType t);
|
| +
|
| + /**
|
| * Compute the least upper bound of two types.
|
| */
|
| DartType getLeastUpperBound(
|
| @@ -797,21 +854,6 @@ abstract class TypeSystem {
|
| }
|
|
|
| /**
|
| - * Given two [InterfaceType]s [type1] and [type2] return their least upper
|
| - * bound in a type system specific manner.
|
| - */
|
| - DartType _interfaceLeastUpperBound(
|
| - TypeProvider provider, InterfaceType type1, InterfaceType type2);
|
| -
|
| - /**
|
| - * Given two [DartType]s [type1] and [type2] at least one of which is a
|
| - * [TypeParameterType], return their least upper bound in a type system
|
| - * specific manner.
|
| - */
|
| - DartType _typeParameterLeastUpperBound(
|
| - TypeProvider provider, DartType type1, DartType type2);
|
| -
|
| - /**
|
| * Given a [DartType] [type], instantiate it with its bounds.
|
| *
|
| * The behavior of this method depends on the type system, for example, in
|
| @@ -923,6 +965,14 @@ abstract class TypeSystem {
|
| TypeParameterTypeImpl.getTypes(typeFormalsAsElements(type));
|
|
|
| /**
|
| + * Make a type concrete. A type is concrete if it is not a function
|
| + * type, or if it is a function type with no dynamic parameters. A
|
| + * non-concrete function type is made concrete by replacing dynamic
|
| + * parameters with Object.
|
| + */
|
| + DartType typeToConcreteType(TypeProvider typeProvider, DartType t);
|
| +
|
| + /**
|
| * Compute the least upper bound of function types [f] and [g].
|
| *
|
| * The spec rules for LUB on function types, informally, are pretty simple
|
| @@ -1005,6 +1055,21 @@ abstract class TypeSystem {
|
| getLeastUpperBound(provider, f, g);
|
|
|
| /**
|
| + * Given two [InterfaceType]s [type1] and [type2] return their least upper
|
| + * bound in a type system specific manner.
|
| + */
|
| + DartType _interfaceLeastUpperBound(
|
| + TypeProvider provider, InterfaceType type1, InterfaceType type2);
|
| +
|
| + /**
|
| + * Given two [DartType]s [type1] and [type2] at least one of which is a
|
| + * [TypeParameterType], return their least upper bound in a type system
|
| + * specific manner.
|
| + */
|
| + DartType _typeParameterLeastUpperBound(
|
| + TypeProvider provider, DartType type1, DartType type2);
|
| +
|
| + /**
|
| * Create either a strong mode or regular type system based on context.
|
| */
|
| static TypeSystem create(AnalysisContext context) {
|
| @@ -1028,6 +1093,11 @@ class TypeSystemImpl extends TypeSystem {
|
| return !from.isDynamic && !to.isDynamic && to.isMoreSpecificThan(from);
|
| }
|
|
|
| + @override
|
| + FunctionType functionTypeToConcreteType(
|
| + TypeProvider typeProvider, FunctionType t) =>
|
| + t;
|
| +
|
| /**
|
| * Instantiate a parameterized type using `dynamic` for all generic
|
| * parameters. Returns the type unchanged if there are no parameters.
|
| @@ -1058,6 +1128,9 @@ class TypeSystemImpl extends TypeSystem {
|
| }
|
|
|
| @override
|
| + DartType typeToConcreteType(TypeProvider typeProvider, DartType t) => t;
|
| +
|
| + @override
|
| DartType _interfaceLeastUpperBound(
|
| TypeProvider provider, InterfaceType type1, InterfaceType type2) {
|
| InterfaceType result =
|
|
|