Skip to content

Commit ce591d1

Browse files
stereotype441Commit Bot
authored and
Commit Bot
committed
Resolve deferred function literals in stages.
This change allows function literals in invocations to be inferred in dependency order. For example, given the following code: U f<T, U>(T Function() g, U Function(T) h) => h(g()); test() { var x = f(() => 0, (y) => [y]); } The function literal `() => 0` is inferred first, causing the type parameter `T` to be assigned the type `int`. Then, `(y) => [x]` is inferred with the benefit of this type assignment, so `y` gets the type `int`, and consequently, `U` gets assigned the type `List<int>`. This completes the support for dart-lang/language#731 (improved inference for fold etc.) Change-Id: I48c22693720a1cc8bbf435643e863834e07f02b1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243002 Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 9c572f0 commit ce591d1

15 files changed

+919
-36
lines changed

pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,7 +2037,7 @@ class InferenceVisitor
20372037
typeContext,
20382038
inferrer.libraryBuilder.library,
20392039
isConst: node.isConst);
2040-
inferrer.typeSchemaEnvironment.downwardsInfer(
2040+
inferrer.typeSchemaEnvironment.partialInfer(
20412041
gatherer,
20422042
listClass.typeParameters,
20432043
inferredTypes,
@@ -2722,7 +2722,7 @@ class InferenceVisitor
27222722
typeContext,
27232723
inferrer.libraryBuilder.library,
27242724
isConst: node.isConst);
2725-
inferrer.typeSchemaEnvironment.downwardsInfer(
2725+
inferrer.typeSchemaEnvironment.partialInfer(
27262726
gatherer,
27272727
mapClass.typeParameters,
27282728
inferredTypes,
@@ -2807,7 +2807,7 @@ class InferenceVisitor
28072807
typeContext,
28082808
inferrer.libraryBuilder.library,
28092809
isConst: node.isConst);
2810-
inferrer.typeSchemaEnvironment.downwardsInfer(
2810+
inferrer.typeSchemaEnvironment.partialInfer(
28112811
gatherer,
28122812
inferrer.coreTypes.setClass.typeParameters,
28132813
inferredTypesForSet,
@@ -5992,7 +5992,7 @@ class InferenceVisitor
59925992
typeContext,
59935993
inferrer.libraryBuilder.library,
59945994
isConst: node.isConst);
5995-
inferrer.typeSchemaEnvironment.downwardsInfer(
5995+
inferrer.typeSchemaEnvironment.partialInfer(
59965996
gatherer,
59975997
setClass.typeParameters,
59985998
inferredTypes,

pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart

Lines changed: 154 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE.md file.
44

5+
import 'package:_fe_analyzer_shared/src/deferred_function_literal_heuristic.dart';
56
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
67
import 'package:_fe_analyzer_shared/src/testing/id.dart';
78
import 'package:_fe_analyzer_shared/src/util/link.dart';
@@ -82,6 +83,59 @@ bool isOverloadableArithmeticOperator(String name) {
8283
identical(name, '%');
8384
}
8485

86+
/// Given a [FunctionExpression], computes a set whose elements consist of (a)
87+
/// an integer corresponding to the zero-based index of each positional
88+
/// parameter of the function expression that has an explicit type annotation,
89+
/// and (b) a string corresponding to the name of each named parameter of the
90+
/// function expression that has an explicit type annotation.
91+
Set<Object> _computeExplicitlyTypedParameterSet(
92+
FunctionExpression functionExpression) {
93+
Set<Object> result = {};
94+
int unnamedParameterIndex = 0;
95+
for (VariableDeclaration positionalParameter
96+
in functionExpression.function.positionalParameters) {
97+
int key = unnamedParameterIndex++;
98+
if (!(positionalParameter as VariableDeclarationImpl).isImplicitlyTyped) {
99+
result.add(key);
100+
}
101+
}
102+
for (VariableDeclaration namedParameter
103+
in functionExpression.function.namedParameters) {
104+
String key = namedParameter.name!;
105+
if (!(namedParameter as VariableDeclarationImpl).isImplicitlyTyped) {
106+
result.add(key);
107+
}
108+
}
109+
return result;
110+
}
111+
112+
/// Given an function type, computes a map based on the parameters whose keys
113+
/// are either the parameter name (for named parameters) or the zero-based
114+
/// integer index (for unnamed parameters), and whose values are the parameter
115+
/// types.
116+
Map<Object, DartType> _computeParameterMap(FunctionType functionType) => {
117+
for (int i = 0; i < functionType.positionalParameters.length; i++)
118+
i: functionType.positionalParameters[i],
119+
for (NamedType namedType in functionType.namedParameters)
120+
namedType.name: namedType.type
121+
};
122+
123+
/// Computes a list of [_ParamInfo] objects corresponding to the invocation
124+
/// parameters that were *not* deferred.
125+
List<_ParamInfo> _computeUndeferredParamInfo(List<DartType> formalTypes,
126+
List<_DeferredParamInfo> deferredFunctionLiterals) {
127+
// TODO(paulberry): test that the right thing happens when evaluation order differs from classic (positional/named) order.
128+
Set<int> evaluationOrderIndicesAlreadyCovered = {
129+
for (_DeferredParamInfo functionLiteral in deferredFunctionLiterals)
130+
functionLiteral.evaluationOrderIndex
131+
};
132+
return [
133+
for (int i = 0; i < formalTypes.length; i++)
134+
if (!evaluationOrderIndicesAlreadyCovered.contains(i))
135+
new _ParamInfo(formalTypes[i])
136+
];
137+
}
138+
85139
/// Enum denoting the kinds of contravariance check that might need to be
86140
/// inserted for a method call.
87141
enum MethodContravarianceCheckKind {
@@ -2290,7 +2344,8 @@ class TypeInferrerImpl implements TypeInferrer {
22902344
explicitTypeArguments == null &&
22912345
calleeTypeParameters.isNotEmpty;
22922346
bool typeChecksNeeded = !isTopLevel;
2293-
bool useFormalAndActualTypes = typeChecksNeeded ||
2347+
bool useFormalAndActualTypes = inferenceNeeded ||
2348+
typeChecksNeeded ||
22942349
isSpecialCasedBinaryOperator ||
22952350
isSpecialCasedTernaryOperator;
22962351

@@ -2331,7 +2386,7 @@ class TypeInferrerImpl implements TypeInferrer {
23312386
calleeTypeParameters,
23322387
typeContext,
23332388
libraryBuilder.library);
2334-
typeSchemaEnvironment.downwardsInfer(gatherer, calleeTypeParameters,
2389+
typeSchemaEnvironment.partialInfer(gatherer, calleeTypeParameters,
23352390
inferredTypes, libraryBuilder.library);
23362391
substitution =
23372392
Substitution.fromPairs(calleeTypeParameters, inferredTypes);
@@ -2471,6 +2526,7 @@ class TypeInferrerImpl implements TypeInferrer {
24712526
(deferredFunctionLiterals ??= []).add(new _DeferredParamInfo(
24722527
formalType: formalType,
24732528
argumentExpression: argumentExpression,
2529+
unparenthesizedExpression: unparenthesizedExpression,
24742530
isNamed: !isExpression,
24752531
evaluationOrderIndex: evaluationOrderIndex,
24762532
index: index));
@@ -2510,26 +2566,44 @@ class TypeInferrerImpl implements TypeInferrer {
25102566
}
25112567
}
25122568
if (deferredFunctionLiterals != null) {
2513-
for (_DeferredParamInfo deferredArgument in deferredFunctionLiterals) {
2514-
ExpressionInferenceResult result = inferArgument(
2515-
deferredArgument.formalType, deferredArgument.argumentExpression,
2516-
isNamed: deferredArgument.isNamed);
2517-
DartType inferredType = _computeInferredType(result);
2518-
Expression expression = result.expression;
2519-
identicalInfo?[deferredArgument.evaluationOrderIndex] =
2520-
flowAnalysis.equalityOperand_end(expression, inferredType);
2521-
if (deferredArgument.isNamed) {
2522-
NamedExpression namedArgument =
2523-
arguments.named[deferredArgument.index];
2524-
namedArgument.value = expression..parent = namedArgument;
2525-
} else {
2526-
arguments.positional[deferredArgument.index] = expression
2527-
..parent = arguments;
2569+
bool isFirstStage = true;
2570+
for (List<_DeferredParamInfo> stage in new _FunctionLiteralDependencies(
2571+
deferredFunctionLiterals,
2572+
calleeType.typeParameters.toSet(),
2573+
inferenceNeeded
2574+
? _computeUndeferredParamInfo(
2575+
formalTypes!, deferredFunctionLiterals)
2576+
: const [])
2577+
.planReconciliationStages()) {
2578+
if (gatherer != null && !isFirstStage) {
2579+
typeSchemaEnvironment.partialInfer(gatherer, calleeTypeParameters,
2580+
inferredTypes!, libraryBuilder.library);
2581+
substitution =
2582+
Substitution.fromPairs(calleeTypeParameters, inferredTypes);
25282583
}
2529-
gatherer?.tryConstrainLower(deferredArgument.formalType, inferredType);
2530-
if (useFormalAndActualTypes) {
2531-
actualTypes![deferredArgument.evaluationOrderIndex] = inferredType;
2584+
for (_DeferredParamInfo deferredArgument in stage) {
2585+
ExpressionInferenceResult result = inferArgument(
2586+
deferredArgument.formalType, deferredArgument.argumentExpression,
2587+
isNamed: deferredArgument.isNamed);
2588+
DartType inferredType = _computeInferredType(result);
2589+
Expression expression = result.expression;
2590+
identicalInfo?[deferredArgument.evaluationOrderIndex] =
2591+
flowAnalysis.equalityOperand_end(expression, inferredType);
2592+
if (deferredArgument.isNamed) {
2593+
NamedExpression namedArgument =
2594+
arguments.named[deferredArgument.index];
2595+
namedArgument.value = expression..parent = namedArgument;
2596+
} else {
2597+
arguments.positional[deferredArgument.index] = expression
2598+
..parent = arguments;
2599+
}
2600+
gatherer?.tryConstrainLower(
2601+
deferredArgument.formalType, inferredType);
2602+
if (useFormalAndActualTypes) {
2603+
actualTypes![deferredArgument.evaluationOrderIndex] = inferredType;
2604+
}
25322605
}
2606+
isFirstStage = false;
25332607
}
25342608
}
25352609
if (identicalInfo != null) {
@@ -5967,15 +6041,14 @@ class ImplicitInstantiation {
59676041
/// Information about an invocation argument that needs to be resolved later due
59686042
/// to the fact that it's a function literal and the `inference-update-1`
59696043
/// feature is enabled.
5970-
class _DeferredParamInfo {
5971-
/// The (unsubstituted) type of the formal parameter corresponding to this
5972-
/// argument.
5973-
final DartType formalType;
5974-
6044+
class _DeferredParamInfo extends _ParamInfo {
59756045
/// The argument expression (possibly wrapped in an arbitrary number of
59766046
/// ParenthesizedExpressions).
59776047
final Expression argumentExpression;
59786048

6049+
/// The unparenthesized argument expression.
6050+
final FunctionExpression unparenthesizedExpression;
6051+
59796052
/// Indicates whether this is a named argument.
59806053
final bool isNamed;
59816054

@@ -5988,9 +6061,63 @@ class _DeferredParamInfo {
59886061
final int index;
59896062

59906063
_DeferredParamInfo(
5991-
{required this.formalType,
6064+
{required DartType formalType,
59926065
required this.argumentExpression,
6066+
required this.unparenthesizedExpression,
59936067
required this.isNamed,
59946068
required this.evaluationOrderIndex,
5995-
required this.index});
6069+
required this.index})
6070+
: super(formalType);
6071+
}
6072+
6073+
/// Extension of the shared [FunctionLiteralDependencies] logic used by the
6074+
/// front end.
6075+
class _FunctionLiteralDependencies extends FunctionLiteralDependencies<
6076+
TypeParameter, _ParamInfo, _DeferredParamInfo> {
6077+
_FunctionLiteralDependencies(
6078+
Iterable<_DeferredParamInfo> deferredParamInfo,
6079+
Iterable<TypeParameter> typeVariables,
6080+
List<_ParamInfo> undeferredParamInfo)
6081+
: super(deferredParamInfo, typeVariables, undeferredParamInfo);
6082+
6083+
@override
6084+
Iterable<TypeParameter> typeVarsFreeInParamParams(
6085+
_DeferredParamInfo paramInfo) {
6086+
DartType type = paramInfo.formalType;
6087+
if (type is FunctionType) {
6088+
Map<Object, DartType> parameterMap = _computeParameterMap(type);
6089+
Set<Object> explicitlyTypedParameters =
6090+
_computeExplicitlyTypedParameterSet(
6091+
paramInfo.unparenthesizedExpression);
6092+
Set<TypeParameter> result = {};
6093+
for (MapEntry<Object, DartType> entry in parameterMap.entries) {
6094+
if (explicitlyTypedParameters.contains(entry.key)) continue;
6095+
result.addAll(allFreeTypeVariables(entry.value));
6096+
}
6097+
return result;
6098+
} else {
6099+
return const [];
6100+
}
6101+
}
6102+
6103+
@override
6104+
Iterable<TypeParameter> typeVarsFreeInParamReturns(_ParamInfo paramInfo) {
6105+
DartType type = paramInfo.formalType;
6106+
if (type is FunctionType) {
6107+
return allFreeTypeVariables(type.returnType);
6108+
} else {
6109+
return allFreeTypeVariables(type);
6110+
}
6111+
}
6112+
}
6113+
6114+
/// Information about an invocation argument that may or may not have already
6115+
/// been resolved, as part of the deferred resolution mechanism for the
6116+
/// `inference-update-1` feature.
6117+
class _ParamInfo {
6118+
/// The (unsubstituted) type of the formal parameter corresponding to this
6119+
/// argument.
6120+
final DartType formalType;
6121+
6122+
_ParamInfo(this.formalType);
59966123
}

pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
128128
getStandardLowerBound(constraint.upper, upper, clientLibrary);
129129
}
130130

131-
/// Performs downwards inference, producing a set of inferred types that may
132-
/// contain references to the "unknown type".
133-
void downwardsInfer(
131+
/// Performs partial (either downwards or horizontal) inference, producing a
132+
/// set of inferred types that may contain references to the "unknown type".
133+
void partialInfer(
134134
TypeConstraintGatherer gatherer,
135135
List<TypeParameter> typeParametersToInfer,
136136
List<DartType> inferredTypes,

pkg/front_end/test/fasta/type_inference/type_schema_environment_test_base.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ abstract class TypeSchemaEnvironmentTestBase {
171171
returnContextTypeNode,
172172
testLibrary);
173173
if (formalTypeNodes == null) {
174-
typeSchemaEnvironment.downwardsInfer(gatherer,
175-
typeParameterNodesToInfer, inferredTypeNodes, testLibrary);
174+
typeSchemaEnvironment.partialInfer(gatherer, typeParameterNodesToInfer,
175+
inferredTypeNodes, testLibrary);
176176
} else {
177177
gatherer.constrainArguments(formalTypeNodes, actualTypeNodes!);
178178
typeSchemaEnvironment.upwardsInfer(gatherer, typeParameterNodesToInfer,

pkg/front_end/test/spell_checking_list_code.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ history
577577
hit
578578
hoc
579579
hopefully
580+
horizontal
580581
href
581582
html
582583
https
@@ -1399,6 +1400,7 @@ uncommon
13991400
unconditionally
14001401
unconstrained
14011402
undeclare
1403+
undeferred
14021404
undergo
14031405
undermine
14041406
undo

pkg/front_end/test/spell_checking_list_tests.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ hkt
323323
home
324324
hoo
325325
hook
326+
horizontally
326327
hosted
327328
hosting
328329
hot
@@ -714,6 +715,7 @@ y's
714715
year
715716
yxxx
716717
yy
718+
zeroth
717719
zipf's
718720
zyx
719721
zz

0 commit comments

Comments
 (0)