[cfe] Handle type dependencies through SynthesizedSourceConstructorBuilder

The types of forwarding constructor parameters are required for
super parameters. In order to handle the dependency correct and
computed the right substitutions through the inheritance chain,
SynthesizedSourceConstructorBuilder now use inferFormalTypes to trigger
copying of inferred parameter types.

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

Change-Id: I365ea5ea1fe5de0ac2422d01878b7f1eac421d24
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/239427
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_helper.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_helper.dart
index 3fe267a..0a19850 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_helper.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_helper.dart
@@ -250,11 +250,13 @@
   final Member original;
   final Substitution substitution;
   final bool copyReturnType;
+  bool _hasBeenInferred = false;
 
   TypeDependency(this.synthesized, this.original, this.substitution,
       {required this.copyReturnType});
 
   void copyInferred() {
+    if (_hasBeenInferred) return;
     for (int i = 0; i < original.function!.positionalParameters.length; i++) {
       VariableDeclaration synthesizedParameter =
           synthesized.function!.positionalParameters[i];
@@ -275,5 +277,6 @@
       synthesized.function!.returnType =
           substitution.substituteType(original.function!.returnType);
     }
+    _hasBeenInferred = true;
   }
 }
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index d90e037..7912ac66 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -1088,11 +1088,11 @@
       //..fileEndOffset = cls.fileOffset
       ..isNonNullableByDefault = cls.enclosingLibrary.isNonNullableByDefault;
 
+    TypeDependency? typeDependency;
     if (hasTypeDependency) {
-      loader.registerTypeDependency(
-          constructor,
-          new TypeDependency(constructor, superConstructor, substitution,
-              copyReturnType: false));
+      typeDependency = new TypeDependency(
+          constructor, superConstructor, substitution,
+          copyReturnType: false);
     }
 
     Procedure? constructorTearOff = createConstructorTearOffProcedure(
@@ -1107,16 +1107,20 @@
       buildConstructorTearOffProcedure(constructorTearOff, constructor,
           classBuilder.cls, classBuilder.libraryBuilder);
     }
-    return new SyntheticSourceConstructorBuilder(
-        classBuilder, constructor, constructorTearOff,
-        // We pass on the original constructor and the cloned function nodes to
-        // ensure that the default values are computed and cloned for the
-        // outline. It is needed to make the default values a part of the
-        // outline for const constructors, and additionally it is required for
-        // a potential subclass using super initializing parameters that will
-        // required the cloning of the default values.
-        definingConstructor: superConstructorBuilder,
-        delayedDefaultValueCloner: delayedDefaultValueCloner);
+    SyntheticSourceConstructorBuilder constructorBuilder =
+        new SyntheticSourceConstructorBuilder(
+            classBuilder, constructor, constructorTearOff,
+            // We pass on the original constructor and the cloned function nodes
+            // to ensure that the default values are computed and cloned for the
+            // outline. It is needed to make the default values a part of the
+            // outline for const constructors, and additionally it is required
+            // for a potential subclass using super initializing parameters that
+            // will required the cloning of the default values.
+            definingConstructor: superConstructorBuilder,
+            delayedDefaultValueCloner: delayedDefaultValueCloner,
+            typeDependency: typeDependency);
+    loader.registerConstructorToBeInferred(constructor, constructorBuilder);
+    return constructorBuilder;
   }
 
   void finishSynthesizedParameters({bool forOutline = false}) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart b/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
index 15e3b74..25777e5 100644
--- a/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_constructor_builder.dart
@@ -26,7 +26,8 @@
 import '../kernel/constructor_tearoff_lowering.dart';
 import '../kernel/expression_generator_helper.dart';
 import '../kernel/hierarchy/class_member.dart' show ClassMember;
-import '../kernel/kernel_helper.dart' show DelayedDefaultValueCloner;
+import '../kernel/kernel_helper.dart'
+    show DelayedDefaultValueCloner, TypeDependency;
 import '../kernel/utils.dart'
     show isRedirectingGenerativeConstructorImplementation;
 import '../messages.dart'
@@ -52,6 +53,9 @@
 
 abstract class SourceConstructorBuilder
     implements ConstructorBuilder, SourceMemberBuilder {
+  /// Infers the types of any untyped initializing formals.
+  void inferFormalTypes(TypeEnvironment typeEnvironment);
+
   void addSuperParameterDefaultValueCloners(
       List<DelayedDefaultValueCloner> delayedDefaultValueCloners);
 }
@@ -224,7 +228,7 @@
     return _constructor;
   }
 
-  /// Infers the types of any untyped initializing formals.
+  @override
   void inferFormalTypes(TypeEnvironment typeEnvironment) {
     if (_hasFormalsInferred) return;
     if (formals != null) {
@@ -321,14 +325,8 @@
     ConstructorBuilder? superTargetBuilder =
         _computeSuperTargetBuilder(initializers);
 
-    if (superTargetBuilder is DeclaredSourceConstructorBuilder) {
+    if (superTargetBuilder is SourceConstructorBuilder) {
       superTargetBuilder.inferFormalTypes(typeEnvironment);
-    } else if (superTargetBuilder is SyntheticSourceConstructorBuilder) {
-      MemberBuilder? superTargetOriginBuilder =
-          superTargetBuilder._effectivelyDefiningConstructor;
-      if (superTargetOriginBuilder is DeclaredSourceConstructorBuilder) {
-        superTargetOriginBuilder.inferFormalTypes(typeEnvironment);
-      }
     }
 
     Constructor superTarget;
@@ -339,10 +337,9 @@
       superFormals = superTargetBuilder.formals!;
     } else if (superTargetBuilder is DillConstructorBuilder) {
       superTarget = superTargetBuilder.constructor;
+      superConstructorFunction = superTargetBuilder.function;
       if (superTargetBuilder is SyntheticSourceConstructorBuilder) {
         superFormals = superTargetBuilder.formals;
-      } else {
-        superConstructorFunction = superTargetBuilder.function;
       }
     } else {
       // The error in this case should be reported elsewhere. Here we perform a
@@ -354,7 +351,27 @@
     List<bool> positionalSuperFormalHasInitializer = [];
     Map<String, DartType?> namedSuperFormalType = {};
     Map<String, bool> namedSuperFormalHasInitializer = {};
-    if (superFormals != null) {
+    // TODO(johnniwinther): Clean this up when [VariableDeclaration] has a
+    // `hasDeclaredInitializer` flag.
+    if (superFormals != null && superConstructorFunction != null) {
+      for (VariableDeclaration formal
+          in superConstructorFunction.positionalParameters) {
+        positionalSuperFormalType.add(formal.type);
+      }
+      for (VariableDeclaration formal
+          in superConstructorFunction.namedParameters) {
+        namedSuperFormalType[formal.name!] = formal.type;
+      }
+      for (FormalParameterBuilder formal in superFormals) {
+        if (formal.isPositional) {
+          positionalSuperFormalHasInitializer
+              .add(formal.hasDeclaredInitializer);
+        } else {
+          namedSuperFormalHasInitializer[formal.name] =
+              formal.hasDeclaredInitializer;
+        }
+      }
+    } else if (superFormals != null) {
       for (FormalParameterBuilder formal in superFormals) {
         if (formal.isPositional) {
           positionalSuperFormalType.add(formal.variable?.type);
@@ -802,19 +819,34 @@
   /// the constructor that effectively defines this constructor.
   MemberBuilder? _immediatelyDefiningConstructor;
   DelayedDefaultValueCloner? _delayedDefaultValueCloner;
+  TypeDependency? _typeDependency;
 
   SyntheticSourceConstructorBuilder(SourceClassBuilder parent,
       Constructor constructor, Procedure? constructorTearOff,
       {MemberBuilder? definingConstructor,
-      DelayedDefaultValueCloner? delayedDefaultValueCloner})
+      DelayedDefaultValueCloner? delayedDefaultValueCloner,
+      TypeDependency? typeDependency})
       : _immediatelyDefiningConstructor = definingConstructor,
         _delayedDefaultValueCloner = delayedDefaultValueCloner,
+        _typeDependency = typeDependency,
         super(constructor, constructorTearOff, parent);
 
   @override
   SourceLibraryBuilder get libraryBuilder =>
       super.libraryBuilder as SourceLibraryBuilder;
 
+  @override
+  void inferFormalTypes(TypeEnvironment typeEnvironment) {
+    if (_immediatelyDefiningConstructor is SourceConstructorBuilder) {
+      (_immediatelyDefiningConstructor as SourceConstructorBuilder)
+          .inferFormalTypes(typeEnvironment);
+    }
+    if (_typeDependency != null) {
+      _typeDependency!.copyInferred();
+      _typeDependency = null;
+    }
+  }
+
   MemberBuilder? get _effectivelyDefiningConstructor {
     MemberBuilder? origin = _immediatelyDefiningConstructor;
     while (origin is SyntheticSourceConstructorBuilder) {
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 084733a..280a71d 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -974,7 +974,7 @@
   }
 
   void registerConstructorToBeInferred(
-      Constructor constructor, DeclaredSourceConstructorBuilder builder) {
+      Constructor constructor, SourceConstructorBuilder builder) {
     _typeInferenceEngine!.toBeInferred[constructor] = builder;
   }
 
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
index 3459623..0327d2c 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
@@ -113,14 +113,14 @@
   /// This is represented as a map from a constructor to its library
   /// builder because the builder is used to report errors due to cyclic
   /// inference dependencies.
-  final Map<Constructor, DeclaredSourceConstructorBuilder> toBeInferred = {};
+  final Map<Constructor, SourceConstructorBuilder> toBeInferred = {};
 
   /// A map containing constructors in the process of being inferred.
   ///
   /// This is used to detect cyclic inference dependencies.  It is represented
   /// as a map from a constructor to its library builder because the builder
   /// is used to report errors.
-  final Map<Constructor, DeclaredSourceConstructorBuilder> beingInferred = {};
+  final Map<Constructor, SourceConstructorBuilder> beingInferred = {};
 
   final Map<Member, TypeDependency> typeDependencies = {};
 
@@ -144,7 +144,7 @@
   void finishTopLevelInitializingFormals() {
     // Field types have all been inferred so we don't need to guard against
     // cyclic dependency.
-    for (DeclaredSourceConstructorBuilder builder in toBeInferred.values) {
+    for (SourceConstructorBuilder builder in toBeInferred.values) {
       builder.inferFormalTypes(typeSchemaEnvironment);
     }
     toBeInferred.clear();
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 1f1f6b2..a0e094b 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -388,8 +388,7 @@
 
   @override
   void inferConstructorParameterTypes(Constructor target) {
-    DeclaredSourceConstructorBuilder? constructor =
-        engine.beingInferred[target];
+    SourceConstructorBuilder? constructor = engine.beingInferred[target];
     if (constructor != null) {
       // There is a cyclic dependency where inferring the types of the
       // initializing formals of a constructor required us to infer the
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart b/pkg/front_end/testcases/super_parameters/issue48708.dart
new file mode 100644
index 0000000..5b2395d
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2022, 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.
+
+class A {}
+
+class Mixin {}
+
+abstract class B<D> {
+  B({
+    required this.field,
+  });
+
+  final D field;
+}
+
+class C extends B<A> with Mixin {
+  C({
+    required super.field,
+  });
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart.strong.expect b/pkg/front_end/testcases/super_parameters/issue48708.dart.strong.expect
new file mode 100644
index 0000000..05ade25
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart.strong.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class Mixin extends core::Object {
+  synthetic constructor •() → self::Mixin
+    : super core::Object::•()
+    ;
+}
+abstract class B<D extends core::Object? = dynamic> extends core::Object {
+  final field self::B::D% field;
+  constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
+    : self::B::field = field, super core::Object::•()
+    ;
+}
+abstract class _C&B&Mixin = self::B<self::A> with self::Mixin /*isAnonymousMixin*/  {
+  synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
+    : super self::B::•(field: field)
+    ;
+}
+class C extends self::_C&B&Mixin {
+  constructor •({required self::A field = #C1}) → self::C
+    : super self::_C&B&Mixin::•(field: field)
+    ;
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart.strong.transformed.expect b/pkg/front_end/testcases/super_parameters/issue48708.dart.strong.transformed.expect
new file mode 100644
index 0000000..f71d862
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart.strong.transformed.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class Mixin extends core::Object {
+  synthetic constructor •() → self::Mixin
+    : super core::Object::•()
+    ;
+}
+abstract class B<D extends core::Object? = dynamic> extends core::Object {
+  final field self::B::D% field;
+  constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
+    : self::B::field = field, super core::Object::•()
+    ;
+}
+abstract class _C&B&Mixin extends self::B<self::A> implements self::Mixin /*isAnonymousMixin,isEliminatedMixin*/  {
+  synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
+    : super self::B::•(field: field)
+    ;
+}
+class C extends self::_C&B&Mixin {
+  constructor •({required self::A field = #C1}) → self::C
+    : super self::_C&B&Mixin::•(field: field)
+    ;
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart.textual_outline.expect b/pkg/front_end/testcases/super_parameters/issue48708.dart.textual_outline.expect
new file mode 100644
index 0000000..963c4ca
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart.textual_outline.expect
@@ -0,0 +1,18 @@
+class A {}
+
+class Mixin {}
+
+abstract class B<D> {
+  B({
+    required this.field,
+  });
+  final D field;
+}
+
+class C extends B<A> with Mixin {
+  C({
+    required super.field,
+  });
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/super_parameters/issue48708.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..429733e
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart.textual_outline_modelled.expect
@@ -0,0 +1,18 @@
+abstract class B<D> {
+  B({
+    required this.field,
+  });
+  final D field;
+}
+
+class A {}
+
+class C extends B<A> with Mixin {
+  C({
+    required super.field,
+  });
+}
+
+class Mixin {}
+
+main() {}
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.expect b/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.expect
new file mode 100644
index 0000000..05ade25
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class Mixin extends core::Object {
+  synthetic constructor •() → self::Mixin
+    : super core::Object::•()
+    ;
+}
+abstract class B<D extends core::Object? = dynamic> extends core::Object {
+  final field self::B::D% field;
+  constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
+    : self::B::field = field, super core::Object::•()
+    ;
+}
+abstract class _C&B&Mixin = self::B<self::A> with self::Mixin /*isAnonymousMixin*/  {
+  synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
+    : super self::B::•(field: field)
+    ;
+}
+class C extends self::_C&B&Mixin {
+  constructor •({required self::A field = #C1}) → self::C
+    : super self::_C&B&Mixin::•(field: field)
+    ;
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.modular.expect b/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.modular.expect
new file mode 100644
index 0000000..05ade25
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.modular.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class Mixin extends core::Object {
+  synthetic constructor •() → self::Mixin
+    : super core::Object::•()
+    ;
+}
+abstract class B<D extends core::Object? = dynamic> extends core::Object {
+  final field self::B::D% field;
+  constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
+    : self::B::field = field, super core::Object::•()
+    ;
+}
+abstract class _C&B&Mixin = self::B<self::A> with self::Mixin /*isAnonymousMixin*/  {
+  synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
+    : super self::B::•(field: field)
+    ;
+}
+class C extends self::_C&B&Mixin {
+  constructor •({required self::A field = #C1}) → self::C
+    : super self::_C&B&Mixin::•(field: field)
+    ;
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.outline.expect b/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.outline.expect
new file mode 100644
index 0000000..4a28ead1
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.outline.expect
@@ -0,0 +1,28 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+}
+class Mixin extends core::Object {
+  synthetic constructor •() → self::Mixin
+    ;
+}
+abstract class B<D extends core::Object? = dynamic> extends core::Object {
+  final field self::B::D% field;
+  constructor •({required self::B::D% field}) → self::B<self::B::D%>
+    ;
+}
+abstract class _C&B&Mixin = self::B<self::A> with self::Mixin /*isAnonymousMixin*/  {
+  synthetic constructor •({self::A field}) → self::_C&B&Mixin
+    : super self::B::•(field: field)
+    ;
+}
+class C extends self::_C&B&Mixin {
+  constructor •({required self::A field}) → self::C
+    ;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.transformed.expect b/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.transformed.expect
new file mode 100644
index 0000000..f71d862
--- /dev/null
+++ b/pkg/front_end/testcases/super_parameters/issue48708.dart.weak.transformed.expect
@@ -0,0 +1,35 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class Mixin extends core::Object {
+  synthetic constructor •() → self::Mixin
+    : super core::Object::•()
+    ;
+}
+abstract class B<D extends core::Object? = dynamic> extends core::Object {
+  final field self::B::D% field;
+  constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
+    : self::B::field = field, super core::Object::•()
+    ;
+}
+abstract class _C&B&Mixin extends self::B<self::A> implements self::Mixin /*isAnonymousMixin,isEliminatedMixin*/  {
+  synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
+    : super self::B::•(field: field)
+    ;
+}
+class C extends self::_C&B&Mixin {
+  constructor •({required self::A field = #C1}) → self::C
+    : super self::_C&B&Mixin::•(field: field)
+    ;
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}