Skip to content

Commit

Permalink
Extend Future.then inference to subclasses
Browse files Browse the repository at this point in the history
Fixes #27101

BUG=
R=jmesserly@google.com

Review URL: https://codereview.chromium.org/2260313002 .
  • Loading branch information
leafpetersen committed Aug 19, 2016
1 parent 266ac1b commit c98c9c3
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 62 deletions.
24 changes: 20 additions & 4 deletions pkg/analyzer/lib/src/generated/resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5766,17 +5766,27 @@ class ResolverVisitor extends ScopedVisitor {
}

/**
* Returns true if this method is `Future.then`.
* 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) {
return element is MethodElement &&
element.name == 'then' &&
element.enclosingElement.type.isDartAsyncFuture;
// 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) =>
typeSystem.isSubtypeOf(type, typeProvider.futureDynamicType);

/**
* Given a downward inference type [fnType], and the declared
* [typeParameterList] for a function expression, determines if we can enable
Expand Down Expand Up @@ -6733,6 +6743,12 @@ class ResolverVisitor extends ScopedVisitor {
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
TypeName classTypeName = node.constructorName.type;
// TODO(leafp): Currently, we may re-infer types here, since we
// sometimes resolve multiple times. We should really check that we
// have not already inferred something. However, the obvious ways to
// check this don't work, since we may have been instantiated
// to bounds in an earlier phase, and we *do* want to do inference
// in that case.
if (classTypeName.typeArguments == null) {
// Given a union of context types ` T0 | T1 | ... | Tn`, find the first
// valid instantiation `new C<Ti>`, if it exists.
Expand Down
14 changes: 9 additions & 5 deletions pkg/analyzer/lib/src/generated/static_type_analyzer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1947,7 +1947,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
DartType returnContext = InferenceContext.getContext(node);
DartType returnType;
if (returnContext is FutureUnionType) {
returnType = fnType.returnType.isDartAsyncFuture
returnType = _resolver.isSubtypeOfFuture(fnType.returnType)
? returnContext.futureOfType
: returnContext.type;
} else {
Expand All @@ -1974,7 +1974,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
// S or Future<S> in a conditional.
if (!argReturnType.isObject && !argReturnType.isDynamic) {
DartType paramReturnType = fnType.typeFormals[0].type;
if (argReturnType.isDartAsyncFuture) {
if (_resolver.isSubtypeOfFuture(argReturnType)) {
// Given an argument of (T) -> Future<S>, instantiate with <S>
paramReturnType =
_typeProvider.futureType.instantiate([paramReturnType]);
Expand All @@ -1986,13 +1986,11 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
..shareParameters(firstParamType.parameters)
..returnType = paramReturnType;
function.type = new FunctionTypeImpl(function);

// Use this as the expected 1st parameter type.
paramTypes[0] = function.type;
}
}
}

return ts.inferGenericFunctionCall(
_typeProvider, fnType, paramTypes, argTypes, returnType);
}
Expand All @@ -2016,6 +2014,13 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
return;
}

// TODO(leafp): Currently, we may re-infer types here, since we
// sometimes resolve multiple times. We should really check that we
// have not already inferred something. However, the obvious ways to
// check this don't work, since we may have been instantiated
// to bounds in an earlier phase, and we *do* want to do inference
// in that case.

// Get back to the uninstantiated generic constructor.
// TODO(jmesserly): should we store this earlier in resolution?
// Or look it up, instead of jumping backwards through the Member?
Expand Down Expand Up @@ -2076,7 +2081,6 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
}

computedType = _computeReturnTypeOfFunction(body, computedType);

functionElement.returnType = computedType;
_recordPropagatedTypeOfFunction(functionElement, node.body);
_recordStaticType(node, functionElement.type);
Expand Down
4 changes: 2 additions & 2 deletions pkg/analyzer/lib/src/generated/type_system.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1666,8 +1666,8 @@ class FutureUnionType extends TypeImpl {
throw new UnsupportedError('Future unions are not used in typedefs');

/**
* Creates a union of `T | Future<T>`, unless `T` is already a future-union,
* in which case it simply returns `T`
* Creates a union of `flatten(T) | Future<flatten(T)>`, unless `T` is
* already a future-union, in which case it simply returns `T`
*/
static DartType from(
DartType type, TypeProvider provider, TypeSystem system) {
Expand Down
30 changes: 30 additions & 0 deletions pkg/analyzer/test/src/summary/resynthesize_ast_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,36 @@ class AstInferredTypeTest extends AbstractResynthesizeTest
super.test_constructors_inferFromArguments_redirectingFactory();
}

@override
@failingTest
void test_futureThen() {
super.test_futureThen();
}

@override
@failingTest
void test_futureThen_conditional() {
super.test_futureThen_conditional();
}

@override
@failingTest
void test_futureThen_upwards() {
super.test_futureThen_upwards();
}

@override
@failingTest
void test_futureUnion_asyncConditional() {
super.test_futureUnion_asyncConditional();
}

@override
@failingTest
void test_futureUnion_downwards() {
super.test_futureUnion_downwards();
}

@override
@failingTest
void test_genericMethods_inferJSBuiltin() {
Expand Down
Loading

0 comments on commit c98c9c3

Please sign in to comment.