[analyzer][cfe] Share constraint generation for non-generic function types

This change combines function-handling logic from the analyzer's
`TypeConstraintGatherer._functionType0` and the CFE's
`TypeConstraintGatherer._isNullabilityAwareSubtypeMatch` methods into
`TypeConstraintGenerator.performSubtypeConstraintGenerationForFunctionTypes`,
which is in `_fe_analyzer_shared`.

The CFE and the analyzer have some pretty significant differences in
how they represent function types:

- In the analyzer, all function parameters are in a single
  `parameters` list; each element of this list (of type
  `ParameterElement`) can be queried to find out if it is named or
  unnamed, and if it is required or optional. A convention enforced
  partially by the `FunctionType` constructor is that the `parameters`
  list stores reqired unnamed parameters first, then either optional
  unnamed parameters or named parameters; named parameters are sorted
  by name. The analyzer provides additional getters
  `namedParameterTypes`, `normalParameterNames`,
  `normalParameterTypes`, `optionalParameterNames`, and
  `optionalParameterTypes`, which provide other views of this
  information (for example, `namedParameterTypes` contains just the
  named parameters, as a map from name to `ParameterElement`).

- In the CFE, unnamed and named parameters are in two separate lists
  (`positionalParameters`, of type `List<DartType>`, and
  `namedParameters`, of type `List<NamedType>`); in
  `positionalParameters`, required parameters come before optional
  ones. A single integer (`requiredParameterCount`) indicates how many
  elements of `positionalParameters` are required, and by convention,
  `namedParameters` is sorted by name.

In order to share logic between these representations, I had to come
up with a common API that these two representations could be easily
adapted to. The analyzer's representation proved to be easier to
adapt, so I based the common API mostly on the CFE's representation,
but with some name changes for clarity. The shared API is:

- `positionalParameterTypes` gets a list of positional parameter types

- `requiredPositionalParameterCount` tells how many entries in
  `positionalParameterTypes` are required.

- `returnType` gets the function type's return type.

- `sortedNamedParameters` gets a list of information about named
  parameters. The list elements are sorted by name, and each element
  of this list is of type `FunctionParameterStructure` (a common
  interface implemented both by the analyzer's `ParameterElement` and
  the CFE's `NamedType`).

- `typeFormals` gets a list of the function type's formal type
  parameters.

To minimize the performance impact of adapting the analyzer to this
API, the analyzer computes `positionalParameterTypes`,
`requiredPositionalParameterCount`, and `sortedNamedParameters` at the
time a `FunctionType` is constructed. Hopefully this should not be too
much of a performance hit, since doing so does not take too much more
effort than checking that the named parameters are sorted (which the
`FunctionType` constructor was already doing).

This is based on previous work by Chloe Stefantsova in
https://dart-review.googlesource.com/c/sdk/+/386480.

Change-Id: Iefe18d72771146399d81747ceab9c929516b0523
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/386322
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart
index a6835d4..f7a47b8 100644
--- a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart
@@ -182,9 +182,6 @@
   /// non-negative n, and some types T1, ..., Tn.
   bool isExtensionType(SharedTypeView<TypeStructure> type);
 
-  /// Returns `true` if [type] is `F`, `F?`, or `F*` for some function type `F`.
-  bool isFunctionType(SharedTypeView<TypeStructure> type);
-
   /// Returns `true` if [type] is `A<T1, ..., Tn>`, `A<T1, ..., Tn>?`, or
   /// `A<T1, ..., Tn>*` for some class, mixin, or enum A, some non-negative n,
   /// and some types T1, ..., Tn. The method returns `false` if [type] is an
@@ -868,6 +865,8 @@
 /// Abstract interface of a type constraint generator.
 abstract class TypeConstraintGenerator<
     TypeStructure extends SharedTypeStructure<TypeStructure>,
+    FunctionParameterStructure extends SharedNamedFunctionParameterStructure<
+        TypeStructure>,
     Variable extends Object,
     TypeParameterStructure extends SharedTypeParameterStructure<TypeStructure>,
     TypeDeclarationType extends Object,
@@ -900,6 +899,156 @@
       TypeDeclarationType type, TypeDeclaration typeDeclaration);
 
   /// Matches [p] against [q] as a subtype against supertype and returns true if
+  /// [p] and [q] are both function types, and [p] is a subtype of [q] under
+  /// some constraints imposed on type parameters occurring in [q]; false if
+  /// both [p] and [q] are function types, but [p] can't possibly be a subtype
+  /// of [q] under any constraints; and null otherwise.
+  ///
+  /// An invariant of the type inference is that only [p] or [q] may be a
+  /// schema (in other words, may contain the unknown type `_`); the other must
+  /// be simply a type. If [leftSchema] is `true`, [p] may contain `_`; if it is
+  /// `false`, [q] may contain `_`.
+  ///
+  /// As the generator computes the constraints making the relation possible, it
+  /// changes its internal state. The current state of the generator can be
+  /// obtained by [currentState], and the generator can be restored to a state
+  /// via [restoreState]. All of the shared constraint generation methods are
+  /// supposed to restore the generator to the prior state in case of a
+  /// mismatch, taking that responsibility away from the caller.
+  bool? performSubtypeConstraintGenerationForFunctionTypes(
+      TypeStructure p, TypeStructure q,
+      {required bool leftSchema, required AstNode? astNodeForTesting}) {
+    if (p is SharedFunctionTypeStructure<TypeStructure, TypeParameterStructure,
+            FunctionParameterStructure> &&
+        q is SharedFunctionTypeStructure<TypeStructure, TypeParameterStructure,
+            FunctionParameterStructure>) {
+      // A function type (M0,..., Mn, [M{n+1}, ..., Mm]) -> R0 is a subtype
+      // match for a function type (N0,..., Nk, [N{k+1}, ..., Nr]) -> R1 with
+      // respect to L under constraints C0 + ... + Cr + C
+      //
+      // If R0 is a subtype match for a type R1 with respect to L under
+      // constraints C.  If n <= k and r <= m.  And for i in 0...r, Ni is a
+      // subtype match for Mi with respect to L under constraints Ci.
+
+      if (p.typeFormals.isEmpty &&
+          q.typeFormals.isEmpty &&
+          p.sortedNamedParameters.isEmpty &&
+          q.sortedNamedParameters.isEmpty &&
+          p.requiredPositionalParameterCount <=
+              q.requiredPositionalParameterCount &&
+          p.positionalParameterTypes.length >=
+              q.positionalParameterTypes.length) {
+        final TypeConstraintGeneratorState state = currentState;
+
+        if (!performSubtypeConstraintGenerationInternal(
+            p.returnType, q.returnType,
+            leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+          restoreState(state);
+          return null;
+        }
+        for (int i = 0; i < q.positionalParameterTypes.length; ++i) {
+          if (!performSubtypeConstraintGenerationInternal(
+              q.positionalParameterTypes[i], p.positionalParameterTypes[i],
+              leftSchema: !leftSchema, astNodeForTesting: astNodeForTesting)) {
+            restoreState(state);
+            return null;
+          }
+        }
+        return true;
+      } else if (p.typeFormals.isEmpty &&
+          q.typeFormals.isEmpty &&
+          p.positionalParameterTypes.length ==
+              p.requiredPositionalParameterCount &&
+          q.positionalParameterTypes.length ==
+              q.requiredPositionalParameterCount &&
+          p.requiredPositionalParameterCount ==
+              q.requiredPositionalParameterCount &&
+          p.sortedNamedParameters.isNotEmpty &&
+          q.sortedNamedParameters.length <= p.sortedNamedParameters.length) {
+        // Function types with named parameters are treated analogously to the
+        // positional parameter case above.
+
+        final TypeConstraintGeneratorState state = currentState;
+
+        if (!performSubtypeConstraintGenerationInternal(
+            p.returnType, q.returnType,
+            leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+          restoreState(state);
+          return null;
+        }
+        for (int i = 0; i < p.positionalParameterTypes.length; ++i) {
+          if (!performSubtypeConstraintGenerationInternal(
+              q.positionalParameterTypes[i], p.positionalParameterTypes[i],
+              leftSchema: !leftSchema, astNodeForTesting: astNodeForTesting)) {
+            restoreState(state);
+            return null;
+          }
+        }
+        // Consume parameter names from p and q in order. Since the named
+        // parameters in p and q are already sorted by name, we can do this by
+        // iterating through both lists in tandem.
+        int i = 0;
+        int j = 0;
+        while (true) {
+          // Determine whether the next parameter should be consumed from p,
+          // q, or both (because the next set of names matches). If the next
+          // parameter should be consumed from p, comparisonResult will be set
+          // to a value < 0. If the next parameter should be consumed from q,
+          // comparisonResult will be set to a value > 0. If the next
+          // parameter should be consumed from both, comparisonResult will be
+          // set to 0.
+          int comparisonResult;
+          if (i >= p.sortedNamedParameters.length) {
+            if (j >= q.sortedNamedParameters.length) {
+              // No parameters left.
+              return true;
+            } else {
+              // No more parameters in p, so the next parameter must come from
+              // q.
+              comparisonResult = 1;
+            }
+          } else if (j >= q.sortedNamedParameters.length) {
+            // No more parameters in q, so the next parameter must come from
+            // p.
+            comparisonResult = -1;
+          } else {
+            comparisonResult = p.sortedNamedParameters[i].name
+                .compareTo(q.sortedNamedParameters[j].name);
+          }
+          if (comparisonResult > 0) {
+            // Extra parameter in q that q that doesn't exist in p. No match.
+            restoreState(state);
+            return null;
+          } else if (comparisonResult < 0) {
+            // Extra parameter in p that doesn't exist in q. Ok if not
+            // required.
+            if (p.sortedNamedParameters[i].isRequired) {
+              restoreState(state);
+              return null;
+            } else {
+              i++;
+            }
+          } else {
+            // The next parameter in p and q matches, so match their types.
+            if (!performSubtypeConstraintGenerationInternal(
+                q.sortedNamedParameters[j].type,
+                p.sortedNamedParameters[i].type,
+                leftSchema: !leftSchema,
+                astNodeForTesting: astNodeForTesting)) {
+              restoreState(state);
+              return null;
+            }
+            i++;
+            j++;
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+
+  /// Matches [p] against [q] as a subtype against supertype and returns true if
   /// [p] and [q] are both FutureOr, with or without nullability suffixes as
   /// defined by [enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr],
   /// and [p] is a subtype of [q] under some constraints imposed on type
@@ -968,7 +1117,9 @@
   /// Matches [p] against [q] as a subtype against supertype and returns true if
   /// [p] and [q] are both type declaration types as defined by the enum
   /// [TypeDeclarationKind], and [p] is a subtype of [q] under some constraints
-  /// imposed on type parameters occurring in [q], and false otherwise.
+  /// imposed on type parameters occurring in [q]; false if both [p] and [q] are
+  /// type declaration types, but [p] can't possibly be a subtype of [q] under
+  /// any constraints; and null otherwise.
   ///
   /// An invariant of the type inference is that only [p] or [q] may be a
   /// schema (in other words, may contain the unknown type `_`); the other must
@@ -1163,6 +1314,10 @@
 
 mixin TypeConstraintGeneratorMixin<
         TypeStructure extends SharedTypeStructure<TypeStructure>,
+        // Work around https://github.com/dart-lang/dart_style/issues/1568
+        // ignore: lines_longer_than_80_chars
+        FunctionParameterStructure extends SharedNamedFunctionParameterStructure<
+            TypeStructure>,
         Variable extends Object,
         // Work around https://github.com/dart-lang/dart_style/issues/1568
         // ignore: lines_longer_than_80_chars
@@ -1170,8 +1325,14 @@
         TypeDeclarationType extends Object,
         TypeDeclaration extends Object,
         AstNode extends Object>
-    on TypeConstraintGenerator<TypeStructure, Variable, TypeParameterStructure,
-        TypeDeclarationType, TypeDeclaration, AstNode> {
+    on TypeConstraintGenerator<
+        TypeStructure,
+        FunctionParameterStructure,
+        Variable,
+        TypeParameterStructure,
+        TypeDeclarationType,
+        TypeDeclaration,
+        AstNode> {
   @override
   bool performSubtypeConstraintGenerationLeftSchema(
       SharedTypeSchemaView<TypeStructure> p, SharedTypeView<TypeStructure> q,
diff --git a/pkg/_fe_analyzer_shared/lib/src/types/shared_type.dart b/pkg/_fe_analyzer_shared/lib/src/types/shared_type.dart
index 6311538..1332c38 100644
--- a/pkg/_fe_analyzer_shared/lib/src/types/shared_type.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/types/shared_type.dart
@@ -11,6 +11,31 @@
     implements SharedTypeStructure<TypeStructure> {}
 
 /// Common interface for data structures used by the implementations to
+/// represent function types.
+abstract interface class SharedFunctionTypeStructure<
+    TypeStructure extends SharedTypeStructure<TypeStructure>,
+    TypeParameterStructure extends SharedTypeParameterStructure<TypeStructure>,
+    FunctionParameterStructure extends SharedNamedFunctionParameterStructure<
+        TypeStructure>> implements SharedTypeStructure<TypeStructure> {
+  /// All the positional parameter types, starting with the required ones, and
+  /// followed by the optional ones.
+  List<TypeStructure> get positionalParameterTypes;
+
+  /// The number of elements of [positionalParameterTypes] that are required
+  /// parameters.
+  int get requiredPositionalParameterCount;
+
+  /// The return type.
+  TypeStructure get returnType;
+
+  /// All the named parameters, sorted by name.
+  List<FunctionParameterStructure> get sortedNamedParameters;
+
+  /// The type parameters of the function type.
+  List<TypeParameterStructure> get typeFormals;
+}
+
+/// Common interface for data structures used by the implementations to
 /// represent a type resulting from a compile-time error.
 ///
 /// The implementations may choose to suppress further errors that arise from
@@ -20,6 +45,20 @@
     implements SharedTypeStructure<TypeStructure> {}
 
 /// Common interface for data structures used by the implementations to
+/// represent a named parameter of a function type.
+abstract interface class SharedNamedFunctionParameterStructure<
+    TypeStructure extends SharedTypeStructure<TypeStructure>> {
+  /// Whether this named parameter is required.
+  bool get isRequired;
+
+  /// The name of the parameter.
+  String get name;
+
+  /// The type of the parameter.
+  TypeStructure get type;
+}
+
+/// Common interface for data structures used by the implementations to
 /// represent a name/type pair.
 abstract interface class SharedNamedTypeStructure<
     TypeStructure extends SharedTypeStructure<TypeStructure>> {
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index fbcd39e..1f7f998 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -2958,11 +2958,6 @@
   }
 
   @override
-  bool isFunctionType(SharedTypeView<Type> type) {
-    return type.unwrapTypeView() is FunctionType;
-  }
-
-  @override
   bool isInterfaceType(SharedTypeView<Type> type) {
     Type unwrappedType = type.unwrapTypeView();
     return unwrappedType is PrimaryType && unwrappedType.isInterfaceType;
diff --git a/pkg/_fe_analyzer_shared/test/mini_types.dart b/pkg/_fe_analyzer_shared/test/mini_types.dart
index 49b4701..39d43e6 100644
--- a/pkg/_fe_analyzer_shared/test/mini_types.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_types.dart
@@ -30,15 +30,16 @@
 /// `_fe_analyzer_shared` package.
 ///
 /// Type parameters are not (yet) supported.
-class FunctionType extends Type {
-  /// The return type.
+class FunctionType extends Type
+    implements
+        SharedFunctionTypeStructure<Type, Never, NamedFunctionParameter> {
+  @override
   final Type returnType;
 
   /// A list of the types of positional parameters.
   final List<Type> positionalParameters;
 
-  /// The number of elements of [positionalParameters] that are required
-  /// parameters.
+  @override
   final int requiredPositionalParameterCount;
 
   /// A list of the named parameters, sorted by name.
@@ -58,6 +59,15 @@
   }
 
   @override
+  List<Type> get positionalParameterTypes => positionalParameters;
+
+  @override
+  List<NamedFunctionParameter> get sortedNamedParameters => namedParameters;
+
+  @override
+  List<Never> get typeFormals => const [];
+
+  @override
   Type? closureWithRespectToUnknown({required bool covariant}) {
     Type? newReturnType =
         returnType.closureWithRespectToUnknown(covariant: covariant);
@@ -163,7 +173,9 @@
 }
 
 /// A named parameter of a function type.
-class NamedFunctionParameter extends NamedType {
+class NamedFunctionParameter extends NamedType
+    implements SharedNamedFunctionParameterStructure<Type> {
+  @override
   final bool isRequired;
 
   NamedFunctionParameter(
diff --git a/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart b/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart
index ab4224d..bf69d57 100644
--- a/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart
+++ b/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart
@@ -12,6 +12,214 @@
 import '../mini_types.dart';
 
 main() {
+  group('performSubtypeConstraintGenerationForFunctionTypes:', () {
+    test('Matching functions with no parameters', () {
+      var tcg = _TypeConstraintGatherer({});
+      check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+              Type('void Function()'), Type('void Function()'),
+              leftSchema: false, astNodeForTesting: Node.placeholder()))
+          .equals(true);
+      check(tcg._constraints).isEmpty();
+    });
+
+    group('Matching functions with positional parameters:', () {
+      test('None optional', () {
+        var tcg = _TypeConstraintGatherer({'T', 'U'});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function(int, String)'), Type('void Function(T, U)'),
+                leftSchema: false, astNodeForTesting: Node.placeholder()))
+            .equals(true);
+        check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+      });
+
+      test('Some optional on LHS', () {
+        var tcg = _TypeConstraintGatherer({'T', 'U'});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function(int, [String])'),
+                Type('void Function(T, U)'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(true);
+        check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+      });
+    });
+
+    group('Non-matching functions with positional parameters:', () {
+      test('Non-matching due to return types', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('int Function(int)'), Type('String Function(int)'),
+                leftSchema: false, astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+
+      test('Non-matching due to parameter types', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function(int)'), Type('void Function(String)'),
+                leftSchema: false, astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+
+      test('Non-matching due to optional parameters on RHS', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function()'), Type('void Function([int])'),
+                leftSchema: false, astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+
+      test('Non-matching due to more parameters being required on LHS', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function(int)'), Type('void Function([int])'),
+                leftSchema: false, astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+    });
+
+    group('Matching functions with named parameters:', () {
+      test('None optional', () {
+        var tcg = _TypeConstraintGatherer({'T', 'U'});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function({required int x, required String y})'),
+                Type('void Function({required T x, required U y})'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(true);
+        check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+      });
+
+      test('Some optional on LHS', () {
+        var tcg = _TypeConstraintGatherer({'T', 'U'});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function({required int x, String y})'),
+                Type('void Function({required T x, required U y})'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(true);
+        check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+      });
+
+      test('Optional named parameter on LHS', () {
+        var tcg = _TypeConstraintGatherer({'T'});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function(int, {String x})'),
+                Type('void Function(T)'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(true);
+        check(tcg._constraints).unorderedEquals(['T <: int']);
+      });
+
+      test('Extra optional named parameter on LHS', () {
+        var tcg = _TypeConstraintGatherer({'T'});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function({String x, int y})'),
+                Type('void Function({T y})'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(true);
+        check(tcg._constraints).unorderedEquals(['T <: int']);
+      });
+    });
+
+    group('Non-matching functions with named parameters:', () {
+      test('Non-matching due to return types', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('int Function({int x})'), Type('String Function({int x})'),
+                leftSchema: false, astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+
+      test('Non-matching due to named parameter types', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function({int x})'),
+                Type('void Function({String x})'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+
+      test('Non-matching due to required named parameter on LHS', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function({required int x})'),
+                Type('void Function()'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+
+      test('Non-matching due to optional named parameter on RHS', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function()'), Type('void Function({int x})'),
+                leftSchema: false, astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+
+      test('Non-matching due to named parameter on RHS, with decoys on LHS',
+          () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function({int x, int y})'),
+                Type('void Function({int z})'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+    });
+
+    test('Matching functions with named and positional parameters', () {
+      var tcg = _TypeConstraintGatherer({'T', 'U'});
+      check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+              Type('void Function(int, {String y})'),
+              Type('void Function(T, {U y})'),
+              leftSchema: false,
+              astNodeForTesting: Node.placeholder()))
+          .equals(true);
+      check(tcg._constraints).unorderedEquals(['T <: int', 'U <: String']);
+    });
+
+    group('Non-matching functions with named and positional parameters:', () {
+      test(
+          'Non-matching due to LHS not accepting optional positional parameter',
+          () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function(int, {String x})'),
+                Type('void Function(int, [String])'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+
+      test('Non-matching due to positional parameter length mismatch', () {
+        var tcg = _TypeConstraintGatherer({});
+        check(tcg.performSubtypeConstraintGenerationForFunctionTypes(
+                Type('void Function(int, {String x})'),
+                Type('void Function(int, String)'),
+                leftSchema: false,
+                astNodeForTesting: Node.placeholder()))
+            .equals(null);
+        check(tcg._constraints).isEmpty();
+      });
+    });
+  });
+
   group('performSubtypeConstraintGenerationForFutureOr:', () {
     test('FutureOr matches FutureOr with constraints based on arguments', () {
       // `FutureOr<T> <# FutureOr<int>` reduces to `T <# int`
@@ -308,11 +516,11 @@
   });
 }
 
-class _TypeConstraintGatherer extends TypeConstraintGenerator<Type, Var,
-        TypeParameter, Type, String, Node>
+class _TypeConstraintGatherer extends TypeConstraintGenerator<Type,
+        NamedFunctionParameter, Var, TypeParameter, Type, String, Node>
     with
-        TypeConstraintGeneratorMixin<Type, Var, TypeParameter, Type, String,
-            Node> {
+        TypeConstraintGeneratorMixin<Type, NamedFunctionParameter, Var,
+            TypeParameter, Type, String, Node> {
   final _typeVariablesBeingConstrained = <TypeParameter>{};
 
   @override
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 655aab3..058d507 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -2198,7 +2198,10 @@
 ///
 /// Clients may not extend, implement or mix-in this class.
 abstract class ParameterElement
-    implements PromotableElement, ConstantEvaluationTarget {
+    implements
+        PromotableElement,
+        ConstantEvaluationTarget,
+        SharedNamedFunctionParameterStructure<DartType> {
   @override
   ParameterElement get declaration;
 
@@ -2255,6 +2258,7 @@
   /// change the meaning of this getter. The parameter `{@required int x}`
   /// will return `false` and the parameter `{@required required int x}`
   /// will return `true`.
+  @override
   bool get isRequired;
 
   /// Whether the parameter is both a required and named parameter.
diff --git a/pkg/analyzer/lib/dart/element/type.dart b/pkg/analyzer/lib/dart/element/type.dart
index 845b75b..372a52a 100644
--- a/pkg/analyzer/lib/dart/element/type.dart
+++ b/pkg/analyzer/lib/dart/element/type.dart
@@ -248,7 +248,11 @@
 ///   T<sub>xk</sub> xk}) &rarr; T</i>.
 ///
 /// Clients may not extend, implement or mix-in this class.
-abstract class FunctionType implements DartType {
+abstract class FunctionType
+    implements
+        DartType,
+        SharedFunctionTypeStructure<DartType, TypeParameterElement,
+            ParameterElement> {
   @override
   Null get element;
 
@@ -298,6 +302,7 @@
   List<ParameterElement> get parameters;
 
   /// The type of object returned by this type of function.
+  @override
   DartType get returnType;
 
   /// The formal type parameters of this generic function; for example,
@@ -307,6 +312,7 @@
   // These are distinct from the `typeParameters` list, which contains type
   // parameters from surrounding contexts, and thus are free type variables
   // from the perspective of this function type.
+  @override
   List<TypeParameterElement> get typeFormals;
 
   /// The type parameters.
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 8f78341..8d7da60 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -106,13 +106,80 @@
   @override
   final NullabilitySuffix nullabilitySuffix;
 
-  FunctionTypeImpl({
-    required this.typeFormals,
+  @override
+  final List<DartType> positionalParameterTypes;
+
+  @override
+  final int requiredPositionalParameterCount;
+
+  @override
+  final List<ParameterElement> sortedNamedParameters;
+
+  factory FunctionTypeImpl({
+    required List<TypeParameterElement> typeFormals,
     required List<ParameterElement> parameters,
+    required DartType returnType,
+    required NullabilitySuffix nullabilitySuffix,
+    InstantiatedTypeAliasElement? alias,
+  }) {
+    int? firstNamedParameterIndex;
+    var requiredPositionalParameterCount = 0;
+    var positionalParameterTypes = <DartType>[];
+    List<ParameterElement> sortedNamedParameters;
+
+    // Check if already sorted.
+    var namedParametersAlreadySorted = true;
+    var lastNamedParameterName = '';
+    for (var i = 0; i < parameters.length; ++i) {
+      var parameter = parameters[i];
+      if (parameter.isNamed) {
+        firstNamedParameterIndex ??= i;
+        var name = parameter.name;
+        if (lastNamedParameterName.compareTo(name) > 0) {
+          namedParametersAlreadySorted = false;
+          break;
+        }
+        lastNamedParameterName = name;
+      } else {
+        positionalParameterTypes.add(parameter.type);
+        if (parameter.isRequiredPositional) {
+          requiredPositionalParameterCount++;
+        }
+      }
+    }
+    sortedNamedParameters = firstNamedParameterIndex == null
+        ? const []
+        : parameters.sublist(firstNamedParameterIndex, parameters.length);
+    if (!namedParametersAlreadySorted) {
+      // Sort named parameters.
+      sortedNamedParameters.sort((a, b) => a.name.compareTo(b.name));
+
+      // Combine into a new list, with sorted named parameters.
+      parameters = parameters.toList();
+      parameters.replaceRange(
+          firstNamedParameterIndex!, parameters.length, sortedNamedParameters);
+    }
+    return FunctionTypeImpl._(
+        typeFormals: typeFormals,
+        parameters: parameters,
+        returnType: returnType,
+        nullabilitySuffix: nullabilitySuffix,
+        positionalParameterTypes: positionalParameterTypes,
+        requiredPositionalParameterCount: requiredPositionalParameterCount,
+        sortedNamedParameters: sortedNamedParameters,
+        alias: alias);
+  }
+
+  FunctionTypeImpl._({
+    required this.typeFormals,
+    required this.parameters,
     required this.returnType,
     required this.nullabilitySuffix,
+    required this.positionalParameterTypes,
+    required this.requiredPositionalParameterCount,
+    required this.sortedNamedParameters,
     super.alias,
-  }) : parameters = _sortNamedParameters(parameters);
+  });
 
   @override
   Null get element => null;
@@ -305,11 +372,14 @@
   @override
   TypeImpl withNullability(NullabilitySuffix nullabilitySuffix) {
     if (this.nullabilitySuffix == nullabilitySuffix) return this;
-    return FunctionTypeImpl(
+    return FunctionTypeImpl._(
       typeFormals: typeFormals,
       parameters: parameters,
       returnType: returnType,
       nullabilitySuffix: nullabilitySuffix,
+      positionalParameterTypes: positionalParameterTypes,
+      requiredPositionalParameterCount: requiredPositionalParameterCount,
+      sortedNamedParameters: sortedNamedParameters,
       alias: alias,
     );
   }
@@ -415,44 +485,6 @@
     }
     return true;
   }
-
-  /// If named parameters are already sorted in [parameters], return it.
-  /// Otherwise, return a new list, in which named parameters are sorted.
-  static List<ParameterElement> _sortNamedParameters(
-    List<ParameterElement> parameters,
-  ) {
-    int? firstNamedParameterIndex;
-
-    // Check if already sorted.
-    var namedParametersAlreadySorted = true;
-    var lastNamedParameterName = '';
-    for (var i = 0; i < parameters.length; ++i) {
-      var parameter = parameters[i];
-      if (parameter.isNamed) {
-        firstNamedParameterIndex ??= i;
-        var name = parameter.name;
-        if (lastNamedParameterName.compareTo(name) > 0) {
-          namedParametersAlreadySorted = false;
-          break;
-        }
-        lastNamedParameterName = name;
-      }
-    }
-    if (namedParametersAlreadySorted) {
-      return parameters;
-    }
-
-    // Sort named parameters.
-    var namedParameters =
-        parameters.sublist(firstNamedParameterIndex!, parameters.length);
-    namedParameters.sort((a, b) => a.name.compareTo(b.name));
-
-    // Combine into a new list, with sorted named parameters.
-    var newParameters = parameters.toList();
-    newParameters.replaceRange(
-        firstNamedParameterIndex, parameters.length, namedParameters);
-    return newParameters;
-  }
 }
 
 class InstantiatedTypeAliasElementImpl implements InstantiatedTypeAliasElement {
diff --git a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
index 8dce2fb..b12474a 100644
--- a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
@@ -24,14 +24,21 @@
 /// to make one type schema a subtype of another.
 class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
         DartType,
+        ParameterElement,
         PromotableElement,
         TypeParameterElement,
         InterfaceType,
         InterfaceElement,
         AstNode>
     with
-        shared.TypeConstraintGeneratorMixin<DartType, PromotableElement,
-            TypeParameterElement, InterfaceType, InterfaceElement, AstNode> {
+        shared.TypeConstraintGeneratorMixin<
+            DartType,
+            ParameterElement,
+            PromotableElement,
+            TypeParameterElement,
+            InterfaceType,
+            InterfaceElement,
+            AstNode> {
   final TypeSystemImpl _typeSystem;
   final Set<TypeParameterElement> _typeParameters = Set.identity();
   final List<
@@ -307,15 +314,13 @@
     // If `Q` is `Function` then the match holds under no constraints:
     //   If `P` is a function type.
     if (_typeSystemOperations.isDartCoreFunction(SharedTypeView(Q))) {
-      if (_typeSystemOperations.isFunctionType(SharedTypeView(P))) {
+      if (P is SharedFunctionTypeStructure) {
         return true;
       }
     }
 
-    if (_typeSystemOperations.isFunctionType(SharedTypeView(P)) &&
-        _typeSystemOperations.isFunctionType(SharedTypeView(Q))) {
-      return _functionType(P as FunctionType, Q as FunctionType, leftSchema,
-          nodeForTesting: nodeForTesting);
+    if (P is FunctionType && Q is FunctionType) {
+      return _functionType(P, Q, leftSchema, nodeForTesting: nodeForTesting);
     }
 
     // A type `P` is a subtype match for `Record` with respect to `L` under no
@@ -451,102 +456,12 @@
   /// respect to `L` under constraints `C0 + ... + Cr + C`.
   bool _functionType0(FunctionType f, FunctionType g, bool leftSchema,
       {required AstNode? nodeForTesting}) {
-    var rewind = _constraints.length;
-
-    // If `R0` is a subtype match for a type `R1` with respect to `L` under
-    // constraints `C`.
-    if (!trySubtypeMatch(f.returnType, g.returnType, leftSchema,
-        nodeForTesting: nodeForTesting)) {
-      _constraints.length = rewind;
-      return false;
+    bool? result = performSubtypeConstraintGenerationForFunctionTypes(f, g,
+        leftSchema: leftSchema, astNodeForTesting: nodeForTesting);
+    if (result != null) {
+      return result;
     }
-
-    var fParameters = f.parameters;
-    var gParameters = g.parameters;
-
-    // And for `i` in `0...r`, `Ni` is a subtype match for `Mi` with respect
-    // to `L` under constraints `Ci`.
-    var fIndex = 0;
-    var gIndex = 0;
-    while (fIndex < fParameters.length && gIndex < gParameters.length) {
-      var fParameter = fParameters[fIndex];
-      var gParameter = gParameters[gIndex];
-      if (fParameter.isRequiredPositional) {
-        if (gParameter.isRequiredPositional) {
-          if (trySubtypeMatch(gParameter.type, fParameter.type, leftSchema,
-              nodeForTesting: nodeForTesting)) {
-            fIndex++;
-            gIndex++;
-          } else {
-            _constraints.length = rewind;
-            return false;
-          }
-        } else {
-          _constraints.length = rewind;
-          return false;
-        }
-      } else if (fParameter.isOptionalPositional) {
-        if (gParameter.isPositional) {
-          if (trySubtypeMatch(gParameter.type, fParameter.type, leftSchema,
-              nodeForTesting: nodeForTesting)) {
-            fIndex++;
-            gIndex++;
-          } else {
-            _constraints.length = rewind;
-            return false;
-          }
-        } else {
-          _constraints.length = rewind;
-          return false;
-        }
-      } else if (fParameter.isNamed) {
-        if (gParameter.isNamed) {
-          var compareNames = fParameter.name.compareTo(gParameter.name);
-          if (compareNames == 0) {
-            if (trySubtypeMatch(gParameter.type, fParameter.type, leftSchema,
-                nodeForTesting: nodeForTesting)) {
-              fIndex++;
-              gIndex++;
-            } else {
-              _constraints.length = rewind;
-              return false;
-            }
-          } else if (compareNames < 0) {
-            if (fParameter.isRequiredNamed) {
-              _constraints.length = rewind;
-              return false;
-            } else {
-              fIndex++;
-            }
-          } else {
-            assert(compareNames > 0);
-            // The subtype must accept all parameters of the supertype.
-            _constraints.length = rewind;
-            return false;
-          }
-        } else {
-          break;
-        }
-      }
-    }
-
-    // The supertype must provide all required parameters to the subtype.
-    while (fIndex < fParameters.length) {
-      var fParameter = fParameters[fIndex++];
-      if (fParameter.isRequired) {
-        _constraints.length = rewind;
-        return false;
-      }
-    }
-
-    // The subtype must accept all parameters of the supertype.
-    assert(fIndex == fParameters.length);
-    if (gIndex < gParameters.length) {
-      _constraints.length = rewind;
-      return false;
-    }
-
-    return true;
+    return false;
   }
 
   /// If `P` is `(M0, ..., Mk)` and `Q` is `(N0, ..., Nk)`, then the match
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 35ad1b9..15f28d2 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -530,11 +530,6 @@
   }
 
   @override
-  bool isFunctionType(SharedTypeView<DartType> type) {
-    return type.unwrapTypeView() is FunctionType;
-  }
-
-  @override
   bool isInterfaceType(SharedTypeView<DartType> type) {
     DartType unwrappedType = type.unwrapTypeView();
     return unwrappedType is InterfaceType &&
diff --git a/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart b/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart
index 270cee8..2cc1e045 100644
--- a/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart
+++ b/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart
@@ -25,6 +25,7 @@
 /// based on an attempt to make one type schema a subtype of another.
 class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
         DartType,
+        NamedType,
         VariableDeclaration,
         StructuralParameter,
         TypeDeclarationType,
@@ -33,6 +34,7 @@
     with
         shared.TypeConstraintGeneratorMixin<
             DartType,
+            NamedType,
             VariableDeclaration,
             StructuralParameter,
             TypeDeclarationType,
@@ -632,11 +634,12 @@
       _protoConstraints.length = baseConstraintCount;
     }
 
-    bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
-        p, q,
-        leftSchema: constrainSupertype, astNodeForTesting: treeNodeForTesting);
-    if (result != null) {
-      return result;
+    bool? constraintGenerationResult =
+        performSubtypeConstraintGenerationForTypeDeclarationTypes(p, q,
+            leftSchema: constrainSupertype,
+            astNodeForTesting: treeNodeForTesting);
+    if (constraintGenerationResult != null) {
+      return constraintGenerationResult;
     }
 
     // If Q is Function then the match holds under no constraints:
@@ -644,90 +647,16 @@
     // If P is a function type.
     if (typeOperations.isDartCoreFunction(new SharedTypeView(q)) &&
         // Coverage-ignore(suite): Not run.
-        typeOperations.isFunctionType(new SharedTypeView(p))) {
+        p is FunctionType) {
       return true;
     }
 
-    // A function type (M0,..., Mn, [M{n+1}, ..., Mm]) -> R0 is a subtype match
-    // for a function type (N0,..., Nk, [N{k+1}, ..., Nr]) -> R1 with respect to
-    // L under constraints C0 + ... + Cr + C
-    //
-    // If R0 is a subtype match for a type R1 with respect to L under
-    // constraints C.  If n <= k and r <= m.  And for i in 0...r, Ni is a
-    // subtype match for Mi with respect to L under constraints Ci.
-    if (typeOperations.isFunctionType(new SharedTypeView(p)) &&
-        typeOperations.isFunctionType(new SharedTypeView(q)) &&
-        (p as FunctionType).typeParameters.isEmpty &&
-        (q as FunctionType).typeParameters.isEmpty &&
-        p.namedParameters.isEmpty &&
-        q.namedParameters.isEmpty &&
-        p.requiredParameterCount <= q.requiredParameterCount &&
-        p.positionalParameters.length >= q.positionalParameters.length) {
-      final int baseConstraintCount = _protoConstraints.length;
-
-      if (_isNullabilityAwareSubtypeMatch(p.returnType, q.returnType,
-          constrainSupertype: constrainSupertype,
-          treeNodeForTesting: treeNodeForTesting)) {
-        bool isMatch = true;
-        for (int i = 0; isMatch && i < q.positionalParameters.length; ++i) {
-          isMatch = isMatch &&
-              _isNullabilityAwareSubtypeMatch(
-                  q.positionalParameters[i], p.positionalParameters[i],
-                  constrainSupertype: !constrainSupertype,
-                  treeNodeForTesting: treeNodeForTesting);
-        }
-        if (isMatch) return true;
-      }
-      // Coverage-ignore-block(suite): Not run.
-      _protoConstraints.length = baseConstraintCount;
-    }
-
-    // Function types with named parameters are treated analogously to the
-    // positional parameter case above.
-    if (typeOperations.isFunctionType(new SharedTypeView(p)) &&
-        typeOperations.isFunctionType(new SharedTypeView(q)) &&
-        (p as FunctionType).typeParameters.isEmpty &&
-        (q as FunctionType).typeParameters.isEmpty &&
-        p.positionalParameters.length == p.requiredParameterCount &&
-        q.positionalParameters.length == q.requiredParameterCount &&
-        p.requiredParameterCount == q.requiredParameterCount &&
-        (p.namedParameters.isNotEmpty ||
-            // Coverage-ignore(suite): Not run.
-            q.namedParameters.isNotEmpty)) {
-      final int baseConstraintCount = _protoConstraints.length;
-
-      if (_isNullabilityAwareSubtypeMatch(p.returnType, q.returnType,
-          constrainSupertype: constrainSupertype,
-          treeNodeForTesting: treeNodeForTesting)) {
-        bool isMatch = true;
-        for (int i = 0;
-            isMatch && i < p.positionalParameters.length;
-            // Coverage-ignore(suite): Not run.
-            ++i) {
-          // Coverage-ignore-block(suite): Not run.
-          isMatch = isMatch &&
-              _isNullabilityAwareSubtypeMatch(
-                  q.positionalParameters[i], p.positionalParameters[i],
-                  constrainSupertype: !constrainSupertype,
-                  treeNodeForTesting: treeNodeForTesting);
-        }
-        Map<String, DartType> pNamedTypes = {};
-        for (int i = 0; isMatch && i < p.namedParameters.length; ++i) {
-          pNamedTypes[p.namedParameters[i].name] = p.namedParameters[i].type;
-        }
-        for (int i = 0; isMatch && i < q.namedParameters.length; ++i) {
-          isMatch =
-              isMatch && pNamedTypes.containsKey(q.namedParameters[i].name);
-          isMatch = isMatch &&
-              _isNullabilityAwareSubtypeMatch(q.namedParameters[i].type,
-                  pNamedTypes[q.namedParameters[i].name]!,
-                  constrainSupertype: !constrainSupertype,
-                  treeNodeForTesting: treeNodeForTesting);
-        }
-        if (isMatch) return true;
-      }
-      // Coverage-ignore-block(suite): Not run.
-      _protoConstraints.length = baseConstraintCount;
+    constraintGenerationResult =
+        performSubtypeConstraintGenerationForFunctionTypes(p, q,
+            leftSchema: constrainSupertype,
+            astNodeForTesting: treeNodeForTesting);
+    if (constraintGenerationResult != null) {
+      return constraintGenerationResult;
     }
 
     // A generic function type <T0 extends B00, ..., Tn extends B0n>F0 is a
@@ -747,10 +676,10 @@
     // with respect to L under constraints C0.  And C1 is C02 + ... + Cn2 + C0.
     // And C2 is C1 with each constraint replaced with its closure with respect
     // to [Z0, ..., Zn].
-    if (typeOperations.isFunctionType(new SharedTypeView(p)) &&
-        typeOperations.isFunctionType(new SharedTypeView(q)) &&
-        (p as FunctionType).typeParameters.isNotEmpty &&
-        (q as FunctionType).typeParameters.isNotEmpty &&
+    if (p is FunctionType &&
+        q is FunctionType &&
+        p.typeParameters.isNotEmpty &&
+        q.typeParameters.isNotEmpty &&
         p.typeParameters.length == q.typeParameters.length) {
       final int baseConstraintCount = _protoConstraints.length;
 
@@ -1052,12 +981,12 @@
           subtype as InterfaceType, supertype as InterfaceType,
           treeNodeForTesting: treeNodeForTesting);
     }
-    if (typeOperations.isFunctionType(new SharedTypeView(subtype))) {
+    if (subtype is FunctionType) {
       if (typeOperations.isInterfaceType(new SharedTypeView(supertype))) {
         return supertype == _environment.coreTypes.functionLegacyRawType ||
             supertype == _environment.coreTypes.objectLegacyRawType;
       } else if (supertype is FunctionType) {
-        return _isFunctionSubtypeMatch(subtype as FunctionType, supertype,
+        return _isFunctionSubtypeMatch(subtype, supertype,
             treeNodeForTesting: treeNodeForTesting);
       }
     }
diff --git a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
index 3180c14..31dcd22 100644
--- a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
+++ b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
@@ -722,11 +722,6 @@
   }
 
   @override
-  bool isFunctionType(SharedTypeView<DartType> type) {
-    return type.unwrapTypeView() is FunctionType;
-  }
-
-  @override
   DartType? matchFutureOrInternal(DartType type) {
     if (type is! FutureOrType) {
       return null;
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 81a960c..955b82e 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -866,6 +866,7 @@
 injecting
 inlines
 insertion
+insists
 inspect
 inspecting
 inspection
@@ -1801,6 +1802,7 @@
 taking
 talk
 talks
+tandem
 target's
 tarjan
 tarjan's
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index b3cb405..3588929 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -74,6 +74,8 @@
 import 'package:_fe_analyzer_shared/src/types/shared_type.dart'
     show
         SharedDynamicTypeStructure,
+        SharedNamedFunctionParameterStructure,
+        SharedFunctionTypeStructure,
         SharedInvalidTypeStructure,
         SharedNamedTypeStructure,
         SharedRecordTypeStructure,
@@ -11605,7 +11607,9 @@
 }
 
 /// A possibly generic function type.
-class FunctionType extends DartType {
+class FunctionType extends DartType
+    implements
+        SharedFunctionTypeStructure<DartType, StructuralParameter, NamedType> {
   final List<StructuralParameter> typeParameters;
   final int requiredParameterCount;
   final List<DartType> positionalParameters;
@@ -11614,6 +11618,7 @@
   @override
   final Nullability declaredNullability;
 
+  @override
   final DartType returnType;
 
   @override
@@ -11643,6 +11648,18 @@
       };
 
   @override
+  List<DartType> get positionalParameterTypes => positionalParameters;
+
+  @override
+  int get requiredPositionalParameterCount => requiredParameterCount;
+
+  @override
+  List<NamedType> get sortedNamedParameters => namedParameters;
+
+  @override
+  List<StructuralParameter> get typeFormals => typeParameters;
+
+  @override
   R accept<R>(DartTypeVisitor<R> v) => v.visitFunctionType(this);
 
   @override
@@ -12176,7 +12193,10 @@
 
 /// A named parameter in [FunctionType].
 class NamedType extends Node
-    implements Comparable<NamedType>, SharedNamedTypeStructure<DartType> {
+    implements
+        Comparable<NamedType>,
+        SharedNamedTypeStructure<DartType>,
+        SharedNamedFunctionParameterStructure<DartType> {
   // Flag used for serialization if [isRequired].
   static const int FlagRequiredNamedType = 1 << 0;
 
@@ -12184,6 +12204,7 @@
   final String name;
   @override
   final DartType type;
+  @override
   final bool isRequired;
 
   const NamedType(this.name, this.type, {this.isRequired = false});