-
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
A question about static forwarders for Java package private methods #14624
Comments
Sounds like a bug then, a fix would be appreciated!
Using -release means we read the JDK symbols from the special symbol files the JDK provide for each release instead of simply reading the classfiles, I guess the symbol files don't export these package private methods. |
For reference, the logic for adding forwarders in Scala 3 is in https://github.com/lampepfl/dotty/blob/1b25f653855ac485f9e0513be9b27c0f29725f6f/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala#L571-L612 |
I would love to take a crack at the bug fix, and I most likely will when I find some time. The question is however, fixing this issue means breaking JVM binary compatibility, as methods that used to be available in previously compiled code would disappear after the fix. Are there any concerns and procedures over that fact? |
If I understand this correctly, since the forwarders point to package private methods, calling them would result in a runtime error since they're not accessible, so removing them doesn't makes things any worse even if it technically breaks the ABI, and so we should be able to make an exception for it /cc @sjrd. |
That's a great summary, thanks. |
Yes, that's correct. At least, it's correct as long as the child Scala class is in a different package than the parent Java class. If they are in the same package, the call would in fact succeed. That said, it is still a relatively serious violation of encapsulation (that is only visible to Java), so I think we should fix it. |
Aha, found the culprit, when generating forwarders we check for accessibility using: diff --git compiler/src/dotty/tools/dotc/core/SymDenotations.scala compiler/src/dotty/tools/dotc/core/SymDenotations.scala
index 7be005ea4b8..e1a54325af0 100644
--- compiler/src/dotty/tools/dotc/core/SymDenotations.scala
+++ compiler/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -1388,7 +1388,7 @@ object SymDenotations {
final def accessBoundary(base: Symbol)(using Context): Symbol =
if (this.is(Private)) owner
else if (this.isAllOf(StaticProtected)) defn.RootClass
- else if (privateWithin.exists && !ctx.phase.erasedTypes) privateWithin
+ else if (privateWithin.exists && (!ctx.phase.erasedTypes || this.is(JavaDefined))) privateWithin
else if (this.is(Protected)) base
else defn.RootClass Do you mind making a PR for that with some appropriate tests? You can make a run test (in tests/run) with a main that checks what forwarders are generated using Java runtime reflection, similar to https://github.com/lampepfl/dotty/blob/main/tests/run/forwarder.scala / https://github.com/lampepfl/dotty/blob/main/tests/run/forwarder.check except using |
On it! |
Oops no, that should be classOf[Foo] since we're looking for the static forwarders defined in the class itself. |
Yes, I already figured that out. Can you please help me with how to run the test I wrote? |
run |
Compiler version 3.1.1
Minimized code
https://github.com/vasilmkd/static-forwarders-question
We encountered this issue in Cats Effect.
In the linked reproduction project, you can find two code paths, which correspond to each of our questions.
and finally:
Notice that the package private method exists in the Java abstract class bytecode output (as it should), it doesn't exist in the bytecode output of
MyObject$
(as it should), but it does exist in the bytecode output ofMyObject
as apublic static
forwarder method. Again, please take notice that this is a public static forwarder to a package private method.Our question is, why is this necessary, and how is this safe from an encapsulation viewpoint? As a reference, Scala 2.13 and 2.12 don't compile these public static forwarders.
and
Notice that, again, public static forwarders were created for package private methods (to verify you can check the source code of
ClassValue
here).Lately, we've been experimenting with using the
-release
flag of javac and scalac to publish Cats Effect using a modern JDK (17 and up), while keeping compatibility with JDK 8. Unfortunately, this broke our Scala 3 CI during the MiMa binary compatibility checks, with the following output (notice that the mentioned missing methods are all of the public static forwarders that were created for package private methods):We tried reproducing the issue locally, and we were indeed able to. The difference seems to be the
-release
flag on Scala 3, which makes Scala 3 not generate the public static forwarders methods, and indeed, MiMa reports this as a binary incompatible change, as it should.Again, we're only seeing this on Scala 3. Scala 2 does not generate these forwarder methods and the behavior doesn't change when
-release
is used (they are again, not generated).Our question here is, why does using
-release
change the behavior of Scala specific features in Scala 3 (AFAIK, it is Scala's choice as a language to generate static forwarder methods for better Java interop).If anything above is unclear or hard to follow, please do not hesitate to ask me for more clarification. Thank you in advance for reading.
cc @djspiewak @armanbilge
The text was updated successfully, but these errors were encountered: