You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
importlanguage.experimental.captureCheckingtraitLoggercaseclassBoxed[T](unbox: T)
// a horrible function, but it typechecksdefmagic(l: Logger^):Logger=classFoo:deffoo:Boxed[Logger^{this}] =// for the following line to typecheck// the capture checker assumes {l} <: {this}Boxed[Logger^{this}](l)
valx=newFoovaly= x.foo.unbox // y: Logger^{x}valz:Logger= y // now the capability becomes pure
z
Output
It typechecks.
Expectation
It should not typecheck. With magic one may make any capability pure.
Why it typechecks:
When typechecking Foo, we assume l being a subcapture of this.
The capture set of Foo is empty, as the reference to l is boxed.
The type of the method foo is Boxed[Logger^{this}].
When selecting x.foo, it is typed as Boxed[Logger^{x}].
We unbox it and get Logger^{x}, which is a subtype of Logger^{}.
…adapted (#19323)
Fixes#19315.
The unsound code:
```scala
import language.experimental.captureChecking
trait Logger
case class Boxed[T](unbox: T)
// a horrible function, but it typechecks
def magic(l: Logger^): Logger =
class Foo:
def foo: Boxed[Logger^{this}] =
// for the following line to typecheck
// the capture checker assumes {l} <: {this}
Boxed[Logger^{this}](l)
val x = new Foo
val y = x.foo.unbox // y: Logger^{x}
val z: Logger = y // now the capability becomes pure
z
```
The `magic` function typechecks before this fix. It casts a capability
to a pure value, which is clearly unsound. The crux of the problem is
`Boxed[Logger^{this}](l)`, which relies on the subcapturing relation
`{l} <: {this}` enabled by the trick implemented `addOuterRefs`.
`addOuterRefs` augment a capture set that contains a self reference
(like `{this}`) with all outer references reachable from `this`. In this
case, the augmented set is `{this, l}`. The reasoning is that, any
captured outer references in the class can be rewritten into a field,
thus being a subcapture of `{this}`:
```
class Foo:
val this_l: Logger^{l} = l
def foo: Boxed[Logger^{this}] = Boxed(this.this_l)
```
But the problem with this unsound example is that, the reference to `l`
is boxed, so `l` is not captured by the class. Therefore, the above
rewriting mismatches with the actual situation.
This PR proposes a simple fix, which simply disable `addOuterRefs` when
box adaptation happens. This is of course imprecise, but all tests pass
with this fix.
Uh oh!
There was an error while loading. Please reload this page.
Compiler version
main
Minimized code
Output
It typechecks.
Expectation
It should not typecheck. With
magic
one may make any capability pure.Why it typechecks:
Foo
, we assumel
being a subcapture ofthis
.Foo
is empty, as the reference tol
is boxed.foo
isBoxed[Logger^{this}]
.x.foo
, it is typed asBoxed[Logger^{x}]
.Logger^{x}
, which is a subtype ofLogger^{}
./cc @odersky
The text was updated successfully, but these errors were encountered: