bug
bug copied to clipboard
Inconsistent behavior of -release (JEP 247)
Given these equivalent Scala and Java sources, and compilers running on JDK 17:
$ cat Main.scala
import jdk.jfr.internal.Repository
class Main {}
$ cat Main.java
import jdk.jfr.internal.Repository;
public class Main {}
$ java -version
java version "17.0.11" 2024-04-16 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.11+7.1 (build 17.0.11+7-LTS-jvmci-23.0-b34)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.11+7.1 (build 17.0.11+7-LTS-jvmci-23.0-b34, mixed mode, sharing)
javac does not allow the use of the the internal API, with or without an explicit --release 17 because it always limits access based on modules:
$ javac Main.java
Main.java:1: error: package jdk.jfr.internal is not visible
import jdk.jfr.internal.Repository;
^
(package jdk.jfr.internal is declared in module jdk.jfr, which does not export it)
1 error
$ javac --release 17 Main.java
Main.java:1: error: package jdk.jfr.internal is not visible
import jdk.jfr.internal.Repository;
^
(package jdk.jfr.internal is declared in module jdk.jfr, which does not export it)
1 error
Without --release the module can be made accessible with --add-exports but this is rejected when used with --release (whether the requested version matches the current JDK version or not):
$ javac --add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED Main.java
$ javac --release 17 --add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED Main.java
error: exporting a package from system module jdk.jfr is not allowed with --release
1 error
$ javac --release 11 --add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED Main.java
error: exporting a package from system module jdk.jfr is not allowed with --release
1 error
Instead of using --release it is possible to manually set both -source and -target version and override the system module path (the module equivalent of the bootclasspath) to get the same effect as --release while allowing the use of --add-exports:
$ javac -source 11 -target 11 --system /Users/stefan.zeiger/.sdkman/candidates/java/11-zulu-local Main.java --add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED
scalac has several inconsistencies with this scheme:
-
By default scalac is not module-aware. It allows access to private APIs without warnings or errors:
$ scalac Main.scala -
Setting an explicit release version enforces module access but only if the version is lower than the current JDK version, otherwise it is treated the same as not using modules:
$ scalac -release:17 Main.scala $ scalac -release:11 Main.scala Main.scala:1: error: object internal is not a member of package jdk.jfr import jdk.jfr.internal.Repository ^ 1 error -
There are no equivalents of
--add-exportsand--systemand thus no way to target an older release without enforcing strict module access.
(This is about Scala 2; I tested it with 2.13.14 but AFAICT there are no changes in this area in any recent 2.13 or 2.12 version since the feature was added; I have not tried it with Scala 3)
The ticket for module support https://github.com/scala/scala-dev/issues/529
Simply adding support for --system without touching any of the other stuff looks straight-forward.
$ /Users/stefan.zeiger/code/scala/build/quick/bin/scalac -target 11 --system /Users/stefan.zeiger/.sdkman/candidates/java/11-zulu-local Main.scala
It still ignores module access but now you can compile against an older image version like with javac. Since javac does not support the combination of --add-exports and --release it is reasonable (and probably unavoidable) the scalac does the same, so if scalac ever gets full module support, module access will be enforced for the loaded image with or without --system and a new --add-exports option has to be added to allow exceptions.
(My prototype implementation closes the system image after each compilation which is probably not good for performance in long-running compile servers. I'll have to run some benchmarks and possibly add caching.)