[cfe] Provide inferred type context to initializing formals

In response to https://github.com/flutter/flutter/issues/160551

Change-Id: I0e4123c76f8d82a6900e12452f0deef2cbb05d36
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404822
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/front_end/lib/src/source/source_factory_builder.dart b/pkg/front_end/lib/src/source/source_factory_builder.dart
index 48575ec..8629118 100644
--- a/pkg/front_end/lib/src/source/source_factory_builder.dart
+++ b/pkg/front_end/lib/src/source/source_factory_builder.dart
@@ -582,6 +582,32 @@
           .createBodyBuilderForOutlineExpression(libraryBuilder,
               createBodyBuilderContext(), declarationBuilder.scope, fileUri);
       Builder? targetBuilder = redirectionTarget.target;
+
+      // Inference of target's formals should happen before building of the
+      // outline expressions in members and before inferring target's type
+      // arguments.
+      //
+      // (1) The outline expressions, such as formal parameter initializers,
+      // need properly inferred type contexts. Among other things, it ensures
+      // that the required coercions, such as int-to-double conversion, are
+      // run.
+      //
+      // (2) Type arguments for the targets of redirecting factories can only
+      // be inferred if the formal parameters of the targets are inferred too.
+      // That may not be the case when the target's parameters are initializing
+      // parameters referring to fields with types that are to be inferred.
+      if (targetBuilder is SourceFunctionBuilderImpl) {
+        List<FormalParameterBuilder>? formals = targetBuilder.formals;
+        if (formals != null) {
+          for (FormalParameterBuilder formal in formals) {
+            TypeBuilder formalType = formal.type;
+            if (formalType is InferableTypeBuilder) {
+              formalType.inferType(classHierarchy);
+            }
+          }
+        }
+      }
+
       if (targetBuilder is SourceMemberBuilder) {
         // Ensure that target has been built.
         targetBuilder.buildOutlineExpressions(
@@ -598,22 +624,6 @@
             fileOffset, fileUri);
       }
 
-      // Type arguments for the targets of redirecting factories can only be
-      // inferred if the formal parameters of the targets are inferred too.
-      // That may not be the case when the target's parameters are initializing
-      // parameters referring to fields with types that are to be inferred.
-      if (targetBuilder is SourceFunctionBuilderImpl) {
-        List<FormalParameterBuilder>? formals = targetBuilder.formals;
-        if (formals != null) {
-          for (FormalParameterBuilder formal in formals) {
-            TypeBuilder formalType = formal.type;
-            if (formalType is InferableTypeBuilder) {
-              formalType.inferType(classHierarchy);
-            }
-          }
-        }
-      }
-
       typeArguments = inferrer.inferRedirectingFactoryTypeArguments(
           helper,
           _procedureInternal.function.returnType,
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index fe92305..73e7208 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -510,6 +510,7 @@
 coerces
 coercing
 coercion
+coercions
 coincides
 coinductively
 collapses
diff --git a/pkg/front_end/testcases/inference/flutter_issue160551.dart b/pkg/front_end/testcases/inference/flutter_issue160551.dart
new file mode 100644
index 0000000..5c1fb7b
--- /dev/null
+++ b/pkg/front_end/testcases/inference/flutter_issue160551.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2025, 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 main() {
+  print(B().foo);
+}
+
+abstract class A {
+  A();
+
+  factory A.redir({double foo}) = B;
+}
+
+class B<T> extends A {
+  B({this.foo = 10});
+  final double foo;
+}
diff --git a/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.expect b/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.expect
new file mode 100644
index 0000000..91834f0
--- /dev/null
+++ b/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.expect
@@ -0,0 +1,24 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  constructor •() → self::A
+    : super core::Object::•()
+    ;
+  static factory redir({core::double foo = #C1}) → self::A /* redirection-target: self::B::•<dynamic>*/
+    return new self::B::•<dynamic>(foo: foo);
+}
+class B<T extends core::Object? = dynamic> extends self::A {
+  final field core::double foo;
+  constructor •({core::double foo = #C1}) → self::B<self::B::T%>
+    : self::B::foo = foo, super self::A::•()
+    ;
+}
+static method main() → void {
+  core::print(new self::B::•<dynamic>().{self::B::foo}{core::double});
+}
+
+constants  {
+  #C1 = 10.0
+}
diff --git a/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.modular.expect b/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.modular.expect
new file mode 100644
index 0000000..91834f0
--- /dev/null
+++ b/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.modular.expect
@@ -0,0 +1,24 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  constructor •() → self::A
+    : super core::Object::•()
+    ;
+  static factory redir({core::double foo = #C1}) → self::A /* redirection-target: self::B::•<dynamic>*/
+    return new self::B::•<dynamic>(foo: foo);
+}
+class B<T extends core::Object? = dynamic> extends self::A {
+  final field core::double foo;
+  constructor •({core::double foo = #C1}) → self::B<self::B::T%>
+    : self::B::foo = foo, super self::A::•()
+    ;
+}
+static method main() → void {
+  core::print(new self::B::•<dynamic>().{self::B::foo}{core::double});
+}
+
+constants  {
+  #C1 = 10.0
+}
diff --git a/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.outline.expect b/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.outline.expect
new file mode 100644
index 0000000..12e985c
--- /dev/null
+++ b/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.outline.expect
@@ -0,0 +1,17 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  constructor •() → self::A
+    ;
+  static factory redir({core::double foo = null}) → self::A /* redirection-target: self::B::•<dynamic>*/
+    return new self::B::•<dynamic>(foo: foo);
+}
+class B<T extends core::Object? = dynamic> extends self::A {
+  final field core::double foo;
+  constructor •({core::double foo = 10.0}) → self::B<self::B::T%>
+    ;
+}
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.transformed.expect
new file mode 100644
index 0000000..91834f0
--- /dev/null
+++ b/pkg/front_end/testcases/inference/flutter_issue160551.dart.strong.transformed.expect
@@ -0,0 +1,24 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  constructor •() → self::A
+    : super core::Object::•()
+    ;
+  static factory redir({core::double foo = #C1}) → self::A /* redirection-target: self::B::•<dynamic>*/
+    return new self::B::•<dynamic>(foo: foo);
+}
+class B<T extends core::Object? = dynamic> extends self::A {
+  final field core::double foo;
+  constructor •({core::double foo = #C1}) → self::B<self::B::T%>
+    : self::B::foo = foo, super self::A::•()
+    ;
+}
+static method main() → void {
+  core::print(new self::B::•<dynamic>().{self::B::foo}{core::double});
+}
+
+constants  {
+  #C1 = 10.0
+}
diff --git a/pkg/front_end/testcases/inference/flutter_issue160551.dart.textual_outline.expect b/pkg/front_end/testcases/inference/flutter_issue160551.dart.textual_outline.expect
new file mode 100644
index 0000000..5f1500d
--- /dev/null
+++ b/pkg/front_end/testcases/inference/flutter_issue160551.dart.textual_outline.expect
@@ -0,0 +1,11 @@
+void main() {}
+
+abstract class A {
+  A();
+  factory A.redir({double foo}) = B;
+}
+
+class B<T> extends A {
+  B({this.foo = 10});
+  final double foo;
+}
diff --git a/pkg/front_end/testcases/inference/flutter_issue160551.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/inference/flutter_issue160551.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..fe81633
--- /dev/null
+++ b/pkg/front_end/testcases/inference/flutter_issue160551.dart.textual_outline_modelled.expect
@@ -0,0 +1,11 @@
+abstract class A {
+  A();
+  factory A.redir({double foo}) = B;
+}
+
+class B<T> extends A {
+  B({this.foo = 10});
+  final double foo;
+}
+
+void main() {}