Thanks! And yes, ModuleFinder.of(Path...) specifies the run-time
derivation, and the runtime follows it exactly. My question is about the
compile-time half: javac derives the same *name* from the file name but
never the version, so a module compiled against an automatic module records
no version in its compiled *requires* attribute in module-info.class, while
the same jar has one at runtime, for example in stacktraces. There is
therefore no record of what version of an automatic module a named module
was compiled against.

As an example:

mkdir -p pkg demo
cat > pkg/Boom.java <<'EOF'
package pkg;
public class Boom { public static void boom() { throw new
IllegalStateException("boom"); } }
EOF
javac -d classes pkg/Boom.java
jar --create --file plain.jar -C classes .

With this, I get the automatic module described without a version as:

java --module-path . --describe-module plain

If I change the file name however, I get a version:

mv plain.jar org.example.plain-2.33.jar

This is expected. I would however expect the same of javac which does not
include a version in module-info.class:

cat > module-info.java <<'EOF'
module demo.app { requires org.example.plain; }
EOF
cat > demo/Main.java <<'EOF'
package demo;
public class Main { public static void main(String[] args) {
pkg.Boom.boom(); } }
EOF
javac --module-path . -d out module-info.java demo/Main.java

If I now inspect the compiled module-info.class, the 2.33 version is
missing:

javap -v out/module-info.class | sed -n '/^Module:/,/exports/p'

Running the app using java does however surface the module:

java --module-path .:out -m demo.app/demo.Main

Where 2.33 is visible in the stack trace. I would have expected that javac
considers the version of the automatic module the same way as java does,
but I am wondering if this is intended or an oversight.

Thanks, Rafael

Am Do., 27. Aug. 2026 um 09:51 Uhr schrieb Alan Bateman <
[email protected]>:

>
> (re-sending with the right domain name)
>
>
> -------- Forwarded Message --------
> Subject: Re: [External] : Different handling of automatic module version
> by java and javac
> Date: Thu, 27 Aug 2026 08:25:47 +0100
> From: Alan Bateman <[email protected]> <[email protected]>
> To: Rafael Winterhalter <[email protected]> <[email protected]>,
> core-libs-dev <[email protected]> <[email protected]>,
> [email protected]
>
> On 26/08/2026 22:34, Rafael Winterhalter wrote:
>
> Hei,
>
> I have run into an asymmetry between javac and the runtime in how the
> version of an automatic module is treated, and I would like to know whether
> it is intended.
>
> At runtime, ModulePath.deriveModuleDescriptor derives both the name and the
> version of an automatic module from the jar file name. It splits the name
> at the first "-" followed by digits, parses the tail with
> ModuleDescriptor.Version.parse and, if that succeeds, calls
> builder.version(vs). The version is therefore part of the automatic
> module's descriptor, and it is visible through Module::getDescriptor and in
> stack traces.
>
> javac derives only the name. Locations.inferModuleName contains a copy of
> the same regular expression, under the comment "from
> ModulePath.deriveModuleDescriptor", but it uses the match only to truncate
> the name and never captures the tail. ModuleSymbol.version is assigned in
> just two places, ClassReader (from the module-info.class of the required
> module) and Modules (from --module-version), so for an automatic module it
> stays null and ClassWriter writes a requires_version_index of 0.
>
> So the version exists at run time but is absent from the compiled
> descriptor of the module that requires it. Compiling against a named module
> whose descriptor carries a version does record that version in requires.
>
> Is dropping the derived version in javac intended? If it is, is that
> written down anywhere? ModuleDescriptor.Requires.compiledVersion() says
> only "the version of the module if recorded at compile-time", which does
> not say when it is recorded.
>
>
> ModuleFinder.of(Path..) [1] is where the derivation of the module name and
> version are specified. Would it be possible to send a few examples where
> you see the different behavior between compile-time and run-time.
>
> -Alan
>
> [1]
> https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/lang/module/ModuleFinder.html#of(java.nio.file.Path...)
>

Reply via email to