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 34a3e3dc720021be87b60adb1dc94f049c926e6a..f9adf852bee599f65b52c93efca7ccb788279804 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 |
@@ -22,6 +22,7 @@ import 'package:kernel/ast.dart' |
DynamicType, |
Expression, |
Field, |
+ FunctionNode, |
FunctionType, |
Initializer, |
InterfaceType, |
@@ -33,10 +34,12 @@ import 'package:kernel/ast.dart' |
ProcedureKind, |
PropertyGet, |
PropertySet, |
+ ReturnStatement, |
Statement, |
SuperMethodInvocation, |
SuperPropertyGet, |
SuperPropertySet, |
+ TypeParameter, |
TypeParameterType, |
VariableDeclaration, |
VoidType; |
@@ -637,6 +640,136 @@ abstract class TypeInferrerImpl extends TypeInferrer { |
return inferredType; |
} |
+ DartType inferLocalFunction(FunctionNode function, DartType typeContext, |
Siggi Cherem (dart-lang)
2017/06/21 22:34:27
I assume this just moved, but otherwise is the sam
Paul Berry
2017/06/22 17:44:20
There are two small changes. See comments below.
|
+ bool typeNeeded, int fileOffset, DartType returnContext, bool isNamed) { |
Paul Berry
2017/06/22 17:44:20
Previously, returnContext was always initially `nu
|
+ bool hasImplicitReturnType = returnContext == null; |
+ if (!isTopLevel) { |
+ for (var parameter in function.positionalParameters) { |
+ if (parameter.initializer != null) { |
+ inferExpression(parameter.initializer, parameter.type, false); |
+ } |
+ } |
+ for (var parameter in function.namedParameters) { |
+ if (parameter.initializer != null) { |
+ inferExpression(parameter.initializer, parameter.type, false); |
+ } |
+ } |
+ } |
+ |
+ // Let `<T0, ..., Tn>` be the set of type parameters of the closure (with |
+ // `n`=0 if there are no type parameters). |
+ List<TypeParameter> typeParameters = function.typeParameters; |
+ |
+ // Let `(P0 x0, ..., Pm xm)` be the set of formal parameters of the closure |
+ // (including required, positional optional, and named optional parameters). |
+ // If any type `Pi` is missing, denote it as `_`. |
+ List<VariableDeclaration> formals = function.positionalParameters.toList() |
+ ..addAll(function.namedParameters); |
+ |
+ // Let `B` denote the closure body. If `B` is an expression function body |
+ // (`=> e`), treat it as equivalent to a block function body containing a |
+ // single `return` statement (`{ return e; }`). |
+ |
+ // Attempt to match `K` as a function type compatible with the closure (that |
+ // is, one having n type parameters and a compatible set of formal |
+ // parameters). If there is a successful match, let `<S0, ..., Sn>` be the |
+ // set of matched type parameters and `(Q0, ..., Qm)` be the set of matched |
+ // formal parameter types, and let `N` be the return type. |
+ Substitution substitution; |
+ List<DartType> formalTypesFromContext = |
+ new List<DartType>.filled(formals.length, null); |
+ if (strongMode && typeContext is FunctionType) { |
+ for (int i = 0; i < formals.length; i++) { |
+ if (i < function.positionalParameters.length) { |
+ formalTypesFromContext[i] = |
+ getPositionalParameterType(typeContext, i); |
+ } else { |
+ formalTypesFromContext[i] = |
+ getNamedParameterType(typeContext, formals[i].name); |
+ } |
+ } |
+ returnContext = typeContext.returnType; |
+ |
+ // Let `[T/S]` denote the type substitution where each `Si` is replaced with |
Siggi Cherem (dart-lang)
2017/06/21 22:34:27
nit: wrap
Paul Berry
2017/06/22 17:44:20
Done.
|
+ // the corresponding `Ti`. |
+ var substitutionMap = <TypeParameter, DartType>{}; |
+ for (int i = 0; i < typeContext.typeParameters.length; i++) { |
+ substitutionMap[typeContext.typeParameters[i]] = |
+ i < typeParameters.length |
+ ? new TypeParameterType(typeParameters[i]) |
+ : const DynamicType(); |
+ } |
+ substitution = Substitution.fromMap(substitutionMap); |
+ } else { |
+ // If the match is not successful because `K` is `_`, let all `Si`, all |
+ // `Qi`, and `N` all be `_`. |
+ |
+ // If the match is not successful for any other reason, this will result in |
+ // a type error, so the implementation is free to choose the best error |
+ // recovery path. |
+ substitution = Substitution.empty; |
+ } |
+ |
+ // Define `Ri` as follows: if `Pi` is not `_`, let `Ri` be `Pi`. |
+ // Otherwise, if `Qi` is not `_`, let `Ri` be the greatest closure of |
+ // `Qi[T/S]` with respect to `?`. Otherwise, let `Ri` be `dynamic`. |
+ for (int i = 0; i < formals.length; i++) { |
+ KernelVariableDeclaration formal = formals[i]; |
+ if (KernelVariableDeclaration.isImplicitlyTyped(formal)) { |
+ DartType inferredType; |
+ if (formalTypesFromContext[i] != null) { |
+ inferredType = greatestClosure(coreTypes, |
+ substitution.substituteType(formalTypesFromContext[i])); |
+ } else { |
+ inferredType = const DynamicType(); |
+ } |
+ instrumentation?.record(Uri.parse(uri), formal.fileOffset, 'type', |
+ new InstrumentationValueForType(inferredType)); |
+ formal.type = inferredType; |
+ } |
+ } |
+ |
+ // Let `N'` be `N[T/S]`. The [ClosureContext] constructor will adjust |
+ // accordingly if the closure is declared with `async`, `async*`, or |
+ // `sync*`. |
+ if (returnContext != null) { |
+ returnContext = substitution.substituteType(returnContext); |
+ } |
+ |
+ // Apply type inference to `B` in return context `N’`, with any references |
+ // to `xi` in `B` having type `Pi`. This produces `B’`. |
+ bool isExpressionFunction = function.body is ReturnStatement; |
+ bool needToSetReturnType = hasImplicitReturnType && |
Paul Berry
2017/06/22 17:44:20
Previously this was:
bool needToSetReturnType = i
|
+ ((isExpressionFunction && !isNamed) || strongMode); |
+ ClosureContext oldClosureContext = this.closureContext; |
+ ClosureContext closureContext = |
+ new ClosureContext(this, function.asyncMarker, returnContext); |
+ this.closureContext = closureContext; |
+ inferStatement(function.body); |
+ |
+ // If the closure is declared with `async*` or `sync*`, let `M` be the least |
+ // upper bound of the types of the `yield` expressions in `B’`, or `void` if |
+ // `B’` contains no `yield` expressions. Otherwise, let `M` be the least |
+ // upper bound of the types of the `return` expressions in `B’`, or `void` |
+ // if `B’` contains no `return` expressions. |
+ DartType inferredReturnType; |
+ if (needToSetReturnType || typeNeeded) { |
+ inferredReturnType = |
+ closureContext.inferReturnType(this, isExpressionFunction); |
+ } |
+ |
+ // Then the result of inference is `<T0, ..., Tn>(R0 x0, ..., Rn xn) B` with |
+ // type `<T0, ..., Tn>(R0, ..., Rn) -> M’` (with some of the `Ri` and `xi` |
+ // denoted as optional or named parameters, if appropriate). |
+ if (needToSetReturnType) { |
+ instrumentation?.record(Uri.parse(uri), fileOffset, 'returnType', |
+ new InstrumentationValueForType(inferredReturnType)); |
+ function.returnType = inferredReturnType; |
+ } |
+ this.closureContext = oldClosureContext; |
+ return typeNeeded ? function.functionType : null; |
+ } |
+ |
/// Performs the core type inference algorithm for method invocations (this |
/// handles both null-aware and non-null-aware method invocations). |
DartType inferMethodInvocation( |