Version 2.18.0-83.0.dev
Merge commit 'bf5690dd0cb13631c59fc6817833edc502cdea31' into 'dev'
diff --git a/pkg/compiler/test/dump_info/data/closures.dart b/pkg/compiler/test/dump_info/data/closures.dart
index 7bc3f58..e8b525a 100644
--- a/pkg/compiler/test/dump_info/data/closures.dart
+++ b/pkg/compiler/test/dump_info/data/closures.dart
@@ -62,7 +62,6 @@
"id": "outputUnit/main",
"kind": "outputUnit",
"name": "main",
- "size": 107495,
"filename": "out",
"imports": []
}]
diff --git a/pkg/compiler/test/dump_info/data/deferred/main.dart b/pkg/compiler/test/dump_info/data/deferred/main.dart
index 2bed74e..84240fe 100644
--- a/pkg/compiler/test/dump_info/data/deferred/main.dart
+++ b/pkg/compiler/test/dump_info/data/deferred/main.dart
@@ -114,7 +114,6 @@
"id": "outputUnit/main",
"kind": "outputUnit",
"name": "main",
- "size": 183730,
"filename": "out",
"imports": []
}]
diff --git a/pkg/compiler/test/dump_info/data/deferred_future/main.dart b/pkg/compiler/test/dump_info/data/deferred_future/main.dart
index 5500fb6..896b6cc 100644
--- a/pkg/compiler/test/dump_info/data/deferred_future/main.dart
+++ b/pkg/compiler/test/dump_info/data/deferred_future/main.dart
@@ -120,7 +120,6 @@
"id": "outputUnit/main",
"kind": "outputUnit",
"name": "main",
- "size": 190176,
"filename": "out",
"imports": []
}]
diff --git a/pkg/compiler/test/dump_info/data/js_members.dart b/pkg/compiler/test/dump_info/data/js_members.dart
index 46efe6b..ded91e2 100644
--- a/pkg/compiler/test/dump_info/data/js_members.dart
+++ b/pkg/compiler/test/dump_info/data/js_members.dart
@@ -138,7 +138,6 @@
"id": "outputUnit/main",
"kind": "outputUnit",
"name": "main",
- "size": 112396,
"filename": "out",
"imports": []
}]
diff --git a/pkg/compiler/test/dump_info/data/members.dart b/pkg/compiler/test/dump_info/data/members.dart
index 4ac2296..4093694 100644
--- a/pkg/compiler/test/dump_info/data/members.dart
+++ b/pkg/compiler/test/dump_info/data/members.dart
@@ -77,7 +77,6 @@
"id": "outputUnit/main",
"kind": "outputUnit",
"name": "main",
- "size": 91101,
"filename": "out",
"imports": []
}]
diff --git a/pkg/compiler/test/dump_info/dump_info_test.dart b/pkg/compiler/test/dump_info/dump_info_test.dart
index 97d9e43..e04902f 100644
--- a/pkg/compiler/test/dump_info/dump_info_test.dart
+++ b/pkg/compiler/test/dump_info/dump_info_test.dart
@@ -22,7 +22,7 @@
final JsonEncoder encoder = const JsonEncoder();
final JsonEncoder indentedEncoder = const JsonEncoder.withIndent(' ');
-String jsonEncode(object, {bool indent = true}) {
+String jsonEncode(Map object, {bool indent = true}) {
var jsonEncoder = indent ? indentedEncoder : encoder;
// Filter block comments since they interfere with ID test comments.
var json =
@@ -30,6 +30,15 @@
return json;
}
+Map filteredJsonObject(Map object, Set<String> filteredFields) {
+ Map filteredObject = {};
+ object.forEach((key, value) {
+ if (filteredFields.contains(key)) return;
+ filteredObject[key] = value;
+ });
+ return filteredObject;
+}
+
main(List<String> args) {
asyncTest(() async {
Directory dataDir = Directory.fromUri(Platform.script.resolve('data'));
@@ -85,8 +94,13 @@
features.addElement(
Tags.dependencies, jsonEncode(dumpInfoState.info.dependencies));
for (final outputUnit in dumpInfoState.info.outputUnits) {
- features.addElement(
- Tags.outputUnits, jsonEncode(outputUnit.accept(converter)));
+ var outputUnitJsonObject = outputUnit.accept(converter);
+ // Remove the size from the main output unit due to high noise ratio.
+ if (outputUnit.name == 'main') {
+ outputUnitJsonObject =
+ filteredJsonObject(outputUnitJsonObject, {'size'});
+ }
+ features.addElement(Tags.outputUnits, jsonEncode(outputUnitJsonObject));
}
features.addElement(
Tags.deferredFiles, jsonEncode(dumpInfoState.info.deferredFiles));
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 55770e1..e71c21bf 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -2037,7 +2037,7 @@
typeContext,
inferrer.libraryBuilder.library,
isConst: node.isConst);
- inferrer.typeSchemaEnvironment.downwardsInfer(
+ inferrer.typeSchemaEnvironment.partialInfer(
gatherer,
listClass.typeParameters,
inferredTypes,
@@ -2722,7 +2722,7 @@
typeContext,
inferrer.libraryBuilder.library,
isConst: node.isConst);
- inferrer.typeSchemaEnvironment.downwardsInfer(
+ inferrer.typeSchemaEnvironment.partialInfer(
gatherer,
mapClass.typeParameters,
inferredTypes,
@@ -2807,7 +2807,7 @@
typeContext,
inferrer.libraryBuilder.library,
isConst: node.isConst);
- inferrer.typeSchemaEnvironment.downwardsInfer(
+ inferrer.typeSchemaEnvironment.partialInfer(
gatherer,
inferrer.coreTypes.setClass.typeParameters,
inferredTypesForSet,
@@ -5992,7 +5992,7 @@
typeContext,
inferrer.libraryBuilder.library,
isConst: node.isConst);
- inferrer.typeSchemaEnvironment.downwardsInfer(
+ inferrer.typeSchemaEnvironment.partialInfer(
gatherer,
setClass.typeParameters,
inferredTypes,
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 3745736..950ab438 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
@@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
+import 'package:_fe_analyzer_shared/src/deferred_function_literal_heuristic.dart';
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:_fe_analyzer_shared/src/testing/id.dart';
import 'package:_fe_analyzer_shared/src/util/link.dart';
@@ -82,6 +83,59 @@
identical(name, '%');
}
+/// Given a [FunctionExpression], computes a set whose elements consist of (a)
+/// an integer corresponding to the zero-based index of each positional
+/// parameter of the function expression that has an explicit type annotation,
+/// and (b) a string corresponding to the name of each named parameter of the
+/// function expression that has an explicit type annotation.
+Set<Object> _computeExplicitlyTypedParameterSet(
+ FunctionExpression functionExpression) {
+ Set<Object> result = {};
+ int unnamedParameterIndex = 0;
+ for (VariableDeclaration positionalParameter
+ in functionExpression.function.positionalParameters) {
+ int key = unnamedParameterIndex++;
+ if (!(positionalParameter as VariableDeclarationImpl).isImplicitlyTyped) {
+ result.add(key);
+ }
+ }
+ for (VariableDeclaration namedParameter
+ in functionExpression.function.namedParameters) {
+ String key = namedParameter.name!;
+ if (!(namedParameter as VariableDeclarationImpl).isImplicitlyTyped) {
+ result.add(key);
+ }
+ }
+ return result;
+}
+
+/// Given an function type, computes a map based on the parameters whose keys
+/// are either the parameter name (for named parameters) or the zero-based
+/// integer index (for unnamed parameters), and whose values are the parameter
+/// types.
+Map<Object, DartType> _computeParameterMap(FunctionType functionType) => {
+ for (int i = 0; i < functionType.positionalParameters.length; i++)
+ i: functionType.positionalParameters[i],
+ for (NamedType namedType in functionType.namedParameters)
+ namedType.name: namedType.type
+ };
+
+/// Computes a list of [_ParamInfo] objects corresponding to the invocation
+/// parameters that were *not* deferred.
+List<_ParamInfo> _computeUndeferredParamInfo(List<DartType> formalTypes,
+ List<_DeferredParamInfo> deferredFunctionLiterals) {
+ // TODO(paulberry): test that the right thing happens when evaluation order differs from classic (positional/named) order.
+ Set<int> evaluationOrderIndicesAlreadyCovered = {
+ for (_DeferredParamInfo functionLiteral in deferredFunctionLiterals)
+ functionLiteral.evaluationOrderIndex
+ };
+ return [
+ for (int i = 0; i < formalTypes.length; i++)
+ if (!evaluationOrderIndicesAlreadyCovered.contains(i))
+ new _ParamInfo(formalTypes[i])
+ ];
+}
+
/// Enum denoting the kinds of contravariance check that might need to be
/// inserted for a method call.
enum MethodContravarianceCheckKind {
@@ -2290,7 +2344,8 @@
explicitTypeArguments == null &&
calleeTypeParameters.isNotEmpty;
bool typeChecksNeeded = !isTopLevel;
- bool useFormalAndActualTypes = typeChecksNeeded ||
+ bool useFormalAndActualTypes = inferenceNeeded ||
+ typeChecksNeeded ||
isSpecialCasedBinaryOperator ||
isSpecialCasedTernaryOperator;
@@ -2331,7 +2386,7 @@
calleeTypeParameters,
typeContext,
libraryBuilder.library);
- typeSchemaEnvironment.downwardsInfer(gatherer, calleeTypeParameters,
+ typeSchemaEnvironment.partialInfer(gatherer, calleeTypeParameters,
inferredTypes, libraryBuilder.library);
substitution =
Substitution.fromPairs(calleeTypeParameters, inferredTypes);
@@ -2471,6 +2526,7 @@
(deferredFunctionLiterals ??= []).add(new _DeferredParamInfo(
formalType: formalType,
argumentExpression: argumentExpression,
+ unparenthesizedExpression: unparenthesizedExpression,
isNamed: !isExpression,
evaluationOrderIndex: evaluationOrderIndex,
index: index));
@@ -2510,26 +2566,44 @@
}
}
if (deferredFunctionLiterals != null) {
- for (_DeferredParamInfo deferredArgument in deferredFunctionLiterals) {
- ExpressionInferenceResult result = inferArgument(
- deferredArgument.formalType, deferredArgument.argumentExpression,
- isNamed: deferredArgument.isNamed);
- DartType inferredType = _computeInferredType(result);
- Expression expression = result.expression;
- identicalInfo?[deferredArgument.evaluationOrderIndex] =
- flowAnalysis.equalityOperand_end(expression, inferredType);
- if (deferredArgument.isNamed) {
- NamedExpression namedArgument =
- arguments.named[deferredArgument.index];
- namedArgument.value = expression..parent = namedArgument;
- } else {
- arguments.positional[deferredArgument.index] = expression
- ..parent = arguments;
+ bool isFirstStage = true;
+ for (List<_DeferredParamInfo> stage in new _FunctionLiteralDependencies(
+ deferredFunctionLiterals,
+ calleeType.typeParameters.toSet(),
+ inferenceNeeded
+ ? _computeUndeferredParamInfo(
+ formalTypes!, deferredFunctionLiterals)
+ : const [])
+ .planReconciliationStages()) {
+ if (gatherer != null && !isFirstStage) {
+ typeSchemaEnvironment.partialInfer(gatherer, calleeTypeParameters,
+ inferredTypes!, libraryBuilder.library);
+ substitution =
+ Substitution.fromPairs(calleeTypeParameters, inferredTypes);
}
- gatherer?.tryConstrainLower(deferredArgument.formalType, inferredType);
- if (useFormalAndActualTypes) {
- actualTypes![deferredArgument.evaluationOrderIndex] = inferredType;
+ for (_DeferredParamInfo deferredArgument in stage) {
+ ExpressionInferenceResult result = inferArgument(
+ deferredArgument.formalType, deferredArgument.argumentExpression,
+ isNamed: deferredArgument.isNamed);
+ DartType inferredType = _computeInferredType(result);
+ Expression expression = result.expression;
+ identicalInfo?[deferredArgument.evaluationOrderIndex] =
+ flowAnalysis.equalityOperand_end(expression, inferredType);
+ if (deferredArgument.isNamed) {
+ NamedExpression namedArgument =
+ arguments.named[deferredArgument.index];
+ namedArgument.value = expression..parent = namedArgument;
+ } else {
+ arguments.positional[deferredArgument.index] = expression
+ ..parent = arguments;
+ }
+ gatherer?.tryConstrainLower(
+ deferredArgument.formalType, inferredType);
+ if (useFormalAndActualTypes) {
+ actualTypes![deferredArgument.evaluationOrderIndex] = inferredType;
+ }
}
+ isFirstStage = false;
}
}
if (identicalInfo != null) {
@@ -5967,15 +6041,14 @@
/// Information about an invocation argument that needs to be resolved later due
/// to the fact that it's a function literal and the `inference-update-1`
/// feature is enabled.
-class _DeferredParamInfo {
- /// The (unsubstituted) type of the formal parameter corresponding to this
- /// argument.
- final DartType formalType;
-
+class _DeferredParamInfo extends _ParamInfo {
/// The argument expression (possibly wrapped in an arbitrary number of
/// ParenthesizedExpressions).
final Expression argumentExpression;
+ /// The unparenthesized argument expression.
+ final FunctionExpression unparenthesizedExpression;
+
/// Indicates whether this is a named argument.
final bool isNamed;
@@ -5988,9 +6061,63 @@
final int index;
_DeferredParamInfo(
- {required this.formalType,
+ {required DartType formalType,
required this.argumentExpression,
+ required this.unparenthesizedExpression,
required this.isNamed,
required this.evaluationOrderIndex,
- required this.index});
+ required this.index})
+ : super(formalType);
+}
+
+/// Extension of the shared [FunctionLiteralDependencies] logic used by the
+/// front end.
+class _FunctionLiteralDependencies extends FunctionLiteralDependencies<
+ TypeParameter, _ParamInfo, _DeferredParamInfo> {
+ _FunctionLiteralDependencies(
+ Iterable<_DeferredParamInfo> deferredParamInfo,
+ Iterable<TypeParameter> typeVariables,
+ List<_ParamInfo> undeferredParamInfo)
+ : super(deferredParamInfo, typeVariables, undeferredParamInfo);
+
+ @override
+ Iterable<TypeParameter> typeVarsFreeInParamParams(
+ _DeferredParamInfo paramInfo) {
+ DartType type = paramInfo.formalType;
+ if (type is FunctionType) {
+ Map<Object, DartType> parameterMap = _computeParameterMap(type);
+ Set<Object> explicitlyTypedParameters =
+ _computeExplicitlyTypedParameterSet(
+ paramInfo.unparenthesizedExpression);
+ Set<TypeParameter> result = {};
+ for (MapEntry<Object, DartType> entry in parameterMap.entries) {
+ if (explicitlyTypedParameters.contains(entry.key)) continue;
+ result.addAll(allFreeTypeVariables(entry.value));
+ }
+ return result;
+ } else {
+ return const [];
+ }
+ }
+
+ @override
+ Iterable<TypeParameter> typeVarsFreeInParamReturns(_ParamInfo paramInfo) {
+ DartType type = paramInfo.formalType;
+ if (type is FunctionType) {
+ return allFreeTypeVariables(type.returnType);
+ } else {
+ return allFreeTypeVariables(type);
+ }
+ }
+}
+
+/// Information about an invocation argument that may or may not have already
+/// been resolved, as part of the deferred resolution mechanism for the
+/// `inference-update-1` feature.
+class _ParamInfo {
+ /// The (unsubstituted) type of the formal parameter corresponding to this
+ /// argument.
+ final DartType formalType;
+
+ _ParamInfo(this.formalType);
}
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
index 7c3f33b..86cc5da 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
@@ -128,9 +128,9 @@
getStandardLowerBound(constraint.upper, upper, clientLibrary);
}
- /// Performs downwards inference, producing a set of inferred types that may
- /// contain references to the "unknown type".
- void downwardsInfer(
+ /// Performs partial (either downwards or horizontal) inference, producing a
+ /// set of inferred types that may contain references to the "unknown type".
+ void partialInfer(
TypeConstraintGatherer gatherer,
List<TypeParameter> typeParametersToInfer,
List<DartType> inferredTypes,
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_environment_test_base.dart b/pkg/front_end/test/fasta/type_inference/type_schema_environment_test_base.dart
index 2e9bf18..3309b0b 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_environment_test_base.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_environment_test_base.dart
@@ -171,8 +171,8 @@
returnContextTypeNode,
testLibrary);
if (formalTypeNodes == null) {
- typeSchemaEnvironment.downwardsInfer(gatherer,
- typeParameterNodesToInfer, inferredTypeNodes, testLibrary);
+ typeSchemaEnvironment.partialInfer(gatherer, typeParameterNodesToInfer,
+ inferredTypeNodes, testLibrary);
} else {
gatherer.constrainArguments(formalTypeNodes, actualTypeNodes!);
typeSchemaEnvironment.upwardsInfer(gatherer, typeParameterNodesToInfer,
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index a7fc5e8..9b124a58 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -577,6 +577,7 @@
hit
hoc
hopefully
+horizontal
href
html
https
@@ -1399,6 +1400,7 @@
unconditionally
unconstrained
undeclare
+undeferred
undergo
undermine
undo
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 869c4d1..7bfe8f0 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -323,6 +323,7 @@
home
hoo
hook
+horizontally
hosted
hosting
hot
@@ -714,6 +715,7 @@
year
yxxx
yy
+zeroth
zipf's
zyx
zz
diff --git a/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart
new file mode 100644
index 0000000..10d064e
--- /dev/null
+++ b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart
@@ -0,0 +1,163 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests that when the feature is enabled, inferred types can flow
+// "horizontally" from a non-closure argument of an invocation to a closure
+// argument.
+
+testLaterUnnamedParameter(void Function<T>(T, void Function(T)) f) {
+ f(0, (x) {
+ x;
+ });
+}
+
+/// This special case verifies that the implementations correctly associate the
+/// zeroth positional parameter with the corresponding argument (even if that
+/// argument isn't in the zeroth position at the call site).
+testLaterUnnamedParameterDependsOnNamedParameter(
+ void Function<T>(void Function(T), {required T a}) f) {
+ f(a: 0, (x) {
+ x;
+ });
+}
+
+testEarlierUnnamedParameter(void Function<T>(void Function(T), T) f) {
+ f((x) {
+ x;
+ }, 0);
+}
+
+testLaterNamedParameter(
+ void Function<T>({required T a, required void Function(T) b}) f) {
+ f(
+ a: 0,
+ b: (x) {
+ x;
+ });
+}
+
+testEarlierNamedParameter(
+ void Function<T>({required void Function(T) a, required T b}) f) {
+ f(
+ a: (x) {
+ x;
+ },
+ b: 0);
+}
+
+/// This special case verifies that the implementations correctly associate the
+/// zeroth positional parameter with the corresponding argument (even if that
+/// argument isn't in the zeroth position at the call site).
+testEarlierNamedParameterDependsOnUnnamedParameter(
+ void Function<T>(T b, {required void Function(T) a}) f) {
+ f(a: (x) {
+ x;
+ }, 0);
+}
+
+testPropagateToReturnType(U Function<T, U>(T, U Function(T)) f) {
+ f(0, (x) => [x]);
+}
+
+testFold(List<int> list) {
+ var a = list.fold(0, (x, y) => (x) + (y));
+ a;
+}
+
+// The test cases below exercise situations where there are multiple closures in
+// the invocation, and they need to be inferred in the right order.
+
+testClosureAsParameterType(U Function<T, U>(T, U Function(T)) f) {
+ f(() => 0, (h) => [h()]);
+}
+
+testPropagateToEarlierClosure(U Function<T, U>(U Function(T), T Function()) f) {
+ f((x) => [x], () => 0);
+}
+
+testPropagateToLaterClosure(U Function<T, U>(T Function(), U Function(T)) f) {
+ f(() => 0, (x) => [x]);
+}
+
+testLongDependencyChain(
+ V Function<T, U, V>(T Function(), U Function(T), V Function(U)) f) {
+ f(() => [0], (x) => x.single, (y) => {y});
+}
+
+testDependencyCycle(Map<T, U> Function<T, U>(T Function(U), U Function(T)) f) {
+ f((x) => [x], (y) => {y});
+}
+
+testNecessaryDueToWrongExplicitParameterType(List<int> list) {
+ var a = list.fold(0, (x, int y) => (x) + (y));
+ a;
+}
+
+testPropagateFromContravariantReturnType(
+ U Function<T, U>(void Function(T) Function(), U Function(T)) f) {
+ f(() => (int i) {}, (x) => [x]);
+}
+
+testPropagateToContravariantParameterType(
+ U Function<T, U>(T Function(), U Function(void Function(T))) f) {
+ f(() => 0, (x) => [x]);
+}
+
+testReturnTypeRefersToMultipleTypeVars(
+ void Function<T, U>(
+ Map<T, U> Function(), void Function(T), void Function(U))
+ f) {
+ f(() => {0: ''}, (k) {
+ k;
+ }, (v) {
+ v;
+ });
+}
+
+testUnnecessaryDueToNoDependency(T Function<T>(T Function(), T) f) {
+ f(() => 0, null);
+}
+
+testUnnecessaryDueToExplicitParameterType(List<int> list) {
+ var a = list.fold(null, (int? x, y) => (x ?? 0) + y);
+ a;
+}
+
+testUnnecessaryDueToExplicitParameterTypeNamed(
+ T Function<T>(T, T Function({required T x, required int y})) f) {
+ var a = f(null, ({int? x, required y}) => (x ?? 0) + y);
+ a;
+}
+
+testParenthesized(void Function<T>(T, void Function(T)) f) {
+ f(0, ((x) {
+ x;
+ }));
+}
+
+testParenthesizedNamed(
+ void Function<T>({required T a, required void Function(T) b}) f) {
+ f(
+ a: 0,
+ b: ((x) {
+ x;
+ }));
+}
+
+testParenthesizedTwice(void Function<T>(T, void Function(T)) f) {
+ f(0, (((x) {
+ x;
+ })));
+}
+
+testParenthesizedTwiceNamed(
+ void Function<T>({required T a, required void Function(T) b}) f) {
+ f(
+ a: 0,
+ b: (((x) {
+ x;
+ })));
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.textual_outline.expect b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.textual_outline.expect
new file mode 100644
index 0000000..7412213
--- /dev/null
+++ b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.textual_outline.expect
@@ -0,0 +1,39 @@
+testLaterUnnamedParameter(void Function<T>(T, void Function(T)) f) {}
+testLaterUnnamedParameterDependsOnNamedParameter(
+ void Function<T>(void Function(T), {required T a}) f) {}
+testEarlierUnnamedParameter(void Function<T>(void Function(T), T) f) {}
+testLaterNamedParameter(
+ void Function<T>({required T a, required void Function(T) b}) f) {}
+testEarlierNamedParameter(
+ void Function<T>({required void Function(T) a, required T b}) f) {}
+testEarlierNamedParameterDependsOnUnnamedParameter(
+ void Function<T>(T b, {required void Function(T) a}) f) {}
+testPropagateToReturnType(U Function<T, U>(T, U Function(T)) f) {}
+testFold(List<int> list) {}
+testClosureAsParameterType(U Function<T, U>(T, U Function(T)) f) {}
+testPropagateToEarlierClosure(
+ U Function<T, U>(U Function(T), T Function()) f) {}
+testPropagateToLaterClosure(U Function<T, U>(T Function(), U Function(T)) f) {}
+testLongDependencyChain(
+ V Function<T, U, V>(T Function(), U Function(T), V Function(U)) f) {}
+testDependencyCycle(Map<T, U> Function<T, U>(T Function(U), U Function(T)) f) {}
+testNecessaryDueToWrongExplicitParameterType(List<int> list) {}
+testPropagateFromContravariantReturnType(
+ U Function<T, U>(void Function(T) Function(), U Function(T)) f) {}
+testPropagateToContravariantParameterType(
+ U Function<T, U>(T Function(), U Function(void Function(T))) f) {}
+testReturnTypeRefersToMultipleTypeVars(
+ void Function<T, U>(
+ Map<T, U> Function(), void Function(T), void Function(U))
+ f) {}
+testUnnecessaryDueToNoDependency(T Function<T>(T Function(), T) f) {}
+testUnnecessaryDueToExplicitParameterType(List<int> list) {}
+testUnnecessaryDueToExplicitParameterTypeNamed(
+ T Function<T>(T, T Function({required T x, required int y})) f) {}
+testParenthesized(void Function<T>(T, void Function(T)) f) {}
+testParenthesizedNamed(
+ void Function<T>({required T a, required void Function(T) b}) f) {}
+testParenthesizedTwice(void Function<T>(T, void Function(T)) f) {}
+testParenthesizedTwiceNamed(
+ void Function<T>({required T a, required void Function(T) b}) f) {}
+main() {}
diff --git a/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..0202485
--- /dev/null
+++ b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.textual_outline_modelled.expect
@@ -0,0 +1,39 @@
+main() {}
+testClosureAsParameterType(U Function<T, U>(T, U Function(T)) f) {}
+testDependencyCycle(Map<T, U> Function<T, U>(T Function(U), U Function(T)) f) {}
+testEarlierNamedParameter(
+ void Function<T>({required void Function(T) a, required T b}) f) {}
+testEarlierNamedParameterDependsOnUnnamedParameter(
+ void Function<T>(T b, {required void Function(T) a}) f) {}
+testEarlierUnnamedParameter(void Function<T>(void Function(T), T) f) {}
+testFold(List<int> list) {}
+testLaterNamedParameter(
+ void Function<T>({required T a, required void Function(T) b}) f) {}
+testLaterUnnamedParameter(void Function<T>(T, void Function(T)) f) {}
+testLaterUnnamedParameterDependsOnNamedParameter(
+ void Function<T>(void Function(T), {required T a}) f) {}
+testLongDependencyChain(
+ V Function<T, U, V>(T Function(), U Function(T), V Function(U)) f) {}
+testNecessaryDueToWrongExplicitParameterType(List<int> list) {}
+testParenthesized(void Function<T>(T, void Function(T)) f) {}
+testParenthesizedNamed(
+ void Function<T>({required T a, required void Function(T) b}) f) {}
+testParenthesizedTwice(void Function<T>(T, void Function(T)) f) {}
+testParenthesizedTwiceNamed(
+ void Function<T>({required T a, required void Function(T) b}) f) {}
+testPropagateFromContravariantReturnType(
+ U Function<T, U>(void Function(T) Function(), U Function(T)) f) {}
+testPropagateToContravariantParameterType(
+ U Function<T, U>(T Function(), U Function(void Function(T))) f) {}
+testPropagateToEarlierClosure(
+ U Function<T, U>(U Function(T), T Function()) f) {}
+testPropagateToLaterClosure(U Function<T, U>(T Function(), U Function(T)) f) {}
+testPropagateToReturnType(U Function<T, U>(T, U Function(T)) f) {}
+testReturnTypeRefersToMultipleTypeVars(
+ void Function<T, U>(
+ Map<T, U> Function(), void Function(T), void Function(U))
+ f) {}
+testUnnecessaryDueToExplicitParameterType(List<int> list) {}
+testUnnecessaryDueToExplicitParameterTypeNamed(
+ T Function<T>(T, T Function({required T x, required int y})) f) {}
+testUnnecessaryDueToNoDependency(T Function<T>(T Function(), T) f) {}
diff --git a/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.expect b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.expect
new file mode 100644
index 0000000..fcb8a97
--- /dev/null
+++ b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.expect
@@ -0,0 +1,116 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:collection" as col;
+
+static method testLaterUnnamedParameter(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testLaterUnnamedParameterDependsOnNamedParameter(<T extends core::Object? = dynamic>((T%) → void, {required a: T%}) → void f) → dynamic {
+ let final core::int #t1 = 0 in f<core::int>((core::int x) → void {
+ x;
+ }, a: #t1){((core::int) → void, {required a: core::int}) → void};
+}
+static method testEarlierUnnamedParameter(<T extends core::Object? = dynamic>((T%) → void, T%) → void f) → dynamic {
+ f<core::int>((core::int x) → void {
+ x;
+ }, 0){((core::int) → void, core::int) → void};
+}
+static method testLaterNamedParameter(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method testEarlierNamedParameter(<T extends core::Object? = dynamic>({required a: (T%) → void, required b: T%}) → void f) → dynamic {
+ f<core::int>(a: (core::int x) → void {
+ x;
+ }, b: 0){({required a: (core::int) → void, required b: core::int}) → void};
+}
+static method testEarlierNamedParameterDependsOnUnnamedParameter(<T extends core::Object? = dynamic>(T%, {required a: (T%) → void}) → void f) → dynamic {
+ f<core::int>(0, a: (core::int x) → void {
+ x;
+ }){(core::int, {required a: (core::int) → void}) → void};
+}
+static method testPropagateToReturnType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(T%, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(0, (core::int x) → core::List<core::int> => <core::int>[x]){(core::int, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testFold(core::List<core::int> list) → dynamic {
+ core::int a = list.{core::Iterable::fold}<core::int>(0, (core::int x, core::int y) → core::int => x.{core::num::+}(y){(core::num) → core::int}){(core::int, (core::int, core::int) → core::int) → core::int};
+ a;
+}
+static method testClosureAsParameterType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(T%, (T%) → U%) → U% f) → dynamic {
+ f<() → core::int, core::List<core::int>>(() → core::int => 0, (() → core::int h) → core::List<core::int> => <core::int>[h(){() → core::int}]){(() → core::int, (() → core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testPropagateToEarlierClosure(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>((T%) → U%, () → T%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>((core::int x) → core::List<core::int> => <core::int>[x], () → core::int => 0){((core::int) → core::List<core::int>, () → core::int) → core::List<core::int>};
+}
+static method testPropagateToLaterClosure(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → T%, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(() → core::int => 0, (core::int x) → core::List<core::int> => <core::int>[x]){(() → core::int, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testLongDependencyChain(<T extends core::Object? = dynamic, U extends core::Object? = dynamic, V extends core::Object? = dynamic>(() → T%, (T%) → U%, (U%) → V%) → V% f) → dynamic {
+ f<core::List<core::int>, core::int, core::Set<core::int>>(() → core::List<core::int> => <core::int>[0], (core::List<core::int> x) → core::int => x.{core::Iterable::single}{core::int}, (core::int y) → core::Set<core::int> => block {
+ final core::Set<core::int> #t2 = col::LinkedHashSet::•<core::int>();
+ #t2.{core::Set::add}{Invariant}(y){(core::int) → core::bool};
+ } =>#t2){(() → core::List<core::int>, (core::List<core::int>) → core::int, (core::int) → core::Set<core::int>) → core::Set<core::int>};
+}
+static method testDependencyCycle(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>((U%) → T%, (T%) → U%) → core::Map<T%, U%> f) → dynamic {
+ f<core::List<core::Object?>, core::Set<core::Object?>>((core::Object? x) → core::List<core::Object?> => <core::Object?>[x], (core::Object? y) → core::Set<core::Object?> => block {
+ final core::Set<core::Object?> #t3 = col::LinkedHashSet::•<core::Object?>();
+ #t3.{core::Set::add}{Invariant}(y){(core::Object?) → core::bool};
+ } =>#t3){((core::Set<core::Object?>) → core::List<core::Object?>, (core::List<core::Object?>) → core::Set<core::Object?>) → core::Map<core::List<core::Object?>, core::Set<core::Object?>>};
+}
+static method testNecessaryDueToWrongExplicitParameterType(core::List<core::int> list) → dynamic {
+ core::int a = list.{core::Iterable::fold}<core::int>(0, (core::int x, core::int y) → core::int => x.{core::num::+}(y){(core::num) → core::int}){(core::int, (core::int, core::int) → core::int) → core::int};
+ a;
+}
+static method testPropagateFromContravariantReturnType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → (T%) → void, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(() → (core::int) → void => (core::int i) → void {}, (core::int x) → core::List<core::int> => <core::int>[x]){(() → (core::int) → void, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testPropagateToContravariantParameterType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → T%, ((T%) → void) → U%) → U% f) → dynamic {
+ f<core::int, core::List<(core::int) → void>>(() → core::int => 0, ((core::int) → void x) → core::List<(core::int) → void> => <(core::int) → void>[x]){(() → core::int, ((core::int) → void) → core::List<(core::int) → void>) → core::List<(core::int) → void>};
+}
+static method testReturnTypeRefersToMultipleTypeVars(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → core::Map<T%, U%>, (T%) → void, (U%) → void) → void f) → dynamic {
+ f<core::int, core::String>(() → core::Map<core::int, core::String> => <core::int, core::String>{0: ""}, (core::int k) → void {
+ k;
+ }, (core::String v) → void {
+ v;
+ }){(() → core::Map<core::int, core::String>, (core::int) → void, (core::String) → void) → void};
+}
+static method testUnnecessaryDueToNoDependency(<T extends core::Object? = dynamic>(() → T%, T%) → T% f) → dynamic {
+ f<core::int?>(() → core::int => 0, null){(() → core::int?, core::int?) → core::int?};
+}
+static method testUnnecessaryDueToExplicitParameterType(core::List<core::int> list) → dynamic {
+ core::int? a = list.{core::Iterable::fold}<core::int?>(null, (core::int? x, core::int y) → core::int => (let final core::int? #t4 = x in #t4 == null ?{core::int} 0 : #t4{core::int}).{core::num::+}(y){(core::num) → core::int}){(core::int?, (core::int?, core::int) → core::int?) → core::int?};
+ a;
+}
+static method testUnnecessaryDueToExplicitParameterTypeNamed(<T extends core::Object? = dynamic>(T%, ({required x: T%, required y: core::int}) → T%) → T% f) → dynamic {
+ core::int? a = f<core::int?>(null, ({core::int? x = #C1, required core::int y = #C1}) → core::int => (let final core::int? #t5 = x in #t5 == null ?{core::int} 0 : #t5{core::int}).{core::num::+}(y){(core::num) → core::int}){(core::int?, ({required x: core::int?, required y: core::int}) → core::int?) → core::int?};
+ a;
+}
+static method testParenthesized(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testParenthesizedNamed(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method testParenthesizedTwice(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testParenthesizedTwiceNamed(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method main() → dynamic {}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.modular.expect b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.modular.expect
new file mode 100644
index 0000000..fcb8a97
--- /dev/null
+++ b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.modular.expect
@@ -0,0 +1,116 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:collection" as col;
+
+static method testLaterUnnamedParameter(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testLaterUnnamedParameterDependsOnNamedParameter(<T extends core::Object? = dynamic>((T%) → void, {required a: T%}) → void f) → dynamic {
+ let final core::int #t1 = 0 in f<core::int>((core::int x) → void {
+ x;
+ }, a: #t1){((core::int) → void, {required a: core::int}) → void};
+}
+static method testEarlierUnnamedParameter(<T extends core::Object? = dynamic>((T%) → void, T%) → void f) → dynamic {
+ f<core::int>((core::int x) → void {
+ x;
+ }, 0){((core::int) → void, core::int) → void};
+}
+static method testLaterNamedParameter(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method testEarlierNamedParameter(<T extends core::Object? = dynamic>({required a: (T%) → void, required b: T%}) → void f) → dynamic {
+ f<core::int>(a: (core::int x) → void {
+ x;
+ }, b: 0){({required a: (core::int) → void, required b: core::int}) → void};
+}
+static method testEarlierNamedParameterDependsOnUnnamedParameter(<T extends core::Object? = dynamic>(T%, {required a: (T%) → void}) → void f) → dynamic {
+ f<core::int>(0, a: (core::int x) → void {
+ x;
+ }){(core::int, {required a: (core::int) → void}) → void};
+}
+static method testPropagateToReturnType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(T%, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(0, (core::int x) → core::List<core::int> => <core::int>[x]){(core::int, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testFold(core::List<core::int> list) → dynamic {
+ core::int a = list.{core::Iterable::fold}<core::int>(0, (core::int x, core::int y) → core::int => x.{core::num::+}(y){(core::num) → core::int}){(core::int, (core::int, core::int) → core::int) → core::int};
+ a;
+}
+static method testClosureAsParameterType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(T%, (T%) → U%) → U% f) → dynamic {
+ f<() → core::int, core::List<core::int>>(() → core::int => 0, (() → core::int h) → core::List<core::int> => <core::int>[h(){() → core::int}]){(() → core::int, (() → core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testPropagateToEarlierClosure(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>((T%) → U%, () → T%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>((core::int x) → core::List<core::int> => <core::int>[x], () → core::int => 0){((core::int) → core::List<core::int>, () → core::int) → core::List<core::int>};
+}
+static method testPropagateToLaterClosure(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → T%, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(() → core::int => 0, (core::int x) → core::List<core::int> => <core::int>[x]){(() → core::int, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testLongDependencyChain(<T extends core::Object? = dynamic, U extends core::Object? = dynamic, V extends core::Object? = dynamic>(() → T%, (T%) → U%, (U%) → V%) → V% f) → dynamic {
+ f<core::List<core::int>, core::int, core::Set<core::int>>(() → core::List<core::int> => <core::int>[0], (core::List<core::int> x) → core::int => x.{core::Iterable::single}{core::int}, (core::int y) → core::Set<core::int> => block {
+ final core::Set<core::int> #t2 = col::LinkedHashSet::•<core::int>();
+ #t2.{core::Set::add}{Invariant}(y){(core::int) → core::bool};
+ } =>#t2){(() → core::List<core::int>, (core::List<core::int>) → core::int, (core::int) → core::Set<core::int>) → core::Set<core::int>};
+}
+static method testDependencyCycle(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>((U%) → T%, (T%) → U%) → core::Map<T%, U%> f) → dynamic {
+ f<core::List<core::Object?>, core::Set<core::Object?>>((core::Object? x) → core::List<core::Object?> => <core::Object?>[x], (core::Object? y) → core::Set<core::Object?> => block {
+ final core::Set<core::Object?> #t3 = col::LinkedHashSet::•<core::Object?>();
+ #t3.{core::Set::add}{Invariant}(y){(core::Object?) → core::bool};
+ } =>#t3){((core::Set<core::Object?>) → core::List<core::Object?>, (core::List<core::Object?>) → core::Set<core::Object?>) → core::Map<core::List<core::Object?>, core::Set<core::Object?>>};
+}
+static method testNecessaryDueToWrongExplicitParameterType(core::List<core::int> list) → dynamic {
+ core::int a = list.{core::Iterable::fold}<core::int>(0, (core::int x, core::int y) → core::int => x.{core::num::+}(y){(core::num) → core::int}){(core::int, (core::int, core::int) → core::int) → core::int};
+ a;
+}
+static method testPropagateFromContravariantReturnType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → (T%) → void, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(() → (core::int) → void => (core::int i) → void {}, (core::int x) → core::List<core::int> => <core::int>[x]){(() → (core::int) → void, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testPropagateToContravariantParameterType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → T%, ((T%) → void) → U%) → U% f) → dynamic {
+ f<core::int, core::List<(core::int) → void>>(() → core::int => 0, ((core::int) → void x) → core::List<(core::int) → void> => <(core::int) → void>[x]){(() → core::int, ((core::int) → void) → core::List<(core::int) → void>) → core::List<(core::int) → void>};
+}
+static method testReturnTypeRefersToMultipleTypeVars(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → core::Map<T%, U%>, (T%) → void, (U%) → void) → void f) → dynamic {
+ f<core::int, core::String>(() → core::Map<core::int, core::String> => <core::int, core::String>{0: ""}, (core::int k) → void {
+ k;
+ }, (core::String v) → void {
+ v;
+ }){(() → core::Map<core::int, core::String>, (core::int) → void, (core::String) → void) → void};
+}
+static method testUnnecessaryDueToNoDependency(<T extends core::Object? = dynamic>(() → T%, T%) → T% f) → dynamic {
+ f<core::int?>(() → core::int => 0, null){(() → core::int?, core::int?) → core::int?};
+}
+static method testUnnecessaryDueToExplicitParameterType(core::List<core::int> list) → dynamic {
+ core::int? a = list.{core::Iterable::fold}<core::int?>(null, (core::int? x, core::int y) → core::int => (let final core::int? #t4 = x in #t4 == null ?{core::int} 0 : #t4{core::int}).{core::num::+}(y){(core::num) → core::int}){(core::int?, (core::int?, core::int) → core::int?) → core::int?};
+ a;
+}
+static method testUnnecessaryDueToExplicitParameterTypeNamed(<T extends core::Object? = dynamic>(T%, ({required x: T%, required y: core::int}) → T%) → T% f) → dynamic {
+ core::int? a = f<core::int?>(null, ({core::int? x = #C1, required core::int y = #C1}) → core::int => (let final core::int? #t5 = x in #t5 == null ?{core::int} 0 : #t5{core::int}).{core::num::+}(y){(core::num) → core::int}){(core::int?, ({required x: core::int?, required y: core::int}) → core::int?) → core::int?};
+ a;
+}
+static method testParenthesized(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testParenthesizedNamed(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method testParenthesizedTwice(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testParenthesizedTwiceNamed(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method main() → dynamic {}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.outline.expect b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.outline.expect
new file mode 100644
index 0000000..54dba27
--- /dev/null
+++ b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.outline.expect
@@ -0,0 +1,54 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method testLaterUnnamedParameter(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic
+ ;
+static method testLaterUnnamedParameterDependsOnNamedParameter(<T extends core::Object? = dynamic>((T%) → void, {required a: T%}) → void f) → dynamic
+ ;
+static method testEarlierUnnamedParameter(<T extends core::Object? = dynamic>((T%) → void, T%) → void f) → dynamic
+ ;
+static method testLaterNamedParameter(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic
+ ;
+static method testEarlierNamedParameter(<T extends core::Object? = dynamic>({required a: (T%) → void, required b: T%}) → void f) → dynamic
+ ;
+static method testEarlierNamedParameterDependsOnUnnamedParameter(<T extends core::Object? = dynamic>(T%, {required a: (T%) → void}) → void f) → dynamic
+ ;
+static method testPropagateToReturnType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(T%, (T%) → U%) → U% f) → dynamic
+ ;
+static method testFold(core::List<core::int> list) → dynamic
+ ;
+static method testClosureAsParameterType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(T%, (T%) → U%) → U% f) → dynamic
+ ;
+static method testPropagateToEarlierClosure(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>((T%) → U%, () → T%) → U% f) → dynamic
+ ;
+static method testPropagateToLaterClosure(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → T%, (T%) → U%) → U% f) → dynamic
+ ;
+static method testLongDependencyChain(<T extends core::Object? = dynamic, U extends core::Object? = dynamic, V extends core::Object? = dynamic>(() → T%, (T%) → U%, (U%) → V%) → V% f) → dynamic
+ ;
+static method testDependencyCycle(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>((U%) → T%, (T%) → U%) → core::Map<T%, U%> f) → dynamic
+ ;
+static method testNecessaryDueToWrongExplicitParameterType(core::List<core::int> list) → dynamic
+ ;
+static method testPropagateFromContravariantReturnType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → (T%) → void, (T%) → U%) → U% f) → dynamic
+ ;
+static method testPropagateToContravariantParameterType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → T%, ((T%) → void) → U%) → U% f) → dynamic
+ ;
+static method testReturnTypeRefersToMultipleTypeVars(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → core::Map<T%, U%>, (T%) → void, (U%) → void) → void f) → dynamic
+ ;
+static method testUnnecessaryDueToNoDependency(<T extends core::Object? = dynamic>(() → T%, T%) → T% f) → dynamic
+ ;
+static method testUnnecessaryDueToExplicitParameterType(core::List<core::int> list) → dynamic
+ ;
+static method testUnnecessaryDueToExplicitParameterTypeNamed(<T extends core::Object? = dynamic>(T%, ({required x: T%, required y: core::int}) → T%) → T% f) → dynamic
+ ;
+static method testParenthesized(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic
+ ;
+static method testParenthesizedNamed(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic
+ ;
+static method testParenthesizedTwice(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic
+ ;
+static method testParenthesizedTwiceNamed(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.transformed.expect b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.transformed.expect
new file mode 100644
index 0000000..e9c3697
--- /dev/null
+++ b/pkg/front_end/testcases/inference_update_1/horizontal_inference.dart.weak.transformed.expect
@@ -0,0 +1,120 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:collection" as col;
+
+static method testLaterUnnamedParameter(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testLaterUnnamedParameterDependsOnNamedParameter(<T extends core::Object? = dynamic>((T%) → void, {required a: T%}) → void f) → dynamic {
+ let final core::int #t1 = 0 in f<core::int>((core::int x) → void {
+ x;
+ }, a: #t1){((core::int) → void, {required a: core::int}) → void};
+}
+static method testEarlierUnnamedParameter(<T extends core::Object? = dynamic>((T%) → void, T%) → void f) → dynamic {
+ f<core::int>((core::int x) → void {
+ x;
+ }, 0){((core::int) → void, core::int) → void};
+}
+static method testLaterNamedParameter(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method testEarlierNamedParameter(<T extends core::Object? = dynamic>({required a: (T%) → void, required b: T%}) → void f) → dynamic {
+ f<core::int>(a: (core::int x) → void {
+ x;
+ }, b: 0){({required a: (core::int) → void, required b: core::int}) → void};
+}
+static method testEarlierNamedParameterDependsOnUnnamedParameter(<T extends core::Object? = dynamic>(T%, {required a: (T%) → void}) → void f) → dynamic {
+ f<core::int>(0, a: (core::int x) → void {
+ x;
+ }){(core::int, {required a: (core::int) → void}) → void};
+}
+static method testPropagateToReturnType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(T%, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(0, (core::int x) → core::List<core::int> => core::_GrowableList::_literal1<core::int>(x)){(core::int, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testFold(core::List<core::int> list) → dynamic {
+ core::int a = list.{core::Iterable::fold}<core::int>(0, (core::int x, core::int y) → core::int => x.{core::num::+}(y){(core::num) → core::int}){(core::int, (core::int, core::int) → core::int) → core::int};
+ a;
+}
+static method testClosureAsParameterType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(T%, (T%) → U%) → U% f) → dynamic {
+ f<() → core::int, core::List<core::int>>(() → core::int => 0, (() → core::int h) → core::List<core::int> => core::_GrowableList::_literal1<core::int>(h(){() → core::int})){(() → core::int, (() → core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testPropagateToEarlierClosure(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>((T%) → U%, () → T%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>((core::int x) → core::List<core::int> => core::_GrowableList::_literal1<core::int>(x), () → core::int => 0){((core::int) → core::List<core::int>, () → core::int) → core::List<core::int>};
+}
+static method testPropagateToLaterClosure(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → T%, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(() → core::int => 0, (core::int x) → core::List<core::int> => core::_GrowableList::_literal1<core::int>(x)){(() → core::int, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testLongDependencyChain(<T extends core::Object? = dynamic, U extends core::Object? = dynamic, V extends core::Object? = dynamic>(() → T%, (T%) → U%, (U%) → V%) → V% f) → dynamic {
+ f<core::List<core::int>, core::int, core::Set<core::int>>(() → core::List<core::int> => core::_GrowableList::_literal1<core::int>(0), (core::List<core::int> x) → core::int => x.{core::Iterable::single}{core::int}, (core::int y) → core::Set<core::int> => block {
+ final core::Set<core::int> #t2 = new col::_CompactLinkedHashSet::•<core::int>();
+ #t2.{core::Set::add}{Invariant}(y){(core::int) → core::bool};
+ } =>#t2){(() → core::List<core::int>, (core::List<core::int>) → core::int, (core::int) → core::Set<core::int>) → core::Set<core::int>};
+}
+static method testDependencyCycle(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>((U%) → T%, (T%) → U%) → core::Map<T%, U%> f) → dynamic {
+ f<core::List<core::Object?>, core::Set<core::Object?>>((core::Object? x) → core::List<core::Object?> => core::_GrowableList::_literal1<core::Object?>(x), (core::Object? y) → core::Set<core::Object?> => block {
+ final core::Set<core::Object?> #t3 = new col::_CompactLinkedHashSet::•<core::Object?>();
+ #t3.{core::Set::add}{Invariant}(y){(core::Object?) → core::bool};
+ } =>#t3){((core::Set<core::Object?>) → core::List<core::Object?>, (core::List<core::Object?>) → core::Set<core::Object?>) → core::Map<core::List<core::Object?>, core::Set<core::Object?>>};
+}
+static method testNecessaryDueToWrongExplicitParameterType(core::List<core::int> list) → dynamic {
+ core::int a = list.{core::Iterable::fold}<core::int>(0, (core::int x, core::int y) → core::int => x.{core::num::+}(y){(core::num) → core::int}){(core::int, (core::int, core::int) → core::int) → core::int};
+ a;
+}
+static method testPropagateFromContravariantReturnType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → (T%) → void, (T%) → U%) → U% f) → dynamic {
+ f<core::int, core::List<core::int>>(() → (core::int) → void => (core::int i) → void {}, (core::int x) → core::List<core::int> => core::_GrowableList::_literal1<core::int>(x)){(() → (core::int) → void, (core::int) → core::List<core::int>) → core::List<core::int>};
+}
+static method testPropagateToContravariantParameterType(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → T%, ((T%) → void) → U%) → U% f) → dynamic {
+ f<core::int, core::List<(core::int) → void>>(() → core::int => 0, ((core::int) → void x) → core::List<(core::int) → void> => core::_GrowableList::_literal1<(core::int) → void>(x)){(() → core::int, ((core::int) → void) → core::List<(core::int) → void>) → core::List<(core::int) → void>};
+}
+static method testReturnTypeRefersToMultipleTypeVars(<T extends core::Object? = dynamic, U extends core::Object? = dynamic>(() → core::Map<T%, U%>, (T%) → void, (U%) → void) → void f) → dynamic {
+ f<core::int, core::String>(() → core::Map<core::int, core::String> => <core::int, core::String>{0: ""}, (core::int k) → void {
+ k;
+ }, (core::String v) → void {
+ v;
+ }){(() → core::Map<core::int, core::String>, (core::int) → void, (core::String) → void) → void};
+}
+static method testUnnecessaryDueToNoDependency(<T extends core::Object? = dynamic>(() → T%, T%) → T% f) → dynamic {
+ f<core::int?>(() → core::int => 0, null){(() → core::int?, core::int?) → core::int?};
+}
+static method testUnnecessaryDueToExplicitParameterType(core::List<core::int> list) → dynamic {
+ core::int? a = list.{core::Iterable::fold}<core::int?>(null, (core::int? x, core::int y) → core::int => (let final core::int? #t4 = x in #t4 == null ?{core::int} 0 : #t4{core::int}).{core::num::+}(y){(core::num) → core::int}){(core::int?, (core::int?, core::int) → core::int?) → core::int?};
+ a;
+}
+static method testUnnecessaryDueToExplicitParameterTypeNamed(<T extends core::Object? = dynamic>(T%, ({required x: T%, required y: core::int}) → T%) → T% f) → dynamic {
+ core::int? a = f<core::int?>(null, ({core::int? x = #C1, required core::int y = #C1}) → core::int => (let final core::int? #t5 = x in #t5 == null ?{core::int} 0 : #t5{core::int}).{core::num::+}(y){(core::num) → core::int}){(core::int?, ({required x: core::int?, required y: core::int}) → core::int?) → core::int?};
+ a;
+}
+static method testParenthesized(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testParenthesizedNamed(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method testParenthesizedTwice(<T extends core::Object? = dynamic>(T%, (T%) → void) → void f) → dynamic {
+ f<core::int>(0, (core::int x) → void {
+ x;
+ }){(core::int, (core::int) → void) → void};
+}
+static method testParenthesizedTwiceNamed(<T extends core::Object? = dynamic>({required a: T%, required b: (T%) → void}) → void f) → dynamic {
+ f<core::int>(a: 0, b: (core::int x) → void {
+ x;
+ }){({required a: core::int, required b: (core::int) → void}) → void};
+}
+static method main() → dynamic {}
+
+constants {
+ #C1 = null
+}
+
+Extra constant evaluation status:
+Evaluated: VariableGet @ org-dartlang-testcase:///horizontal_inference.dart:20:8 -> IntConstant(0)
+Extra constant evaluation: evaluated: 154, effectively constant: 1
diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart
index a7d73a0..f776684 100644
--- a/pkg/kernel/lib/type_algebra.dart
+++ b/pkg/kernel/lib/type_algebra.dart
@@ -8,6 +8,17 @@
import 'core_types.dart';
import 'src/replacement_visitor.dart';
+/// Returns all free type variables in [type].
+///
+/// Returns the set of all [TypeParameter]s that are referred to by a
+/// [TypeParameterType] in [type] that are not bound to an enclosing
+/// [FunctionType].
+Set<TypeParameter> allFreeTypeVariables(DartType type) {
+ _AllFreeTypeVariablesVisitor visitor = new _AllFreeTypeVariablesVisitor();
+ visitor.visit(type);
+ return visitor.freeTypeVariables;
+}
+
/// Returns a type where all occurrences of the given type parameters have been
/// replaced with the corresponding types.
///
@@ -295,6 +306,80 @@
}
}
+class _AllFreeTypeVariablesVisitor implements DartTypeVisitor<void> {
+ final Set<TypeParameter> boundVariables = {};
+
+ final Set<TypeParameter> freeTypeVariables = {};
+
+ void visit(DartType node) => node.accept(this);
+
+ @override
+ bool defaultDartType(DartType node) {
+ throw new UnsupportedError("Unsupported type $node (${node.runtimeType}.");
+ }
+
+ @override
+ void visitNeverType(NeverType node) {}
+ @override
+ void visitNullType(NullType node) {}
+ @override
+ void visitInvalidType(InvalidType node) {}
+ @override
+ void visitDynamicType(DynamicType node) {}
+ @override
+ void visitVoidType(VoidType node) {}
+
+ @override
+ void visitInterfaceType(InterfaceType node) {
+ for (DartType typeArgument in node.typeArguments) {
+ typeArgument.accept(this);
+ }
+ }
+
+ @override
+ void visitExtensionType(ExtensionType node) {
+ for (DartType typeArgument in node.typeArguments) {
+ typeArgument.accept(this);
+ }
+ }
+
+ @override
+ void visitFutureOrType(FutureOrType node) {
+ node.typeArgument.accept(this);
+ }
+
+ @override
+ void visitTypedefType(TypedefType node) {
+ for (DartType typeArgument in node.typeArguments) {
+ typeArgument.accept(this);
+ }
+ }
+
+ @override
+ void visitFunctionType(FunctionType node) {
+ boundVariables.addAll(node.typeParameters);
+ for (TypeParameter typeParameter in node.typeParameters) {
+ typeParameter.bound.accept(this);
+ typeParameter.defaultType.accept(this);
+ }
+ for (DartType positionalParameter in node.positionalParameters) {
+ positionalParameter.accept(this);
+ }
+ for (NamedType namedParameter in node.namedParameters) {
+ namedParameter.type.accept(this);
+ }
+ node.returnType.accept(this);
+ boundVariables.removeAll(node.typeParameters);
+ }
+
+ @override
+ void visitTypeParameterType(TypeParameterType node) {
+ if (!boundVariables.contains(node.parameter)) {
+ freeTypeVariables.add(node.parameter);
+ }
+ }
+}
+
class _NullSubstitution extends Substitution {
static const _NullSubstitution instance = const _NullSubstitution();
diff --git a/tests/language/inference_update_1/horizontal_inference_enabled_test.dart b/tests/language/inference_update_1/horizontal_inference_enabled_test.dart
index d7fc7e1..c683153 100644
--- a/tests/language/inference_update_1/horizontal_inference_enabled_test.dart
+++ b/tests/language/inference_update_1/horizontal_inference_enabled_test.dart
@@ -16,6 +16,16 @@
});
}
+/// This special case verifies that the implementations correctly associate the
+/// zeroth positional parameter with the corresponding argument (even if that
+/// argument isn't in the zeroth position at the call site).
+testLaterUnnamedParameterDependsOnNamedParameter(
+ void Function<T>(void Function(T), {required T a}) f) {
+ f(a: 0, (x) {
+ x.expectStaticType<Exactly<int>>();
+ });
+}
+
testEarlierUnnamedParameter(void Function<T>(void Function(T), T) f) {
f((x) {
x.expectStaticType<Exactly<int>>();
@@ -40,6 +50,16 @@
b: 0);
}
+/// This special case verifies that the implementations correctly associate the
+/// zeroth positional parameter with the corresponding argument (even if that
+/// argument isn't in the zeroth position at the call site).
+testEarlierNamedParameterDependsOnUnnamedParameter(
+ void Function<T>(T b, {required void Function(T) a}) f) {
+ f(a: (x) {
+ x.expectStaticType<Exactly<int>>();
+ }, 0);
+}
+
testPropagateToReturnType(U Function<T, U>(T, U Function(T)) f) {
f(0, (x) => [x]).expectStaticType<Exactly<List<int>>>();
}
diff --git a/tools/VERSION b/tools/VERSION
index a8dd7e6..a5e92bf 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 82
+PRERELEASE 83
PRERELEASE_PATCH 0
\ No newline at end of file