bug
bug copied to clipboard
Subclasses should not share fields with superclasses if superclass field accessor is non-final
class A(val y: Int)
class B(y: Int) extends A(y) {
def yy = y
}
class C extends B(0) {
override val y = -1
}
object Test {
def main(args: Array[String]): Unit = {
println(new C().yy) // prints 0 as expected
}
}
Compared with:
class A(val y: Int)
class B(override val y: Int) extends A(y) {
def yy = y
}
class C extends B(0) {
override val y = -1
}
object Test {
def main(args: Array[String]): Unit = {
println(new C().yy) // prints -1
}
}
I'm sure this is a duplicate, but I couldn't find the original issue right now, and need to record this for posterity. Might not actually be a bug now that I think about it.
Imported From: https://issues.scala-lang.org/browse/SI-9330?orig=1 Reporter: @retronym Affected Versions: 2.11.6
@adriaanm said: I think the behavior is correct in both cases. When you make B's y a val, it becomes available for overriding.
as of Scala 3.2.2, both examples give:
overriding val parameter value y in class B is deprecated, will be illegal in a future version
in Scala 2, I suppose we could do the same under -Xsource:3
The runtime behavior is the same in for 3.2.2 and 2.13.14. In the latest 3.4.1, the deprecation is an error.
I wonder if "prints 0 as expected" is actually what we should expect.
class A(val y: Int)
class B(y: Int) extends A(y) {
def yy = y // invokespecial A.y
}
The bytecode is super[A].y (invokespecial) because of parameter aliasing. B.y has alias A.y, the access to B.y is rewritten to A.y, which ends up in bytecode as a super call. So there's no virtual call, yy always calls the A.y accessor.
In the second example
class A(val y: Int)
class B(override val y: Int) extends A(y) {
def yy = y // `invokevirtual B.y`
}
there's also no field generated in B, but the call to y is a virtual call to B.y (parameter aliasing replaces the field read of y in the accessor B.y by super[A].y, it doesn't change yy).