scala3
scala3 copied to clipboard
Unsafe unboxing conversion around function types?
Compiler version
PR #15768
Minimized code
class C {
def bad() = println("I've gone bad!")
}
type Observe[T] = (T => Unit) -> Unit
def unsafe(cap: {*} C) = cap.bad()
def box[T](v: T) : Observe[T] = {
(fn: T => Unit) => fn(v)
}
def main() : Int = {
val boxed : Observe[{*} C] = box(new C)
boxed(unsafe)
0
}
Output
It compiles.
Expectation
Ondrej and I were discussing this, and we think this is a bug as somewhere the boxed {*} C in the Observe[box {*} C] type is getting unboxed, we think.
Where do you think an error should be reported?
new C has type C, so it is pure. I believe that's why things typecheck. On the other hand, if I replace new C with newC where
def newC: {*} C = C()
I get
-- [E007] Type Mismatch Error: i15772.scala:15:36 ------------------------------
15 | val boxed : Observe[{*} C] = box(newC)
| ^^^^
| Found: {*} C
| Required: box {*} C
I would've thought that would've occurred when calling unsafe, in the original example. I'm actually surprised this gives a typing error, as wouldn't we implicitly box newC here in this case?
You can't box if the capture set is *.
Fair enough, shouldn't there still be a compilation error at unboxing as we don't know if the boxed value is pure? Here's a revised example where we box something impure, and unsafe is still allowed to be invoked:
class C(arg: {*} C) {
def bad() = println("I've gone bad!")
}
def main(x: {*} C) : Int = {
val c : {x} C = new C(x)
val boxed : Observe[{*} C] = box(c)
boxed(unsafe)
0
}
I think that's because the whole thing is in a global method. Methods don't capture anything. If you make main a function literal, you'll find in #15768 that the literal does capture c and x.