[cfe] Handle structural against type parameter type in subtype tests

Closes https://github.com/dart-lang/sdk/issues/56633

This is also a follow-up to
https://dart-review.googlesource.com/c/sdk/+/382000/comment/9801301a_05a82175/

Change-Id: Id6f791292ee424ca0bf971cdd0c99dfbb6b79a10
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/385421
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/front_end/testcases/general/issue56633.dart b/pkg/front_end/testcases/general/issue56633.dart
new file mode 100644
index 0000000..f610aa3
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue56633.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+T Function<S extends T>() method<T>(S Function<S extends T>() f) => f;
+
+abstract class A<T> {
+  T Function<S extends T>(S s) foo = <S extends T>(S s) => s;
+}
+
+typedef F<T, S extends T> = int;
+typedef G<T> = F<T, S> Function<S extends T>();
diff --git a/pkg/front_end/testcases/general/issue56633.dart.strong.expect b/pkg/front_end/testcases/general/issue56633.dart.strong.expect
new file mode 100644
index 0000000..37aa6d8
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue56633.dart.strong.expect
@@ -0,0 +1,20 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef F<unrelated T extends core::Object? = dynamic, unrelated S extends T% = dynamic> = core::int;
+typedef G<invariant T extends core::Object? = dynamic> = <S extends T% = dynamic>() → core::int;
+abstract class A<T extends core::Object? = dynamic> extends core::Object {
+  covariant-by-class field <S extends self::A::T% = dynamic>(S%) → self::A::T% foo = <S extends self::A::T%>(S% s) → S% => s;
+  synthetic constructor •() → self::A<self::A::T%>
+    : super core::Object::•()
+    ;
+}
+static method method<T extends core::Object? = dynamic>(<S extends self::method::T% = dynamic>() → S% f) → <S extends self::method::T% = dynamic>() → self::method::T%
+  return f;
+static method /* from org-dartlang-sdk:///sdk/lib/_internal/vm_shared/lib/integers_patch.dart */ _#F#fromEnvironment#tearOff<unrelated T extends core::Object? = dynamic, unrelated S extends self::_#F#fromEnvironment#tearOff::T% = dynamic>(core::String name, {core::int defaultValue = #C1}) → core::int
+  return core::int::fromEnvironment(name, defaultValue: defaultValue);
+
+constants  {
+  #C1 = 0
+}
diff --git a/pkg/front_end/testcases/general/issue56633.dart.strong.modular.expect b/pkg/front_end/testcases/general/issue56633.dart.strong.modular.expect
new file mode 100644
index 0000000..37aa6d8
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue56633.dart.strong.modular.expect
@@ -0,0 +1,20 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef F<unrelated T extends core::Object? = dynamic, unrelated S extends T% = dynamic> = core::int;
+typedef G<invariant T extends core::Object? = dynamic> = <S extends T% = dynamic>() → core::int;
+abstract class A<T extends core::Object? = dynamic> extends core::Object {
+  covariant-by-class field <S extends self::A::T% = dynamic>(S%) → self::A::T% foo = <S extends self::A::T%>(S% s) → S% => s;
+  synthetic constructor •() → self::A<self::A::T%>
+    : super core::Object::•()
+    ;
+}
+static method method<T extends core::Object? = dynamic>(<S extends self::method::T% = dynamic>() → S% f) → <S extends self::method::T% = dynamic>() → self::method::T%
+  return f;
+static method /* from org-dartlang-sdk:///sdk/lib/_internal/vm_shared/lib/integers_patch.dart */ _#F#fromEnvironment#tearOff<unrelated T extends core::Object? = dynamic, unrelated S extends self::_#F#fromEnvironment#tearOff::T% = dynamic>(core::String name, {core::int defaultValue = #C1}) → core::int
+  return core::int::fromEnvironment(name, defaultValue: defaultValue);
+
+constants  {
+  #C1 = 0
+}
diff --git a/pkg/front_end/testcases/general/issue56633.dart.strong.outline.expect b/pkg/front_end/testcases/general/issue56633.dart.strong.outline.expect
new file mode 100644
index 0000000..3534b2a
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue56633.dart.strong.outline.expect
@@ -0,0 +1,15 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef F<unrelated T extends core::Object? = dynamic, unrelated S extends T% = dynamic> = core::int;
+typedef G<invariant T extends core::Object? = dynamic> = <S extends T% = dynamic>() → core::int;
+abstract class A<T extends core::Object? = dynamic> extends core::Object {
+  covariant-by-class field <S extends self::A::T% = dynamic>(S%) → self::A::T% foo;
+  synthetic constructor •() → self::A<self::A::T%>
+    ;
+}
+static method method<T extends core::Object? = dynamic>(<S extends self::method::T% = dynamic>() → S% f) → <S extends self::method::T% = dynamic>() → self::method::T%
+  ;
+static method /* from org-dartlang-sdk:///sdk/lib/_internal/vm_shared/lib/integers_patch.dart */ _#F#fromEnvironment#tearOff<unrelated T extends core::Object? = dynamic, unrelated S extends self::_#F#fromEnvironment#tearOff::T% = dynamic>(core::String name, {has-declared-initializer core::int defaultValue}) → core::int
+  return core::int::fromEnvironment(name, defaultValue: defaultValue);
diff --git a/pkg/front_end/testcases/general/issue56633.dart.strong.transformed.expect b/pkg/front_end/testcases/general/issue56633.dart.strong.transformed.expect
new file mode 100644
index 0000000..37aa6d8
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue56633.dart.strong.transformed.expect
@@ -0,0 +1,20 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef F<unrelated T extends core::Object? = dynamic, unrelated S extends T% = dynamic> = core::int;
+typedef G<invariant T extends core::Object? = dynamic> = <S extends T% = dynamic>() → core::int;
+abstract class A<T extends core::Object? = dynamic> extends core::Object {
+  covariant-by-class field <S extends self::A::T% = dynamic>(S%) → self::A::T% foo = <S extends self::A::T%>(S% s) → S% => s;
+  synthetic constructor •() → self::A<self::A::T%>
+    : super core::Object::•()
+    ;
+}
+static method method<T extends core::Object? = dynamic>(<S extends self::method::T% = dynamic>() → S% f) → <S extends self::method::T% = dynamic>() → self::method::T%
+  return f;
+static method /* from org-dartlang-sdk:///sdk/lib/_internal/vm_shared/lib/integers_patch.dart */ _#F#fromEnvironment#tearOff<unrelated T extends core::Object? = dynamic, unrelated S extends self::_#F#fromEnvironment#tearOff::T% = dynamic>(core::String name, {core::int defaultValue = #C1}) → core::int
+  return core::int::fromEnvironment(name, defaultValue: defaultValue);
+
+constants  {
+  #C1 = 0
+}
diff --git a/pkg/front_end/testcases/general/issue56633.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue56633.dart.textual_outline.expect
new file mode 100644
index 0000000..3e28908
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue56633.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+T Function<S extends T>() method<T>(S Function<S extends T>() f) => f;
+
+abstract class A<T> {
+  T Function<S extends T>(S s) foo = <S extends T>(S s) => s;
+}
+
+typedef F<T, S extends T> = int;
+
+typedef G<T> = F<T, S> Function<S extends T>();
diff --git a/pkg/front_end/testcases/general/issue56633.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue56633.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..3e28908
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue56633.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+T Function<S extends T>() method<T>(S Function<S extends T>() f) => f;
+
+abstract class A<T> {
+  T Function<S extends T>(S s) foo = <S extends T>(S s) => s;
+}
+
+typedef F<T, S extends T> = int;
+
+typedef G<T> = F<T, S> Function<S extends T>();
diff --git a/pkg/kernel/lib/src/types.dart b/pkg/kernel/lib/src/types.dart
index 7294727..f3d28a6 100644
--- a/pkg/kernel/lib/src/types.dart
+++ b/pkg/kernel/lib/src/types.dart
@@ -407,8 +407,20 @@
         return result.and(new IsSubtypeOf.basedSolelyOnNullabilities(
             sTypeParameterType, tTypeParameterType));
 
-      case (StructuralParameterType(), TypeParameterType()):
-        return const IsSubtypeOf.never();
+      case (
+          StructuralParameterType sStructuralParameterType,
+          TypeParameterType tTypeParameterType
+        ):
+        IsSubtypeOf result = performNullabilityAwareSubtypeCheck(
+            sStructuralParameterType.bound, t);
+        if (sStructuralParameterType.nullability == Nullability.undetermined &&
+            tTypeParameterType.nullability == Nullability.undetermined) {
+          // The two nullabilities are undetermined, but are connected via
+          // additional constraint, namely that they will be equal at run time.
+          return result;
+        }
+        return result.and(new IsSubtypeOf.basedSolelyOnNullabilities(
+            sStructuralParameterType, tTypeParameterType));
 
       case (
           IntersectionType sIntersectionType,