-
Notifications
You must be signed in to change notification settings - Fork 71
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
Can't detect binary incompatibility when trait extends abstract class #225
Comments
FYI, similar (but maybe different) issue #118 |
(cherry picked from commit 8938a51)
Just re-reading this, I wonder if the fact that class B is |
The fact that class B is abstract is not relevant: the reproduction fails the same and MiMa doesn't detect it the same. The fact that it's a class and not a trait is relevant, when it's a trait MiMa doesn't report it for Scala 2.12+, which is correct because Scala 2.12+ implements it as default methods. That's what #118 was about. |
Well it looks like the problem might be that in class B
trait A extends B
|
This is as expected. An interface cannot extend a class. Similar to a self-type, the fact that A2 extends B is not visible to Java but unlike a self-type it is externally visible in Scala so the compiler may make use of it. The incompatibility in the example is really the trait instantiation in I can't think of a way of detecting this short of examining the Scala signatures, which means MiMa needs to be able to read those for all supported Scala versions. |
(one of the motivations for scala/scala-dev#601) |
I see. Makes sense, but it's also tragic. So public traits that don't extend Object are binary hazards. How do we help users like Yoshida-san from running into these problems? @sjrd, as a fellow binary compatibility advocate, does that change your stance on scala/scala-dev#601? |
Forgot to say: thanks Stefan for the explanation. Tbh this has kind of blown my mind. If declare a class extends the trait does scalac gives it the traits (non) superclasses? I guess it makes type A not a subtype of B even though it extends class B. Confusion. |
i don't follow entirely, but think of a trait extending a class as a downsteam obligation for any classes extending that trait (same as with a self type) -- the earliest opportunity, we'll add that trait's declared superclass as a parent for a class that mixes in the trait |
so, any instance of the trait will also be an instance of its declared superclass |
Yeah I get the mechanics now, but it breaks my mental model (invariant) that extends means parent and supertype. Still learning fundamentals in Scala, it's incredible/scary. |
Agreed -- again, the surprise element is why I think we should get rid of it, but I guess I should argue that over at the other ticket :-) |
It's similar to a trait that can't be compiled to an interface (which was most traits before 2.12, fewer now). When you create a class that extends the trait the compiler has to add all the stuff that was not allowed in the interface. |
Yeah, I'm with Adriaan, we should remove from trait that which is not allowed in an interface. |
No, because you can reproduce the exact same issue with a self-type, and no one is advocating getting rid of self types. |
How so? Welcome to Scala 2.12.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_202-ea).
Type in expressions for evaluation. Or try :help.
scala> abstract class B {
| def foo: Int = 42
| }
defined class B
scala> trait A { self: B => }
defined trait A
scala> val a = new A {}
<console>:12: error: illegal inheritance;
self-type A does not conform to A's selftype A with B
val a = new A {}
^ |
package test
trait B {
def foo(): Int = 42
}
package test
object App {
def main(args: Array[String]): Unit = {
val b = new B {}
println(b.foo())
}
} $ ~/opt/scala-2.12.6/bin/scalac Lib.scala
$ ~/opt/scala-2.12.6/bin/scalac -cp . App.scala
$ ~/opt/scala-2.12.6/bin/scala -cp . test.App
42
package test
abstract class A {
def bar(): Int = 42
}
trait B { self: A =>
def foo(): Int = bar()
} $ ~/opt/scala-2.12.6/bin/scalac Lib.scala
$ ~/opt/scala-2.12.6/bin/scala -cp . test.App
java.lang.ClassCastException: test.App$$anon$1 cannot be cast to test.A
at test.B.foo(Lib.scala:8)
at test.B.foo$(Lib.scala:8)
at test.App$$anon$1.foo(App.scala:5)
at test.App$.main(App.scala:6)
at test.App.main(App.scala)
at ... |
Thanks, it's slightly different, but fundamentally the same problem. Note to self's brain-MiMa:
In addition to the other binary compatibility hazardous problems, like super calls, fields, and etc... Maybe I'll just avoid traits altogether... |
That's why a |
library x v1
library x v2
mima said library x v1 => v2 binary compatible.
another library y
main
run main
The text was updated successfully, but these errors were encountered: