-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Allowing contravariant type parameter has inconsistent values breaks the dynamic binding #22373
Comments
just as notice, same behaviour in scala 2.13.16 |
@XYZboom I am not yet entirely convinced that we should not allow it but maybe should emit a warning. Maybe not that intuitive but
It only inherits the method from Base and due to
it would result in an error with But back to SubBase, since we have two overloads and I guess they resolve to different upper bounds after type erasure, we kind of get the following after type erasure:
if B were defined as
But again, back to SubBase, you could have overriden Put differently, if you define SubBase as follows:
then you would see twice the same output as your implementation follows LSP (but I agree, it can ask for trouble, but it is a handy feature sometimes, so please don't take it away from me 👼). |
@robstoll I agree to use 'warning' instead of prohibiting users from doing so. But I think this is still not safe enough, perhaps it should be prohibited and a compiler option should be set to allow users to turn on this feature, just like some checks in typescript. Besides, I have another solution. The cause of this bug is that user defined // User defined SubBase
class SubBase extends Base, SubB {
override def func(e: SubA): Unit = {
println("SubBase SubA")
}
}
// The actually SubBase generated by compiler
class SubBase extends Base, SubB {
// ACC_BRIDGE ACC_SYNTHETIC
override def func(e: A): Unit = {
if (e.isInstanceOf[SubA]) {
func(e.asInstanceOf[SubA])
return
}
// if user overrides this function in SubBase,
// replace super call with user defined codes.
// if super function is abstract, the above if an this super call is no need.
return super.func(e)
}
override def func(e: SubA): Unit = {
println("SubBase SubA")
}
} But this solution will loop endlessly on your last example. And does not work when |
Compiler version
3.6.3-RC2, 3.6.4-RC1-bin-20241231-1f0c576-NIGHTLY
Minimized code
Output
Expectation
Since
b1
andb
are the same object and call functions with the same signature, their behavior should be the same.Reason why this bug occurs
As far as we know, JVM will use bridging methods to call functions in subclasses after type erasure. When calling a method in something such as
B[SubA]
, the actually called method is this bridge method.The following is the result of using the
javap -c -s -v B1
to view the bridging method of ClassB1
:We can see a
ACC_BRIDGE, ACC_SYNTHETIC
in this method. Actually, this method overridesdef func(e: E): Unit
in traitB
. That's whyB1
performed AS EXPECTED.But in class
SubBase
the bridge method calls the method inBase
:public void func(A); descriptor: (LA;)V flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: invokestatic #22 // InterfaceMethod Base.func$:(LBase;LA;)V 5: return
Since the bridge method in the JVM must exist, we cannot allow
SubBase
to have inconsistent values. Although semantically feasible.The text was updated successfully, but these errors were encountered: