[cfe] Update nullability of variables with extension type bounds

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

Change-Id: Icb8f1b601ced44ee70d0d58e5daeefa9a99155df
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347922
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder/extension_type_declaration_builder.dart b/pkg/front_end/lib/src/fasta/builder/extension_type_declaration_builder.dart
index 1fb27c1..215c8c2 100644
--- a/pkg/front_end/lib/src/fasta/builder/extension_type_declaration_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/extension_type_declaration_builder.dart
@@ -40,6 +40,9 @@
   /// Calls [f] for each member declared in this extension.
   void forEach(void f(String name, Builder builder));
 
+  /// Returns `true` if the interfaces of the declaration are built.
+  bool get hasInterfacesBuilt;
+
   @override
   Uri get fileUri;
 }
@@ -79,4 +82,14 @@
 
   @override
   String get debugName => "ExtensionTypeDeclarationBuilder";
+
+  @override
+  bool get hasInterfacesBuilt {
+    if (interfaceBuilders == null) {
+      return true;
+    } else {
+      return interfaceBuilders!.length ==
+          extensionTypeDeclaration.implements.length;
+    }
+  }
 }
diff --git a/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart b/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart
index f4d68b9..82dd820 100644
--- a/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart
@@ -319,10 +319,16 @@
     }
     // If the bound is not set yet, the actual value is not important yet as it
     // will be set later.
+    TypeBuilder? boundBuilder = bound;
+    TypeDeclarationBuilder? boundDeclarationBuilder =
+        boundBuilder is NamedTypeBuilder ? boundBuilder.declaration : null;
     bool needsPostUpdate =
         nullabilityBuilder.isOmitted && hasUnsetParameterBound ||
             library is SourceLibraryBuilder &&
-                library.hasPendingNullability(parameterBound);
+                library.hasPendingNullability(parameterBound) ||
+            nullabilityBuilder.isOmitted &&
+                boundDeclarationBuilder is ExtensionTypeDeclarationBuilder &&
+                !boundDeclarationBuilder.hasInterfacesBuilt;
     Nullability nullability;
     if (nullabilityBuilder.isOmitted) {
       if (needsPostUpdate) {
@@ -665,11 +671,17 @@
     }
     // If the bound is not set yet, the actual value is not important yet as it
     // will be set later.
+    TypeBuilder? boundBuilder = bound;
+    TypeDeclarationBuilder? boundDeclarationBuilder =
+        boundBuilder is NamedTypeBuilder ? boundBuilder.declaration : null;
     bool needsPostUpdate = nullabilityBuilder.isOmitted &&
             identical(
                 parameter.bound, StructuralParameter.unsetBoundSentinel) ||
         library is SourceLibraryBuilder &&
-            library.hasPendingNullability(parameter.bound);
+            library.hasPendingNullability(parameter.bound) ||
+        nullabilityBuilder.isOmitted &&
+            boundDeclarationBuilder is ExtensionTypeDeclarationBuilder &&
+            !boundDeclarationBuilder.hasInterfacesBuilt;
     Nullability nullability;
     if (nullabilityBuilder.isOmitted) {
       if (needsPostUpdate) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index ec8004f..86ea406 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -2475,6 +2475,9 @@
       }
       libraries.add(target);
     }
+    for (SourceLibraryBuilder library in sourceLibraryBuilders) {
+      library.processPendingNullabilities();
+    }
     ticker.logMs("Built component");
   }
 
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index c865025..dc650c2 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -283,6 +283,7 @@
 erase
 err
 esc
+est
 estat
 et
 everytime
@@ -833,6 +834,7 @@
 wrongly
 ws
 x's
+xf
 xlate
 xrequired
 xxx
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart b/pkg/front_end/testcases/extension_types/issue54625.dart
new file mode 100644
index 0000000..00d6f16
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart
@@ -0,0 +1,27 @@
+// 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.
+
+void cfeAwait<XF extends F>(XF xf) async {
+  (await xf).st<E<int>>();
+  (await xf).st<E<int>>;
+
+  var v1 = await xf;
+  v1.toRadixString(16);
+  int v2 = v1;
+
+  int? v3 = v1;
+  v1 = v3;
+}
+
+extension type F(Future<int> _) implements Future<int> {}
+
+void main() {
+  cfeAwait<F>(F(Future<int>.value(1)));
+}
+
+extension Est<T> on T {
+  void st<X extends E<T>>() {}
+}
+
+typedef E<S> = S Function(S);
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart.strong.expect b/pkg/front_end/testcases/extension_types/issue54625.dart.strong.expect
new file mode 100644
index 0000000..ff8eaa5
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart.strong.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+typedef E<invariant S extends core::Object? = dynamic> = (S%) → S%;
+extension Est<T extends core::Object? = dynamic> on T% {
+  method st = self::Est|st;
+  method tearoff st = self::Est|get#st;
+}
+extension type F(asy::Future<core::int> _) implements asy::Future<core::int> {
+  abstract extension-type-member representation-field get _() → asy::Future<core::int>;
+  constructor • = self::F|constructor#;
+  constructor tearoff • = self::F|constructor#_#new#tearOff;
+}
+static method cfeAwait<XF extends self::F /* = asy::Future<core::int> */>(self::cfeAwait::XF xf) → void async /* emittedValueType= void */ {
+  self::Est|st<core::int, (core::int) → core::int>(await xf);
+  self::Est|get#st<core::int>(await xf)<(core::int) → core::int>;
+  core::int v1 = await xf;
+  v1.{core::int::toRadixString}(16){(core::int) → core::String};
+  core::int v2 = v1;
+  core::int? v3 = v1;
+  v1 = v3{core::int};
+}
+static extension-type-member method F|constructor#(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */ {
+  lowered final self::F /* = asy::Future<core::int> */ #this = _;
+  return #this;
+}
+static extension-type-member method F|constructor#_#new#tearOff(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */
+  return self::F|constructor#(_);
+static method main() → void {
+  self::cfeAwait<self::F /* = asy::Future<core::int> */>(self::F|constructor#(asy::Future::value<core::int>(1)));
+}
+static extension-member method Est|st<T extends core::Object? = dynamic, X extends (self::Est|st::T%) → self::Est|st::T% = (dynamic) → dynamic>(lowered final self::Est|st::T% #this) → void {}
+static extension-member method Est|get#st<T extends core::Object? = dynamic>(lowered final self::Est|get#st::T% #this) → <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void
+  return <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void => self::Est|st<self::Est|get#st::T%, X>(#this);
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/issue54625.dart.strong.transformed.expect
new file mode 100644
index 0000000..ff8eaa5
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart.strong.transformed.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+typedef E<invariant S extends core::Object? = dynamic> = (S%) → S%;
+extension Est<T extends core::Object? = dynamic> on T% {
+  method st = self::Est|st;
+  method tearoff st = self::Est|get#st;
+}
+extension type F(asy::Future<core::int> _) implements asy::Future<core::int> {
+  abstract extension-type-member representation-field get _() → asy::Future<core::int>;
+  constructor • = self::F|constructor#;
+  constructor tearoff • = self::F|constructor#_#new#tearOff;
+}
+static method cfeAwait<XF extends self::F /* = asy::Future<core::int> */>(self::cfeAwait::XF xf) → void async /* emittedValueType= void */ {
+  self::Est|st<core::int, (core::int) → core::int>(await xf);
+  self::Est|get#st<core::int>(await xf)<(core::int) → core::int>;
+  core::int v1 = await xf;
+  v1.{core::int::toRadixString}(16){(core::int) → core::String};
+  core::int v2 = v1;
+  core::int? v3 = v1;
+  v1 = v3{core::int};
+}
+static extension-type-member method F|constructor#(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */ {
+  lowered final self::F /* = asy::Future<core::int> */ #this = _;
+  return #this;
+}
+static extension-type-member method F|constructor#_#new#tearOff(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */
+  return self::F|constructor#(_);
+static method main() → void {
+  self::cfeAwait<self::F /* = asy::Future<core::int> */>(self::F|constructor#(asy::Future::value<core::int>(1)));
+}
+static extension-member method Est|st<T extends core::Object? = dynamic, X extends (self::Est|st::T%) → self::Est|st::T% = (dynamic) → dynamic>(lowered final self::Est|st::T% #this) → void {}
+static extension-member method Est|get#st<T extends core::Object? = dynamic>(lowered final self::Est|get#st::T% #this) → <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void
+  return <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void => self::Est|st<self::Est|get#st::T%, X>(#this);
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/issue54625.dart.textual_outline.expect
new file mode 100644
index 0000000..fa45c87
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart.textual_outline.expect
@@ -0,0 +1,11 @@
+void cfeAwait<XF extends F>(XF xf) async {}
+
+extension type F(Future<int> _) implements Future<int> {}
+
+void main() {}
+
+extension Est<T> on T {
+  void st<X extends E<T>>() {}
+}
+
+typedef E<S> = S Function(S);
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/issue54625.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..96d0fe8
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart.textual_outline_modelled.expect
@@ -0,0 +1,11 @@
+extension Est<T> on T {
+  void st<X extends E<T>>() {}
+}
+
+extension type F(Future<int> _) implements Future<int> {}
+
+typedef E<S> = S Function(S);
+
+void cfeAwait<XF extends F>(XF xf) async {}
+
+void main() {}
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart.weak.expect b/pkg/front_end/testcases/extension_types/issue54625.dart.weak.expect
new file mode 100644
index 0000000..ff8eaa5
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart.weak.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+typedef E<invariant S extends core::Object? = dynamic> = (S%) → S%;
+extension Est<T extends core::Object? = dynamic> on T% {
+  method st = self::Est|st;
+  method tearoff st = self::Est|get#st;
+}
+extension type F(asy::Future<core::int> _) implements asy::Future<core::int> {
+  abstract extension-type-member representation-field get _() → asy::Future<core::int>;
+  constructor • = self::F|constructor#;
+  constructor tearoff • = self::F|constructor#_#new#tearOff;
+}
+static method cfeAwait<XF extends self::F /* = asy::Future<core::int> */>(self::cfeAwait::XF xf) → void async /* emittedValueType= void */ {
+  self::Est|st<core::int, (core::int) → core::int>(await xf);
+  self::Est|get#st<core::int>(await xf)<(core::int) → core::int>;
+  core::int v1 = await xf;
+  v1.{core::int::toRadixString}(16){(core::int) → core::String};
+  core::int v2 = v1;
+  core::int? v3 = v1;
+  v1 = v3{core::int};
+}
+static extension-type-member method F|constructor#(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */ {
+  lowered final self::F /* = asy::Future<core::int> */ #this = _;
+  return #this;
+}
+static extension-type-member method F|constructor#_#new#tearOff(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */
+  return self::F|constructor#(_);
+static method main() → void {
+  self::cfeAwait<self::F /* = asy::Future<core::int> */>(self::F|constructor#(asy::Future::value<core::int>(1)));
+}
+static extension-member method Est|st<T extends core::Object? = dynamic, X extends (self::Est|st::T%) → self::Est|st::T% = (dynamic) → dynamic>(lowered final self::Est|st::T% #this) → void {}
+static extension-member method Est|get#st<T extends core::Object? = dynamic>(lowered final self::Est|get#st::T% #this) → <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void
+  return <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void => self::Est|st<self::Est|get#st::T%, X>(#this);
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart.weak.modular.expect b/pkg/front_end/testcases/extension_types/issue54625.dart.weak.modular.expect
new file mode 100644
index 0000000..ff8eaa5
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart.weak.modular.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+typedef E<invariant S extends core::Object? = dynamic> = (S%) → S%;
+extension Est<T extends core::Object? = dynamic> on T% {
+  method st = self::Est|st;
+  method tearoff st = self::Est|get#st;
+}
+extension type F(asy::Future<core::int> _) implements asy::Future<core::int> {
+  abstract extension-type-member representation-field get _() → asy::Future<core::int>;
+  constructor • = self::F|constructor#;
+  constructor tearoff • = self::F|constructor#_#new#tearOff;
+}
+static method cfeAwait<XF extends self::F /* = asy::Future<core::int> */>(self::cfeAwait::XF xf) → void async /* emittedValueType= void */ {
+  self::Est|st<core::int, (core::int) → core::int>(await xf);
+  self::Est|get#st<core::int>(await xf)<(core::int) → core::int>;
+  core::int v1 = await xf;
+  v1.{core::int::toRadixString}(16){(core::int) → core::String};
+  core::int v2 = v1;
+  core::int? v3 = v1;
+  v1 = v3{core::int};
+}
+static extension-type-member method F|constructor#(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */ {
+  lowered final self::F /* = asy::Future<core::int> */ #this = _;
+  return #this;
+}
+static extension-type-member method F|constructor#_#new#tearOff(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */
+  return self::F|constructor#(_);
+static method main() → void {
+  self::cfeAwait<self::F /* = asy::Future<core::int> */>(self::F|constructor#(asy::Future::value<core::int>(1)));
+}
+static extension-member method Est|st<T extends core::Object? = dynamic, X extends (self::Est|st::T%) → self::Est|st::T% = (dynamic) → dynamic>(lowered final self::Est|st::T% #this) → void {}
+static extension-member method Est|get#st<T extends core::Object? = dynamic>(lowered final self::Est|get#st::T% #this) → <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void
+  return <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void => self::Est|st<self::Est|get#st::T%, X>(#this);
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/issue54625.dart.weak.outline.expect
new file mode 100644
index 0000000..c3c2e45
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart.weak.outline.expect
@@ -0,0 +1,27 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+typedef E<invariant S extends core::Object? = dynamic> = (S%) → S%;
+extension Est<T extends core::Object? = dynamic> on T% {
+  method st = self::Est|st;
+  method tearoff st = self::Est|get#st;
+}
+extension type F(asy::Future<core::int> _) implements asy::Future<core::int> {
+  abstract extension-type-member representation-field get _() → asy::Future<core::int>;
+  constructor • = self::F|constructor#;
+  constructor tearoff • = self::F|constructor#_#new#tearOff;
+}
+static method cfeAwait<XF extends self::F /* = asy::Future<core::int> */>(self::cfeAwait::XF xf) → void async 
+  ;
+static extension-type-member method F|constructor#(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */
+  ;
+static extension-type-member method F|constructor#_#new#tearOff(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */
+  return self::F|constructor#(_);
+static method main() → void
+  ;
+static extension-member method Est|st<T extends core::Object? = dynamic, X extends (self::Est|st::T%) → self::Est|st::T% = (dynamic) → dynamic>(lowered final self::Est|st::T% #this) → void
+  ;
+static extension-member method Est|get#st<T extends core::Object? = dynamic>(lowered final self::Est|get#st::T% #this) → <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void
+  return <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void => self::Est|st<self::Est|get#st::T%, X>(#this);
diff --git a/pkg/front_end/testcases/extension_types/issue54625.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/issue54625.dart.weak.transformed.expect
new file mode 100644
index 0000000..ff8eaa5
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625.dart.weak.transformed.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+typedef E<invariant S extends core::Object? = dynamic> = (S%) → S%;
+extension Est<T extends core::Object? = dynamic> on T% {
+  method st = self::Est|st;
+  method tearoff st = self::Est|get#st;
+}
+extension type F(asy::Future<core::int> _) implements asy::Future<core::int> {
+  abstract extension-type-member representation-field get _() → asy::Future<core::int>;
+  constructor • = self::F|constructor#;
+  constructor tearoff • = self::F|constructor#_#new#tearOff;
+}
+static method cfeAwait<XF extends self::F /* = asy::Future<core::int> */>(self::cfeAwait::XF xf) → void async /* emittedValueType= void */ {
+  self::Est|st<core::int, (core::int) → core::int>(await xf);
+  self::Est|get#st<core::int>(await xf)<(core::int) → core::int>;
+  core::int v1 = await xf;
+  v1.{core::int::toRadixString}(16){(core::int) → core::String};
+  core::int v2 = v1;
+  core::int? v3 = v1;
+  v1 = v3{core::int};
+}
+static extension-type-member method F|constructor#(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */ {
+  lowered final self::F /* = asy::Future<core::int> */ #this = _;
+  return #this;
+}
+static extension-type-member method F|constructor#_#new#tearOff(asy::Future<core::int> _) → self::F /* = asy::Future<core::int> */
+  return self::F|constructor#(_);
+static method main() → void {
+  self::cfeAwait<self::F /* = asy::Future<core::int> */>(self::F|constructor#(asy::Future::value<core::int>(1)));
+}
+static extension-member method Est|st<T extends core::Object? = dynamic, X extends (self::Est|st::T%) → self::Est|st::T% = (dynamic) → dynamic>(lowered final self::Est|st::T% #this) → void {}
+static extension-member method Est|get#st<T extends core::Object? = dynamic>(lowered final self::Est|get#st::T% #this) → <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void
+  return <X extends (self::Est|get#st::T%) → self::Est|get#st::T% = (dynamic) → dynamic>() → void => self::Est|st<self::Est|get#st::T%, X>(#this);
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart b/pkg/front_end/testcases/extension_types/issue54625_2.dart
new file mode 100644
index 0000000..6ebad7c
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart
@@ -0,0 +1,22 @@
+// 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.
+
+X Function<X extends E>() foo() => throw 0;
+
+class A<Supertype, Subtype extends Supertype> {}
+
+A<X, Null> Function<X extends E>() // Error.
+  test1() => throw 0;
+
+A<Object, X> Function<X extends E>() // Ok.
+  test2() => throw 0;
+
+extension type E(num it) implements num {}
+
+Null returnsNull<Y extends E>() => null;
+
+test3() {
+  var f = foo();
+  f = returnsNull; // Error.
+}
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart.strong.expect b/pkg/front_end/testcases/extension_types/issue54625_2.dart.strong.expect
new file mode 100644
index 0000000..9dd094f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart.strong.expect
@@ -0,0 +1,53 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:9:1: Error: Type argument 'Null' doesn't conform to the bound 'Supertype' of the type variable 'Subtype' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// A<X, Null> Function<X extends E>() // Error.
+// ^
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:7:20: Context: This is the type variable whose bound isn't conformed to.
+// class A<Supertype, Subtype extends Supertype> {}
+//                    ^
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+//   f = returnsNull; // Error.
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<Supertype extends core::Object? = dynamic, Subtype extends self::A::Supertype% = dynamic> extends core::Object {
+  synthetic constructor •() → self::A<self::A::Supertype%, self::A::Subtype%>
+    : super core::Object::•()
+    ;
+}
+extension type E(core::num it) implements core::num {
+  abstract extension-type-member representation-field get it() → core::num;
+  constructor • = self::E|constructor#;
+  constructor tearoff • = self::E|constructor#_#new#tearOff;
+}
+static method foo() → <X extends self::E /* = core::num */ = dynamic>() → X
+  return throw 0;
+static method test1() → <X extends self::E /* = core::num */ = dynamic>() → self::A<X, Null>
+  return throw 0;
+static method test2() → <X extends self::E /* = core::num */ = dynamic>() → self::A<core::Object, X>
+  return throw 0;
+static extension-type-member method E|constructor#(core::num it) → self::E /* = core::num */ {
+  lowered final self::E /* = core::num */ #this = it;
+  return #this;
+}
+static extension-type-member method E|constructor#_#new#tearOff(core::num it) → self::E /* = core::num */
+  return self::E|constructor#(it);
+static method returnsNull<Y extends self::E /* = core::num */>() → Null
+  return null;
+static method test3() → dynamic {
+  <X extends self::E /* = core::num */ = dynamic>() → X f = self::foo();
+  f = invalid-expression "pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+  f = returnsNull; // Error.
+      ^" in #C1 as{TypeError} Never;
+}
+
+constants  {
+  #C1 = static-tearoff self::returnsNull
+}
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/issue54625_2.dart.strong.transformed.expect
new file mode 100644
index 0000000..9dd094f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart.strong.transformed.expect
@@ -0,0 +1,53 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:9:1: Error: Type argument 'Null' doesn't conform to the bound 'Supertype' of the type variable 'Subtype' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// A<X, Null> Function<X extends E>() // Error.
+// ^
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:7:20: Context: This is the type variable whose bound isn't conformed to.
+// class A<Supertype, Subtype extends Supertype> {}
+//                    ^
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+//   f = returnsNull; // Error.
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<Supertype extends core::Object? = dynamic, Subtype extends self::A::Supertype% = dynamic> extends core::Object {
+  synthetic constructor •() → self::A<self::A::Supertype%, self::A::Subtype%>
+    : super core::Object::•()
+    ;
+}
+extension type E(core::num it) implements core::num {
+  abstract extension-type-member representation-field get it() → core::num;
+  constructor • = self::E|constructor#;
+  constructor tearoff • = self::E|constructor#_#new#tearOff;
+}
+static method foo() → <X extends self::E /* = core::num */ = dynamic>() → X
+  return throw 0;
+static method test1() → <X extends self::E /* = core::num */ = dynamic>() → self::A<X, Null>
+  return throw 0;
+static method test2() → <X extends self::E /* = core::num */ = dynamic>() → self::A<core::Object, X>
+  return throw 0;
+static extension-type-member method E|constructor#(core::num it) → self::E /* = core::num */ {
+  lowered final self::E /* = core::num */ #this = it;
+  return #this;
+}
+static extension-type-member method E|constructor#_#new#tearOff(core::num it) → self::E /* = core::num */
+  return self::E|constructor#(it);
+static method returnsNull<Y extends self::E /* = core::num */>() → Null
+  return null;
+static method test3() → dynamic {
+  <X extends self::E /* = core::num */ = dynamic>() → X f = self::foo();
+  f = invalid-expression "pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+  f = returnsNull; // Error.
+      ^" in #C1 as{TypeError} Never;
+}
+
+constants  {
+  #C1 = static-tearoff self::returnsNull
+}
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/issue54625_2.dart.textual_outline.expect
new file mode 100644
index 0000000..f1b0276
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart.textual_outline.expect
@@ -0,0 +1,13 @@
+X Function<X extends E>() foo() => throw 0;
+
+class A<Supertype, Subtype extends Supertype> {}
+
+A<X, Null> Function<X extends E>() test1() => throw 0;
+
+A<Object, X> Function<X extends E>() test2() => throw 0;
+
+extension type E(num it) implements num {}
+
+Null returnsNull<Y extends E>() => null;
+
+test3() {}
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/issue54625_2.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..731fab7
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+A<Object, X> Function<X extends E>() test2() => throw 0;
+
+A<X, Null> Function<X extends E>() test1() => throw 0;
+
+Null returnsNull<Y extends E>() => null;
+
+X Function<X extends E>() foo() => throw 0;
+
+class A<Supertype, Subtype extends Supertype> {}
+
+extension type E(num it) implements num {}
+
+test3() {}
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.expect b/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.expect
new file mode 100644
index 0000000..9dd094f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.expect
@@ -0,0 +1,53 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:9:1: Error: Type argument 'Null' doesn't conform to the bound 'Supertype' of the type variable 'Subtype' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// A<X, Null> Function<X extends E>() // Error.
+// ^
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:7:20: Context: This is the type variable whose bound isn't conformed to.
+// class A<Supertype, Subtype extends Supertype> {}
+//                    ^
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+//   f = returnsNull; // Error.
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<Supertype extends core::Object? = dynamic, Subtype extends self::A::Supertype% = dynamic> extends core::Object {
+  synthetic constructor •() → self::A<self::A::Supertype%, self::A::Subtype%>
+    : super core::Object::•()
+    ;
+}
+extension type E(core::num it) implements core::num {
+  abstract extension-type-member representation-field get it() → core::num;
+  constructor • = self::E|constructor#;
+  constructor tearoff • = self::E|constructor#_#new#tearOff;
+}
+static method foo() → <X extends self::E /* = core::num */ = dynamic>() → X
+  return throw 0;
+static method test1() → <X extends self::E /* = core::num */ = dynamic>() → self::A<X, Null>
+  return throw 0;
+static method test2() → <X extends self::E /* = core::num */ = dynamic>() → self::A<core::Object, X>
+  return throw 0;
+static extension-type-member method E|constructor#(core::num it) → self::E /* = core::num */ {
+  lowered final self::E /* = core::num */ #this = it;
+  return #this;
+}
+static extension-type-member method E|constructor#_#new#tearOff(core::num it) → self::E /* = core::num */
+  return self::E|constructor#(it);
+static method returnsNull<Y extends self::E /* = core::num */>() → Null
+  return null;
+static method test3() → dynamic {
+  <X extends self::E /* = core::num */ = dynamic>() → X f = self::foo();
+  f = invalid-expression "pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+  f = returnsNull; // Error.
+      ^" in #C1 as{TypeError} Never;
+}
+
+constants  {
+  #C1 = static-tearoff self::returnsNull
+}
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.modular.expect b/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.modular.expect
new file mode 100644
index 0000000..9dd094f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.modular.expect
@@ -0,0 +1,53 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:9:1: Error: Type argument 'Null' doesn't conform to the bound 'Supertype' of the type variable 'Subtype' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// A<X, Null> Function<X extends E>() // Error.
+// ^
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:7:20: Context: This is the type variable whose bound isn't conformed to.
+// class A<Supertype, Subtype extends Supertype> {}
+//                    ^
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+//   f = returnsNull; // Error.
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<Supertype extends core::Object? = dynamic, Subtype extends self::A::Supertype% = dynamic> extends core::Object {
+  synthetic constructor •() → self::A<self::A::Supertype%, self::A::Subtype%>
+    : super core::Object::•()
+    ;
+}
+extension type E(core::num it) implements core::num {
+  abstract extension-type-member representation-field get it() → core::num;
+  constructor • = self::E|constructor#;
+  constructor tearoff • = self::E|constructor#_#new#tearOff;
+}
+static method foo() → <X extends self::E /* = core::num */ = dynamic>() → X
+  return throw 0;
+static method test1() → <X extends self::E /* = core::num */ = dynamic>() → self::A<X, Null>
+  return throw 0;
+static method test2() → <X extends self::E /* = core::num */ = dynamic>() → self::A<core::Object, X>
+  return throw 0;
+static extension-type-member method E|constructor#(core::num it) → self::E /* = core::num */ {
+  lowered final self::E /* = core::num */ #this = it;
+  return #this;
+}
+static extension-type-member method E|constructor#_#new#tearOff(core::num it) → self::E /* = core::num */
+  return self::E|constructor#(it);
+static method returnsNull<Y extends self::E /* = core::num */>() → Null
+  return null;
+static method test3() → dynamic {
+  <X extends self::E /* = core::num */ = dynamic>() → X f = self::foo();
+  f = invalid-expression "pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+  f = returnsNull; // Error.
+      ^" in #C1 as{TypeError} Never;
+}
+
+constants  {
+  #C1 = static-tearoff self::returnsNull
+}
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.outline.expect
new file mode 100644
index 0000000..f53c533
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.outline.expect
@@ -0,0 +1,38 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:9:1: Error: Type argument 'Null' doesn't conform to the bound 'Supertype' of the type variable 'Subtype' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// A<X, Null> Function<X extends E>() // Error.
+// ^
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:7:20: Context: This is the type variable whose bound isn't conformed to.
+// class A<Supertype, Subtype extends Supertype> {}
+//                    ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<Supertype extends core::Object? = dynamic, Subtype extends self::A::Supertype% = dynamic> extends core::Object {
+  synthetic constructor •() → self::A<self::A::Supertype%, self::A::Subtype%>
+    ;
+}
+extension type E(core::num it) implements core::num {
+  abstract extension-type-member representation-field get it() → core::num;
+  constructor • = self::E|constructor#;
+  constructor tearoff • = self::E|constructor#_#new#tearOff;
+}
+static method foo() → <X extends self::E /* = core::num */ = dynamic>() → X
+  ;
+static method test1() → <X extends self::E /* = core::num */ = dynamic>() → self::A<X, Null>
+  ;
+static method test2() → <X extends self::E /* = core::num */ = dynamic>() → self::A<core::Object, X>
+  ;
+static extension-type-member method E|constructor#(core::num it) → self::E /* = core::num */
+  ;
+static extension-type-member method E|constructor#_#new#tearOff(core::num it) → self::E /* = core::num */
+  return self::E|constructor#(it);
+static method returnsNull<Y extends self::E /* = core::num */>() → Null
+  ;
+static method test3() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.transformed.expect
new file mode 100644
index 0000000..9dd094f
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/issue54625_2.dart.weak.transformed.expect
@@ -0,0 +1,53 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:9:1: Error: Type argument 'Null' doesn't conform to the bound 'Supertype' of the type variable 'Subtype' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// A<X, Null> Function<X extends E>() // Error.
+// ^
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:7:20: Context: This is the type variable whose bound isn't conformed to.
+// class A<Supertype, Subtype extends Supertype> {}
+//                    ^
+//
+// pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+//   f = returnsNull; // Error.
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<Supertype extends core::Object? = dynamic, Subtype extends self::A::Supertype% = dynamic> extends core::Object {
+  synthetic constructor •() → self::A<self::A::Supertype%, self::A::Subtype%>
+    : super core::Object::•()
+    ;
+}
+extension type E(core::num it) implements core::num {
+  abstract extension-type-member representation-field get it() → core::num;
+  constructor • = self::E|constructor#;
+  constructor tearoff • = self::E|constructor#_#new#tearOff;
+}
+static method foo() → <X extends self::E /* = core::num */ = dynamic>() → X
+  return throw 0;
+static method test1() → <X extends self::E /* = core::num */ = dynamic>() → self::A<X, Null>
+  return throw 0;
+static method test2() → <X extends self::E /* = core::num */ = dynamic>() → self::A<core::Object, X>
+  return throw 0;
+static extension-type-member method E|constructor#(core::num it) → self::E /* = core::num */ {
+  lowered final self::E /* = core::num */ #this = it;
+  return #this;
+}
+static extension-type-member method E|constructor#_#new#tearOff(core::num it) → self::E /* = core::num */
+  return self::E|constructor#(it);
+static method returnsNull<Y extends self::E /* = core::num */>() → Null
+  return null;
+static method test3() → dynamic {
+  <X extends self::E /* = core::num */ = dynamic>() → X f = self::foo();
+  f = invalid-expression "pkg/front_end/testcases/extension_types/issue54625_2.dart:21:7: Error: A value of type 'Null Function<Y extends E>()' can't be assigned to a variable of type 'X Function<X extends E>()' because 'Null' is nullable and 'X' isn't.
+  f = returnsNull; // Error.
+      ^" in #C1 as{TypeError} Never;
+}
+
+constants  {
+  #C1 = static-tearoff self::returnsNull
+}