Add type inference for super method invocations.

I implemented the correct logic for both SuperMethodInvocation and
DirectMethodInvocation, since there are TODO comments in the code
indicating that the former will be replaced by the latter when
possible.

R=scheglov@google.com

Review-Url: https://codereview.chromium.org/2954403002 .
This commit is contained in:
Paul Berry 2017-06-26 12:42:44 -07:00
parent 2e55f22810
commit e24eab66d9
15 changed files with 165 additions and 53 deletions

View file

@ -897,13 +897,15 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
if (!target.isAccessor) {
if (areArgumentsCompatible(target.function, node.arguments)) {
Expression result = new KernelDirectMethodInvocation(
new ThisExpression()..fileOffset = node.fileOffset,
new KernelThisExpression()..fileOffset = node.fileOffset,
target,
node.arguments);
node.arguments)
..fileOffset = node.fileOffset;
// TODO(ahe): Use [DirectMethodInvocation] when possible, that is,
// remove the next line:
result =
new KernelSuperMethodInvocation(node.name, node.arguments, null);
new KernelSuperMethodInvocation(node.name, node.arguments, target)
..fileOffset = node.fileOffset;
return result;
} else {
isNoSuchMethod = true;
@ -915,7 +917,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
node.name.name, node.arguments, node.fileOffset);
}
Expression receiver = new KernelDirectPropertyGet(
new ThisExpression()..fileOffset = node.fileOffset, target);
new KernelThisExpression()..fileOffset = node.fileOffset, target);
// TODO(ahe): Use [DirectPropertyGet] when possible, that is, remove the
// next line:
receiver = new KernelSuperPropertyGet(node.name, target);
@ -992,7 +994,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
noSuchMethodName,
new Arguments(<Expression>[
library.loader.instantiateInvocation(
new ThisExpression()..fileOffset = charOffset,
new KernelThisExpression()..fileOffset = charOffset,
name,
arguments,
charOffset,

View file

@ -761,7 +761,8 @@ class SuperPropertyAccessor extends kernel.SuperPropertyAccessor
isConstantExpression: true,
isImplicitCall: true);
} else {
return new DirectMethodInvocation(new ThisExpression(), getter, arguments)
return new DirectMethodInvocation(
new KernelThisExpression(), getter, arguments)
..fileOffset = offset;
}
}

View file

@ -511,7 +511,7 @@ class ThisIndexAccessor extends Accessor {
: super(helper, token);
Expression _makeSimpleRead() {
return new KernelMethodInvocation(new ThisExpression(), indexGetName,
return new KernelMethodInvocation(new KernelThisExpression(), indexGetName,
new KernelArguments(<Expression>[index]),
interfaceTarget: getter);
}
@ -519,8 +519,8 @@ class ThisIndexAccessor extends Accessor {
Expression _makeSimpleWrite(Expression value, bool voidContext,
KernelComplexAssignment complexAssignment) {
if (!voidContext) return _makeWriteAndReturn(value, complexAssignment);
var write = new KernelMethodInvocation(new ThisExpression(), indexSetName,
new KernelArguments(<Expression>[index, value]),
var write = new KernelMethodInvocation(new KernelThisExpression(),
indexSetName, new KernelArguments(<Expression>[index, value]),
interfaceTarget: setter)
..fileOffset = offsetForToken(token);
complexAssignment?.write = write;
@ -533,8 +533,8 @@ class ThisIndexAccessor extends Accessor {
}
Expression _makeRead(KernelComplexAssignment complexAssignment) {
var read = new KernelMethodInvocation(new ThisExpression(), indexGetName,
new KernelArguments(<Expression>[indexAccess()]),
var read = new KernelMethodInvocation(new KernelThisExpression(),
indexGetName, new KernelArguments(<Expression>[indexAccess()]),
interfaceTarget: getter)
..fileOffset = offsetForToken(token);
complexAssignment?.read = read;
@ -544,8 +544,8 @@ class ThisIndexAccessor extends Accessor {
Expression _makeWrite(Expression value, bool voidContext,
KernelComplexAssignment complexAssignment) {
if (!voidContext) return _makeWriteAndReturn(value, complexAssignment);
var write = new KernelMethodInvocation(new ThisExpression(), indexSetName,
new KernelArguments(<Expression>[indexAccess(), value]),
var write = new KernelMethodInvocation(new KernelThisExpression(),
indexSetName, new KernelArguments(<Expression>[indexAccess(), value]),
interfaceTarget: setter)
..fileOffset = offsetForToken(token);
complexAssignment?.write = write;
@ -556,7 +556,7 @@ class ThisIndexAccessor extends Accessor {
Expression value, KernelComplexAssignment complexAssignment) {
var valueVariable = new VariableDeclaration.forValue(value);
var write = new KernelMethodInvocation(
new ThisExpression(),
new KernelThisExpression(),
indexSetName,
new KernelArguments(
<Expression>[indexAccess(), new VariableGet(valueVariable)]),

View file

@ -530,21 +530,23 @@ class KernelDirectMethodInvocation extends DirectMethodInvocation
Expression receiver, Procedure target, Arguments arguments)
: super(receiver, target, arguments);
KernelDirectMethodInvocation.byReference(
Expression receiver, Reference targetReference, Arguments arguments)
: super.byReference(receiver, targetReference, arguments);
@override
void _collectDependencies(KernelDependencyCollector collector) {
// TODO(paulberry): Determine the right thing to do here.
throw 'TODO(paulberry)';
// DirectMethodInvocation can only occur as a result of a use of `super`,
// and `super` can't appear inside a field initializer. So this code should
// never be reached.
internalError(
'Unexpected call to _collectDependencies for DirectMethodInvocation');
}
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset,
'target', new InstrumentationValueForMember(target));
return inferrer.inferMethodInvocation(
this, receiver, fileOffset, false, typeContext, typeNeeded,
interfaceMember: target, methodName: target.name, arguments: arguments);
}
}
@ -1286,8 +1288,9 @@ class KernelMethodInvocation extends MethodInvocation
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
return inferrer.inferMethodInvocation(this, receiver, fileOffset, this,
_isImplicitCall, typeContext, typeNeeded);
return inferrer.inferMethodInvocation(
this, receiver, fileOffset, _isImplicitCall, typeContext, typeNeeded,
desugaredInvocation: this);
}
}
@ -1341,11 +1344,11 @@ class KernelNullAwareMethodInvocation extends Let implements KernelExpression {
this,
variable.initializer,
fileOffset,
_desugaredInvocation,
false,
typeContext,
typeNeeded || inferrer.strongMode,
receiverVariable: variable);
receiverVariable: variable,
desugaredInvocation: _desugaredInvocation);
if (inferrer.strongMode) {
body.staticType = inferredType;
}
@ -1807,8 +1810,13 @@ class KernelSuperMethodInvocation extends SuperMethodInvocation
@override
DartType _inferExpression(
KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) {
// TODO(scheglov): implement.
return typeNeeded ? const DynamicType() : null;
inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset,
'target', new InstrumentationValueForMember(interfaceTarget));
return inferrer.inferMethodInvocation(this, new KernelThisExpression(),
fileOffset, false, typeContext, typeNeeded,
interfaceMember: interfaceTarget,
methodName: name,
arguments: arguments);
}
}

View file

@ -780,11 +780,14 @@ abstract class TypeInferrerImpl extends TypeInferrer {
Expression expression,
Expression receiver,
int fileOffset,
MethodInvocation desugaredInvocation,
bool isImplicitCall,
DartType typeContext,
bool typeNeeded,
{VariableDeclaration receiverVariable}) {
{VariableDeclaration receiverVariable,
MethodInvocation desugaredInvocation,
Member interfaceMember,
Name methodName,
Arguments arguments}) {
typeNeeded =
listener.methodInvocationEnter(expression, typeContext) || typeNeeded;
// First infer the receiver so we can look up the method that was invoked.
@ -793,31 +796,32 @@ abstract class TypeInferrerImpl extends TypeInferrer {
receiverVariable?.type = receiverType;
}
bool isOverloadedArithmeticOperator = false;
Member interfaceMember =
interfaceMember ??=
findMethodInvocationMember(receiverType, desugaredInvocation);
methodName ??= desugaredInvocation.name;
arguments ??= desugaredInvocation.arguments;
if (interfaceMember is Procedure) {
isOverloadedArithmeticOperator = typeSchemaEnvironment
.isOverloadedArithmeticOperatorAndType(interfaceMember, receiverType);
}
var calleeType = getCalleeFunctionType(interfaceMember, receiverType,
desugaredInvocation.name, !isImplicitCall);
var calleeType = getCalleeFunctionType(
interfaceMember, receiverType, methodName, !isImplicitCall);
bool forceArgumentInference = false;
if (isDryRun) {
if (_isUserDefinableOperator(desugaredInvocation.name.name)) {
if (_isUserDefinableOperator(methodName.name)) {
// If this is an overloadable arithmetic operator, then type inference
// might depend on the RHS, so conservatively assume it does.
forceArgumentInference =
isOverloadableArithmeticOperator(desugaredInvocation.name.name);
isOverloadableArithmeticOperator(methodName.name);
} else {
// If no type arguments were given, then type inference might depend on
// the arguments (because the called method might be generic), so
// conservatively assume it does.
forceArgumentInference =
getExplicitTypeArguments(desugaredInvocation.arguments) == null;
forceArgumentInference = getExplicitTypeArguments(arguments) == null;
}
}
var inferredType = inferInvocation(typeContext, typeNeeded, fileOffset,
calleeType, calleeType.returnType, desugaredInvocation.arguments,
calleeType, calleeType.returnType, arguments,
isOverloadedArithmeticOperator: isOverloadedArithmeticOperator,
receiverType: receiverType,
forceArgumentInference: forceArgumentInference);

View file

@ -20,7 +20,7 @@ class B extends self::A {
;
method method() → dynamic {
core::print("B.method x: ${this.x} y: ${this.y}");
super.method();
super.{self::A::method}();
}
}
static method main() → dynamic {

View file

@ -0,0 +1,18 @@
// Copyright (c) 2017, 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.
/*@testedFeatures=inference*/
library test;
class C {
int f() => 0;
}
class D extends C {
void g() {
var /*@type=int*/ x = super. /*@target=C::f*/ f();
}
}
main() {}

View file

@ -0,0 +1,20 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
method f() → core::int
return 0;
}
class D extends self::C {
constructor •() → void
: super self::C::•()
;
method g() → void {
dynamic x = super.f();
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,20 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
method f() → core::int
return 0;
}
class D extends self::C {
constructor •() → void
: super self::C::•()
;
method g() → void {
dynamic x = super.{self::C::f}();
}
}
static method main() → dynamic {}

View file

@ -0,0 +1,18 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
;
method f() → core::int
;
}
class D extends self::C {
constructor •() → void
;
method g() → void
;
}
static method main() → dynamic
;

View file

@ -0,0 +1,20 @@
library test;
import self as self;
import "dart:core" as core;
class C extends core::Object {
constructor •() → void
: super core::Object::•()
;
method f() → core::int
return 0;
}
class D extends self::C {
constructor •() → void
: super self::C::•()
;
method g() → void {
core::int x = super.{self::C::f}();
}
}
static method main() → dynamic {}

View file

@ -360,6 +360,7 @@ inference/simple_literal_null: Crash
inference/static_method_tear_off: Crash
inference/string_literal: Crash
inference/subexpressions_of_explicitly_typed_fields: Crash
inference/super_method_invocation: RuntimeError
inference/this_reference: Crash
inference/top_level_return_and_yield: Crash
inference/toplevel_inference_toplevel_var: Crash

View file

@ -9,7 +9,7 @@ class C extends core::Object {
operator ==(dynamic other) → dynamic
return throw "x";
method test() → dynamic {
super.==(null);
super.{core::Object::==}(null);
}
}
static method main() → dynamic {

View file

@ -17,7 +17,7 @@ class Foo extends self::Object&Mixin {
: super core::Object::•()
;
method foo() → dynamic
return super.foo();
return super.{self::Mixin::foo}();
method bar() → dynamic
return super.{self::Mixin::field};
}

View file

@ -45,14 +45,14 @@ class C extends self::B {
: super self::B::•()
;
method test() → dynamic {
super.~();
self::use(super.~());
super.unary-();
self::use(super.unary-());
super.==(87);
self::use(super.==(87));
!super.==(87);
self::use(!super.==(87));
super.{self::A::~}();
self::use(super.{self::A::~}());
super.{self::A::unary-}();
self::use(super.{self::A::unary-}());
super.{self::A::==}(87);
self::use(super.{self::A::==}(87));
!super.{self::A::==}(87);
self::use(!super.{self::A::==}(87));
super.{self::A::a};
self::use(super.{self::A::a});
super.{self::B::b};
@ -139,10 +139,10 @@ class C extends self::B {
self::use(super.{self::A::i}.call());
super.{self::A::[]}(87).call();
self::use(super.{self::A::[]}(87).call());
super.m();
self::use(super.m());
super.m(87);
self::use(super.m(87));
super.{self::A::m}();
self::use(super.{self::A::m}());
super.{self::A::m}(87);
self::use(super.{self::A::m}(87));
super.a = 42;
self::use(super.a = 42);
super.b = 42;