ponyc icon indicating copy to clipboard operation
ponyc copied to clipboard

`this->X!` should not be a subtype of `this->X`

Open plietar opened this issue 9 years ago • 5 comments

Reduced test case, this creates three references x, y and z to the same object,

class Foo[X]
    fun foo(x: this->X) =>
        let y : this->X = x
        let z : this->X = x
        None

Unless X is bounded by #share (or similar), this should not be allowed.

This can be used to create two iso references to the same object, as shown in the snippet below. The clone method aliases this.a, which results in a this->A!. However this bug allows it to cast it to a this->A.

class Counter
  var i: USize = 0
  fun ref inc() => i = i + 1

class Box[A]
    var a: (A | None)
    new create(x: (A | None)) =>
        this.a = consume x

    fun ref take() : A^ ? =>
        (this.a = None) as A^

    fun clone() : Box[this->A] =>
        Box[this->A](this.a)

actor Main
  new create(env: Env) =>
    try
        let x: Box[Counter iso] = Box[Counter iso](Counter)
        let y: Box[Counter iso] = x.clone()

        let a : Counter iso = x.take()
        let a' : Counter iso = y.take()

        env.out.print(a.i.string())
        a'.inc()
        env.out.print(a.i.string())
    else
        None
    end

Playground link : https://is.gd/6gwQK4

Pony version :

0.12.3 [release]
compiled with: llvm 3.9.1 -- cc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609

plietar avatar Apr 04 '17 11:04 plietar

Simpler test case, which relies on X! ≤ ref->X. The method alias duplicates any reference, including an iso or trn.

class Foo
  fun alias[X](x: X!) : X^ =>
    let y : ref->X = consume x
    consume y

plietar avatar Apr 04 '17 23:04 plietar

This looks like a bug in viewpoint.c. In particular, for the first example, in is_arrow_sub_arrow we compare ask if this->X <: this->X!. This calls viewpoint_reifypair, which results in:

(lldb) call ast_print(r_sub)
(tupletype
  (-> ref (typeparamref (id X) #any !))
  (-> val (typeparamref (id X) #any !))
  (-> box (typeparamref (id X) #any !))
)

(lldb) call ast_print(r_super)
(tupletype
  (-> ref (typeparamref (id X) #any x))
  (-> val (typeparamref (id X) #any x))
  (-> box (typeparamref (id X) #any x))
)

Which is good! But then we need to compare those. And when we have:

(lldb) call ast_print(sub)
(-> ref (typeparamref (id X) #any !))

(lldb) call ast_print(super)
(-> ref (typeparamref (id X) #any x))

It reifies as:

(lldb) call ast_print(r_sub)
(tupletype
  (typeparamref (id X) tag x)
  (typeparamref (id X) box x)
  (typeparamref (id X) ref x)
  (typeparamref (id X) val x)
  (typeparamref (id X) box x)
  (typeparamref (id X) tag x)
)

(lldb) call ast_print(r_super)
(tupletype
  (typeparamref (id X) tag x)
  (typeparamref (id X) box x)
  (typeparamref (id X) ref x)
  (typeparamref (id X) val x)
  (typeparamref (id X) box x)
  (typeparamref (id X) tag x)
)

This is bad! r_super is ref->X, not ref->X! so the reification should be:

(tupletype
  (typeparamref (id X) iso x)
  (typeparamref (id X) trn x)
  (typeparamref (id X) ref x)
  (typeparamref (id X) val x)
  (typeparamref (id X) box x)
  (typeparamref (id X) tag x)
)

sylvanc avatar Apr 26 '17 20:04 sylvanc

This no longer compiles. I believe this has been fixed, but leaving open for discussion at sync.

main.pony:14:22: argument not assignable to parameter
        Box[this->A](this.a)
                     ^
    Info:
    main.pony:14:22: argument type is (this->A #any | None val)
            Box[this->A](this.a)
                         ^
    main.pony:7:16: parameter type requires (this->A #any ^ | None val^)
        new create(x: (A | None)) =>
                   ^
    main.pony:14:22: A iso is not a subtype of A iso ^: the subtype has no constraint
            Box[this->A](this.a)
                         ^
    main.pony:14:22: A trn is not a subtype of A trn ^: the subtype has no constraint
            Box[this->A](this.a)
                         ^
    main.pony:14:22: (ref->A iso, ref->A trn, ref->A ref, ref->A val, ref->A box, ref->A tag) is not a pairwise subtype of (ref->A iso ^, ref->A trn ^, ref->A ref ^, ref->A val ^, ref->A box ^, ref->A tag ^)
            Box[this->A](this.a)
                         ^
    main.pony:14:22: (ref->A #any, val->A #any, box->A #any) is not a pairwise subtype of (ref->A #any ^, val->A #any ^, box->A #any ^)
            Box[this->A](this.a)
                         ^
    main.pony:14:22: A #any is not a subtype of None val^: the subtype has no constraint
            Box[this->A](this.a)
                         ^
    main.pony:14:22: this->A #any is not a subtype of any element of (this->A #any ^ | None val^)
            Box[this->A](this.a)
                         ^
    main.pony:14:22: not every element of (this->A #any | None val) is a subtype of (this->A #any ^ | None val^)
            Box[this->A](this.a)
                         ^

SeanTAllen avatar Oct 07 '23 20:10 SeanTAllen

Note that this example: https://github.com/ponylang/ponyc/issues/1798#issuecomment-291670380 still compiles and is problematic. The main example no longer works because ! isn't used implicitly in those cases anymore, but the issue still exists when it's used expicitly.

jasoncarr0 avatar Oct 07 '23 22:10 jasoncarr0