Skip to content

Kernel representation of forwarding stubs needs to point to "actual" interface target in outline #31519

Closed
@stereotype441

Description

@stereotype441

Consider the following program:

class A {
  void foo(int x) {}
}
abstract class I<T> {
  void foo(T x);
}
class B extends A<int> implements I {}
void bar(B b) {
  b.foo(1);
}
main() {
  bar(new B());
}

This compiles to the following kernel representation:

library;
import self as self;
import "dart:core" as core;

class A extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
  method foo(core::int x) → void {}
}
abstract class I<T extends core::Object> extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
  abstract method foo(generic-covariant-impl generic-covariant-interface self::I::T x) → void;
}
class B extends self::A implements self::I<dynamic> {
  default constructor •() → void
    : super self::A::•()
    ;
  forwarding-stub method foo(generic-covariant-impl dynamic x) → void
    return super.{self::A::foo}(x);
}
static method bar(self::B b) → void {
  b.{self::B::foo}(1);
}
static method main() → dynamic {
  self::bar(new self::B::•());
}

Note that the call from bar to b.foo has an interface target of self::B::foo, which is a forwarding stub.

When the analyzer builds its resolution information based on the kernel representation, it can't consider the call to foo to resolve to self::B::foo, since there is no such function in the user's source code.
It needs to resolve it to self::A::foo, the method that would have been the interface target if the forwarding stub hadn't been there. It can't get this information from the body of B::foo for two reasons:

(1) In a separate compilation scenario, the body of B::foo may not be present when analyzing bar; we may only have an outline for B::foo.

(2) In certain circumstances a forwarding stub is created that lacks a body, e.g.:

abstract class A {
  void foo(int x);
}
abstract class I {
  void foo(Object x);
}
class B extends A implements I {}

which has the kernel representation:

library;
import self as self;
import "dart:core" as core;

abstract class A extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
  abstract method foo(core::int x) → void;
}
abstract class I extends core::Object {
  default constructor •() → void
    : super core::Object::•()
    ;
  abstract method foo(core::Object x) → void;
}
class B extends self::A implements self::I {
  default constructor •() → void
    : super self::A::•()
    ;
  abstract forwarding-stub method foo(core::Object x) → void;
}

So we need the kernel representation of a forwarding stub to contain a pointer to the interface target that would have been used if the forwarding stub had not been there; and we need this information to be present in the kernel outline. The analyzer can use this information to find the "actual" (non-forwarding) interface target.

Metadata

Metadata

Assignees

Labels

P1A high priority bug; for example, a single project is unusable or has many test failurescustomer-analyzerfront-end-kernellegacy-area-front-endLegacy: Use area-dart-model instead.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions