Java 19 Pattern Matching Compilation Error: "the switch statement does not cover all possible input values"
Asked Answered
B

2

10

Using the Brian Goetz article: https://www.infoq.com/articles/data-oriented-programming-java/

sealed interface Opt<T> { 
    record Some<T>(T value) implements Opt<T> { }
    record None<T>() implements Opt<T> { }
}

This compiles and runs as expected. The exhaustive pattern matching works:

Opt<String> optValue = doCalc(value);
switch (optValue) {
  case Opt.Some<String> some -> System.out.printf("got string: %s%n", some.value());
  case Opt.None<String> none -> System.out.printf("got none%n");
};

This variation where I use the new Record patterns preview feature, breaks the exhaustive pattern matching, where this won't compile without adding a default case statement:

Opt<String> optValue = doCalc(value);
switch (optValue) {
    case Opt.Some<String>(String v) -> System.out.printf("got string: %s%n", v);
    case Opt.None<String> none -> System.out.printf("got none%n");
};

With OpenJDK Runtime Environment (build 19-ea+32-2220), I get the compilation error: the switch statement does not cover all possible input values.

When I add a default case statement, and the program works, but I don't get exhaustive pattern matching.

If I remove the record pattern matching, the program works.

If I create a variation of this without generics, that uses sealed classes, exhaustive pattern matching, and record patterns, it works.

However, it seems the combination of record patterns, generics and exhaustive pattern matching does not work.

Bub answered 21/6, 2022 at 15:20 Comment(5)
You can recompile the interface and add new values which doesn't break binary compatibility, but does break compile time safety without a default case.Handcraft
@Bub did you try to remove generics from the example? Does it work when you do so?Patrinapatriot
@PanagiotisBougioukos, if I remove generics, it works. I'm not doing some exotic edge case, this seems a very simple example. I presume they will fix this closer to the GA release.Bub
JDK is in rampdown so I'd suggest posting this on the compiler issue list as soon as possible, otherwise if this is a bug, it won't be fixed until 20.Commendam
Either @BrianGoetz article is wrong or javac has a bug.Commendam
B
3

This is a known bug in Java 19. This was confirmed by Brian Goetz himself on the amber-dev mailing list.

UPDATE: This issue is completely fixed in Java 20.

Bub answered 6/10, 2022 at 17:31 Comment(0)
P
5

JDK 19 has only Early-Access Builds available now.

I think I have found the important part for this question in the JEPS 427 documentation

The progress of Pattern Matching (Third Preview) could also be tracked from here.

According to the following JEPS example, I would expect this to work, so I think that you should wait first for the first JDK 19 build for general availability and try again. If it still not works then I think you should proceed and report it as a bug.

Some extra care is needed when a permitted direct subclass only implements a specific parameterization of a (generic) sealed superclass. For example:

sealed interface I<T> permits A, B {}
final class A<X> implements I<String> {}
final class B<Y> implements I<Y> {}

static int testGenericSealedExhaustive(I<Integer> i) {
    return switch (i) {
        // Exhaustive as no A case possible!
        case B<Integer> bi -> 42;
    }
}

The only permitted subclasses of I are A and B, but the compiler can detect that the switch block need only cover the class B to be exhaustive since the selector expression is of type I.

I also think that the comment from @Kayaman is not the case here as it is explicitly stated in the documentation how this case is handled to be safe.

To defend against incompatible separate compilation, in this case of a switch over a sealed class where the switch block is exhaustive and there is no match-all case, the compiler automatically adds a default label whose code throws an IncompatibleClassChangeError. This label will only be reached if the sealed interface is changed and the switch code is not recompiled. In effect, the compiler hardens your code for you.

Patrinapatriot answered 21/6, 2022 at 17:23 Comment(0)
B
3

This is a known bug in Java 19. This was confirmed by Brian Goetz himself on the amber-dev mailing list.

UPDATE: This issue is completely fixed in Java 20.

Bub answered 6/10, 2022 at 17:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.