Open
Description
Compiler version
3.3.0-RC1-bin-20221115-e587a81-NIGHTLY
Minimized code
tl;dr sbt app/runMain Bar
in armanbilge/sandbox@345b785
- Compile old
Foo
.
trait Foo {
def main(args: Array[String]): Unit = ()
}
- Compile
Bar
.
object Bar extends Foo
- Compile new
Foo
.
trait Foo {
lazy val sameSame: AnyRef = new AnyRef
def main(args: Array[String]): Unit = println(sameSame eq sameSame)
}
Output
- Run
Bar
with newFoo
.
sbt:sandbox> app/runMain Bar
[info] running Bar
false
Expectation
Lazy val semantics dictate that sameSame eq sameSame
should always be true
.
Analysis
If we look at the decompiled bytecode for new Foo
, we see it has emitted a default public Object sameSame()
method which it is using as an accessor. However, because Bar
was compiled against an old Foo
, it is not overriding that method. Therefore the lazy val
effectively has the semantics of a def
.
It seems like that should not be a default
method, since the default implementation does not have the correct semantics.
import scala.Predef$;
import scala.runtime.BoxesRunTime;
public interface Foo {
public static void $init$(Foo $this) {
}
public static Object sameSame$(Foo $this) {
return $this.sameSame();
}
default public Object sameSame() {
return new Object();
}
public static void main$(Foo $this, String[] args) {
$this.main(args);
}
default public void main(String[] args) {
Predef$.MODULE$.println((Object)BoxesRunTime.boxToBoolean((this.sameSame() == this.sameSame() ? 1 : 0) != 0));
}
}
Credits
@s5bug for stumbling on this by way of bkirwi/decline#461.