[VM/AOT/TFA] Do not attribute calls via fields/getters with inference results

MethodInvocation nodes which have field or getter as interface target
actually correspond to 2 calls: 1) getter; 2) implicit call() on the result
of the getter.

While this was correctly modeled in summaries in TFA, it is not correct
to attribute such MethodInvocation nodes with results of the inference,
as results correspond to an each distinct call. To fix this, Call
statements in summaries are created without attaching them to a
corresponding kernel AST node.

Change-Id: I3ea28ed85550192f8b592a926cac0eae23810811
Reviewed-on: https://dart-review.googlesource.com/c/88424
Commit-Queue: Martin Kustermann <kustermann@google.com>
Auto-Submit: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Alexander Markov 2019-01-07 10:22:40 +00:00 committed by commit-bot@chromium.org
parent a10f6aac38
commit 3b29f5ad81
6 changed files with 36 additions and 9 deletions

View file

@ -918,16 +918,19 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
return _makeCall(
node, new DynamicSelector(CallKind.Method, node.name), args);
}
// TODO(dartbug.com/34497): Once front-end desugars calls via
// fields/getters, handling of field and getter targets here
// can be turned into assertions.
if ((target is Field) || ((target is Procedure) && target.isGetter)) {
// Call via field.
final fieldValue = _makeCall(
node,
// Call via field/getter.
final value = _makeCall(
null,
(node.receiver is ThisExpression)
? new VirtualSelector(target, callKind: CallKind.PropertyGet)
: new InterfaceSelector(target, callKind: CallKind.PropertyGet),
new Args<TypeExpr>([receiver]));
_makeCall(
null, DynamicSelector.kCall, new Args.withReceiver(args, fieldValue));
null, DynamicSelector.kCall, new Args.withReceiver(args, value));
return _staticType(node);
} else {
// TODO(alexmarkov): overloaded arithmetic operators

View file

@ -114,7 +114,7 @@ class B4 extends core::Object {
[@vm.inferred-type.metadata=dart.core::_Closure?]static field core::Function unknown4 = () → dynamic => self::bb4;
static method test1() → void {
self::B1 bb = new self::B1::•();
[@vm.inferred-type.metadata=#lib::A1 (skip check)] bb.{self::B1::aa1}(1, 2, 3, 4, new self::T1::•());
bb.{self::B1::aa1}(1, 2, 3, 4, new self::T1::•());
self::ok = false;
[@vm.direct-call.metadata=#lib::T1::doTest1??] [@vm.inferred-type.metadata=!? (skip check)] [@vm.direct-call.metadata=#lib::A1::foo] [@vm.inferred-type.metadata=#lib::T1?] [@vm.direct-call.metadata=#lib::B1::aa1] [@vm.inferred-type.metadata=#lib::A1] bb.{self::B1::aa1}.{self::A1::foo}.{self::T1::doTest1}();
exp::Expect::isTrue([@vm.inferred-type.metadata=dart.core::bool?] self::ok);

View file

@ -18,7 +18,7 @@ class TearOffDynamicMethod extends core::Object {
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false] field dynamic bazz;
constructor •(dynamic arg) → self::TearOffDynamicMethod
: self::TearOffDynamicMethod::bazz = arg.foo, super core::Object::•() {
[@vm.inferred-type.metadata=!? (skip check)] this.{self::TearOffDynamicMethod::bazz}();
this.{self::TearOffDynamicMethod::bazz}();
}
}
static method knownResult() → dynamic

View file

@ -26,5 +26,5 @@ class TearOffInterfaceMethod extends core::Object {
static method knownResult() → dynamic
return new self::B::•();
static method main(core::List<core::String> args) → dynamic {
[@vm.inferred-type.metadata=!? (skip check)] new self::TearOffInterfaceMethod::•(new self::B::•()).{self::TearOffInterfaceMethod::bazz}();
new self::TearOffInterfaceMethod::•(new self::B::•()).{self::TearOffInterfaceMethod::bazz}();
}

View file

@ -3639,6 +3639,9 @@ Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p) {
const Function* interface_target = &Function::null_function();
const NameIndex itarget_name =
ReadCanonicalNameReference(); // read interface_target_reference.
// TODO(dartbug.com/34497): Once front-end desugars calls via
// fields/getters, filtering of field and getter interface targets here
// can be turned into assertions.
if (!H.IsRoot(itarget_name) && !H.IsField(itarget_name) &&
!H.IsGetter(itarget_name)) {
interface_target = &Function::ZoneHandle(

View file

@ -18,13 +18,34 @@ class Box {
var box = new Box();
void foo() {
void test1() {
Expect.isFalse(box.fun(42) is Function);
Expect.isTrue(box.fun(42) is String);
}
class Callable {
String call(int i) => 'qwe';
}
class Box2 {
Callable get fun => new Callable();
}
var box2 = new Box2();
void test2() {
Expect.isFalse(box2.fun is Function);
Expect.isTrue(box2.fun is Callable);
Expect.isFalse(box2.fun(42) is Function);
Expect.isFalse(box2.fun(42) is Callable);
Expect.isTrue(box2.fun(42) is String);
}
void main() {
for (int i = 0; i < 20; ++i) {
foo();
test1();
}
for (int i = 0; i < 20; ++i) {
test2();
}
}