Closed armanbilge closed 1 year ago
Isn't this a MiMa bug? "Binary compatible" doesn't just mean JVM linkage. MiMa should tell me if clients require recompilation.
"Binary compatible" doesn't just mean JVM linkage. MiMa should tell me if clients require recompilation.
Hm, this has not been my understanding of MiMa to date. See also https://github.com/lightbend/mima/issues/200 which is not a linking error, but still requires client recompilation.
As I pointed out on the dotty issue, why not just emit an abstract method here instead of a default one? What is the utility of a default method that must be overridden to achieve the correct semantics? This would have the benefit of triggering MiMa, since it would also be a linking error.
The default method carries the initialization code for the rhs of the lazy val
. The override generated in the subclass calls it with super.sameSame()
to initialize the field the first time it is accessed. So we cannot make it abstract
.
We could argue that it would have been better to put that initialization code in a different method. But that's not something we can change now while remaining binary-compatible with existing compiled code.
But that's not something we can change now while remaining binary-compatible with existing compiled code.
But note that any code that is relying on this binary-compatibility is actually bugged.
No. Every class that correctly implements the trait, i.e., that is compiled with the trait already has the lazy val
, relies on actually calling that method. If you remove it, you break all lazy val
s defined in traits everywhere.
Sorry, hum, I see now. The problem is that the initializer and the accessor have been made the same method, and it's too late to change that.
I was going to propose adding a line to the FAQ on when to avoid traits, but it seems necessary to first propose adding the FAQ to the docs.
Reproduction steps
tl;dr
sbt app/runMain Bar
in https://github.com/armanbilge/sandbox/commit/48881c4402029c1bb8055d17ad53a52b2abf8e52Foo
.Compile
Bar
.Compile new
Foo
.Run
Bar
with newFoo
.Problem
Lazy val semantics dictate that
sameSame eq sameSame
should always betrue
.Analysis
If we look at the decompiled bytecode for new
Foo
, we see it has emitted adefault public Object sameSame()
method which it is using as an accessor. However, becauseBar
was compiled against an oldFoo
, it is not overriding that method. Therefore thelazy val
effectively has the semantics of adef
.It seems like that should not be a
default
method, since the default implementation does not have the correct semantics.Credits
@s5bug for stumbling on this by way of https://github.com/bkirwi/decline/issues/461.