mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:43:57 +00:00
[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:
parent
a10f6aac38
commit
3b29f5ad81
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}();
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue