Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1194)

Unified Diff: pkg/analyzer/lib/src/generated/type_system.dart

Issue 1893053002: Handle fuzzy optional parameters correctly. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/generated/error_verifier.dart ('k') | pkg/analyzer/lib/src/task/strong/checker.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 =
« no previous file with comments | « pkg/analyzer/lib/src/generated/error_verifier.dart ('k') | pkg/analyzer/lib/src/task/strong/checker.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698