Chromium Code Reviews| Index: pkg/analyzer/lib/src/generated/resolver.dart |
| diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart |
| index 5f0397fd8a6424039f2cd424581c57ce9f726570..ae01583c38c14742f87cf4746db9ee277f10e5ad 100644 |
| --- a/pkg/analyzer/lib/src/generated/resolver.dart |
| +++ b/pkg/analyzer/lib/src/generated/resolver.dart |
| @@ -4191,13 +4191,7 @@ class InferenceContext { |
| return; |
| } |
| - if (context is FutureUnionType) { |
| - // Try and match the Future type first. |
| - if (_typeSystem.isSubtypeOf(inferred, context.futureOfType) || |
| - _typeSystem.isSubtypeOf(inferred, context.type)) { |
| - setType(node, inferred); |
| - } |
| - } else if (_typeSystem.isSubtypeOf(inferred, context)) { |
| + if (_typeSystem.isSubtypeOf(inferred, context)) { |
| setType(node, inferred); |
| } |
| } else { |
| @@ -4360,17 +4354,17 @@ class InferenceContext { |
| static DartType getContext(AstNode node) => node?.getProperty(_typeProperty); |
| /** |
| - * Look for a single contextual type attached to [node], and returns the type |
| - * if found, otherwise null. |
| - * |
| - * If [node] has a contextual union type like `T | Future<T>` this will |
| - * simplify it to only return `T`. If the caller can handle a union type, |
| - * [getContext] should be used instead. |
| - */ |
| + * Look for a single contextual type attached to [node], and returns the type |
| + * if found, otherwise null. |
| + * |
| + * If [node] has a contextual union type like `T | Future<T>` this will |
| + * simplify it to only return `T`. If the caller can handle a union type, |
| + * [getContext] should be used instead. |
| + */ |
| static DartType getType(AstNode node) { |
| DartType t = getContext(node); |
| - if (t is FutureUnionType) { |
| - return t.type; |
| + if (t is InterfaceType && t.isDartAsyncFutureOr) { |
| + return t.typeArguments[0]; // The T in FutureOr<T> |
| } |
| return t; |
| } |
| @@ -4378,15 +4372,19 @@ class InferenceContext { |
| /** |
| * Like [getContext] but expands a union type into a list of types. |
| */ |
| - static Iterable<DartType> getTypes(AstNode node) { |
| + Iterable<DartType> getTypes(AstNode node) { |
| DartType t = getContext(node); |
| if (t == null) { |
| return DartType.EMPTY_LIST; |
| } |
| - if (t is FutureUnionType) { |
| - return t.types; |
| + if (t is InterfaceType && t.isDartAsyncFutureOr) { |
| + var tArg = t.typeArguments[0]; // The T in FutureOr<T> |
| + return [ |
| + _typeProvider.futureType.instantiate([tArg]), |
| + tArg |
| + ]; |
| } |
| - return <DartType>[t]; |
| + return [t]; |
| } |
| /** |
| @@ -5217,22 +5215,6 @@ class ResolverVisitor extends ScopedVisitor { |
| } |
| /** |
| - * Returns true if this method is `Future.then` or an override thereof. |
| - * |
| - * If so we will apply special typing rules in strong mode, to handle the |
| - * implicit union of `S | Future<S>` |
| - */ |
| - bool isFutureThen(Element element) { |
| - // If we are a method named then |
| - if (element is MethodElement && element.name == 'then') { |
| - DartType type = element.enclosingElement.type; |
| - // On Future or a subtype, then we're good. |
| - return (type.isDartAsyncFuture || isSubtypeOfFuture(type)); |
| - } |
| - return false; |
| - } |
| - |
| - /** |
| * Returns true if this type is any subtype of the built in Future type. |
| */ |
| bool isSubtypeOfFuture(DartType type) => |
| @@ -5528,8 +5510,7 @@ class ResolverVisitor extends ScopedVisitor { |
| Object visitAwaitExpression(AwaitExpression node) { |
| DartType contextType = InferenceContext.getContext(node); |
| if (contextType != null) { |
| - var futureUnion = |
| - FutureUnionType.from(contextType, typeProvider, typeSystem); |
| + var futureUnion = _createFutureOr(contextType); |
| InferenceContext.setType(node.expression, futureUnion); |
| } |
| return super.visitAwaitExpression(node); |
| @@ -6070,29 +6051,8 @@ class ResolverVisitor extends ScopedVisitor { |
| matchFunctionTypeParameters(node.typeParameters, functionType); |
| if (functionType is FunctionType) { |
| _inferFormalParameterList(node.parameters, functionType); |
| - |
| - DartType returnType; |
| - ParameterElement parameterElement = |
| - resolutionMap.staticParameterElementForExpression(node); |
| - if (isFutureThen(parameterElement?.enclosingElement)) { |
| - var futureThenType = |
| - InferenceContext.getContext(node.parent) as FunctionType; |
| - |
| - // Pretend the return type of Future<T>.then<S> first parameter is |
| - // |
| - // T -> (S | Future<S>) |
| - // |
| - // We can't represent this in Dart so we populate it here during |
| - // inference. |
| - var typeParamS = |
| - futureThenType.returnType.flattenFutures(typeSystem); |
| - returnType = |
| - FutureUnionType.from(typeParamS, typeProvider, typeSystem); |
| - } else { |
| - returnType = _computeReturnOrYieldType(functionType.returnType); |
| - } |
| - |
| - InferenceContext.setType(node.body, returnType); |
| + InferenceContext.setType( |
| + node.body, _computeReturnOrYieldType(functionType.returnType)); |
| } |
| } |
| super.visitFunctionExpression(node); |
| @@ -6221,7 +6181,7 @@ class ResolverVisitor extends ScopedVisitor { |
| // TODO(jmesserly): if we support union types for real, `new C<Ti | Tj>` |
| // will become a valid possibility. Right now the only allowed union is |
| // `T | Future<T>` so we can take a simple approach. |
| - for (var contextType in InferenceContext.getTypes(node)) { |
| + for (var contextType in inferenceContext.getTypes(node)) { |
| if (contextType is InterfaceType && |
| contextType.typeArguments != null && |
| contextType.typeArguments.isNotEmpty) { |
| @@ -6657,12 +6617,51 @@ class ResolverVisitor extends ScopedVisitor { |
| } |
| // async functions expect `Future<T> | T` |
| var futureTypeParam = declaredType.flattenFutures(typeSystem); |
| - return FutureUnionType.from(futureTypeParam, typeProvider, typeSystem); |
| + return _createFutureOr(futureTypeParam); |
| } |
| return declaredType; |
| } |
| /** |
| + * Creates a union of `T | Future<T>`, unless `T` is already a future or a |
| + * future-union, in which case it simply returns `T`. |
| + * |
| + * Conceptually this is used as the inverse of the `flatten(T)` operation, |
| + * defined as: |
| + * |
| + * - `flatten(Future<T>) -> T` |
| + * - `flatten(T) -> T` |
| + * |
| + * Thus the inverse will give us `T | Future<T>`. |
| + * |
| + * If [type] is top (dynamic or Object) then the resulting union type is |
|
Leaf
2017/01/24 21:56:26
Can you give some examples of why you want to do t
Jennifer Messerly
2017/01/25 00:33:35
This code already existed -- it was moved from "Fu
|
| + * equivalent to top, so we simply return it. |
| + * |
| + * If [type] is bottom, it is equivalent to `Future<bottom>` |
| + * |
| + * For a similar reason `Future<T> | Future<Future<T>>` is equivalent to just |
|
Leaf
2017/01/24 21:56:26
I'd really like to avoid putting this in if at all
Jennifer Messerly
2017/01/25 00:33:35
(same comment as above -- this code already existe
|
| + * `Future<T>`, so we return it. Note that it is not possible to get a |
| + * `Future<T>` as a result of `flatten`, so a this case likely indicates a |
| + * type error in the code, but it will be reported elsewhere. |
| + */ |
| + DartType _createFutureOr(DartType type) { |
| + if (type.isObject || type.isDynamic) { |
| + return type; |
| + } |
| + if (!identical(type, type.flattenFutures(typeSystem))) { |
| + // As noted above, this most likely represents erroneous input. |
| + return type; |
| + } |
| + if (type.isDartAsyncFutureOr) { |
| + return type; |
| + } |
| + if (type.isBottom) { |
| + return typeProvider.futureType.instantiate([type]); |
| + } |
| + return typeProvider.futureOrType.instantiate([type]); |
| + } |
| + |
| + /** |
| * The given expression is the expression used to compute the iterator for a |
| * for-each statement. Attempt to compute the type of objects that will be |
| * assigned to the loop variable and return that type. Return `null` if the |
| @@ -8287,8 +8286,23 @@ class TypeNameResolver { |
| } |
| DartType type = null; |
| if (element is ClassElement) { |
| - _setElement(typeName, element); |
| type = element.type; |
| + // In non-strong mode `FutureOr<T>` is treated as `dynamic` |
| + if (!typeSystem.isStrong && type.isDartAsyncFutureOr) { |
| + type = dynamicType; |
| + _setElement(typeName, type.element); |
| + typeName.staticType = type; |
| + node.type = type; |
| + if (argumentList != null) { |
| + NodeList<TypeAnnotation> arguments = argumentList.arguments; |
| + if (arguments.length != 1) { |
| + reportErrorForNode(_getInvalidTypeParametersErrorCode(node), node, |
| + [typeName.name, 1, arguments.length]); |
| + } |
| + } |
| + return; |
| + } |
| + _setElement(typeName, element); |
| } else if (element is FunctionTypeAliasElement) { |
| _setElement(typeName, element); |
| type = element.type; |