fixes #25477, downward inference of generic methods

* we now push the context return type down when inferring
* we also take this into account in our initial downards inference for arguments
* adds a common AST interface for FunctionExpressionInvocation and MethodInvocation, so we can handle these more uniformly

R=brianwilkerson@google.com, leafp@google.com

Review URL: https://codereview.chromium.org/1720433002 .
This commit is contained in:
John Messerly 2016-02-22 14:48:39 -08:00
parent 9001fa7d06
commit 0c4e64c8ef
8 changed files with 255 additions and 210 deletions

View file

@ -536,7 +536,7 @@ abstract class AstNode {
* (either AST nodes or tokens) that make up the contents of this node,
* including doc comments but excluding other comments.
*/
Iterable /*<AstNode | Token>*/ get childEntities;
Iterable/*<AstNode | Token>*/ get childEntities;
/**
* Return the offset of the character immediately following the last character
@ -591,7 +591,7 @@ abstract class AstNode {
* Use the given [visitor] to visit this node. Return the value returned by
* the visitor as a result of visiting this node.
*/
dynamic /* =E */ accept /*<E>*/ (AstVisitor /*<E>*/ visitor);
dynamic /* =E */ accept/*<E>*/(AstVisitor/*<E>*/ visitor);
/**
* Return the most immediate ancestor of this node for which the [predicate]
@ -3994,7 +3994,7 @@ abstract class FunctionExpression extends Expression {
*
* Clients may not extend, implement or mix-in this class.
*/
abstract class FunctionExpressionInvocation extends Expression {
abstract class FunctionExpressionInvocation extends InvocationExpression {
/**
* Initialize a newly created function expression invocation.
*/
@ -4003,11 +4003,6 @@ abstract class FunctionExpressionInvocation extends Expression {
TypeArgumentList typeArguments,
ArgumentList argumentList) = FunctionExpressionInvocationImpl;
/**
* Return the list of arguments to the method.
*/
ArgumentList get argumentList;
/**
* Set the list of arguments to the method to the given [argumentList].
*/
@ -4093,12 +4088,6 @@ abstract class FunctionExpressionInvocation extends Expression {
*/
void set staticInvokeType(DartType type);
/**
* Return the type arguments to be applied to the method being invoked, or
* `null` if no type arguments were provided.
*/
TypeArgumentList get typeArguments;
/**
* Set the type arguments to be applied to the method being invoked to the
* given [typeArguments].
@ -4977,6 +4966,61 @@ abstract class InterpolationString extends InterpolationElement {
void set value(String value);
}
/**
* The invocation of a function or method; either a
* [FunctionExpressionInvocation] or a [MethodInvocation].
*
* Clients may not extend, implement or mix-in this class.
*/
abstract class InvocationExpression extends Expression {
/**
* Return the function type of the invocation based on the propagated type
* information, or `null` if the AST structure has not been resolved, or if
* the invoke could not be resolved.
*
* This will usually be a [FunctionType], but it can also be an
* [InterfaceType] with a `call` method, `dynamic`, `Function`, or a `@proxy`
* interface type that implements `Function`.
*/
DartType propagatedInvokeType;
/**
* Return the function type of the invocation based on the static type
* information, or `null` if the AST structure has not been resolved, or if
* the invoke could not be resolved.
*
* This will usually be a [FunctionType], but it can also be an
* [InterfaceType] with a `call` method, `dynamic`, `Function`, or a `@proxy`
* interface type that implements `Function`.
*/
DartType staticInvokeType;
/**
* Return the list of arguments to the method.
*/
ArgumentList get argumentList;
/**
* The expression that identifies the function or method being invoked.
* For example:
*
* (o.m)<TArgs>(args); // target will be `o.m`
* o.m<TArgs>(args); // target will be `m`
*
* In either case, the [function.staticType] will be the
* [staticInvokeType] before applying type arguments `TArgs`. Similarly,
* [function.propagatedType] will be the [propagatedInvokeType]
* before applying type arguments `TArgs`.
*/
Expression get function;
/**
* Return the type arguments to be applied to the method being invoked, or
* `null` if no type arguments were provided.
*/
TypeArgumentList get typeArguments;
}
/**
* An is expression.
*
@ -5519,7 +5563,7 @@ abstract class MethodDeclaration extends ClassMember {
*
* Clients may not extend, implement or mix-in this class.
*/
abstract class MethodInvocation extends Expression {
abstract class MethodInvocation extends InvocationExpression {
/**
* Initialize a newly created method invocation. The [target] and [operator]
* can be `null` if there is no target.
@ -5531,11 +5575,6 @@ abstract class MethodInvocation extends Expression {
TypeArgumentList typeArguments,
ArgumentList argumentList) = MethodInvocationImpl;
/**
* Return the list of arguments to the method.
*/
ArgumentList get argumentList;
/**
* Set the list of arguments to the method to the given [argumentList].
*/
@ -5630,12 +5669,6 @@ abstract class MethodInvocation extends Expression {
*/
void set target(Expression expression);
/**
* Return the type arguments to be applied to the method being invoked, or
* `null` if no type arguments were provided.
*/
TypeArgumentList get typeArguments;
/**
* Set the type arguments to be applied to the method being invoked to the
* given [typeArguments].

View file

@ -5054,24 +5054,13 @@ class FunctionExpressionImpl extends ExpressionImpl
* > functionExpressionInvocation ::=
* > [Expression] [TypeArgumentList]? [ArgumentList]
*/
class FunctionExpressionInvocationImpl extends ExpressionImpl
class FunctionExpressionInvocationImpl extends InvocationExpressionImpl
implements FunctionExpressionInvocation {
/**
* The expression producing the function being invoked.
*/
Expression _function;
/**
* The type arguments to be applied to the method being invoked, or `null` if
* no type arguments were provided.
*/
TypeArgumentList _typeArguments;
/**
* The list of arguments to the function.
*/
ArgumentList _argumentList;
/**
* The element associated with the function being invoked based on static type
* information, or `null` if the AST structure has not been resolved or the
@ -5079,16 +5068,6 @@ class FunctionExpressionInvocationImpl extends ExpressionImpl
*/
ExecutableElement staticElement;
/**
* The function type of the method invocation, or `null` if the AST
* structure has not been resolved, or if the invoke could not be resolved.
*
* This will usually be a [FunctionType], but it can also be an
* [InterfaceType] with a `call` method, `dynamic`, `Function`, or a `@proxy`
* interface type that implements `Function`.
*/
DartType staticInvokeType;
/**
* The element associated with the function being invoked based on propagated
* type information, or `null` if the AST structure has not been resolved or
@ -5096,27 +5075,13 @@ class FunctionExpressionInvocationImpl extends ExpressionImpl
*/
ExecutableElement propagatedElement;
/**
* Like [staticInvokeType], but reflects propagated type information.
*/
DartType propagatedInvokeType;
/**
* Initialize a newly created function expression invocation.
*/
FunctionExpressionInvocationImpl(Expression function,
TypeArgumentList typeArguments, ArgumentList argumentList) {
TypeArgumentList typeArguments, ArgumentList argumentList)
: super(typeArguments, argumentList) {
_function = _becomeParentOf(function);
_typeArguments = _becomeParentOf(typeArguments);
_argumentList = _becomeParentOf(argumentList);
}
@override
ArgumentList get argumentList => _argumentList;
@override
void set argumentList(ArgumentList argumentList) {
_argumentList = _becomeParentOf(argumentList);
}
@override
@ -5149,14 +5114,6 @@ class FunctionExpressionInvocationImpl extends ExpressionImpl
@override
int get precedence => 15;
@override
TypeArgumentList get typeArguments => _typeArguments;
@override
void set typeArguments(TypeArgumentList typeArguments) {
_typeArguments = _becomeParentOf(typeArguments);
}
@override
accept(AstVisitor visitor) => visitor.visitFunctionExpressionInvocation(this);
@ -6166,6 +6123,55 @@ class InterpolationStringImpl extends InterpolationElementImpl
void visitChildren(AstVisitor visitor) {}
}
/**
* Common base class for [FunctionExpressionInvocationImpl] and
* [MethodInvocationImpl].
*/
abstract class InvocationExpressionImpl extends ExpressionImpl
implements InvocationExpression {
/**
* The type arguments to be applied to the method being invoked, or `null` if
* no type arguments were provided.
*/
TypeArgumentList _typeArguments;
/**
* The list of arguments to the function.
*/
ArgumentList _argumentList;
@override
DartType propagatedInvokeType;
@override
DartType staticInvokeType;
/**
* Initialize a newly created invocation.
*/
InvocationExpressionImpl(
TypeArgumentList typeArguments, ArgumentList argumentList) {
_typeArguments = _becomeParentOf(typeArguments);
_argumentList = _becomeParentOf(argumentList);
}
@override
ArgumentList get argumentList => _argumentList;
@override
void set argumentList(ArgumentList argumentList) {
_argumentList = _becomeParentOf(argumentList);
}
@override
TypeArgumentList get typeArguments => _typeArguments;
@override
void set typeArguments(TypeArgumentList typeArguments) {
_typeArguments = _becomeParentOf(typeArguments);
}
}
/**
* An is expression.
*
@ -6959,7 +6965,8 @@ class MethodDeclarationImpl extends ClassMemberImpl
* > methodInvocation ::=
* > ([Expression] '.')? [SimpleIdentifier] [TypeArgumentList]? [ArgumentList]
*/
class MethodInvocationImpl extends ExpressionImpl implements MethodInvocation {
class MethodInvocationImpl extends InvocationExpressionImpl
implements MethodInvocation {
/**
* The expression producing the object on which the method is defined, or
* `null` if there is no target (that is, the target is implicitly `this`).
@ -6979,32 +6986,6 @@ class MethodInvocationImpl extends ExpressionImpl implements MethodInvocation {
*/
SimpleIdentifier _methodName;
/**
* The type arguments to be applied to the method being invoked, or `null` if
* no type arguments were provided.
*/
TypeArgumentList _typeArguments;
/**
* The list of arguments to the method.
*/
ArgumentList _argumentList;
/**
* The function type of the method invocation, or `null` if the AST
* structure has not been resolved, or if the invoke could not be resolved.
*
* This will usually be a [FunctionType], but it can also be an
* [InterfaceType] with a `call` method, `dynamic`, `Function`, or a `@proxy`
* interface type that implements `Function`.
*/
DartType staticInvokeType;
/**
* Like [staticInvokeType], but reflects propagated type information.
*/
DartType propagatedInvokeType;
/**
* Initialize a newly created method invocation. The [target] and [operator]
* can be `null` if there is no target.
@ -7014,19 +6995,10 @@ class MethodInvocationImpl extends ExpressionImpl implements MethodInvocation {
this.operator,
SimpleIdentifier methodName,
TypeArgumentList typeArguments,
ArgumentList argumentList) {
ArgumentList argumentList)
: super(typeArguments, argumentList) {
_target = _becomeParentOf(target);
_methodName = _becomeParentOf(methodName);
_typeArguments = _becomeParentOf(typeArguments);
_argumentList = _becomeParentOf(argumentList);
}
@override
ArgumentList get argumentList => _argumentList;
@override
void set argumentList(ArgumentList argumentList) {
_argumentList = _becomeParentOf(argumentList);
}
@override
@ -7049,6 +7021,9 @@ class MethodInvocationImpl extends ExpressionImpl implements MethodInvocation {
@override
Token get endToken => _argumentList.endToken;
@override
Expression get function => methodName;
@override
bool get isCascaded =>
operator != null && operator.type == TokenType.PERIOD_PERIOD;
@ -7087,14 +7062,6 @@ class MethodInvocationImpl extends ExpressionImpl implements MethodInvocation {
_target = _becomeParentOf(expression);
}
@override
TypeArgumentList get typeArguments => _typeArguments;
@override
void set typeArguments(TypeArgumentList typeArguments) {
_typeArguments = _becomeParentOf(typeArguments);
}
@override
accept(AstVisitor visitor) => visitor.visitMethodInvocation(this);

View file

@ -8162,7 +8162,7 @@ class ResolverVisitor extends ScopedVisitor {
safelyVisit(node.function);
node.accept(elementResolver);
_inferFunctionExpressionsParametersTypes(node.argumentList);
InferenceContext.setType(node.argumentList, node.function.staticType);
_inferArgumentTypesFromContext(node);
safelyVisit(node.argumentList);
node.accept(typeAnalyzer);
return null;
@ -8381,15 +8381,32 @@ class ResolverVisitor extends ScopedVisitor {
safelyVisit(node.typeArguments);
node.accept(elementResolver);
_inferFunctionExpressionsParametersTypes(node.argumentList);
DartType contextType = node.staticInvokeType;
if (contextType is FunctionType) {
InferenceContext.setType(node.argumentList, contextType);
}
_inferArgumentTypesFromContext(node);
safelyVisit(node.argumentList);
node.accept(typeAnalyzer);
return null;
}
void _inferArgumentTypesFromContext(InvocationExpression node) {
DartType contextType = node.staticInvokeType;
if (contextType is FunctionType) {
DartType originalType = node.function.staticType;
DartType returnContextType = InferenceContext.getType(node);
TypeSystem ts = typeSystem;
if (returnContextType != null &&
node.typeArguments == null &&
originalType is FunctionType &&
originalType.typeFormals.isNotEmpty &&
ts is StrongTypeSystemImpl) {
contextType = ts.inferGenericFunctionCall(typeProvider, originalType,
DartType.EMPTY_LIST, DartType.EMPTY_LIST, returnContextType);
}
InferenceContext.setType(node.argumentList, contextType);
}
}
@override
Object visitNamedExpression(NamedExpression node) {
InferenceContext.setType(node.expression, InferenceContext.getType(node));

View file

@ -510,7 +510,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
@override
Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
if (_strongMode) {
_inferFunctionInvocationGeneric(node);
_inferGenericInvoke(node);
}
DartType staticType = _computeInvokeReturnType(node.staticInvokeType);
_recordStaticType(node, staticType);
@ -742,7 +742,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
SimpleIdentifier methodNameNode = node.methodName;
Element staticMethodElement = methodNameNode.staticElement;
if (_strongMode) {
_inferMethodInvocationGeneric(node);
_inferGenericInvoke(node);
}
// Record types of the variable invoked as a function.
if (staticMethodElement is VariableElement) {
@ -1814,26 +1814,6 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
}
}
/**
* Similar to [_inferMethodInvocationGeneric] but for function expression
* invocations.
*/
// TODO(jmesserly): if we had a common AST interface between these two nodes,
// we could remove this duplicated code.
bool _inferFunctionInvocationGeneric(FunctionExpressionInvocation node) {
DartType instantiatedType = node.staticInvokeType;
DartType originalType = node.function.staticType;
if (instantiatedType is FunctionType && originalType is FunctionType) {
FunctionType inferred = _inferGenericInvoke(instantiatedType,
originalType, node.typeArguments, node.argumentList);
if (inferred != null) {
node.staticInvokeType = inferred;
return true;
}
}
return false;
}
/**
* Given an uninstantiated generic type, try to infer the instantiated generic
* type from the surrounding context.
@ -1849,41 +1829,42 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
return type;
}
FunctionType _inferGenericInvoke(FunctionType invokeType, FunctionType fnType,
TypeArgumentList typeArguments, ArgumentList argumentList) {
bool _inferGenericInvoke(InvocationExpression node) {
TypeSystem ts = _typeSystem;
if (typeArguments == null && ts is StrongTypeSystemImpl) {
if (fnType.typeFormals.isNotEmpty &&
ts.instantiateToBounds(fnType) == invokeType) {
// Get the parameters that correspond to the uninstantiated generic.
List<ParameterElement> rawParameters =
ResolverVisitor.resolveArgumentsToParameters(
argumentList, fnType.parameters, null);
DartType fnType = node.function.staticType;
if (node.typeArguments == null &&
fnType is FunctionType &&
fnType.typeFormals.isNotEmpty &&
ts is StrongTypeSystemImpl) {
ArgumentList argumentList = node.argumentList;
// Get the parameters that correspond to the uninstantiated generic.
List<ParameterElement> rawParameters = ResolverVisitor
.resolveArgumentsToParameters(argumentList, fnType.parameters, null);
List<DartType> paramTypes = <DartType>[];
List<DartType> argTypes = <DartType>[];
for (int i = 0, length = rawParameters.length; i < length; i++) {
ParameterElement parameter = rawParameters[i];
if (parameter != null) {
paramTypes.add(parameter.type);
argTypes.add(argumentList.arguments[i].staticType);
}
}
FunctionType inferred = ts.inferCallFromArguments(
_typeProvider, fnType, paramTypes, argTypes);
if (inferred != fnType) {
// Fix up the parameter elements based on inferred method.
List<ParameterElement> inferredParameters =
ResolverVisitor.resolveArgumentsToParameters(
argumentList, inferred.parameters, null);
argumentList.correspondingStaticParameters = inferredParameters;
return inferred;
List<DartType> paramTypes = <DartType>[];
List<DartType> argTypes = <DartType>[];
for (int i = 0, length = rawParameters.length; i < length; i++) {
ParameterElement parameter = rawParameters[i];
if (parameter != null) {
paramTypes.add(parameter.type);
argTypes.add(argumentList.arguments[i].staticType);
}
}
FunctionType inferred = ts.inferGenericFunctionCall(_typeProvider, fnType,
paramTypes, argTypes, InferenceContext.getType(node));
if (inferred != node.staticInvokeType) {
// Fix up the parameter elements based on inferred method.
List<ParameterElement> inferredParameters =
ResolverVisitor.resolveArgumentsToParameters(
argumentList, inferred.parameters, null);
argumentList.correspondingStaticParameters = inferredParameters;
node.staticInvokeType = inferred;
return true;
}
}
return null;
return false;
}
/**
@ -1905,27 +1886,6 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
}
}
/**
* Given a generic method invocation [node], attempt to infer the method's
* type variables, using the actual types of the arguments.
*/
bool _inferMethodInvocationGeneric(MethodInvocation node) {
DartType instantiatedType = node.staticInvokeType;
DartType originalType = node.methodName.staticType;
// TODO(jmesserly): support generic `call` methods.
// Perhaps we should always record a FunctionType in staticInvokeType
// and the methodName's staticType.
if (instantiatedType is FunctionType && originalType is FunctionType) {
FunctionType inferred = _inferGenericInvoke(instantiatedType,
originalType, node.typeArguments, node.argumentList);
if (inferred != null) {
node.staticInvokeType = inferred;
return true;
}
}
return false;
}
/**
* Given a method invocation [node], attempt to infer a better
* type for the result if it is an inline JS invocation

View file

@ -68,11 +68,12 @@ class StrongTypeSystemImpl implements TypeSystem {
/// As a simplification, we do not actually store all constraints on each type
/// parameter Tj. Instead we track Uj and Lj where U is the upper bound and
/// L is the lower bound of that type parameter.
FunctionType inferCallFromArguments(
FunctionType inferGenericFunctionCall(
TypeProvider typeProvider,
FunctionType fnType,
List<DartType> correspondingParameterTypes,
List<DartType> argumentTypes) {
List<DartType> argumentTypes,
DartType returnContextType) {
if (fnType.typeFormals.isEmpty) {
return fnType;
}
@ -84,6 +85,10 @@ class StrongTypeSystemImpl implements TypeSystem {
var inferringTypeSystem =
new _StrongInferenceTypeSystem(typeProvider, fnType.typeFormals);
if (returnContextType != null) {
inferringTypeSystem.isSubtypeOf(fnType.returnType, returnContextType);
}
for (int i = 0; i < argumentTypes.length; i++) {
// Try to pass each argument to each parameter, recording any type
// parameter bounds that were implied by this assignment.
@ -105,7 +110,7 @@ class StrongTypeSystemImpl implements TypeSystem {
* 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.
*
* This is similar to [inferCallFromArguments], but the return type is also
* This is similar to [inferGenericFunctionCall], but the return type is also
* considered as part of the solution.
*
* If this function is called with a [contextType] that is also

View file

@ -467,6 +467,27 @@ class StrongGenericFunctionInferenceTest {
[intType]);
}
void test_returnTypeFromContext() {
// <T>() -> T
var t = TypeBuilder.variable('T');
var f = TypeBuilder.function(types: [t], required: [], result: t);
expect(_inferCall(f, [], stringType), [stringType]);
}
void test_returnTypeWithBoundFromContext() {
// <T extends num>() -> T
var t = TypeBuilder.variable('T', bound: numType);
var f = TypeBuilder.function(types: [t], required: [], result: t);
expect(_inferCall(f, [], doubleType), [doubleType]);
}
void test_returnTypeWithBoundFromInvalidContext() {
// <T extends num>() -> T
var t = TypeBuilder.variable('T', bound: numType);
var f = TypeBuilder.function(types: [t], required: [], result: t);
expect(_inferCall(f, [], stringType), [numType]);
}
void test_unifyParametersToFunctionParam() {
// <T>(f(T t), g(T t)) -> T
var t = TypeBuilder.variable('T');
@ -498,9 +519,10 @@ class StrongGenericFunctionInferenceTest {
expect(_inferCall(f, []), [numType]);
}
List<DartType> _inferCall(FunctionTypeImpl ft, List<DartType> arguments) {
FunctionType inferred = typeSystem.inferCallFromArguments(
typeProvider, ft, ft.parameters.map((p) => p.type).toList(), arguments);
List<DartType> _inferCall(FunctionTypeImpl ft, List<DartType> arguments,
[DartType returnType]) {
FunctionType inferred = typeSystem.inferGenericFunctionCall(typeProvider,
ft, ft.parameters.map((p) => p.type).toList(), arguments, returnType);
return inferred.typeArguments;
}
}
@ -1314,7 +1336,7 @@ class LeastUpperBoundTest {
if (returns == null) {
returns = voidType;
}
return ElementFactory
.functionElement8(required, returns, optional: optional, named: named)
.type;

View file

@ -1738,16 +1738,16 @@ void main() {
x = foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/('1', '2', '3');
foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1);
x = foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/('1');
x = /*severe:STATIC_TYPE_ERROR*/foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/(1, 2, 3);
x = /*severe:STATIC_TYPE_ERROR*/foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1);
x = /*info:DYNAMIC_CAST*/foo/*warning:EXTRA_POSITIONAL_ARGUMENTS*/(1, 2, 3);
x = /*info:DYNAMIC_CAST*/foo/*warning:NOT_ENOUGH_REQUIRED_ARGUMENTS*/(1);
// named arguments
bar(y: 1, x: 2, /*warning:UNDEFINED_NAMED_PARAMETER*/z: 3);
x = bar(/*warning:UNDEFINED_NAMED_PARAMETER*/z: '1', x: '2', y: '3');
bar(y: 1);
x = bar(x: '1', /*warning:UNDEFINED_NAMED_PARAMETER*/z: 42);
x = /*severe:STATIC_TYPE_ERROR*/bar(y: 1, x: 2, /*warning:UNDEFINED_NAMED_PARAMETER*/z: 3);
x = /*severe:STATIC_TYPE_ERROR*/bar(x: 1);
x = /*info:DYNAMIC_CAST*/bar(y: 1, x: 2, /*warning:UNDEFINED_NAMED_PARAMETER*/z: 3);
x = /*info:DYNAMIC_CAST*/bar(x: 1);
}
''');
});

View file

@ -1695,7 +1695,7 @@ main() {
Iterable<Future<int>> list = <int>[1, 2, 3].map(make);
Future<List<int>> results = Future.wait(list);
Future<String> results2 = results.then((List<int> list)
=> list.fold('', (String x, int y) => x + y.toString()));
=> list.fold('', /*info:INFERRED_TYPE_CLOSURE*/(x, y) => x + y.toString()));
}
''');
});
@ -1838,7 +1838,7 @@ main() {
test('correctly recognize generic upper bound', () {
// Regression test for https://github.com/dart-lang/sdk/issues/25740.
checkFile('''
checkFile(r'''
class Foo<T extends Pattern> {
void method/*<U extends T>*/(dynamic/*=U*/ u) {}
}
@ -1853,11 +1853,52 @@ main() {
}
''');
});
test('basic downwards inference', () {
checkFile(r'''
/*=T*/ f/*<S, T>*/(/*=S*/ s) => null;
main() {
String x = f(42);
String y = (f)(42);
}
''');
});
test('downwards inference affects arguments', () {
checkFile(r'''
/*=T*/ f/*<T>*/(List/*<T>*/ s) => null;
main() {
String x = f(/*info:INFERRED_TYPE_LITERAL*/['hi']);
String y = f(/*info:INFERRED_TYPE_LITERAL*/[/*severe:STATIC_TYPE_ERROR*/42]);
}
''');
});
test('downwards inference fold', () {
// Regression from https://github.com/dart-lang/sdk/issues/25491
// The first example works now, but the latter requires a full solution to
// https://github.com/dart-lang/sdk/issues/25490
checkFile(r'''
void main() {
List<int> o;
int y = o.fold(0, /*info:INFERRED_TYPE_CLOSURE*/(x, y) => x + y);
var z = o.fold(0, /*info:INFERRED_TYPE_CLOSURE*/(x, y) => /*info:DYNAMIC_INVOKE*/x + y);
y = /*info:DYNAMIC_CAST*/z;
}
void functionExpressionInvocation() {
List<int> o;
int y = (o.fold)(0, /*info:INFERRED_TYPE_CLOSURE*/(x, y) => x + y);
var z = (o.fold)(0, /*info:INFERRED_TYPE_CLOSURE*/(x, y) => /*info:DYNAMIC_INVOKE*/x + y);
y = /*info:DYNAMIC_CAST*/z;
}
''');
});
});
// Regression test for https://github.com/dart-lang/dev_compiler/issues/47
test('null literal should not infer as bottom', () {
checkFile('''
checkFile(r'''
var h = null;
void foo(int f(Object _)) {}