On Fri, 23 Jan 2026 16:23:20 GMT, Jan Lahoda <[email protected]> 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:
>> Root(R2(R2 _), R2(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 delay the more detailed
>> computation to some late...
>
> Jan Lahoda has updated the pull request incrementally with one additional
> commit since the last revision:
>
> Attempting to use the number of 'base checks' as the limit, instead of
> wall-clock time.
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java line 771:
> 769: exhaustivenessResult.notExhaustiveDetails()
> 770: .stream()
> 771: .sorted((pd1, pd2) ->
> pd1.toString().compareTo(pd2.toString()))
Maybe we could have pattern descriptions be comparable?
src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
line 43:
> 41: # option name the name of a command line option
> 42: # path a path
> 43: # patter a pattern/pattern description
Suggestion:
# pattern a pattern/pattern description
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2727309680
PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2727310628