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