Chromium Code Reviews| Index: pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart |
| diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart |
| index 231ea66864a451eb4dbc5dabd22255b14c3720d1..e85423d815b02dcb8175c677e1eaec41700b1955 100644 |
| --- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart |
| +++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart |
| @@ -13,9 +13,15 @@ import 'package:kernel/ast.dart' |
| Constructor, |
| DartType, |
| DynamicType, |
| - FunctionNode, |
| + Field, |
| + FunctionType, |
| InterfaceType, |
| - Member; |
| + Member, |
| + Name, |
| + Procedure, |
| + TypeParameter, |
| + TypeParameterType; |
| +import 'package:kernel/class_hierarchy.dart'; |
| import 'package:kernel/core_types.dart'; |
| import 'package:kernel/type_algebra.dart'; |
| @@ -57,6 +63,9 @@ abstract class TypeInferrer<S, E, V, F> { |
| /// possible without knowing the identity of the type parameters. It defers to |
| /// abstract methods for everything else. |
| abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| + static final FunctionType _functionReturningDynamic = |
| + new FunctionType(const [], const DynamicType()); |
| + |
| @override |
| final String uri; |
| @@ -69,6 +78,8 @@ abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| final bool strongMode; |
| + final ClassHierarchy classHierarchy; |
| + |
| final Instrumentation instrumentation; |
| final TypeSchemaEnvironment typeSchemaEnvironment; |
| @@ -80,6 +91,7 @@ abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| TypeInferrerImpl(TypeInferenceEngineImpl<F> engine, this.uri) |
| : coreTypes = engine.coreTypes, |
| strongMode = engine.strongMode, |
| + classHierarchy = engine.classHierarchy, |
| instrumentation = engine.instrumentation, |
| typeSchemaEnvironment = engine.typeSchemaEnvironment; |
| @@ -152,6 +164,7 @@ abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| void forEachArgument(void callback(String name, E expression)), |
| void setInferredTypeArguments(List<DartType> types)) { |
| List<DartType> inferredTypes; |
| + FunctionType constructorType = target.function.functionType; |
| Substitution substitution; |
| List<DartType> formalTypes; |
| List<DartType> actualTypes; |
| @@ -176,8 +189,8 @@ abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| int i = 0; |
| forEachArgument((name, expression) { |
| DartType formalType = name != null |
| - ? _getNamedParameterType(target.function, name) |
| - : _getPositionalParameterType(target.function, i++); |
| + ? _getNamedParameterType(constructorType, name) |
| + : _getPositionalParameterType(constructorType, i++); |
| DartType inferredFormalType = substitution != null |
| ? substitution.substituteType(formalType) |
| : formalType; |
| @@ -391,6 +404,98 @@ abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| : null; |
| } |
| + /// Performs the core type inference algorithm for method invocations. |
| + /// |
| + /// [typeContext], [typeNeeded], and the return value behave as described in |
| + /// [inferExpression]. |
| + /// |
| + /// [offset] is the location of the method invocation in the source file. |
| + /// [receiver] is the object whose method is being invoked, and [methodName] |
| + /// is the name fo the method. [explicitTypeArguments] is the set of type |
|
scheglov
2017/05/09 23:45:57
/fo/of/s
Paul Berry
2017/05/10 16:24:28
Done.
|
| + /// arguments explicitly provided, or `null` if no type arguments were |
| + /// provided. [forEachArgument] is a callback which can be used to iterate |
| + /// through all invocation arguments (both named and positional). |
| + /// [setInferredTypeArguments] is a callback which can be used to record the |
| + /// inferred type arguments. |
| + DartType inferMethodInvocation( |
| + DartType typeContext, |
| + bool typeNeeded, |
| + int offset, |
| + E receiver, |
| + Name methodName, |
| + List<DartType> explicitTypeArguments, |
| + void forEachArgument(void callback(String name, E expression)), |
| + void setInferredTypeArguments(List<DartType> types)) { |
| + // First infer the receiver so we can look up the method that was invoked. |
| + var receiverType = inferExpression(receiver, null, true); |
| + // TODO(paulberry): can we share some of the code below with |
| + // inferConstructorInvocation? |
|
Leaf
2017/05/10 04:23:09
The specification is definitely intended to treat
Paul Berry
2017/05/10 16:24:28
Acknowledged. The TODO comment is mostly because
|
| + var memberFunctionType = _getCalleeFunctionType(receiverType, methodName); |
| + List<TypeParameter> memberTypeParameters = |
| + memberFunctionType.typeParameters; |
| + bool inferenceNeeded = explicitTypeArguments == null && |
| + strongMode && |
| + memberTypeParameters.isNotEmpty; |
| + List<DartType> inferredTypes; |
| + Substitution substitution; |
| + List<DartType> formalTypes; |
| + List<DartType> actualTypes; |
| + if (inferenceNeeded) { |
| + inferredTypes = new List<DartType>.filled( |
| + memberTypeParameters.length, const UnknownType()); |
| + typeSchemaEnvironment.inferGenericFunctionOrType( |
| + memberFunctionType.returnType, |
| + memberTypeParameters, |
| + null, |
| + null, |
| + typeContext, |
| + inferredTypes); |
| + substitution = |
| + Substitution.fromPairs(memberTypeParameters, inferredTypes); |
| + formalTypes = []; |
| + actualTypes = []; |
| + } else if (explicitTypeArguments != null) { |
| + substitution = |
| + Substitution.fromPairs(memberTypeParameters, inferredTypes); |
|
Leaf
2017/05/10 04:23:09
Is this right? Looking at class Substitution from
Paul Berry
2017/05/10 16:24:28
Whoops, you're right. I wonder why this didn't ca
|
| + } |
| + int i = 0; |
| + forEachArgument((name, expression) { |
| + DartType formalType = name != null |
| + ? _getNamedParameterType(memberFunctionType, name) |
| + : _getPositionalParameterType(memberFunctionType, i++); |
| + DartType inferredFormalType = substitution != null |
| + ? substitution.substituteType(formalType) |
| + : formalType; |
| + var expressionType = |
| + inferExpression(expression, inferredFormalType, inferenceNeeded); |
| + if (inferenceNeeded) { |
| + formalTypes.add(formalType); |
| + actualTypes.add(expressionType); |
| + } |
| + }); |
| + if (inferenceNeeded) { |
| + typeSchemaEnvironment.inferGenericFunctionOrType( |
| + memberFunctionType.returnType, |
| + memberTypeParameters, |
| + formalTypes, |
| + actualTypes, |
| + typeContext, |
| + inferredTypes); |
| + substitution = |
| + Substitution.fromPairs(memberTypeParameters, inferredTypes); |
| + instrumentation?.record(Uri.parse(uri), offset, 'typeArgs', |
| + new InstrumentationValueForTypeArgs(inferredTypes)); |
| + setInferredTypeArguments(inferredTypes); |
| + } |
| + if (typeNeeded) { |
| + return substitution == null |
| + ? memberFunctionType.returnType |
| + : substitution.substituteType(memberFunctionType.returnType); |
| + } else { |
| + return null; |
| + } |
| + } |
| + |
| /// Performs the core type inference algorithm for null literals. |
| /// |
| /// [typeContext], [typeNeeded], and the return value behave as described in |
| @@ -479,15 +584,55 @@ abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| return inferExpression(value, declaredType, typeNeeded); |
| } |
| - DartType _getNamedParameterType(FunctionNode function, String name) { |
| + FunctionType _getCalleeFunctionType(DartType receiverType, Name methodName) { |
|
Leaf
2017/05/10 04:23:09
Surely this should be factored out somewhere? Or
Paul Berry
2017/05/10 16:24:28
Hmm, I'm not sure. Kernel has a slot for interfac
|
| + if (receiverType is InterfaceType) { |
| + var member = |
| + classHierarchy.getInterfaceMember(receiverType.classNode, methodName); |
| + if (member == null) return _functionReturningDynamic; |
| + var memberClass = member.enclosingClass; |
| + if (member is Procedure) { |
| + var memberFunctionType = member.function.functionType; |
| + if (memberClass.typeParameters.isNotEmpty) { |
| + var castedType = classHierarchy.getClassAsInstanceOf( |
| + receiverType.classNode, memberClass); |
| + memberFunctionType = Substitution |
| + .fromInterfaceType(Substitution |
| + .fromInterfaceType(receiverType) |
| + .substituteType(castedType.asInterfaceType)) |
| + .substituteType(memberFunctionType); |
| + } |
| + return memberFunctionType; |
| + } else if (member is Field) { |
| + // TODO(paulberry): handle this case |
| + return _functionReturningDynamic; |
| + } else { |
| + return _functionReturningDynamic; |
| + } |
| + } else if (receiverType is DynamicType) { |
| + return _functionReturningDynamic; |
| + } else if (receiverType is FunctionType) { |
| + // TODO(paulberry): handle the case of invoking .call() or .toString() on |
| + // a function type. |
| + return _functionReturningDynamic; |
| + } else if (receiverType is TypeParameterType) { |
| + // TODO(paulberry): use the bound |
| + return _functionReturningDynamic; |
| + } else { |
| + // TODO(paulberry): handle the case of invoking .toString() on a type |
| + // that's none of the above (e.g. `dynamic` or `bottom`) |
| + return _functionReturningDynamic; |
| + } |
| + } |
| + |
| + DartType _getNamedParameterType(FunctionType functionType, String name) { |
| // TODO(paulberry): is there a kernel function that does this binary search? |
|
Leaf
2017/05/10 04:23:09
There's just got to be...otherwise something is ve
Paul Berry
2017/05/10 16:24:28
I dug a little deeper and all I've found is see so
|
| int lower = 0; |
| - int upper = function.namedParameters.length - 1; |
| + int upper = functionType.namedParameters.length - 1; |
| while (lower <= upper) { |
| int pivot = (lower + upper) ~/ 2; |
| - int comparison = name.compareTo(function.namedParameters[pivot].name); |
| + int comparison = name.compareTo(functionType.namedParameters[pivot].name); |
| if (comparison == 0) { |
| - return function.namedParameters[pivot].type; |
| + return functionType.namedParameters[pivot].type; |
| } else if (comparison < 0) { |
| upper = pivot - 1; |
| } else { |
| @@ -497,9 +642,9 @@ abstract class TypeInferrerImpl<S, E, V, F> extends TypeInferrer<S, E, V, F> { |
| return const DynamicType(); |
| } |
| - DartType _getPositionalParameterType(FunctionNode function, int i) { |
| - if (i < function.positionalParameters.length) { |
| - return function.positionalParameters[i].type; |
| + DartType _getPositionalParameterType(FunctionType functionType, int i) { |
| + if (i < functionType.positionalParameters.length) { |
| + return functionType.positionalParameters[i]; |
| } else { |
| return const DynamicType(); |
| } |