Skip to content

Missing dynamic checks in DDC with call method allows heap unsoundness #29915

Closed
@eernstg

Description

@eernstg

As a variant of the example shown in #29913, the following example illustrates that DDC does not generate all the needed checks in order to ensure heap soundness when we mix covariance (with a generic class) and contravariance (with a function type), using a call method. The program is accepted by dartanalyzer --strong n018.dart with no issues.

class A {}
class B extends A {}
class C extends B {}

typedef void F<T>(T t);

class ClassF<T> {
  T x;
  void call(T t) {
    x = t;
  }
}

main() {
  ClassF<C> cc = new ClassF<C>();
  ClassF<A> ca = cc; // An upcast, per covariance.
  F<A> f = ca; // "No-op cast to function", but should fail in strong mode.
  // Still running, though!
  print(f.runtimeType); // 'ClassF<C>', cannot be considered an `F<A>`.
  f(new A()); // Statically safe, but dynamically the arg should be a C.
}

When compiling and executing the program using dartdevc.dart from 5982ace, 2017-06-15 14:18:09, ddc n018.dart, the resulting output is as follows:

ClassF<C>

/usr/local/google/home/eernst/devel/dart/work/sdk/pkg/dev_compiler/lib/js/common/dart_sdk.js:9877
          throw e;
          ^
Type 'A' is not a subtype of type 'C'

This differs from the situation in #29913 in that we do get the dynamic argument type check at the invocation of the call method (presumably because that's a normal class-covariance check), but we still have an unsound heap for a while, because f in main refers to an instance of ClassF<C>, and such an object cannot be considered to have type F<A> as declared.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions