@@ -4697,8 +4697,6 @@ class InferenceContext {
4697
4697
* A stack of return types for all of the enclosing
4698
4698
* functions and methods.
4699
4699
*/
4700
- // TODO(leafp) Handle the implicit union type for Futures
4701
- // https://github.com/dart-lang/sdk/issues/25322
4702
4700
final List <DartType > _returnStack = < DartType > [];
4703
4701
4704
4702
InferenceContext ._(this ._errorReporter, TypeProvider typeProvider,
@@ -4727,9 +4725,12 @@ class InferenceContext {
4727
4725
if (_returnStack.isEmpty) {
4728
4726
return ;
4729
4727
}
4730
- DartType inferred = _inferredReturn.last;
4731
- inferred = _typeSystem.getLeastUpperBound (_typeProvider, type, inferred);
4732
- _inferredReturn[_inferredReturn.length - 1 ] = inferred;
4728
+ DartType context = _returnStack.last;
4729
+ if (context is ! FutureUnionType ) {
4730
+ DartType inferred = _inferredReturn.last;
4731
+ inferred = _typeSystem.getLeastUpperBound (_typeProvider, type, inferred);
4732
+ _inferredReturn[_inferredReturn.length - 1 ] = inferred;
4733
+ }
4733
4734
}
4734
4735
4735
4736
/**
@@ -4766,8 +4767,7 @@ class InferenceContext {
4766
4767
* Push a block function body's return type onto the return stack.
4767
4768
*/
4768
4769
void pushReturnContext (BlockFunctionBody node) {
4769
- DartType returnType = getType (node);
4770
- _returnStack.add (returnType);
4770
+ _returnStack.add (getContext (node));
4771
4771
_inferredReturn.add (BottomTypeImpl .instance);
4772
4772
}
4773
4773
@@ -4908,19 +4908,50 @@ class InferenceContext {
4908
4908
node? .setProperty (_typeProperty, null );
4909
4909
}
4910
4910
4911
+ /**
4912
+ * Look for a single contextual type attached to [node] , and returns the type
4913
+ * if found, otherwise null.
4914
+ *
4915
+ * If [node] has a contextual union type like `T | Future<T>` this will
4916
+ * simplify it to only return `T` . If the caller can handle a union type,
4917
+ * [getContext] should be used instead.
4918
+ */
4919
+ static DartType getType (AstNode node) {
4920
+ DartType t = getContext (node);
4921
+ if (t is FutureUnionType ) {
4922
+ return t.type;
4923
+ }
4924
+ return t;
4925
+ }
4926
+
4911
4927
/**
4912
4928
* Look for contextual type information attached to [node] . Returns
4913
4929
* the type if found, otherwise null.
4930
+ *
4931
+ * If [node] has a contextual union type like `T | Future<T>` this will be
4932
+ * returned. You can use [getType] if you prefer to only get the `T` .
4914
4933
*/
4915
- static DartType getType (AstNode node) => node? .getProperty (_typeProperty);
4934
+ static DartType getContext (AstNode node) => node? .getProperty (_typeProperty);
4935
+
4936
+ /**
4937
+ * Like [getContext] but expands a union type into a list of types.
4938
+ */
4939
+ static Iterable <DartType > getTypes (AstNode node) {
4940
+ DartType t = getContext (node);
4941
+ if (t == null ) {
4942
+ return DartType .EMPTY_LIST ;
4943
+ }
4944
+ if (t is FutureUnionType ) {
4945
+ return t.types;
4946
+ }
4947
+ return < DartType > [t];
4948
+ }
4916
4949
4917
4950
/**
4918
4951
* Attach contextual type information [type] to [node] for use during
4919
4952
* inference.
4920
4953
*/
4921
4954
static void setType (AstNode node, DartType type) {
4922
- // TODO(jmesserly): this sets the type even when it's dynamic.
4923
- // Can we skip that?
4924
4955
node? .setProperty (_typeProperty, type);
4925
4956
}
4926
4957
@@ -4929,7 +4960,7 @@ class InferenceContext {
4929
4960
* inference.
4930
4961
*/
4931
4962
static void setTypeFromNode (AstNode innerNode, AstNode outerNode) {
4932
- setType (innerNode, getType (outerNode));
4963
+ setType (innerNode, getContext (outerNode));
4933
4964
}
4934
4965
}
4935
4966
@@ -6013,13 +6044,11 @@ class ResolverVisitor extends ScopedVisitor {
6013
6044
6014
6045
@override
6015
6046
Object visitAwaitExpression (AwaitExpression node) {
6016
- // TODO(leafp): Handle the implicit union type here
6017
- // https://github.com/dart-lang/sdk/issues/25322
6018
- DartType contextType = InferenceContext .getType (node);
6047
+ DartType contextType = InferenceContext .getContext (node);
6019
6048
if (contextType != null ) {
6020
- InterfaceType futureT = typeProvider.futureType
6021
- . instantiate ([ contextType. flattenFutures ( typeSystem)] );
6022
- InferenceContext .setType (node.expression, futureT );
6049
+ var futureUnion =
6050
+ FutureUnionType . from ( contextType, typeProvider, typeSystem);
6051
+ InferenceContext .setType (node.expression, futureUnion );
6023
6052
}
6024
6053
return super .visitAwaitExpression (node);
6025
6054
}
@@ -6073,7 +6102,7 @@ class ResolverVisitor extends ScopedVisitor {
6073
6102
if (operatorType == TokenType .QUESTION_QUESTION ) {
6074
6103
// Set the right side, either from the context, or using the information
6075
6104
// from the left side if it is more precise.
6076
- DartType contextType = InferenceContext .getType (node);
6105
+ DartType contextType = InferenceContext .getContext (node);
6077
6106
DartType leftType = leftOperand? .staticType;
6078
6107
if (contextType == null || contextType.isDynamic) {
6079
6108
contextType = leftType;
@@ -6551,8 +6580,24 @@ class ResolverVisitor extends ScopedVisitor {
6551
6580
matchFunctionTypeParameters (node.typeParameters, functionType);
6552
6581
if (functionType is FunctionType ) {
6553
6582
_inferFormalParameterList (node.parameters, functionType);
6554
- DartType returnType =
6555
- _computeReturnOrYieldType (functionType.returnType);
6583
+
6584
+ DartType returnType;
6585
+ if (_isFutureThenLambda (node)) {
6586
+ var futureThenType =
6587
+ InferenceContext .getContext (node.parent) as FunctionType ;
6588
+
6589
+ // Pretend the return type of Future<T>.then<S> first parameter is
6590
+ //
6591
+ // T -> (S | Future<S>)
6592
+ //
6593
+ // We can't represent this in Dart so we populate it here during
6594
+ // inference.
6595
+ returnType = FutureUnionType .from (
6596
+ futureThenType.returnType, typeProvider, typeSystem);
6597
+ } else {
6598
+ returnType = _computeReturnOrYieldType (functionType.returnType);
6599
+ }
6600
+
6556
6601
InferenceContext .setType (node.body, returnType);
6557
6602
}
6558
6603
}
@@ -6668,30 +6713,37 @@ class ResolverVisitor extends ScopedVisitor {
6668
6713
Object visitInstanceCreationExpression (InstanceCreationExpression node) {
6669
6714
TypeName classTypeName = node.constructorName.type;
6670
6715
if (classTypeName.typeArguments == null ) {
6671
- DartType contextType = InferenceContext .getType (node);
6672
- if (contextType is InterfaceType &&
6673
- contextType.typeArguments != null &&
6674
- contextType.typeArguments.length > 0 ) {
6675
- // TODO(jmesserly): for generic methods we use the
6676
- // StrongTypeSystemImpl.inferGenericFunctionCall, which appears to
6677
- // be a tad more powerful than matchTypes.
6678
- //
6679
- // For example it can infer this case:
6680
- //
6681
- // class E<S, T> extends A<C<S>, T> { ... }
6682
- // A<C<int>, String> a0 = /*infer<int, String>*/new E("hello");
6683
- //
6684
- // See _inferArgumentTypesFromContext in this file for use of it.
6685
- List <DartType > targs =
6686
- inferenceContext.matchTypes (classTypeName.type, contextType);
6687
- if (targs != null && targs.any ((t) => ! t.isDynamic)) {
6688
- ClassElement classElement = classTypeName.type.element;
6689
- InterfaceType rawType = classElement.type;
6690
- InterfaceType fullType =
6691
- rawType.substitute2 (targs, rawType.typeArguments);
6692
- // The element resolver uses the type on the constructor name, so
6693
- // infer it first
6694
- typeAnalyzer.inferConstructorName (node.constructorName, fullType);
6716
+ // Given a union of context types ` T0 | T1 | ... | Tn`, find the first
6717
+ // valid instantiation `new C<Ti>`, if it exists.
6718
+ // TODO(jmesserly): if we support union types for real, `new C<Ti | Tj>`
6719
+ // will become a valid possibility. Right now the only allowed union is
6720
+ // `T | Future<T>` so we can take a simple approach.
6721
+ for (var contextType in InferenceContext .getTypes (node)) {
6722
+ if (contextType is InterfaceType &&
6723
+ contextType.typeArguments != null &&
6724
+ contextType.typeArguments.isNotEmpty) {
6725
+ // TODO(jmesserly): for generic methods we use the
6726
+ // StrongTypeSystemImpl.inferGenericFunctionCall, which appears to
6727
+ // be a tad more powerful than matchTypes.
6728
+ //
6729
+ // For example it can infer this case:
6730
+ //
6731
+ // class E<S, T> extends A<C<S>, T> { ... }
6732
+ // A<C<int>, String> a0 = /*infer<int, String>*/new E("hello");
6733
+ //
6734
+ // See _inferArgumentTypesFromContext in this file for use of it.
6735
+ List <DartType > targs =
6736
+ inferenceContext.matchTypes (classTypeName.type, contextType);
6737
+ if (targs != null && targs.any ((t) => ! t.isDynamic)) {
6738
+ ClassElement classElement = classTypeName.type.element;
6739
+ InterfaceType rawType = classElement.type;
6740
+ InterfaceType fullType =
6741
+ rawType.substitute2 (targs, rawType.typeArguments);
6742
+ // The element resolver uses the type on the constructor name, so
6743
+ // infer it first
6744
+ typeAnalyzer.inferConstructorName (node.constructorName, fullType);
6745
+ break ;
6746
+ }
6695
6747
}
6696
6748
}
6697
6749
}
@@ -6805,7 +6857,7 @@ class ResolverVisitor extends ScopedVisitor {
6805
6857
6806
6858
@override
6807
6859
Object visitNamedExpression (NamedExpression node) {
6808
- InferenceContext .setType (node.expression, InferenceContext . getType ( node) );
6860
+ InferenceContext .setTypeFromNode (node.expression, node);
6809
6861
return super .visitNamedExpression (node);
6810
6862
}
6811
6863
@@ -6819,7 +6871,7 @@ class ResolverVisitor extends ScopedVisitor {
6819
6871
6820
6872
@override
6821
6873
Object visitParenthesizedExpression (ParenthesizedExpression node) {
6822
- InferenceContext .setType (node.expression, InferenceContext . getType ( node) );
6874
+ InferenceContext .setTypeFromNode (node.expression, node);
6823
6875
return super .visitParenthesizedExpression (node);
6824
6876
}
6825
6877
@@ -6937,7 +6989,7 @@ class ResolverVisitor extends ScopedVisitor {
6937
6989
6938
6990
@override
6939
6991
Object visitVariableDeclaration (VariableDeclaration node) {
6940
- InferenceContext .setType (node.initializer, InferenceContext . getType ( node) );
6992
+ InferenceContext .setTypeFromNode (node.initializer, node);
6941
6993
super .visitVariableDeclaration (node);
6942
6994
VariableElement element = node.element;
6943
6995
if (element.initializer != null && node.initializer != null ) {
@@ -7081,21 +7133,22 @@ class ResolverVisitor extends ScopedVisitor {
7081
7133
if (! isGenerator && ! isAsynchronous) {
7082
7134
return declaredType;
7083
7135
}
7084
- if (isGenerator) {
7085
- if (declaredType is ! InterfaceType ) {
7086
- return null ;
7136
+ if (declaredType is InterfaceType ) {
7137
+ if (isGenerator) {
7138
+ // If it's sync* we expect Iterable<T>
7139
+ // If it's async* we expect Stream<T>
7140
+ InterfaceType rawType = isAsynchronous
7141
+ ? typeProvider.streamDynamicType
7142
+ : typeProvider.iterableDynamicType;
7143
+ // Match the types to instantiate the type arguments if possible
7144
+ List <DartType > typeArgs =
7145
+ inferenceContext.matchTypes (rawType, declaredType);
7146
+ return (typeArgs? .length == 1 ) ? typeArgs[0 ] : null ;
7087
7147
}
7088
- // If it's synchronous, we expect Iterable<T>, otherwise Stream<T>
7089
- InterfaceType rawType = isAsynchronous
7090
- ? typeProvider.streamDynamicType
7091
- : typeProvider.iterableDynamicType;
7092
- // Match the types to instantiate the type arguments if possible
7093
- List <DartType > typeArgs =
7094
- inferenceContext.matchTypes (rawType, declaredType);
7095
- return (typeArgs? .length == 1 ) ? typeArgs[0 ] : null ;
7148
+ // async functions expect `Future<T> | T`
7149
+ return new FutureUnionType (declaredType, typeProvider, typeSystem);
7096
7150
}
7097
- // Must be asynchronous to reach here, so strip off any layers of Future
7098
- return declaredType.flattenFutures (typeSystem);
7151
+ return declaredType;
7099
7152
}
7100
7153
7101
7154
/**
@@ -7339,6 +7392,19 @@ class ResolverVisitor extends ScopedVisitor {
7339
7392
return false ;
7340
7393
}
7341
7394
7395
+ /**
7396
+ * Returns true if this expression is being passed to `Future.then` .
7397
+ *
7398
+ * If so we will apply special typing rules in strong mode, to handle the
7399
+ * implicit union of `S | Future<S>`
7400
+ */
7401
+ bool _isFutureThenLambda (FunctionExpression node) {
7402
+ Element element = node.staticParameterElement? .enclosingElement;
7403
+ return element is MethodElement &&
7404
+ element.name == 'then' &&
7405
+ element.enclosingElement.type.isDartAsyncFuture;
7406
+ }
7407
+
7342
7408
/**
7343
7409
* Return `true` if the given variable is accessed within a closure in the given
7344
7410
* [AstNode] and also mutated somewhere in variable scope. This information is only
0 commit comments