-
Notifications
You must be signed in to change notification settings - Fork 3.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
Emit bridge method forwarders with BRIDGE flag #7035
Conversation
Need to also backport #7017 |
I'm a little worried about binary compatibility though. Do we have a reason to believe that these forwarders for bridges were never selected by the Java 8 compiler? It seems the extra forwarders don't have any special flags in bytecode (scala/bug#10812). Just removing the forwarders might break existing java classfiles. As a workaround, we could try to mark forwarders for bridge methods with additional flags (SYNTHETIC and/or BRIDGE) and see how the java compilers (8, 11, intellij, eclipse) like that. |
This does not look binary compatible. For example, a call to
I was about the suggest the same as Lukas; we can be bug compatible in 2.12.x by marking these as |
2.12.7-bin-LOCAL20180810 (added flags)Simply adding flags didn't fix the problem. public static akka.testkit.TestKitSettings get(akka.actor.ActorSystem);
descriptor: (Lakka/actor/ActorSystem;)Lakka/testkit/TestKitSettings;
flags: (0x1049) ACC_PUBLIC, ACC_STATIC, ACC_BRIDGE, ACC_SYNTHETIC
public static akka.actor.Extension get(akka.actor.ActorSystem);
descriptor: (Lakka/actor/ActorSystem;)Lakka/actor/Extension;
flags: (0x1049) ACC_PUBLIC, ACC_STATIC, ACC_BRIDGE, ACC_SYNTHETIC 2.12.7-bin-LOCAL20180809 (backport)The patched version emits one forwarder for public static akka.testkit.TestKitSettings get(akka.actor.ActorSystem);
descriptor: (Lakka/actor/ActorSystem;)Lakka/testkit/TestKitSettings;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC 2.12.62.12.6 emits two public static akka.testkit.TestKitSettings get(akka.actor.ActorSystem);
descriptor: (Lakka/actor/ActorSystem;)Lakka/testkit/TestKitSettings;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
public static akka.actor.Extension get(akka.actor.ActorSystem);
descriptor: (Lakka/actor/ActorSystem;)Lakka/actor/Extension;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC Does this mean that I have to figure out which condition is emitting the bad forwarder and put the flags only for those? |
2.12.7-bin-LOCAL20180810b (added BRIDGE flag only to one of them) public static akka.testkit.TestKitSettings get(akka.actor.ActorSystem);
descriptor: (Lakka/actor/ActorSystem;)Lakka/testkit/TestKitSettings;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
public static akka.actor.Extension get(akka.actor.ActorSystem);
descriptor: (Lakka/actor/ActorSystem;)Lakka/actor/Extension;
flags: (0x0049) ACC_PUBLIC, ACC_STATIC, ACC_BRIDGE Found a combination that seems to be working. |
// Without it, Java 11 finds them to be ambiguous. (scala/bug#11061) | ||
val membersAfterPickler = exitingPickler(moduleClass.info.membersBasedOnFlags(BCodeHelpers.ExcludedForwarderFlags, symtab.Flags.METHOD)).toList | ||
val members = moduleClass.info.membersBasedOnFlags(BCodeHelpers.ExcludedForwarderFlags, symtab.Flags.METHOD) | ||
for (m <- members) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we, instead, get the method symbols in the same way as before, and just make sure we copy over the bridge
flags to the forwareder methods? this would be the least risky change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean like:
- isBridge = !membersAfterUncurry.contains[Symbol](m),
+ isBridge = m.isBridge,
? If I did that the test fails:
[info] Test scala.lang.annotations.BytecodeTest.bridgeFlag started
[error] Test scala.lang.annotations.BytecodeTest.bridgeFlag failed: java.lang.AssertionError: expected:<List(f()Ljava/lang/String;0x9, f()Ljava/lang/Object;0x49)> but was:<List(f()Ljava/lang/String;0x9, f()Ljava/lang/Object;0x9)>, took 2.672 sec
[error] at scala.lang.annotations.BytecodeTest.$anonfun$bridgeFlag$1(BytecodeTest.scala:30)
[error] at scala.lang.annotations.BytecodeTest.$anonfun$bridgeFlag$1$adapted(BytecodeTest.scala:24)
[error] at scala.collection.immutable.List.foreach(List.scala:388)
[error] at scala.lang.annotations.BytecodeTest.bridgeFlag(BytecodeTest.scala:24)
so maybe isBridge
is not set at the symbol?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eed3si9n ah, that was because bridges were not included in the member search... so instead, the member search returned the method from the superclass (which the bridge would then override / implement), and this one doesn't have the bridge flag of course. Here's a fix: https://github.com/scala/scala/compare/2.12.x...lrytz:pr7035?expand=1. I think you can squash all these commits into one and label it [nomerge]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. Thanks for the hand holding :) A squashed commit is pushed with [nomerge]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about it, the tests should be in https://github.com/scala/scala/blob/2.12.x/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala. Otherwise LGTM!
Fixes scala/bug#11061 Ref scala/bug#10812 On 2.13.x branch scala#6531 removed the mirror class forwarders for bridge methods. I would like to do same in 2.12.x since Java 11-ea started to find them ambiguous as seen in akka/akka#25449 / scala/bug#11061. To keep binary compatibility, I am still emitting the forwarders for bridge methods, but with `ACC_BRIDGE` flag.
@lrytz Yea. That makes sense. |
/rebuild |
Thanks, @eed3si9n! |
@@ -1161,7 +1170,12 @@ object BCodeHelpers { | |||
val ExcludedForwarderFlags = { | |||
import scala.tools.nsc.symtab.Flags._ | |||
// Should include DEFERRED but this breaks findMember. | |||
SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO | |||
// Note that BRIDGE is *not* excluded. Trying to exclude bridges by flag doesn't work, findMembers | |||
// will then include the member from the parent (which the bridge overrides / implements). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is true. Unfortunately there was an unintended side-effect: before this PR, the findMembers
call for forwarder methods would return the member from the parent overridden by the bridge. This member is potentially abstract, in which case the forwarder is not emitted (https://github.com/scala/scala/blob/v2.12.7/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala#L892).
After this PR, findMembers
returns the bridge, which is concrete, so a forwarder is added. This is the cause for scala/bug#11207.
In 2.12.7, scala#7035 added the `bridge` flag to static forwarders that are generated for bridge methods. (2.13 geneartes no forwarders for bridges, but we wanted to stay binary compatible in 2.12.) Unfortunately the change caused even more bridges to be generated, namely for bridge methods that implement an abstract member. Now we exclude them again, which brings the binary interface back to the state of 2.12.6. Fixes scala/bug#11207
In 2.12.7, scala#7035 added the `bridge` flag to static forwarders that are generated for bridge methods. (2.13 geneartes no forwarders for bridges, but we wanted to stay binary compatible in 2.12.) Unfortunately the change caused even more bridges to be generated, namely for bridge methods that implement an abstract member. Now we exclude them again, which brings the binary interface back to the state of 2.12.6. Fixes scala/bug#11207
In 2.12.7, scala#7035 added the `bridge` flag to static forwarders that are generated for bridge methods. (2.13 geneartes no forwarders for bridges, but we wanted to stay binary compatible in 2.12.) Unfortunately the change caused even more bridges to be generated, namely for bridge methods that implement an abstract member. Now we exclude them again, which brings the binary interface back to the state of 2.12.6. Fixes scala/bug#11207
In 2.12.6 and before, the Scala compiler emits static forwarders for bridge methods in top-level modules. These forwarders are emitted by mistake, the filter to exclude bridges did not work as expected. These bridge forwarders make the Java compiler on JDK 11 report ambiguity errors when using static forwarders (scala/bug#11061). PR scala#7035 fixed this for 2.12.7 by adding the `ACC_BRIDGE` flag to static forwarders for bridges. We decided to keep these bridges for binary compatibility. However, the new flag causes the eclipse Java compiler (and apparently also IntelliJ) to report ambiguity errors when using static forwarders (scala/bug#11271). In 2.13.x the Scala compiler no longer emits static forwarders for bridges (PR scala#6531). This PR brings the same behavior to 2.12.8. This change breaks binary compatibility. However, in the examples we tested, the Java compiler emits references to the non-bridge methods, so compiled code continues to work if a library is replaced by a new version that doesn't have forwarders for bridges: ``` $> cat T.scala class A[T] { def get: T = ??? } object T extends A[String] { override def get: String = "hi" } $> ~/scala/scala-2.12.7/bin/scalac T.scala ``` Generates two forwarders in `T.class` ``` // access flags 0x49 public static bridge get()Ljava/lang/Object; // access flags 0x9 public static get()Ljava/lang/String; ``` ``` $> javac -version javac 1.8.0_181 $> cat Test.java public class Test { public static void main(String[] args) { System.out.println(T.get()); } } $> javac Test.java ``` Generates in Test.class ``` INVOKESTATIC T.get ()Ljava/lang/String; ```
Fixes scala/bug#11061
Ref scala/bug#10812
On 2.13.x branch #6531 removed the forwarder for bridge methods. I would like to do same in 2.12.x since Java 11-ea started to find them ambiguous as seen in akka/akka#25449 / scala/bug#11061.
The problem statement in scala/bug#10812 was:
The situation has changed in Java 11-ea, which @chbatey reported in akka/akka#25449 / scala/bug#11061:
This shows that Java 11-ea thinks the forwarder to be ambiguous.
To keep binary compatibility, I am still emitting the forwarder for bridge methods, but with
ACC_BRIDGE
flag. I've locally built this backport and tested it in the repro build https://github.com/eed3si9n/override-in-jdk11.2.12.6 on Java 11-ea:
2.12.7-bin-LOCAL20180810b (added BRIDGE flag to one of them)
javap: