RFR: 8367530: The exhaustiveness errors could be improved [v5]

Aggelos Biboudis abimpoudis at openjdk.org
Thu Nov 13 11:41:15 UTC 2025


On Fri, 7 Nov 2025 09:56:44 GMT, Jan Lahoda <jlahoda at openjdk.org> wrote:

>> Consider code like:
>> 
>> package test;
>> public class Test {
>>     private int test(Root r) {
>>         return switch (r) {
>>             case Root(R2(R1 _), R2(R1 _)) -> 0;
>>             case Root(R2(R1 _), R2(R2 _)) -> 0;
>>             case Root(R2(R2 _), R2(R1 _)) -> 0;
>>         };
>>     }
>>     sealed interface Base {}
>>     record R1() implements Base {}
>>     record R2(Base b1) implements Base {}
>>     record Root(R2 b2, R2 b3) {}
>> }
>> ``` 
>> 
>> This is missing a case for `Root(R2(R2 _), R2(R2 _))`. javac will produce an error correctly, but the error is not very helpful:
>> 
>> $ javac test/Test.java
>> .../test/Test.java:4: error: the switch expression does not cover all possible input values
>>         return switch (r) {
>>                ^
>> 1 error
>> 
>> 
>> The goal of this PR is to improve the error, at least in some cases to something along these lines:
>> 
>> $ javac test/Test.java 
>> .../test/Test.java:4: error: the switch expression does not cover all possible input values
>>         return switch (r) {
>>                ^
>>   missing patterns: 
>>     test.Test.Root(test.Test.R2(test.Test.R2 _), test.Test.R2(test.Test.R2 _))
>> 1 error
>> 
>> 
>> The (very simplified) way it works in a recursive (or induction) way:
>> - start with defining the missing pattern as the binding pattern for the selector type. This would certainly exhaust the switch.
>> - for a current missing pattern, try to enhance it:
>>     - if the current type is a sealed type, try to expand to its (direct) permitted subtypes. Remove those that are not needed.
>>     - if the current (binding pattern) type is a record type, expand it to a record type, generate all possible combinations of its component types based on sealed hierarchies. Remove those that are not needed.
>> 
>> This approach relies heavily on our ability to compute exhaustiveness, which is evaluated repeatedly in the process.
>> 
>> There are some cases where the algorithm does not produce ideal results (see the tests), but overall seems much better than what we have now.
>> 
>> Another significant limitation is the speed of the process. Evaluating exhaustiveness is not a fast process, and this algorithm evaluates exhaustiveness repeatedly, potentially for many combinations of patterns (esp. for record patterns). So part of the proposal here is to have a time deadline for the computation. The default is 5s, and can be changed by `-XDexhaustivityTimeout=<timeout-in-ms>`.
>> 
>> There's also an open possibility for select tools to...
>
> Jan Lahoda has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Reflecting review comments.

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java line 1023:

> 1021: 
> 1022:             Set<PatternDescription> combinedPatterns =
> 1023:                     joinSets(basePatterns, replace(inMissingPatterns, toExpand, reducedAdded));

Suggestion:

    Set<PatternDescription> combinedPatterns =      
              Stream.concat(basePatterns.stream(), 
                                        replace(inMissingPatterns, toExpand, reducedAdded).stream())
                          .collect(Collectors.toSet());


Would something like that work, just to avoid the indirection in `joinSets` which is used only once?

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java line 1035:

> 1033:     /*
> 1034:      * Sort patterns so that those that those that are prefered for removal
> 1035:      * are in front of those that are preferred to remain (when there's a choice).

Suggestion:

     * Sort patterns so that those that are preferred for removal are in front
     * of those that are preferred to remain (when there's a choice).

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java line 1117:

> 1115:             }
> 1116: 
> 1117:             //assert?

Address this todo?

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java line 1137:

> 1135:                 }
> 1136:             }
> 1137:             Assert.check(index != (-1));

Small suggestion for understanding better what the assertion would imply.

Suggestion:

            // 'index' must be one of rootPatternRecord.nested; if not, `isUnderRoot` is inconsistent.
            Assert.check(index != (-1));

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java line 1179:

> 1177:      * with values from {@code updatedNestedPatterns}. Resulting {@code RecordPatterns}s
> 1178:      * are sent to {@code target}.
> 1179:      */

/*
     * Using {@code basePattern} as a starting point, generate new {@code
     * RecordPattern}s, such that all corresponding components but one, are the
     * same. The component described by the {@code replaceComponent} index is
     * replaced with all {@code PatternDescription}s taken from {@code
     * updatedNestedPatterns} and the resulting {@code RecordPatterns}s are sent
     * to {@code target}.
     */

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java line 1197:

> 1195: 
> 1196:     /* The stricness of determining the equivalent of patterns, used in
> 1197:      * nestedComponentsEquivalent.

Also in `computeCoverage`. Can you provide an example/sentence of two strictly equivalent patterns and two loosely equivalent?

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2523091742
PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2523095311
PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2523107981
PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2523121185
PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2523080301
PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2523051094


More information about the compiler-dev mailing list