-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Trait encoding vs the persnickety JVM 9 verifier: final fields only updatable from within <init> #408
Comments
/cc @adriaanm, this is one of the topics I'm thinking about ahead of JVMLS. |
I had a discussion about this behavior with Oracle folks on previous JVM
language summit. They have verified that Dotty's trait encoding with initial methods
does not have this issue.
We perform option 3 at compile time, which has binary compatibility implications, and indeed should be better done via bootstrap method.
…On 25 July 2017 7:02:33 am Jason Zaugg ***@***.***> wrote:
/cc @adriaanm, this is one of the topics I'm thinking about ahead of JVMLS.
--
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
#408 (comment)
|
There is also a similar issue with our module classes:
|
For modules we no longer mark the field |
FTR, Scala.js uses Option 2. Our IR has always been that strict, that "final" fields ( |
Here's the dotty treatment for comparison.
import scala.Predef;
public interface T {
default public void $init$() {
Predef..MODULE$.println((Object)"2");
}
public int x();
default public int initial$x() {
Predef..MODULE$.println((Object)"1");
return 42;
}
}
/*
* Decompiled with CFR 0_121.
*/
public interface U
extends T {
@Override
default public void $init$() {
}
@Override
public int x();
@Override
default public int initial$x() {
return 43;
}
}
/*
* Decompiled with CFR 0_121.
*/
public class C
implements T,
U {
private final int x;
public C() {
T.super.initial$x();
T.super.$init$();
this.x = U.super.initial$x();
}
@Override
public int x() {
return this.x;
}
} The binary fragilities here are things like:
That's the part that might be addressed by link time generation of the contents of |
John Rose's discussion of the possibility of VM support for "larval objects" seem related here. |
Also worth noting that Scala 2's trait encoding already has a very similar problem with non-final trait vals: adding/removing the override val in an intermediate trait requires recompilation of a subclass. trait T {
val x = 42
println(x)
}
trait U extends T {
override val x = 43
println(x)
}
class C extends U
object Test {
def main(args: Array[String]): Unit = {
new C()
}
}
|
I'm not sure that "Option 3: link time trait constructor inlining" is actually feasible. The problems I see are:
|
Here's how to do it: https://gist.github.com/77f2d51b8581d0f85d3d93b5ba195686.
Confirmed in discussions that this is the case :( |
I cannot follow the indirection from the gist Gist to your laptop's file system :) |
D'oh! Updated. |
WIP implementation of the change to Fields/Constructors to make the fields non-final and adding the fence is in one of the commits in: scala/scala#6425 |
Final Fields
JVM 9 closes a loophole in the verifier around final field updates outside of
<init>
. It is predicated on the new classfile version for backwards compat. Some JIT optimizations are disabled for such almost-final fields (at least,FoldStableValues
).Scala's trait encoding violates this rule. Below, we assign to
x
inT$_setter_$x_$eq
.Option 1: Status Quo
Don't support emitting classes with the new classfile version. This robs users of a valuable mechanism to fail fast when running classes that depend on the Java 9 standard library. We're also likely to run into a case where some desirable VM feature is only available under the new version.
Option 2: Drop the
final
modifier in bytecodeWe would lose a guarantee under the memory model, but we could get this back with an explicit
VarHandle.xxxFence
at the end of the constructor of a class with an "almost final" field.We'd also miss out on some JIT optimizations. These could be regained if/when the
@Stable
annotation was exposed outside of the JDK, which is under discussion for a future JEPOption 3: link time trait constructor inlining ?
If we could inline the
T.$init$
intoC.<init>
, but somehow do this at link time so as not to violate separate compilation constraints, we'd be in better shape.C.<init>
could use a bootstrap method that delegated to a bootstrap method helper in each trait that builds up the right sequence of calls.Option 4: rearrange trait encoding, dotty style
Leave the assignments in the class constructor and have it call initializer methods for each val. Don't forget to execute side-effects (statements in the trait body) in the right order! Consider binary compatibility pros/cons!
The text was updated successfully, but these errors were encountered: