Perform substitution on type argument/parameter bounds.

Fixes regression #34528. Introduces a new test to mitigate future
regressions.

Closes #34528.

Change-Id: Iaf0a8f44db2a8b4a8151c391ddb8dff69910fe3a
Reviewed-on: https://dart-review.googlesource.com/75840
Commit-Queue: Daniel Hillerström <hillerstrom@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Erik Ernst <eernst@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index 71abcf1..ba3dc08 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -6399,29 +6399,25 @@
         r"""Try removing the body, or not making this a redirecting constructor.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<
-    Message Function(
-        String name,
-        DartType _type,
-        DartType
-            _type2)> templateRedirectingFactoryIncompatibleBounds = const Template<
-        Message Function(String name, DartType _type, DartType _type2)>(
-    messageTemplate:
-        r"""The type variable '#name' has bound '#type' but the context expects a type variable with bound '#type2'.""",
-    tipTemplate: r"""Try redirecting to a different constructor.""",
-    withArguments: _withArgumentsRedirectingFactoryIncompatibleBounds);
+const Template<Message Function(DartType _type, DartType _type2)>
+    templateRedirectingFactoryIncompatibleTypeArgument =
+    const Template<Message Function(DartType _type, DartType _type2)>(
+        messageTemplate: r"""The type '#type' doesn't extend '#type2'.""",
+        tipTemplate: r"""Try using a different type as argument.""",
+        withArguments:
+            _withArgumentsRedirectingFactoryIncompatibleTypeArgument);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String name, DartType _type, DartType _type2)>
-    codeRedirectingFactoryIncompatibleBounds =
-    const Code<Message Function(String name, DartType _type, DartType _type2)>(
-        "RedirectingFactoryIncompatibleBounds",
-        templateRedirectingFactoryIncompatibleBounds,
-        analyzerCode: "REDIRECT_TO_INVALID_RETURN_TYPE");
+const Code<Message Function(DartType _type, DartType _type2)>
+    codeRedirectingFactoryIncompatibleTypeArgument =
+    const Code<Message Function(DartType _type, DartType _type2)>(
+        "RedirectingFactoryIncompatibleTypeArgument",
+        templateRedirectingFactoryIncompatibleTypeArgument,
+        analyzerCode: "TYPE_ARGUMENT_NOT_MATCHING_BOUNDS");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsRedirectingFactoryIncompatibleBounds(
-    String name, DartType _type, DartType _type2) {
+Message _withArgumentsRedirectingFactoryIncompatibleTypeArgument(
+    DartType _type, DartType _type2) {
   NameSystem nameSystem = new NameSystem();
   StringBuffer buffer;
   buffer = new StringBuffer();
@@ -6432,11 +6428,10 @@
   new Printer(buffer, syntheticNames: nameSystem).writeNode(_type2);
   String type2 = '$buffer';
 
-  return new Message(codeRedirectingFactoryIncompatibleBounds,
-      message:
-          """The type variable '${name}' has bound '${type}' but the context expects a type variable with bound '${type2}'.""",
-      tip: """Try redirecting to a different constructor.""",
-      arguments: {'name': name, 'type': _type, 'type2': _type2});
+  return new Message(codeRedirectingFactoryIncompatibleTypeArgument,
+      message: """The type '${type}' doesn't extend '${type2}'.""",
+      tip: """Try using a different type as argument.""",
+      arguments: {'type': _type, 'type2': _type2});
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
index cbb9b93..3ad8c9c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
@@ -70,7 +70,7 @@
         templateOverrideTypeMismatchParameter,
         templateOverrideTypeMismatchReturnType,
         templateOverrideTypeVariablesMismatch,
-        templateRedirectingFactoryIncompatibleBounds,
+        templateRedirectingFactoryIncompatibleTypeArgument,
         templateRedirectingFactoryInvalidNamedParameterType,
         templateRedirectingFactoryInvalidPositionalParameterType,
         templateRedirectingFactoryMissingNamedParameter,
@@ -103,7 +103,8 @@
         Scope,
         TypeVariableBuilder;
 
-import 'redirecting_factory_body.dart' show RedirectingFactoryBody;
+import 'redirecting_factory_body.dart'
+    show getRedirectingFactoryBody, RedirectingFactoryBody;
 
 import 'kernel_target.dart' show KernelTarget;
 
@@ -1190,9 +1191,9 @@
 
   // Computes the function type of a given redirection target. Returns [null] if
   // the type of actual target could not be computed.
-  FunctionType computeRedirecteeType(
-      ConstructorReferenceBuilder redirectionTarget,
+  FunctionType computeRedirecteeType(KernelRedirectingFactoryBuilder factory,
       TypeEnvironment typeEnvironment) {
+    ConstructorReferenceBuilder redirectionTarget = factory.redirectionTarget;
     FunctionNode target;
     bool isConstructor = false;
     Class targetClass; // Used when the redirection target is a constructor.
@@ -1225,51 +1226,43 @@
       return null;
     }
 
-    FunctionType inferredType = target.functionType;
-    if (redirectionTarget.typeArguments != null &&
-        inferredType.typeParameters.length !=
-            redirectionTarget.typeArguments.length) {
+    List<DartType> typeArguments =
+        getRedirectingFactoryBody(factory.target).typeArguments;
+    FunctionType targetFunctionType = target.functionType;
+    if (typeArguments != null &&
+        targetFunctionType.typeParameters.length != typeArguments.length) {
       addProblem(
           templateTypeArgumentMismatch
-              .withArguments(inferredType.typeParameters.length),
+              .withArguments(targetFunctionType.typeParameters.length),
           redirectionTarget.charOffset,
           noLength);
       return null;
     }
 
     // Compute the substitution of the target class type parameters if
-    // [redirectionTarget] has any type arguments. Any built type arguments are
-    // stored in [typeArguments] for later use.
+    // [redirectionTarget] has any type arguments.
     Substitution substitution;
-    List<DartType> typeArguments;
-    if (redirectionTarget.typeArguments != null &&
-        redirectionTarget.typeArguments.length > 0) {
-      typeArguments = new List<DartType>();
-      for (var i = 0; i < inferredType.typeParameters.length; i++) {
-        var typeParameter = inferredType.typeParameters[i];
-        var typeArgument = redirectionTarget.typeArguments[i].build(library);
+    bool hasProblem = false;
+    if (typeArguments != null && typeArguments.length > 0) {
+      substitution = Substitution.fromPairs(
+          targetFunctionType.typeParameters, typeArguments);
+      for (int i = 0; i < targetFunctionType.typeParameters.length; i++) {
+        TypeParameter typeParameter = targetFunctionType.typeParameters[i];
+        DartType typeParameterBound =
+            substitution.substituteType(typeParameter.bound);
+        DartType typeArgument = typeArguments[i];
         // Check whether the [typeArgument] respects the bounds of [typeParameter].
-        if (typeArgument is TypeParameterType) {
-          if (!typeEnvironment.isSubtypeOf(
-              typeArgument.bound, typeParameter.bound)) {
-            // TODO(hillerstrom): Use dmitrays' error message once his "bounds
-            // checking" CL has landed.
-            addProblem(
-                templateRedirectingFactoryIncompatibleBounds.withArguments(
-                    typeArgument.parameter.name,
-                    typeArgument.bound,
-                    typeParameter.bound),
-                redirectionTarget.charOffset,
-                noLength);
-            return null;
-          }
+        if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound)) {
+          addProblem(
+              templateRedirectingFactoryIncompatibleTypeArgument.withArguments(
+                  typeArgument, typeParameterBound),
+              redirectionTarget.charOffset,
+              noLength);
+          hasProblem = true;
         }
-        typeArguments.add(typeArgument);
       }
-      substitution =
-          Substitution.fromPairs(inferredType.typeParameters, typeArguments);
-    } else if (redirectionTarget.typeArguments == null &&
-        inferredType.typeParameters.length > 0) {
+    } else if (typeArguments == null &&
+        targetFunctionType.typeParameters.length > 0) {
       // TODO(hillerstrom): In this case, we need to perform type inference on
       // the redirectee to obtain actual type arguments which would allow the
       // following program to type check:
@@ -1286,21 +1279,21 @@
 
     FunctionType redirecteeType;
     // If the target is a constructor then we need to patch the return type of
-    // the inferred type, because the type inferrer always infers the return
-    // type to be "void", whereas the inferred return type of a factory is its
-    // enclosing class. TODO(hillerstrom): It may be worthwhile to change the
-    // typing of constructors such that the return type is its enclosing class.
+    // [targetFunctionType], because constructors always have return type to be
+    // "void", whereas the return type of a factory is its enclosing
+    // class. TODO(hillerstrom): It may be worthwhile to change the typing of
+    // constructors such that the return type is its enclosing class.
     if (isConstructor) {
       DartType returnType =
           new InterfaceType(targetClass, typeArguments ?? const <DartType>[]);
 
       redirecteeType = new FunctionType(
-          inferredType.positionalParameters, returnType,
-          namedParameters: inferredType.namedParameters,
-          typeParameters: inferredType.typeParameters,
-          requiredParameterCount: inferredType.requiredParameterCount);
+          targetFunctionType.positionalParameters, returnType,
+          namedParameters: targetFunctionType.namedParameters,
+          typeParameters: targetFunctionType.typeParameters,
+          requiredParameterCount: targetFunctionType.requiredParameterCount);
     } else {
-      redirecteeType = inferredType;
+      redirecteeType = targetFunctionType;
     }
 
     // Substitute if necessary.
@@ -1309,7 +1302,7 @@
         : (substitution.substituteType(redirecteeType.withoutTypeParameters)
             as FunctionType);
 
-    return redirecteeType;
+    return hasProblem ? null : redirecteeType;
   }
 
   String computeRedirecteeName(ConstructorReferenceBuilder redirectionTarget) {
@@ -1329,7 +1322,7 @@
     FunctionType factoryType =
         factory.procedure.function.functionType.withoutTypeParameters;
     FunctionType redirecteeType =
-        computeRedirecteeType(factory.redirectionTarget, typeEnvironment);
+        computeRedirecteeType(factory, typeEnvironment);
 
     // TODO(hillerstrom): It would be preferable to know whether a failure
     // happened during [_computeRedirecteeType].
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 9f85ec7..2c8472f 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -2978,15 +2978,13 @@
         A.g(int x, int y) {}
       }
 
-RedirectingFactoryIncompatibleBounds:
-  template: "The type variable '#name' has bound '#type' but the context expects a type variable with bound '#type2'."
-  tip: "Try redirecting to a different constructor."
-  analyzerCode: REDIRECT_TO_INVALID_RETURN_TYPE
+RedirectingFactoryIncompatibleTypeArgument:
+  template: "The type '#type' doesn't extend '#type2'."
+  tip: "Try using a different type as argument."
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
   script:
     - >-
       class A<T extends int> {
-         factory A() = B<T>;
+         factory A() = B<T, int>;
       }
-      class B<S extends String> implements A<int> {
-         B();
-      }
+      class B<T extends int, S extends String> implements A<T> {}
diff --git a/tests/co19/co19-kernel.status b/tests/co19/co19-kernel.status
index e276dd0..5ee4078 100644
--- a/tests/co19/co19-kernel.status
+++ b/tests/co19/co19-kernel.status
@@ -62,6 +62,7 @@
 Language/Classes/Constructors/Constant_Constructors/initializer_not_a_constant_t01: MissingCompileTimeError
 Language/Classes/Constructors/Constant_Constructors/initializer_not_a_constant_t02: MissingCompileTimeError
 Language/Classes/Constructors/Constant_Constructors/invalid_constant_initializer_t02: MissingCompileTimeError
+Language/Classes/Constructors/Factories/arguments_type_t01: CompileTimeError # Expects a warning, but this is an error in Dart 2
 Language/Classes/Constructors/Factories/function_type_t01: CompileTimeError # Expects a warning but this is an error in Dart 2
 Language/Classes/Constructors/Generative_Constructors/static_type_t01: CompileTimeError
 Language/Classes/Constructors/name_t01: MissingCompileTimeError
@@ -557,6 +558,7 @@
 Language/Expressions/Instance_Creation/New/execution_t13: CompileTimeError
 Language/Expressions/Instance_Creation/New/named_constructor_t01: CompileTimeError
 Language/Expressions/Instance_Creation/New/named_constructor_t02: CompileTimeError
+Language/Expressions/Instance_Creation/New/redirecting_factory_constructor_t01: CompileTimeError # Issue 33308
 Language/Expressions/Instance_Creation/New/redirecting_factory_constructor_t02: CompileTimeError
 Language/Expressions/Instance_Creation/New/type_argument_t01: CompileTimeError
 Language/Expressions/Instance_Creation/New/type_t01: CompileTimeError
diff --git a/tests/co19_2/co19_2-kernel.status b/tests/co19_2/co19_2-kernel.status
index a7fe5f7..7c11557 100644
--- a/tests/co19_2/co19_2-kernel.status
+++ b/tests/co19_2/co19_2-kernel.status
@@ -14,7 +14,6 @@
 Language/Classes/Abstract_Instance_Members/same_name_static_method_in_superclass_t05: MissingCompileTimeError # Legal, see #33235
 Language/Classes/Abstract_Instance_Members/same_name_static_method_in_superclass_t06: MissingCompileTimeError # Legal, see #33235
 Language/Classes/Constructors/Constant_Constructors/potentially_constant_expression_t01: MissingCompileTimeError # Issue 34192
-Language/Classes/Constructors/Factories/arguments_type_t01: MissingCompileTimeError # Issue 33308
 Language/Classes/Constructors/Generative_Constructors/final_variables_t01: CompileTimeError # Expects a warning, but this is an error in Dart 2
 Language/Classes/Constructors/Generative_Constructors/initializers_t15: CompileTimeError
 Language/Classes/Constructors/name_t01: MissingCompileTimeError # Legal, see #33235
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index 06730ab..8d5af69 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -237,7 +237,6 @@
 duplicate_export_collision_test/01: MissingCompileTimeError # Issue 12916
 f_bounded_quantification_test/01: MissingCompileTimeError # Issue 33308
 f_bounded_quantification_test/02: MissingCompileTimeError # Issue 33308
-factory4_test/00: MissingCompileTimeError # Issue 31590
 generic_methods_bounds_test/01: MissingCompileTimeError # Issue 33308
 generic_methods_recursive_bound_test/02: MissingCompileTimeError # Issue 33308
 issue31596_super_test/02: MissingCompileTimeError # Issue 31596
@@ -1289,6 +1288,7 @@
 
 [ $fasta && $strong ]
 issue18628_2_test/01: MissingCompileTimeError
+redirecting_factory_bounds_test/02: MissingCompileTimeError # Related to issue 33308
 type_promotion_logical_and_test/01: MissingCompileTimeError
 
 [ $fasta && !$strong ]
diff --git a/tests/language_2/redirecting_factory_bounds_test.dart b/tests/language_2/redirecting_factory_bounds_test.dart
new file mode 100644
index 0000000..e8dbabf
--- /dev/null
+++ b/tests/language_2/redirecting_factory_bounds_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2018, 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.
+
+// Bounds checking on redirecting factories.
+
+class Foo<T> {}
+
+class Baz<T> {}
+
+class Foobar<T> implements Foo<T> {}
+
+class Bar<
+          T // A comment to prevent dartfmt from joining the lines.
+          extends Foo<T>      //# 00: ok
+          extends Baz<Foo<T>> //# 01: compile-time error
+          extends Foobar<T>   //# 02: compile-time error
+  > {
+  Bar.named();
+  factory Bar() = Qux<T>;
+}
+
+class Qux<
+          T // A comment to prevent dartfmt from joining the lines.
+          extends Foo<T> //# 00: continued
+          extends Foo<T> //# 01: continued
+          extends Foo<T> //# 02: continued
+         > extends Bar<T> {
+  Qux() : super.named();
+}
+
+
+
+class A<T extends int> {
+  factory A() = B<
+                  T // A comment to prevent dartfmt from joining the lines.
+                  , int //# 03: compile-time error
+                  , String //# 04: ok
+                 >;
+}
+
+class B<T extends int
+        , S extends String //# 03: continued
+        , S extends String //# 04: continued
+       > implements A<T> {}
+
+void main() {
+  new Bar<Null>();
+  new A<int>();
+}
diff --git a/tests/lib_2/lib_2_kernel.status b/tests/lib_2/lib_2_kernel.status
index 72e6ad5..f39040a 100644
--- a/tests/lib_2/lib_2_kernel.status
+++ b/tests/lib_2/lib_2_kernel.status
@@ -35,6 +35,8 @@
 mirrors/generic_interface_test/01: MissingCompileTimeError
 mirrors/metadata_allowed_values_test/13: MissingCompileTimeError
 mirrors/metadata_allowed_values_test/14: MissingCompileTimeError
+mirrors/redirecting_factory_test/01: CompileTimeError # Issue 33308
+mirrors/redirecting_factory_test/none: CompileTimeError # Issue 33308
 
 [ $arch == simarm64 && $strong && ($compiler == dartk || $compiler == dartkb) ]
 isolate/mint_maker_test: Timeout # Please triage.