7

I found this minimal set of code which seems to break any Java version I can find. (Also reproducible in https://www.onlinegdb.com/online_java_compiler with Java 23.0.2+7-58). The example is not meant to do anything useful except to demonstrate the issue — my actual code is more complex.

record VehicleInput(Vehicle vehicle) {}
sealed interface Vehicle permits Car {}
record Car(String licensePlate) implements Vehicle {}
sealed interface Animal permits Cat, Dog {}
record Cat() implements Animal {}
record Dog() implements Animal {}
class VehicleProgressor {
 private boolean progress(VehicleInput input, Animal animal) {
 if (input.vehicle() instanceof Car(String licensePlate)) {
 return true;
 }
 return switch (animal) {
 case Cat cat -> true;
 case Dog dog -> true;
 };
 }
}
public class Main {
 public static void main(String[] args) {
 System.out.println(Runtime.version());
 new VehicleProgressor();
 }
}

(Versions managed with sdkman)

$ sdk install java 24.0.1-amzn
$ sdk use java 24.0.1-amzn

(The error output is exactly the same with java 21.0.7-amzn, java 24.0.1-amzn, java 25.ea.25-open, and java 25.ea.25-graal.)

Compiling works:

$ javac Main.java

but running fails with:

$ java Main
24.0.1+9-FR
Exception in thread "main" java.lang.VerifyError: Inconsistent stackmap frames at branch target 96
Exception Details:
 Location:
 VehicleProgressor.progress(LVehicleInput;LAnimal;)Z @96: aload_3
 Reason:
 Type top (current frame, locals[5]) is not assignable to 'Cat' (stack map, locals[5])
 Current Frame:
 bci: @50
 flags: { }
 locals: { 'VehicleProgressor', 'VehicleInput', 'Animal', 'Animal', integer }
 stack: { integer }
 Stackmap Frame:
 bci: @96
 flags: { }
 locals: { 'VehicleProgressor', 'VehicleInput', 'Animal', 'Animal', integer, 'Cat' }
 stack: { }
 Bytecode:
 0000000: 2bb6 0007 3a05 1905 c100 0d99 0015 1905
 0000010: c000 0d4e 2db6 000f 3a06 1906 3a04 04ac
 0000020: 2c59 b800 1357 4e03 3604 2d15 04ba 0019
 0000030: 0000 ab00 0000 001a 0000 0002 0000 0000
 0000040: 0000 0024 0000 0001 0000 002e bb00 1d59
 0000050: 0101 b700 1fbf 2dc0 0022 3a05 04a7 000a
 0000060: 2dc0 0024 3a06 04ac 4ebb 001d 592d b600
 0000070: 282d b700 1fbf
 Exception Handler Table:
 bci [21, 24] => handler: 104
 Stackmap Table:
 same_frame(@32)
 append_frame(@42,Object[#50],Integer)
 same_frame(@76)
 same_frame(@86)
 append_frame(@96,Object[#34])
 full_frame(@103,{Object[#43],Object[#8],Object[#50]},{Integer})
 same_locals_1_stack_item_frame(@104,Object[#38])
 at Main.main(Main.java:30)

Is this a bug in Java/JDK?

If I change the class in the above code into:

class VehicleProgressor {
 private boolean progress(VehicleInput input, Animal animal) {
 if (input.vehicle() instanceof Car) {
 return true;
 }
 return switch (animal) {
 case Cat cat -> true;
 case Dog dog -> true;
 };
 }
}

or into:

class VehicleProgressor {
 private boolean progress(VehicleInput input, Animal animal) {
 if (input.vehicle() instanceof Car(String licensePlate)) {
 return true;
 }
 return true;
 }
}

or into:

class VehicleProgressor {
 private boolean progress(Vehicle vehicle, Animal animal) {
 if (vehicle instanceof Car(String licensePlate)) {
 return true;
 }
 return switch (animal) {
 case Cat cat -> true;
 case Dog dog -> true;
 };
 }
}

it runs fine just printing the version number.

The super strange part is that I'm not even calling the progress method anywhere. I also tried having everything in separate files as public interfaces/records, but that made no difference.

Thom A
97.7k12 gold badges67 silver badges102 bronze badges
asked Jun 4, 2025 at 14:01
10
  • 7
    I don't think the compiler is expected to produce invalid bytecode for any source code. The JVM verifies a class file during link time, so whether or not you call progress is irrelevant. Commented Jun 4, 2025 at 14:09
  • 7
    Could you please report this issue to [email protected] or report it through the Oracle bug tracker at bugreport.java.com. Thanks Commented Jun 4, 2025 at 14:22
  • 1
    @JorgeCampos The Eclipse IDE uses the Eclipse Java Compiler (EJC) and not javac. Commented Jun 4, 2025 at 16:04
  • 3
    @JorgeCampos Both compilers are pretty much independent so they have different bugs. They are just both written in an attempt to adhere to the same specifications (specifically the JLS). Sometimes EJC is right and sometimes javac is right. Having multiple compilers can be useful as a mutual quality check - if they behave differently, one of them likely has a bug (or in very rare cases, the specification might not be exactly clear for example on whether record Abc(){} is a valid module-info.java IIRC). Commented Jun 4, 2025 at 16:17
  • 3
    "Is this a bug in Java/JDK?" -- Yes (from the javadoc of VerifyError: "Thrown when the "verifier" detects that a class file, though well formed, contains some sort of internal inconsistency or security problem.") Commented Jun 4, 2025 at 18:15

1 Answer 1

6

Turned out to be a clear bug. Reported based on @Jorn Vernee's suggestion to [email protected] .

Based on the commenters, seems to only be an issue with javac and not with Eclipse Compiler for Java (ECJ).

Update: The bug could be reproduced with an even more minimal example; PR for the bugfix with additional details: https://github.com/openjdk/jdk/pull/25849

Targeted to be included in version 26: https://bugs.openjdk.org/browse/JDK-8358801

answered Jun 4, 2025 at 18:54
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.