Skip to content

final val in trait should not be encoded as abstract method #8602

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

Closed
ohze opened this issue Mar 24, 2020 · 4 comments · Fixed by #10496
Closed

final val in trait should not be encoded as abstract method #8602

ohze opened this issue Mar 24, 2020 · 4 comments · Fixed by #10496
Assignees
Milestone

Comments

@ohze
Copy link

ohze commented Mar 24, 2020

minimized code

trait Scala {
  final val x = 1
}
class Java implements Scala { }

Compilation output

[error] /Users/thanhbv/ohze/dotty-example/src/main/java/Java.java:1:1: Java is not abstract and does not override abstract method x() in Scala
[error] (Compile / compileIncremental) javac returned non-zero exit code

expectation

compile successfully as in scala 2

inspect

dotty-example % javap target/scala-2.13/classes/Scala.class 
Compiled from "Scala.scala"
public interface Scala {
  public static int x$(Scala);
  public int x();
  public static void $init$(Scala);
}
dotty-example % javap target/scala-0.23/classes/Scala.class
Compiled from "Scala.scala"
public interface Scala {
  public void $init$();
  public abstract int x();
  public int initial$x();
}
@odersky
Copy link
Contributor

odersky commented Apr 5, 2020

@sjrd's work on switching back to scalac's trait encoding might fix this as well.

@TheElectronWill
Copy link
Contributor

The result is now different but still not java-friendly:

Compiled from "Scala.scala"
public interface Scala {
  public static void $init$(Scala);
  public default void $init$();
  public abstract int x();
  public abstract void Scala$_setter_$x_$eq(int);
}

@sjrd
Copy link
Member

sjrd commented Nov 25, 2020

So, this is really really tricky. It's not the first time that we run into trouble with constant expression final vals in mixin composition, but so far I had managed to find "workarounds". This time I feel completely stuck.

The big problem is that, when we get to the phases that perform mixin composition and allocation of fields, in dotc we have lost the knowledge of what was a constant expression final val, and what wasn't. To illustrate what I mean, if we take the following trait:

trait Foo {
  final val foo = 5
  final val foobar: Int = 5
  val bar: Option[Int] = Some(5)
}

by the time we get to mixin or memoize, foo and foobar look exactly the same:

package <empty> {
  @scala.annotation.internal.SourceFile("tests\\run\\hello.scala") trait Foo()
     extends
   Object {
    final def foo(): Int = 5
    final def foobar(): Int = 5
    def bar(): Option = Some.apply(scala.Int.box(5))
  }
}

Yet, for scalac they are different. foo is concrete in the trait and requires no field in the subclasses, but foobar() is abstract and needs to be mixed in subclasses (it needs to, because a trait setter is generated for it and called from the trait $init$ method.

scalac can do different things because the decision to allocate fields is done before erasure, where we still see the ConstantTypes. In dotc, everything is done after erasure, so we only see the type Int. Worse, from a subclass, there is no way we can see the right-hand-side, and hence the previous "workarounds" are dead too.

My conclusion at this point is that I need to preserve the information that a val was a final val with a ConstantType until we get to mixin/memoize. It seems I will need to add that information as an additional flag.

sjrd added a commit to dotty-staging/dotty that referenced this issue Nov 25, 2020
This matches what Scala 2 does (addressing binary compatibility
with Scala 2) and allows a trait with only such `final val`s to be
extended from Java.
sjrd added a commit to dotty-staging/dotty that referenced this issue Nov 25, 2020
This matches what Scala 2 does (addressing binary compatibility
with Scala 2) and allows a trait with only such `final val`s to be
extended from Java.
@sjrd
Copy link
Member

sjrd commented Nov 25, 2020

It turns out time travel does the trick, of course 🤦‍♂️

sjrd added a commit that referenced this issue Nov 25, 2020
…l-val

Fix #8602: Do not create mixins for constant-expression final vals.
@Kordyjan Kordyjan added this to the 3.0.0 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants