Open iogrm opened 1 week ago
Thanks @iogrm for the minimization!
Turns out this is very similar to https://github.com/scala/scala/pull/10775 / https://github.com/scala/bug/issues/12990 which I recently looked at. I simplified the example a tiny bit:
class C(val x: String) extends AnyVal
abstract class Test {
def c: C
def test(v: Int): Any =
v match {
case 1 if true => c
case _ => c
}
}
AST after patmat:
<method> def test(v: scala.this.Int): scala.this.Any = {
case <synthetic> val x1: scala.this.Int = v;
x1 match {
case 1 => if (true)
Test.this.c
else
default3()
case _ => default3(){
Test.this.c
}
}
}
The match
tree is left in place because it's a match that can be translated to a switch bytecode by the backend. In the original example, the match is on a string, which is also translated to a switch thanks to https://github.com/scala/scala/pull/8451.
Now erasure comes along and realizes that the result has to be boxed: Test.this.c
returns a String
(the value class is erased), but the return type of test
is Any
. So erasure adds new C
:
<method> def test(v: scala.this.Int): lang.this.Object = {
case <synthetic> val x1: scala.this.Int = v;
(x1: scala.this.Int) match {
case 1 => if (true)
new C.<init>(Test.this.c())
else
new C.<init>(default3())
case _ => default3(){
new C.<init>(Test.this.c())
}
}
}
The resulting AST new C.<init>(default3())
is not correct, as default3()
is not a method call that returns a value. It's a jump to a "label".
I guess this is a bug in erasure, it should realize that the boxing is already added to the default case. The jump to default3()
could just be left as it is.
Reproduction steps
openjdk 17 and 21 scala version 2.13.10 and 2.13.14 and 2.13.15 sbt version 1.10.1 and 1.10.2
build.sbt
I also tested with case classes and sealed traits. Also, the predicate in if statement (
true
in this sample code) can be anything, the issue will be the same.Problem
Compilation fails with unhelpful message:
And sometimes