Description
While we are going to abandon forward binary compatibility in the long run, we still need to preserve it for the 3.0 transition (probably until we switch to a 3.x library in 3.1 and tooling has been adapted to the new compatibility model).
Currently bridge methods are a big problem for forward compatibility. Example:
trait A[T] {
protected[this] def x: T
def foo: T = x
}
class B[T <: String](protected[this] val x: T) extends A[T] {
//override def foo: T = super.foo
}
Enabling the override in B
creates two separate methods:
public T foo();
descriptor: ()Ljava/lang/String;
flags: ACC_PUBLIC
public java.lang.Object foo();
descriptor: ()Ljava/lang/Object;
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Any caller that sees an instance of B
as B
will call the "real" method with the refined signature (due to the tighter upper bound of T
compared to the original version), callers that only see an A
call the bridge method (which overrides the inherited signature) instead. This is not forward compatible because the "real" method did not exist in the old version.
There is no good reason for the bridge method to exist in such a case, except to potentially avoid some casts due to the tighter bounds. Note that without the explicit override the class still overrides foo
in order to delegate to the interface's default implementation but this method uses the original bounds so there is no bridge method.
This case frequently comes up in the collections library where the C
and CC[_]
type parameters are refined through the hierarchy. There is no workaround via casts or @unsafSomething
annotations so we have to resort to hacking the override into the original implementation in some superclass using isInstanceOf
. Depending on access restrictions of other parts of the class this is not always possible. Where it is possible the extra type checks make all uses slower (even when not related to the class that should be changed) and create more work down the line to switch to the proper design the next time we can break forward compatibility.
We should add an annotation @nobridge
than can be added to an overridden method to prevent the generation of a bridge method. There are cases where a bridge method is really needed (when access restrictions get relaxed) but in most cases it could be suppressed. The only work required when breaking forward compatibility would be the removal of these annotations.