[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
+}