[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