[fasta] Check type arguments in method invocations while doing inference

Fixes #34899

Bug: http://dartbug.com/34899
Change-Id: I4db7612925d0938cc6ec381c4de8073d50d61aac
Reviewed-on: https://dart-review.googlesource.com/c/81266
Commit-Queue: Peter von der Ahé <ahe@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
Auto-Submit: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 4086c43..a3d119d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -215,11 +215,6 @@
   /// invocations are to be resolved in a separate step.
   final List<Expression> redirectingFactoryInvocations = <Expression>[];
 
-  /// In some cases checks of the type arguments in method invocations can't be
-  /// done right away, because some type arguments within the receiver
-  /// expression are yet to be inferred.
-  final List<MethodInvocation> delayedBoundsChecks = <MethodInvocation>[];
-
   BodyBuilder(
       this.library,
       this.member,
@@ -563,11 +558,6 @@
     }
 
     resolveRedirectingFactoryTargets();
-    for (MethodInvocation node in delayedBoundsChecks) {
-      library.checkBoundsInMethodInvocation(
-          node, classBuilder?.target, typeEnvironment);
-    }
-    delayedBoundsChecks.clear();
   }
 
   @override
@@ -838,11 +828,6 @@
     }
 
     resolveRedirectingFactoryTargets();
-    for (MethodInvocation node in delayedBoundsChecks) {
-      library.checkBoundsInMethodInvocation(
-          node, classBuilder?.target, typeEnvironment);
-    }
-    delayedBoundsChecks.clear();
   }
 
   void resolveRedirectingFactoryTargets() {
@@ -4571,7 +4556,6 @@
           receiver, callName, forest.castArguments(arguments),
           isImplicitCall: true)
         ..fileOffset = forest.readOffset(arguments);
-      delayedBoundsChecks.add(node);
       return node;
     }
 
@@ -4594,7 +4578,6 @@
           receiver, name, forest.castArguments(arguments),
           isImplicitCall: isImplicitCall, interfaceTarget: interfaceTarget)
         ..fileOffset = offset;
-      delayedBoundsChecks.add(node);
       return node;
     }
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index be9f445..7aaef01 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -876,18 +876,10 @@
         }
       }
     }
-    bool hadExplicitTypeArguments =
-        getExplicitTypeArguments(node.arguments) != null;
     var inferenceResult = inferrer.inferMethodInvocation(
         node, node.receiver, node.fileOffset, node._isImplicitCall, typeContext,
         desugaredInvocation: node);
     node.inferredType = inferenceResult.type;
-    KernelLibraryBuilder inferrerLibrary = inferrer.library;
-    if (!hadExplicitTypeArguments && inferrerLibrary is KernelLibraryBuilder) {
-      inferrerLibrary.checkBoundsInMethodInvocation(
-          node, inferrer.thisType?.classNode, inferrer.typeSchemaEnvironment,
-          inferred: true);
-    }
   }
 
   void visitNamedFunctionExpressionJudgment(
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
index b836e24..982bed4 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
@@ -25,7 +25,6 @@
         ListLiteral,
         MapLiteral,
         Member,
-        MethodInvocation,
         Name,
         Procedure,
         ProcedureKind,
@@ -94,6 +93,8 @@
 import '../source/source_library_builder.dart'
     show DeclarationBuilder, SourceLibraryBuilder;
 
+import '../type_inference/type_inferrer.dart' show TypeInferrerImpl;
+
 import 'kernel_builder.dart'
     show
         AccessErrorBuilder,
@@ -1814,24 +1815,16 @@
   }
 
   void checkBoundsInMethodInvocation(
-      MethodInvocation node, Class thisClass, TypeEnvironment typeEnvironment,
+      DartType receiverType,
+      TypeEnvironment typeEnvironment,
+      TypeInferrerImpl typeInferrer,
+      Name name,
+      Member interfaceTarget,
+      Arguments arguments,
+      int offset,
       {bool inferred = false}) {
     if (!loader.target.strongMode) return;
-    if (node.arguments.types.isEmpty) return;
-    DartType savedThisType = typeEnvironment.thisType;
-    if (thisClass != null) {
-      typeEnvironment.thisType = new InterfaceType(
-          thisClass,
-          thisClass.typeParameters
-              .map((p) => new TypeParameterType(p))
-              .toList());
-    }
-    DartType receiverType;
-    try {
-      receiverType = node.receiver.getStaticType(typeEnvironment);
-    } finally {
-      typeEnvironment.thisType = savedThisType;
-    }
+    if (arguments.types.isEmpty) return;
     Class klass;
     List<DartType> klassArguments;
     if (receiverType is InterfaceType) {
@@ -1845,15 +1838,14 @@
       substitutionMap[klass.typeParameters[i]] = klassArguments[i];
     }
     // TODO(dmitryas): Find a better way than relying on [interfaceTarget].
-    Member method =
-        typeEnvironment.hierarchy.getDispatchTarget(klass, node.name) ??
-            node.interfaceTarget;
+    Member method = typeEnvironment.hierarchy.getDispatchTarget(klass, name) ??
+        interfaceTarget;
     if (method == null || method is! Procedure) {
       return;
     }
     List<TypeParameter> methodParameters = method.function.typeParameters;
     // The error is to be reported elsewhere.
-    if (methodParameters.length != node.arguments.types.length) return;
+    if (methodParameters.length != arguments.types.length) return;
     List<TypeParameter> instantiatedMethodParameters =
         new List<TypeParameter>.filled(methodParameters.length, null);
     for (int i = 0; i < instantiatedMethodParameters.length; ++i) {
@@ -1867,7 +1859,7 @@
           substitute(methodParameters[i].bound, substitutionMap);
     }
     List<Object> violations = typeEnvironment.findBoundViolationsElementwise(
-        instantiatedMethodParameters, node.arguments.types,
+        instantiatedMethodParameters, arguments.types,
         typedefInstantiations: typedefInstantiations);
     if (violations != null) {
       String targetName = "${klass.name}";
@@ -1878,7 +1870,7 @@
         }
         targetName += ">";
       }
-      targetName += "::${node.name.name}";
+      targetName += "::${name.name}";
       for (int i = 0; i < violations.length; i += 3) {
         DartType argument = violations[i];
         TypeParameter variable = violations[i + 1];
@@ -1906,7 +1898,7 @@
           }
         }
 
-        reportBoundViolation(message, node.fileOffset, variable);
+        reportBoundViolation(message, offset, variable);
       }
     }
   }
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 5b2dfa6..6c4a2c6 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
@@ -1603,6 +1603,7 @@
             interfaceMember, receiverType);
     var calleeType = getCalleeType(interfaceMember, receiverType);
     var functionType = getCalleeFunctionType(calleeType, !isImplicitCall);
+
     if (interfaceMember != null &&
         calleeType is! DynamicType &&
         calleeType != coreTypes.functionClass.rawType &&
@@ -1641,6 +1642,39 @@
         parent?.replaceChild(expression, errorNode);
       }
     }
+
+    // If [arguments] were inferred, check them.
+    // TODO(dmitryas): Figure out why [library] is sometimes null.
+    if (library != null) {
+      // [actualReceiverType], [interfaceTarget], and [actualMethodName] below
+      // are for a workaround for the cases like the following:
+      //
+      //     class C1 { var f = new C2(); }
+      //     class C2 { int call<X extends num>(X x) => 42; }
+      //     main() { C1 c = new C1(); c.f("foobar"); }
+      DartType actualReceiverType;
+      Member interfaceTarget;
+      Name actualMethodName;
+      if (calleeType is InterfaceType) {
+        actualReceiverType = calleeType;
+        interfaceTarget = null;
+        actualMethodName = callName;
+      } else {
+        actualReceiverType = receiverType;
+        interfaceTarget = interfaceMember is Member ? interfaceMember : null;
+        actualMethodName = methodName;
+      }
+      library.checkBoundsInMethodInvocation(
+          actualReceiverType,
+          typeSchemaEnvironment,
+          this,
+          actualMethodName,
+          interfaceTarget,
+          arguments,
+          fileOffset,
+          inferred: getExplicitTypeArguments(arguments) == null);
+    }
+
     return new ExpressionInferenceResult(null, inferredType);
   }
 
diff --git a/pkg/front_end/testcases/inference/generic_methods_correctly_recognize_generic_upper_bound.dart.strong.expect b/pkg/front_end/testcases/inference/generic_methods_correctly_recognize_generic_upper_bound.dart.strong.expect
index 8c847fd..f83187e 100644
--- a/pkg/front_end/testcases/inference/generic_methods_correctly_recognize_generic_upper_bound.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/generic_methods_correctly_recognize_generic_upper_bound.dart.strong.expect
@@ -4,11 +4,6 @@
 // Try specifying type arguments explicitly so that they conform to the bounds.
 //       . /*error:COULD_NOT_INFER*/ /*@typeArgs=int*/ /*@target=Foo::method*/ method(
 //                                                                             ^
-//
-// pkg/front_end/testcases/inference/generic_methods_correctly_recognize_generic_upper_bound.dart:26:77: Error: Type argument 'dart.core::int' violates the corresponding type variable bound of 'Foo<dart.core::String>::method'.
-// Try changing type arguments so that they conform to the bounds.
-//       . /*error:COULD_NOT_INFER*/ /*@typeArgs=int*/ /*@target=Foo::method*/ method(
-//                                                                             ^
 
 // Unhandled errors:
 //
@@ -16,11 +11,6 @@
 // Try specifying type arguments explicitly so that they conform to the bounds.
 //       . /*error:COULD_NOT_INFER*/ /*@typeArgs=int*/ /*@target=Foo::method*/ method(
 //                                                                             ^
-//
-// pkg/front_end/testcases/inference/generic_methods_correctly_recognize_generic_upper_bound.dart:26:77: Error: Type argument 'dart.core::int' violates the corresponding type variable bound of 'Foo<dart.core::String>::method'.
-// Try changing type arguments so that they conform to the bounds.
-//       . /*error:COULD_NOT_INFER*/ /*@typeArgs=int*/ /*@target=Foo::method*/ method(
-//                                                                             ^
 
 library test;
 import self as self;
diff --git a/pkg/front_end/testcases/issue34899.dart.strong.expect b/pkg/front_end/testcases/issue34899.dart.strong.expect
new file mode 100644
index 0000000..67daf6b
--- /dev/null
+++ b/pkg/front_end/testcases/issue34899.dart.strong.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+class Foo<T extends core::Object = dynamic> extends core::Object {
+  final field () → asy::Future<dynamic> quux;
+  generic-covariant-impl field self::Foo::T t;
+  constructor •(() → asy::Future<dynamic> quux, self::Foo::T t) → self::Foo<self::Foo::T>
+    : self::Foo::quux = quux, self::Foo::t = t, super core::Object::•()
+    ;
+  method call() → asy::Future<self::Foo::T>
+    return this.{self::Foo::quux}().{asy::Future::then}<self::Foo::T>((dynamic _) → self::Foo::T => this.{self::Foo::t});
+}
+class Bar extends core::Object {
+  field self::Foo<self::Baz> qux = null;
+  synthetic constructor •() → self::Bar
+    : super core::Object::•()
+    ;
+  method quuz() → asy::Future<void>
+    return this.{self::Bar::qux}().{asy::Future::then}<self::Grault>((self::Baz baz) → self::Grault => this.{self::Bar::corge}(baz)).{asy::Future::then}<void>((self::Grault grault) → void => this.{self::Bar::garply}(grault));
+  method corge(self::Baz baz) → self::Grault
+    return null;
+  method garply(self::Grault grault) → void {}
+}
+class Baz extends core::Object {
+  synthetic constructor •() → self::Baz
+    : super core::Object::•()
+    ;
+}
+class Grault extends core::Object {
+  synthetic constructor •() → self::Grault
+    : super core::Object::•()
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index ad1b919..f42496b 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -85,7 +85,7 @@
 instantiate_to_bound/typedef_super_bounded_type: Fail # Issue 33444
 invalid_type: TypeCheckError
 invocations: Fail
-issue34899: Crash
+issue34899: TypeCheckError
 literals: Fail
 map: Fail
 micro: Fail