[analyzer][cfe] Share type constraint generation

Part of https://github.com/dart-lang/sdk/issues/54902

Change-Id: I15f7a78c9390466f5a48400f81def175faeb9fd2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395020
Reviewed-by: Erik Ernst <eernst@google.com>
Commit-Queue: 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 ba5a970..75410e9 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
@@ -196,12 +196,17 @@
   @override
   bool isNever(SharedTypeView<TypeStructure> type);
 
-  /// Returns `true` if `Null` is not a subtype of all types matching
-  /// [typeSchema].
+  /// Returns `true` if `Null` is a subtype of all types matching [type].
   ///
-  /// The predicate of [isNonNullable] could be computed directly with a subtype
-  /// query, but the implementations can do that more efficiently.
-  bool isNonNullable(SharedTypeSchemaView<TypeStructure> typeSchema);
+  /// The predicate of [isNullableInternal] could be computed directly with a
+  /// subtype query, but the implementations can do that more efficiently.
+  bool isNullableInternal(TypeStructure type);
+
+  /// Returns `true` if `Null` is not a subtype of all types matching [type].
+  ///
+  /// The predicate of [isNonNullableInternal] could be computed directly with
+  /// a subtype query, but the implementations can do that more efficiently.
+  bool isNonNullableInternal(TypeStructure type);
 
   /// Returns `true` if [type] is `Null`.
   bool isNull(SharedTypeView<TypeStructure> type);
@@ -943,12 +948,12 @@
   /// Add constraint: [lower] <: [typeParameter] <: TOP.
   void addLowerConstraintForParameter(
       TypeParameterStructure typeParameter, TypeStructure lower,
-      {required AstNode? nodeForTesting});
+      {required AstNode? astNodeForTesting});
 
   /// Add constraint: BOTTOM <: [typeParameter] <: [upper].
   void addUpperConstraintForParameter(
       TypeParameterStructure typeParameter, TypeStructure upper,
-      {required AstNode? nodeForTesting});
+      {required AstNode? astNodeForTesting});
 
   /// Returns the type arguments of the supertype of [type] that is an
   /// instantiation of [typeDeclaration]. If none of the supertypes of [type]
@@ -1543,6 +1548,9 @@
     return false;
   }
 
+  /// Type parameters being constrained by [TypeConstraintGenerator].
+  Iterable<TypeParameterStructure> get typeParametersToConstrain;
+
   /// Implementation backing [performSubtypeConstraintGenerationLeftSchema] and
   /// [performSubtypeConstraintGenerationRightSchema].
   ///
@@ -1568,7 +1576,154 @@
   /// [performSubtypeConstraintGenerationRightSchema] from the mixin.
   bool performSubtypeConstraintGenerationInternal(
       TypeStructure p, TypeStructure q,
-      {required bool leftSchema, required AstNode? astNodeForTesting});
+      {required bool leftSchema, required AstNode? astNodeForTesting}) {
+    // If `P` is `_` then the match holds with no constraints.
+    if (p is SharedUnknownTypeStructure) {
+      return true;
+    }
+
+    // If `Q` is `_` then the match holds with no constraints.
+    if (q is SharedUnknownTypeStructure) {
+      return true;
+    }
+
+    // If `P` is a type variable `X` in `L`, then the match holds:
+    //   Under constraint `_ <: X <: Q`.
+    NullabilitySuffix pNullability = p.nullabilitySuffix;
+    if (typeAnalyzerOperations.matchInferableParameter(new SharedTypeView(p))
+        case var pParameter?
+        when pNullability == NullabilitySuffix.none &&
+            typeParametersToConstrain.contains(pParameter)) {
+      addUpperConstraintForParameter(pParameter, q,
+          astNodeForTesting: astNodeForTesting);
+      return true;
+    }
+
+    // If `Q` is a type variable `X` in `L`, then the match holds:
+    //   Under constraint `P <: X <: _`.
+    NullabilitySuffix qNullability = q.nullabilitySuffix;
+    if (typeAnalyzerOperations.matchInferableParameter(new SharedTypeView(q))
+        case var qParameter?
+        when qNullability == NullabilitySuffix.none &&
+            typeParametersToConstrain.contains(qParameter) &&
+            (!inferenceUsingBoundsIsEnabled ||
+                (qParameter.bound == null ||
+                    typeAnalyzerOperations.isSubtypeOfInternal(
+                        p,
+                        typeAnalyzerOperations.greatestClosureOfTypeInternal(
+                            qParameter.bound!,
+                            [...typeParametersToConstrain]))))) {
+      addLowerConstraintForParameter(qParameter, p,
+          astNodeForTesting: astNodeForTesting);
+      return true;
+    }
+
+    // If `P` and `Q` are identical types, then the subtype match holds
+    // under no constraints.
+    if (p == q) {
+      return true;
+    }
+
+    // Note that it's not necessary to rewind [_constraints] to its prior state
+    // in case [performSubtypeConstraintGenerationForFutureOr] returns false, as
+    // [performSubtypeConstraintGenerationForFutureOr] handles the rewinding of
+    // the state itself.
+    if (performSubtypeConstraintGenerationForRightFutureOr(p, q,
+        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+      return true;
+    }
+
+    if (performSubtypeConstraintGenerationForRightNullableType(p, q,
+        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+      return true;
+    }
+
+    // If `P` is `FutureOr<P0>` the match holds under constraint set `C1 + C2`:
+    if (performSubtypeConstraintGenerationForLeftFutureOr(p, q,
+        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+      return true;
+    }
+
+    // If `P` is `P0?` the match holds under constraint set `C1 + C2`:
+    if (performSubtypeConstraintGenerationForLeftNullableType(p, q,
+        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+      return true;
+    }
+
+    // If `Q` is `dynamic`, `Object?`, or `void` then the match holds under
+    // no constraints.
+    if (q is SharedDynamicTypeStructure ||
+        q is SharedVoidTypeStructure ||
+        q == typeAnalyzerOperations.objectQuestionType.unwrapTypeView()) {
+      return true;
+    }
+
+    // If `P` is `Never` then the match holds under no constraints.
+    if (typeAnalyzerOperations.isNever(new SharedTypeView(p))) {
+      return true;
+    }
+
+    // If `Q` is `Object`, then the match holds under no constraints:
+    //  Only if `P` is non-nullable.
+    if (q == typeAnalyzerOperations.objectType.unwrapTypeView()) {
+      return typeAnalyzerOperations.isNonNullableInternal(p);
+    }
+
+    // If `P` is `Null`, then the match holds under no constraints:
+    //  Only if `Q` is nullable.
+    if (pNullability == NullabilitySuffix.none &&
+        typeAnalyzerOperations.isNull(new SharedTypeView(p))) {
+      return typeAnalyzerOperations.isNullableInternal(q);
+    }
+
+    // If `P` is a type variable `X` with bound `B` (or a promoted type
+    // variable `X & B`), the match holds with constraint set `C`:
+    //   If `B` is a subtype match for `Q` with constraint set `C`.
+    // Note: we have already eliminated the case that `X` is a variable in `L`.
+    if (typeAnalyzerOperations.matchTypeParameterBoundInternal(p)
+        case var bound?) {
+      if (performSubtypeConstraintGenerationInternal(bound, q,
+          leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+        return true;
+      }
+    }
+
+    bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
+        p, q,
+        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting);
+    if (result != null) {
+      return result;
+    }
+
+    // If `Q` is `Function` then the match holds under no constraints:
+    //   If `P` is a function type.
+    if (typeAnalyzerOperations.isDartCoreFunction(new SharedTypeView(q))) {
+      if (p is SharedFunctionTypeStructure) {
+        return true;
+      }
+    }
+
+    if (performSubtypeConstraintGenerationForFunctionTypes(p, q,
+        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+      return true;
+    }
+
+    // A type `P` is a subtype match for `Record` with respect to `L` under no
+    // constraints:
+    //   If `P` is a record type or `Record`.
+    if (typeAnalyzerOperations.isDartCoreRecord(new SharedTypeView(q))) {
+      if (p is SharedRecordTypeStructure<TypeStructure>) {
+        return true;
+      }
+    }
+
+    if (performSubtypeConstraintGenerationForRecordTypes(p, q,
+        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
+      return true;
+    }
+
+    return false;
+  }
 
   /// Matches type schema [p] against type [q] as a subtype against supertype,
   /// assuming [p] is the constraining type schema, and [q] contains the type
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 9b57522..98c400b 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -2994,32 +2994,6 @@
   }
 
   @override
-  bool isNonNullable(SharedTypeSchemaView<Type> type) {
-    Type unwrappedType = type.unwrapTypeSchemaView();
-    if (unwrappedType is DynamicType ||
-        unwrappedType is SharedUnknownTypeStructure ||
-        unwrappedType is VoidType ||
-        unwrappedType is NullType) {
-      return false;
-    } else if (unwrappedType
-        case TypeParameterType(
-          :var promotion,
-          nullabilitySuffix: NullabilitySuffix.none
-        )) {
-      return promotion != null &&
-          isNonNullable(SharedTypeSchemaView(promotion));
-    } else if (type.nullabilitySuffix == NullabilitySuffix.question) {
-      return false;
-    } else if (matchFutureOrInternal(unwrappedType) case Type typeArgument?) {
-      return isNonNullable(SharedTypeSchemaView(typeArgument));
-    }
-    // TODO(cstefantsova): Update to a fast-pass implementation when the
-    // mini-ast testing framework supports looking up superinterfaces of
-    // extension types or looking up bounds of type parameters.
-    return _typeSystem.isSubtype(NullType.instance, unwrappedType);
-  }
-
-  @override
   bool isNull(SharedTypeView<Type> type) {
     return type.unwrapTypeView() is NullType;
   }
@@ -3311,6 +3285,53 @@
       return null;
     }
   }
+
+  @override
+  bool isNonNullableInternal(Type type) {
+    Type unwrappedType = type;
+    if (unwrappedType is DynamicType ||
+        unwrappedType is SharedUnknownTypeStructure ||
+        unwrappedType is VoidType ||
+        unwrappedType is NullType ||
+        unwrappedType is InvalidType) {
+      return false;
+    } else if (unwrappedType
+        case TypeParameterType(
+          :var promotion,
+          :var typeParameter,
+          nullabilitySuffix: NullabilitySuffix.none
+        )) {
+      if (promotion != null) {
+        return isNonNullableInternal(promotion);
+      } else {
+        return isNonNullableInternal(typeParameter.bound);
+      }
+    } else if (type.nullabilitySuffix == NullabilitySuffix.question) {
+      return false;
+    } else if (matchFutureOrInternal(unwrappedType) case Type typeArgument?) {
+      return isNonNullableInternal(typeArgument);
+    }
+    return true;
+  }
+
+  @override
+  bool isNullableInternal(Type type) {
+    Type unwrappedType = type;
+    if (unwrappedType is DynamicType ||
+        unwrappedType is SharedUnknownTypeStructure ||
+        unwrappedType is VoidType ||
+        unwrappedType is NullType) {
+      return true;
+    } else if (type.nullabilitySuffix == NullabilitySuffix.question) {
+      return false;
+    } else if (matchFutureOrInternal(unwrappedType) case Type typeArgument?) {
+      return isNullableInternal(typeArgument);
+    }
+    // TODO(cstefantsova): Update to a fast-pass implementation when the
+    // mini-ast testing framework supports looking up superinterfaces of
+    // extension types or looking up bounds of type parameters.
+    return _typeSystem.isSubtype(NullType.instance, unwrappedType);
+  }
 }
 
 /// Representation of an expression or statement in the pseudo-Dart language
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 aaefd0b..8fe86f5 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
@@ -2,7 +2,6 @@
 // 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.
 
-import 'package:_fe_analyzer_shared/src/type_inference/nullability_suffix.dart';
 import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart';
 import 'package:_fe_analyzer_shared/src/types/shared_type.dart';
 import 'package:checks/checks.dart';
@@ -862,7 +861,8 @@
     with
         TypeConstraintGeneratorMixin<Type, NamedFunctionParameter, Var,
             TypeParameter, Type, String, Node> {
-  final _typeVariablesBeingConstrained = <TypeParameter>{};
+  @override
+  final Set<TypeParameter> typeParametersToConstrain = <TypeParameter>{};
 
   @override
   final bool enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr;
@@ -876,7 +876,7 @@
       {this.enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr = false})
       : super(inferenceUsingBoundsIsEnabled: false) {
     for (var typeVariableName in typeVariablesBeingConstrained) {
-      _typeVariablesBeingConstrained
+      typeParametersToConstrain
           .add(TypeRegistry.addTypeParameter(typeVariableName));
     }
   }
@@ -911,109 +911,19 @@
   }
 
   @override
-  bool performSubtypeConstraintGenerationInternal(Type p, Type q,
-      {required bool leftSchema, required Node? astNodeForTesting}) {
-    // If `P` is `_` then the match holds with no constraints.
-    if (p is SharedUnknownTypeStructure) {
-      return true;
-    }
-
-    // If `Q` is `_` then the match holds with no constraints.
-    if (q is SharedUnknownTypeStructure) {
-      return true;
-    }
-
-    // If T is a type variable being constrained, then `T <# Q` matches,
-    // generating the constraint `T <: Q`.
-    if (typeAnalyzerOperations.matchInferableParameter(SharedTypeView(p))
-        case var typeVar?
-        when p.nullabilitySuffix == NullabilitySuffix.none &&
-            _typeVariablesBeingConstrained.contains(typeVar)) {
-      addUpperConstraintForParameter(typeVar, q, nodeForTesting: null);
-      return true;
-    }
-
-    // If T is a type variable being constrained, then `P <# T` matches,
-    // generating the constraint `P <: T`.
-    if (typeAnalyzerOperations.matchInferableParameter(SharedTypeView(q))
-        case var typeVar?
-        when q.nullabilitySuffix == NullabilitySuffix.none &&
-            _typeVariablesBeingConstrained.contains(typeVar)) {
-      addLowerConstraintForParameter(typeVar, p, nodeForTesting: null);
-      return true;
-    }
-
-    // If `P` and `Q` are identical types, then the subtype match holds
-    // under no constraints.
-    if (p == q) {
-      return true;
-    }
-
-    if (performSubtypeConstraintGenerationForRightFutureOr(p, q,
-        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
-      return true;
-    }
-
-    if (performSubtypeConstraintGenerationForRightNullableType(p, q,
-        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
-      return true;
-    }
-
-    if (performSubtypeConstraintGenerationForLeftFutureOr(p, q,
-        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
-      return true;
-    }
-
-    if (performSubtypeConstraintGenerationForLeftNullableType(p, q,
-        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
-      return true;
-    }
-
-    if (q is SharedDynamicTypeStructure ||
-        q is SharedVoidTypeStructure ||
-        q == typeAnalyzerOperations.objectQuestionType.unwrapTypeView()) {
-      return true;
-    }
-
-    if (typeAnalyzerOperations.matchTypeParameterBoundInternal(p)
-        case var bound?) {
-      if (performSubtypeConstraintGenerationInternal(bound, q,
-          leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
-        return true;
-      }
-    }
-
-    bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
-        p, q,
-        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting);
-    if (result != null) {
-      return result;
-    }
-
-    if (performSubtypeConstraintGenerationForRecordTypes(p, q,
-        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting)) {
-      return true;
-    }
-
-    // Assume for the moment that nothing else matches.
-    // TODO(paulberry): expand this as needed.
-    return false;
-  }
-
-  @override
   void restoreState(TypeConstraintGeneratorState state) {
     _constraints.length = state.count;
   }
 
   @override
   void addUpperConstraintForParameter(TypeParameter typeParameter, Type upper,
-      {required Node? nodeForTesting}) {
+      {required Node? astNodeForTesting}) {
     _constraints.add('$typeParameter <: $upper');
   }
 
   @override
   void addLowerConstraintForParameter(TypeParameter typeParameter, Type lower,
-      {required Node? nodeForTesting}) {
+      {required Node? astNodeForTesting}) {
     _constraints.add('$lower <: $typeParameter');
   }
 
diff --git a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
index 90be4eb..9ceb835 100644
--- a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
@@ -769,14 +769,13 @@
 
     DartType typeParameterToInferBound = typeParameterToInfer.bound!;
     TypeConstraintGatherer typeConstraintGatherer = TypeConstraintGatherer(
-        typeSystem: _typeSystem,
         typeSystemOperations: _typeSystemOperations,
         typeParameters: _typeFormals,
         inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled,
         dataForTesting: null);
-    typeConstraintGatherer.trySubtypeMatch(
-        lower, typeParameterToInferBound, /* leftSchema */ true,
-        nodeForTesting: null);
+    typeConstraintGatherer.performSubtypeConstraintGenerationInternal(
+        lower, typeParameterToInferBound,
+        leftSchema: true, astNodeForTesting: null);
     var constraintsPerTypeVariable =
         typeConstraintGatherer.computeConstraints();
     for (var typeParameter in constraintsPerTypeVariable.keys) {
@@ -887,13 +886,12 @@
       {required bool covariant,
       required AstNode? nodeForTesting}) {
     var gatherer = TypeConstraintGatherer(
-        typeSystem: _typeSystem,
         typeParameters: _typeParameters,
         typeSystemOperations: _typeSystemOperations,
         inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled,
         dataForTesting: dataForTesting);
-    var success = gatherer.trySubtypeMatch(t1, t2, !covariant,
-        nodeForTesting: nodeForTesting);
+    var success = gatherer.performSubtypeConstraintGenerationInternal(t1, t2,
+        leftSchema: !covariant, astNodeForTesting: nodeForTesting);
     if (success) {
       var constraints = gatherer.computeConstraints();
       for (var entry in constraints.entries) {
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 c3bb278..3a78ee3 100644
--- a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
@@ -16,7 +16,6 @@
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/type_algebra.dart';
 import 'package:analyzer/src/dart/element/type_schema.dart';
-import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
 
 /// Creates sets of [GeneratedTypeConstraint]s for type parameters, based on an
@@ -38,8 +37,9 @@
             InterfaceType,
             InterfaceElement,
             AstNode> {
-  final TypeSystemImpl _typeSystem;
-  final Set<TypeParameterElement> _typeParameters = Set.identity();
+  @override
+  final Set<TypeParameterElement> typeParametersToConstrain = Set.identity();
+
   final List<
       GeneratedTypeConstraint<DartType, TypeParameterElement,
           PromotableElement>> _constraints = [];
@@ -47,14 +47,12 @@
   final TypeConstraintGenerationDataForTesting? dataForTesting;
 
   TypeConstraintGatherer({
-    required TypeSystemImpl typeSystem,
     required Iterable<TypeParameterElement> typeParameters,
     required TypeSystemOperations typeSystemOperations,
     required super.inferenceUsingBoundsIsEnabled,
     required this.dataForTesting,
-  })  : _typeSystem = typeSystem,
-        _typeSystemOperations = typeSystemOperations {
-    _typeParameters.addAll(typeParameters);
+  }) : _typeSystemOperations = typeSystemOperations {
+    typeParametersToConstrain.addAll(typeParameters);
   }
 
   @override
@@ -73,15 +71,15 @@
   @override
   void addLowerConstraintForParameter(
       TypeParameterElement element, DartType lower,
-      {required AstNode? nodeForTesting}) {
+      {required AstNode? astNodeForTesting}) {
     GeneratedTypeConstraint<DartType, TypeParameterElement, PromotableElement>
         generatedTypeConstraint = GeneratedTypeConstraint<
             DartType,
             TypeParameterElement,
             PromotableElement>.lower(element, SharedTypeSchemaView(lower));
     _constraints.add(generatedTypeConstraint);
-    if (dataForTesting != null && nodeForTesting != null) {
-      (dataForTesting!.generatedTypeConstraints[nodeForTesting] ??= [])
+    if (dataForTesting != null && astNodeForTesting != null) {
+      (dataForTesting!.generatedTypeConstraints[astNodeForTesting] ??= [])
           .add(generatedTypeConstraint);
     }
   }
@@ -89,15 +87,15 @@
   @override
   void addUpperConstraintForParameter(
       TypeParameterElement element, DartType upper,
-      {required AstNode? nodeForTesting}) {
+      {required AstNode? astNodeForTesting}) {
     GeneratedTypeConstraint<DartType, TypeParameterElement, PromotableElement>
         generatedTypeConstraint = GeneratedTypeConstraint<
             DartType,
             TypeParameterElement,
             PromotableElement>.upper(element, SharedTypeSchemaView(upper));
     _constraints.add(generatedTypeConstraint);
-    if (dataForTesting != null && nodeForTesting != null) {
-      (dataForTesting!.generatedTypeConstraints[nodeForTesting] ??= [])
+    if (dataForTesting != null && astNodeForTesting != null) {
+      (dataForTesting!.generatedTypeConstraints[astNodeForTesting] ??= [])
           .add(generatedTypeConstraint);
     }
   }
@@ -110,7 +108,7 @@
     var result = <TypeParameterElement,
         MergedTypeConstraint<DartType, TypeParameterElement, PromotableElement,
             InterfaceType, InterfaceElement>>{};
-    for (var parameter in _typeParameters) {
+    for (var parameter in typeParametersToConstrain) {
       result[parameter] = MergedTypeConstraint<DartType, TypeParameterElement,
           PromotableElement, InterfaceType, InterfaceElement>(
         lower: SharedTypeSchemaView(UnknownInferredType.instance),
@@ -142,13 +140,13 @@
             constraint.typeParameter,
             typeAnalyzerOperations.leastClosureOfTypeInternal(
                 constraint.constraint.unwrapTypeSchemaView(), eliminator),
-            nodeForTesting: astNodeForTesting);
+            astNodeForTesting: astNodeForTesting);
       } else {
         addLowerConstraintForParameter(
             constraint.typeParameter,
             typeAnalyzerOperations.greatestClosureOfTypeInternal(
                 constraint.constraint.unwrapTypeSchemaView(), eliminator),
-            nodeForTesting: astNodeForTesting);
+            astNodeForTesting: astNodeForTesting);
       }
     }
   }
@@ -205,170 +203,9 @@
   }
 
   @override
-  bool performSubtypeConstraintGenerationInternal(DartType p, DartType q,
-      {required bool leftSchema, required AstNode? astNodeForTesting}) {
-    return trySubtypeMatch(p, q, leftSchema, nodeForTesting: astNodeForTesting);
-  }
-
-  @override
   void restoreState(shared.TypeConstraintGeneratorState state) {
     _constraints.length = state.count;
   }
-
-  /// Tries to match [P] as a subtype for [Q].
-  ///
-  /// If [P] is a subtype of [Q] under some constraints, the constraints making
-  /// the relation possible are recorded to [_constraints], and `true` is
-  /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
-  /// and `false` is returned.
-  bool trySubtypeMatch(DartType P, DartType Q, bool leftSchema,
-      {required AstNode? nodeForTesting}) {
-    // If `P` is `_` then the match holds with no constraints.
-    if (P is SharedUnknownTypeStructure) {
-      return true;
-    }
-
-    // If `Q` is `_` then the match holds with no constraints.
-    if (Q is SharedUnknownTypeStructure) {
-      return true;
-    }
-
-    // If `P` is a type variable `X` in `L`, then the match holds:
-    //   Under constraint `_ <: X <: Q`.
-    var P_nullability = P.nullabilitySuffix;
-    if (_typeSystemOperations.matchInferableParameter(SharedTypeView(P))
-        case var P_element?
-        when P_nullability == NullabilitySuffix.none &&
-            _typeParameters.contains(P_element)) {
-      addUpperConstraintForParameter(P_element, Q,
-          nodeForTesting: nodeForTesting);
-      return true;
-    }
-
-    // If `Q` is a type variable `X` in `L`, then the match holds:
-    //   Under constraint `P <: X <: _`.
-    var Q_nullability = Q.nullabilitySuffix;
-    if (_typeSystemOperations.matchInferableParameter(SharedTypeView(Q))
-        case var Q_element?
-        when Q_nullability == NullabilitySuffix.none &&
-            _typeParameters.contains(Q_element) &&
-            (!inferenceUsingBoundsIsEnabled ||
-                (Q_element.bound == null ||
-                    _typeSystemOperations.isSubtypeOfInternal(
-                        P,
-                        _typeSystemOperations.greatestClosureOfTypeInternal(
-                            Q_element.bound!, [..._typeParameters]))))) {
-      addLowerConstraintForParameter(Q_element, P,
-          nodeForTesting: nodeForTesting);
-      return true;
-    }
-
-    // If `P` and `Q` are identical types, then the subtype match holds
-    // under no constraints.
-    if (P == Q) {
-      return true;
-    }
-
-    // Note that it's not necessary to rewind [_constraints] to its prior state
-    // in case [performSubtypeConstraintGenerationForFutureOr] returns false, as
-    // [performSubtypeConstraintGenerationForFutureOr] handles the rewinding of
-    // the state itself.
-    if (performSubtypeConstraintGenerationForRightFutureOr(P, Q,
-        leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
-      return true;
-    }
-
-    if (performSubtypeConstraintGenerationForRightNullableType(P, Q,
-        leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
-      return true;
-    }
-
-    // If `P` is `FutureOr<P0>` the match holds under constraint set `C1 + C2`:
-    if (performSubtypeConstraintGenerationForLeftFutureOr(P, Q,
-        leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
-      return true;
-    }
-
-    // If `P` is `P0?` the match holds under constraint set `C1 + C2`:
-    if (performSubtypeConstraintGenerationForLeftNullableType(P, Q,
-        leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
-      return true;
-    }
-
-    // If `Q` is `dynamic`, `Object?`, or `void` then the match holds under
-    // no constraints.
-    if (Q is SharedDynamicTypeStructure ||
-        Q is SharedVoidTypeStructure ||
-        Q == _typeSystemOperations.objectQuestionType.unwrapTypeView()) {
-      return true;
-    }
-
-    // If `P` is `Never` then the match holds under no constraints.
-    if (_typeSystemOperations.isNever(SharedTypeView(P))) {
-      return true;
-    }
-
-    // If `Q` is `Object`, then the match holds under no constraints:
-    //  Only if `P` is non-nullable.
-    if (Q == _typeSystemOperations.objectType.unwrapTypeView()) {
-      return _typeSystem.isNonNullable(P);
-    }
-
-    // If `P` is `Null`, then the match holds under no constraints:
-    //  Only if `Q` is nullable.
-    if (P_nullability == NullabilitySuffix.none &&
-        _typeSystemOperations.isNull(SharedTypeView(P))) {
-      return _typeSystem.isNullable(Q);
-    }
-
-    // If `P` is a type variable `X` with bound `B` (or a promoted type
-    // variable `X & B`), the match holds with constraint set `C`:
-    //   If `B` is a subtype match for `Q` with constraint set `C`.
-    // Note: we have already eliminated the case that `X` is a variable in `L`.
-    if (typeAnalyzerOperations.matchTypeParameterBoundInternal(P)
-        case var bound?) {
-      if (performSubtypeConstraintGenerationInternal(bound, Q,
-          leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
-        return true;
-      }
-    }
-
-    bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
-        P, Q,
-        leftSchema: leftSchema, astNodeForTesting: nodeForTesting);
-    if (result != null) {
-      return result;
-    }
-
-    // If `Q` is `Function` then the match holds under no constraints:
-    //   If `P` is a function type.
-    if (_typeSystemOperations.isDartCoreFunction(SharedTypeView(Q))) {
-      if (P is SharedFunctionTypeStructure) {
-        return true;
-      }
-    }
-
-    if (performSubtypeConstraintGenerationForFunctionTypes(P, Q,
-        leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
-      return true;
-    }
-
-    // A type `P` is a subtype match for `Record` with respect to `L` under no
-    // constraints:
-    //   If `P` is a record type or `Record`.
-    if (_typeSystemOperations.isDartCoreRecord(SharedTypeView(Q))) {
-      if (P is SharedRecordTypeStructure<DartType>) {
-        return true;
-      }
-    }
-
-    if (performSubtypeConstraintGenerationForRecordTypes(P, Q,
-        leftSchema: leftSchema, astNodeForTesting: nodeForTesting)) {
-      return true;
-    }
-
-    return false;
-  }
 }
 
 /// Data structure maintaining intermediate type inference results, such as
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 075c051..d9d547f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -579,8 +579,8 @@
   }
 
   @override
-  bool isNonNullable(SharedTypeSchemaView<DartType> typeSchema) {
-    return typeSystem.isNonNullable(typeSchema.unwrapTypeSchemaView());
+  bool isNonNullableInternal(DartType type) {
+    return typeSystem.isNonNullable(type);
   }
 
   @override
@@ -589,6 +589,11 @@
   }
 
   @override
+  bool isNullableInternal(DartType type) {
+    return typeSystem.isNullable(type);
+  }
+
+  @override
   bool isObject(SharedTypeView<DartType> type) {
     return type.unwrapTypeView().isDartCoreObject &&
         type.nullabilitySuffix == NullabilitySuffix.none;
diff --git a/pkg/analyzer/test/src/dart/element/type_constraint_gatherer_test.dart b/pkg/analyzer/test/src/dart/element/type_constraint_gatherer_test.dart
index 68dc69b..68f4939 100644
--- a/pkg/analyzer/test/src/dart/element/type_constraint_gatherer_test.dart
+++ b/pkg/analyzer/test/src/dart/element/type_constraint_gatherer_test.dart
@@ -1267,7 +1267,6 @@
     List<String> expected,
   ) {
     var gatherer = TypeConstraintGatherer(
-      typeSystem: typeSystem,
       typeParameters: typeParameters,
       typeSystemOperations:
           TypeSystemOperations(typeSystem, strictCasts: false),
@@ -1275,8 +1274,8 @@
       dataForTesting: null,
     );
 
-    var isMatch =
-        gatherer.trySubtypeMatch(P, Q, leftSchema, nodeForTesting: null);
+    var isMatch = gatherer.performSubtypeConstraintGenerationInternal(P, Q,
+        leftSchema: leftSchema, astNodeForTesting: null);
     expect(isMatch, isTrue);
 
     var constraints = gatherer.computeConstraints();
@@ -1296,7 +1295,6 @@
     bool leftSchema,
   ) {
     var gatherer = TypeConstraintGatherer(
-      typeSystem: typeSystem,
       typeParameters: typeParameters,
       typeSystemOperations:
           TypeSystemOperations(typeSystem, strictCasts: false),
@@ -1304,8 +1302,8 @@
       dataForTesting: null,
     );
 
-    var isMatch =
-        gatherer.trySubtypeMatch(P, Q, leftSchema, nodeForTesting: null);
+    var isMatch = gatherer.performSubtypeConstraintGenerationInternal(P, Q,
+        leftSchema: leftSchema, astNodeForTesting: null);
     expect(isMatch, isFalse);
     expect(gatherer.isConstraintSetEmpty, isTrue);
   }
diff --git a/pkg/front_end/lib/src/builder/type_builder.dart b/pkg/front_end/lib/src/builder/type_builder.dart
index b415fd7..fad6fdd 100644
--- a/pkg/front_end/lib/src/builder/type_builder.dart
+++ b/pkg/front_end/lib/src/builder/type_builder.dart
@@ -651,11 +651,9 @@
   String get fullName => name;
 
   @override
-  // Coverage-ignore(suite): Not run.
   int get fullNameOffset => nameOffset;
 
   @override
-  // Coverage-ignore(suite): Not run.
   int get fullNameLength => noLength;
 }
 
diff --git a/pkg/front_end/lib/src/kernel/expression_generator.dart b/pkg/front_end/lib/src/kernel/expression_generator.dart
index e50a6d7..cd07287 100644
--- a/pkg/front_end/lib/src/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/kernel/expression_generator.dart
@@ -4416,7 +4416,6 @@
   }
 
   @override
-  // Coverage-ignore(suite): Not run.
   Expression buildSelectorAccess(
       Selector send, int operatorOffset, bool isNullAware) {
     return buildProblem();
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 84c8eb4..7e4d2c6 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
@@ -2,8 +2,6 @@
 // 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.
 
-import 'package:_fe_analyzer_shared/src/type_inference/nullability_suffix.dart'
-    show NullabilitySuffix;
 import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart'
     as shared
     show
@@ -39,7 +37,8 @@
             TreeNode> {
   final List<GeneratedTypeConstraint> _protoConstraints = [];
 
-  final List<StructuralParameter> _parametersToConstrain;
+  @override
+  final List<StructuralParameter> typeParametersToConstrain;
 
   final OperationsCfe typeOperations;
 
@@ -53,7 +52,7 @@
       required TypeInferenceResultForTesting? inferenceResultForTesting,
       required super.inferenceUsingBoundsIsEnabled})
       : typeOperations = typeOperations,
-        _parametersToConstrain =
+        typeParametersToConstrain =
             new List<StructuralParameter>.of(typeParameters),
         _inferenceResultForTesting = inferenceResultForTesting;
 
@@ -127,14 +126,14 @@
             typeOperations.leastClosureOfTypeInternal(
                 constraint.constraint.unwrapTypeSchemaView(),
                 typeParametersToEliminate),
-            nodeForTesting: astNodeForTesting);
+            astNodeForTesting: astNodeForTesting);
       } else {
         addLowerConstraintForParameter(
             constraint.typeParameter,
             typeOperations.greatestClosureOfTypeInternal(
                 constraint.constraint.unwrapTypeSchemaView(),
                 typeParametersToEliminate),
-            nodeForTesting: astNodeForTesting);
+            astNodeForTesting: astNodeForTesting);
       }
     }
   }
@@ -168,7 +167,7 @@
   /// Returns the set of type constraints that was gathered.
   Map<StructuralParameter, MergedTypeConstraint> computeConstraints() {
     Map<StructuralParameter, MergedTypeConstraint> result = {};
-    for (StructuralParameter parameter in _parametersToConstrain) {
+    for (StructuralParameter parameter in typeParametersToConstrain) {
       result[parameter] = new MergedTypeConstraint(
           lower: new SharedTypeSchemaView(const UnknownType()),
           upper: new SharedTypeSchemaView(const UnknownType()),
@@ -187,8 +186,8 @@
   /// a subtype of [type] under any set of constraints.
   bool tryConstrainLower(DartType type, DartType bound,
       {required TreeNode? treeNodeForTesting}) {
-    return _isNullabilityAwareSubtypeMatch(bound, type,
-        constrainSupertype: true, treeNodeForTesting: treeNodeForTesting);
+    return performSubtypeConstraintGenerationInternal(bound, type,
+        leftSchema: true, astNodeForTesting: treeNodeForTesting);
   }
 
   /// Tries to constrain type parameters in [type], so that [type] <: [bound].
@@ -197,21 +196,21 @@
   /// a subtype of [bound] under any set of constraints.
   bool tryConstrainUpper(DartType type, DartType bound,
       {required TreeNode? treeNodeForTesting}) {
-    return _isNullabilityAwareSubtypeMatch(type, bound,
-        constrainSupertype: false, treeNodeForTesting: treeNodeForTesting);
+    return performSubtypeConstraintGenerationInternal(type, bound,
+        leftSchema: false, astNodeForTesting: treeNodeForTesting);
   }
 
   @override
   void addLowerConstraintForParameter(
       StructuralParameter parameter, DartType lower,
-      {required TreeNode? nodeForTesting}) {
+      {required TreeNode? astNodeForTesting}) {
     GeneratedTypeConstraint generatedTypeConstraint =
         new GeneratedTypeConstraint.lower(
             parameter, new SharedTypeSchemaView(lower));
-    if (nodeForTesting != null && _inferenceResultForTesting != null) {
+    if (astNodeForTesting != null && _inferenceResultForTesting != null) {
       // Coverage-ignore-block(suite): Not run.
-      (_inferenceResultForTesting.generatedTypeConstraints[nodeForTesting] ??=
-              [])
+      (_inferenceResultForTesting
+              .generatedTypeConstraints[astNodeForTesting] ??= [])
           .add(generatedTypeConstraint);
     }
     _protoConstraints.add(generatedTypeConstraint);
@@ -220,14 +219,14 @@
   @override
   void addUpperConstraintForParameter(
       StructuralParameter parameter, DartType upper,
-      {required TreeNode? nodeForTesting}) {
+      {required TreeNode? astNodeForTesting}) {
     GeneratedTypeConstraint generatedTypeConstraint =
         new GeneratedTypeConstraint.upper(
             parameter, new SharedTypeSchemaView(upper));
-    if (nodeForTesting != null && _inferenceResultForTesting != null) {
+    if (astNodeForTesting != null && _inferenceResultForTesting != null) {
       // Coverage-ignore-block(suite): Not run.
-      (_inferenceResultForTesting.generatedTypeConstraints[nodeForTesting] ??=
-              [])
+      (_inferenceResultForTesting
+              .generatedTypeConstraints[astNodeForTesting] ??= [])
           .add(generatedTypeConstraint);
     }
     _protoConstraints.add(generatedTypeConstraint);
@@ -236,32 +235,18 @@
   @override
   bool performSubtypeConstraintGenerationInternal(DartType p, DartType q,
       {required bool leftSchema, required TreeNode? astNodeForTesting}) {
-    return _isNullabilityAwareSubtypeMatch(p, q,
-        constrainSupertype: leftSchema, treeNodeForTesting: astNodeForTesting);
-  }
+    if (p is SharedInvalidTypeStructure<DartType> ||
+        q is SharedInvalidTypeStructure<DartType>) {
+      return false;
+    }
 
-  /// Matches [p] against [q] as a subtype against supertype.
-  ///
-  /// If [p] is a subtype of [q] under some constraints, the constraints making
-  /// the relation possible are recorded to [_protoConstraints], and `true` is
-  /// returned. Otherwise, [_protoConstraints] is left unchanged (or rolled
-  /// back), and `false` is returned.
-  ///
-  /// If [constrainSupertype] is true, the type parameters to constrain occur in
-  /// [supertype]; otherwise, they occur in [subtype].  If one type contains the
-  /// type parameters to constrain, the other one isn't allowed to contain them.
-  /// The type that contains the type parameters isn't allowed to also contain
-  /// [UnknownType], that is, to be a type schema.
-  bool _isNullabilityAwareSubtypeMatch(DartType p, DartType q,
-      {required bool constrainSupertype,
-      required TreeNode? treeNodeForTesting}) {
     // If the type parameters being constrained occur in the supertype (that is,
     // [q]), the subtype (that is, [p]) is not allowed to contain them.  To
     // check that, the assert below uses the equivalence of the following: X ->
     // Y  <=>  !X || Y.
     assert(
-        !constrainSupertype ||
-            !containsStructuralParameter(p, _parametersToConstrain.toSet(),
+        !leftSchema ||
+            !containsStructuralParameter(p, typeParametersToConstrain.toSet(),
                 unhandledTypeHandler: (DartType type, ignored) =>
                     type is UnknownType
                         ? false
@@ -277,7 +262,7 @@
     // that is, the supertype should be fully known.  To check that, the assert
     // below uses the equivalence of the following: X -> Y  <=>  !X || Y.
     assert(
-        !constrainSupertype || isKnown(q),
+        !leftSchema || isKnown(q),
         "Failed implication check: "
         "constrainSupertype -> isKnown(q)");
 
@@ -286,7 +271,7 @@
     // that is, the subtype should be fully known.  To check that, the assert
     // below uses the equivalence of the following: X -> Y  <=>  !X || Y.
     assert(
-        constrainSupertype || isKnown(p),
+        leftSchema || isKnown(p),
         "Failed implication check: "
         "!constrainSupertype -> isKnown(p)");
 
@@ -295,8 +280,8 @@
     // check that, the assert below uses the equivalence of the following: X ->
     // Y  <=>  !X || Y.
     assert(
-        constrainSupertype ||
-            !containsStructuralParameter(q, _parametersToConstrain.toSet(),
+        leftSchema ||
+            !containsStructuralParameter(q, typeParametersToConstrain.toSet(),
                 unhandledTypeHandler: (DartType type, ignored) =>
                     type is UnknownType
                         ? false
@@ -307,174 +292,7 @@
         "Failed implication check: "
         "!constrainSupertype -> !containsStructuralParameter(q)");
 
-    if (p is InvalidType || q is InvalidType) return false;
-
-    // If P is _ then the match holds with no constraints.
-    if (p is SharedUnknownTypeStructure) return true;
-
-    // If Q is _ then the match holds with no constraints.
-    if (q is SharedUnknownTypeStructure) return true;
-
-    // If P is a type parameter X in L, then the match holds:
-    //
-    // Under constraint _ <: X <: Q.
-    NullabilitySuffix pNullability = p.nullabilitySuffix;
-    if (typeOperations.matchInferableParameter(new SharedTypeView(p))
-        case StructuralParameter pParameter?
-        when pNullability == NullabilitySuffix.none &&
-            _parametersToConstrain.contains(pParameter)) {
-      addUpperConstraintForParameter(pParameter, q,
-          nodeForTesting: treeNodeForTesting);
-      return true;
-    }
-
-    // If Q is a type parameter X in L, then the match holds:
-    //
-    // Under constraint P <: X <: _.
-    NullabilitySuffix qNullability = q.nullabilitySuffix;
-    if (typeOperations.matchInferableParameter(new SharedTypeView(q))
-        case StructuralParameter qParameter?
-        when qNullability == NullabilitySuffix.none &&
-            _parametersToConstrain.contains(qParameter) &&
-            (!inferenceUsingBoundsIsEnabled ||
-                typeOperations.isSubtypeOfInternal(
-                    p,
-                    typeOperations.greatestClosureOfTypeInternal(
-                        qParameter.bound, _parametersToConstrain)))) {
-      addLowerConstraintForParameter(qParameter, p,
-          nodeForTesting: treeNodeForTesting);
-      return true;
-    }
-
-    // If P and Q are identical types, then the subtype match holds under no
-    // constraints.
-    //
-    // We're only checking primitive types for equality, because the algorithm
-    // will recurse over non-primitive types anyway.
-    if (identical(p, q) ||
-        isPrimitiveDartType(p) && isPrimitiveDartType(q) && p == q) {
-      return true;
-    }
-
-    if (performSubtypeConstraintGenerationForRightFutureOr(p, q,
-        leftSchema: constrainSupertype,
-        astNodeForTesting: treeNodeForTesting)) {
-      return true;
-    }
-
-    // If Q is Q0? the match holds under constraint set C:
-    //
-    // If P is P0? and P0 is a subtype match for Q0 under constraint set C.
-    // Or if P is dynamic or void and Object is a subtype match for Q0 under
-    // constraint set C.
-    // Or if P is a subtype match for Q0 under non-empty constraint set C.
-    // Or if P is a subtype match for Null under constraint set C.
-    // Or if P is a subtype match for Q0 under empty constraint set C.
-    if (performSubtypeConstraintGenerationForRightNullableType(p, q,
-        leftSchema: constrainSupertype,
-        astNodeForTesting: treeNodeForTesting)) {
-      return true;
-    }
-
-    // If P is FutureOr<P0> the match holds under constraint set C1 + C2:
-    //
-    // If Future<P0> is a subtype match for Q under constraint set C1.
-    // And if P0 is a subtype match for Q under constraint set C2.
-    if (performSubtypeConstraintGenerationForLeftFutureOr(p, q,
-        leftSchema: constrainSupertype,
-        astNodeForTesting: treeNodeForTesting)) {
-      return true;
-    }
-
-    // If P is P0? the match holds under constraint set C1 + C2:
-    //
-    // If P0 is a subtype match for Q under constraint set C1.
-    // And if Null is a subtype match for Q under constraint set C2.
-    if (performSubtypeConstraintGenerationForLeftNullableType(p, q,
-        leftSchema: constrainSupertype,
-        astNodeForTesting: treeNodeForTesting)) {
-      return true;
-    }
-
-    // If Q is dynamic, Object?, or void then the match holds under no
-    // constraints.
-    if (q is SharedDynamicTypeStructure ||
-        q is SharedVoidTypeStructure ||
-        q == typeOperations.objectQuestionType.unwrapTypeView()) {
-      return true;
-    }
-
-    // If P is Never then the match holds under no constraints.
-    if (typeOperations.isNever(new SharedTypeView(p))) {
-      return true;
-    }
-
-    // If Q is Object, then the match holds under no constraints:
-    //
-    // Only if P is non-nullable.
-    if (q == typeOperations.objectType.unwrapTypeView()) {
-      return typeOperations.isNonNullable(new SharedTypeSchemaView(p));
-    }
-
-    // If P is Null, then the match holds under no constraints:
-    //
-    // Only if Q is nullable.
-    if (typeOperations.isNull(new SharedTypeView(p))) {
-      return q.nullability == Nullability.nullable;
-    }
-
-    // If P is a type parameter X with bound B (or a promoted type parameter X &
-    // B), the match holds with constraint set C:
-    //
-    // If B is a subtype match for Q with constraint set C.  Note that we have
-    // already eliminated the case that X is a variable in L.
-    if (typeAnalyzerOperations.matchTypeParameterBoundInternal(p)
-        case DartType bound?) {
-      if (performSubtypeConstraintGenerationInternal(bound, q,
-          leftSchema: constrainSupertype,
-          astNodeForTesting: treeNodeForTesting)) {
-        return true;
-      }
-    }
-
-    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:
-    //
-    // If P is a function type.
-    if (typeOperations.isDartCoreFunction(new SharedTypeView(q)) &&
-        // Coverage-ignore(suite): Not run.
-        p is FunctionType) {
-      return true;
-    }
-
-    if (performSubtypeConstraintGenerationForFunctionTypes(p, q,
-        leftSchema: constrainSupertype,
-        astNodeForTesting: treeNodeForTesting)) {
-      return true;
-    }
-
-    // A type P is a subtype match for Record with respect to L under no
-    // constraints:
-    //
-    // If P is a record type or Record.
-    if (typeOperations.isDartCoreRecord(new SharedTypeView(q)) &&
-        p is RecordType) {
-      return true;
-    }
-
-    if (performSubtypeConstraintGenerationForRecordTypes(p, q,
-        leftSchema: constrainSupertype,
-        astNodeForTesting: treeNodeForTesting)) {
-      return true;
-    }
-
-    return false;
+    return super.performSubtypeConstraintGenerationInternal(p, q,
+        leftSchema: leftSchema, astNodeForTesting: astNodeForTesting);
   }
 }
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 bb2855e..adab1f4 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
@@ -926,12 +926,6 @@
   }
 
   @override
-  bool isNonNullable(SharedTypeSchemaView<DartType> typeSchema) {
-    return typeSchema.unwrapTypeSchemaView().nullability ==
-        Nullability.nonNullable;
-  }
-
-  @override
   StructuralParameter? matchInferableParameter(SharedTypeView<DartType> type) {
     DartType unwrappedType = type.unwrapTypeView();
     if (unwrappedType is StructuralParameterType) {
@@ -1030,6 +1024,16 @@
       return null;
     }
   }
+
+  @override
+  bool isNonNullableInternal(DartType type) {
+    return type.nullability == Nullability.nonNullable;
+  }
+
+  @override
+  bool isNullableInternal(DartType type) {
+    return type.nullability == Nullability.nullable;
+  }
 }
 
 /// Type inference results used for testing.
diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart
index 546ba09..7191fb7 100644
--- a/pkg/front_end/test/coverage_suite_expected.dart
+++ b/pkg/front_end/test/coverage_suite_expected.dart
@@ -390,7 +390,7 @@
   ),
   // 100.0%.
   "package:front_end/src/builder/type_builder.dart": (
-    hitCount: 53,
+    hitCount: 56,
     missCount: 0,
   ),
   // 100.0%.
@@ -560,7 +560,7 @@
   ),
   // 100.0%.
   "package:front_end/src/kernel/body_builder.dart": (
-    hitCount: 7128,
+    hitCount: 7147,
     missCount: 0,
   ),
   // 100.0%.
@@ -620,7 +620,7 @@
   ),
   // 100.0%.
   "package:front_end/src/kernel/expression_generator.dart": (
-    hitCount: 2515,
+    hitCount: 2517,
     missCount: 0,
   ),
   // 100.0%.
@@ -650,7 +650,7 @@
   ),
   // 100.0%.
   "package:front_end/src/kernel/hierarchy/extension_type_members.dart": (
-    hitCount: 386,
+    hitCount: 388,
     missCount: 0,
   ),
   // 100.0%.
@@ -1036,7 +1036,7 @@
   ),
   // 100.0%.
   "package:front_end/src/type_inference/type_constraint_gatherer.dart": (
-    hitCount: 179,
+    hitCount: 109,
     missCount: 0,
   ),
   // 100.0%.
@@ -1046,7 +1046,7 @@
   ),
   // 100.0%.
   "package:front_end/src/type_inference/type_inference_engine.dart": (
-    hitCount: 545,
+    hitCount: 547,
     missCount: 0,
   ),
   // 100.0%.
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 50b0067..fc46d44 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -2698,6 +2698,7 @@
 revise
 revision
 revisit
+rewind
 rewrite
 rewriter
 rewriting