RFR: 8311038: Incorrect exhaustivity computation

Jan Lahoda jlahoda at openjdk.org
Thu Jun 29 13:30:03 UTC 2023


Consider this code:

public class Test {

    record Rec(Object t) {}

    private void test2() {
        Rec r = new Rec("test");
        int res = switch (r) {
            case Rec(String x): {
                yield x.length();
            }
            case Rec(Object x): {
                yield -3;
            }
        };
    }

}


It is obviously exhaustive (there's `Rec(Object x)`, which is unconditional on `Rec`), but javac fails to compile this code:

/tmp/Test.java:7: error: the switch expression does not cover all possible input values
        int res = switch (r) {
                  ^
1 error


The cause seems to be that when binding patterns for the (nested) `String` and `Object` are reduced, they are reduced to `String` and `ConstantDesc`, which is no longer exhaustive for `Object`.

This is a result of this code:
https://github.com/openjdk/jdk/blob/07734f6dde2b29574b6ef98eeb9e007d8801a3ea/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java#L886
which is intended to help with patterns that don't stand directly in the selector's hierarchy, like:
https://github.com/openjdk/jdk/blob/07734f6dde2b29574b6ef98eeb9e007d8801a3ea/test/langtools/tools/javac/patterns/Exhaustiveness.java#L1494
where `I3` is only related to the selector (`I`) via a permitted subtype.

Part of the problem is that `Object` is removed from the pattern set - adding `ConstantDesc` is not a problem. The proposal in this patch is to keep supertypes of the type that is being added in the pattern set. This will lead to pattern set `ConstantDesc`, `Object` and `String`, which should be OK for correctness, although generally enhancing the pattern set might slow down the processing.

There are two complicating factors, which make the observed bug surprising and difficult to diagnose:
 - if (in this example) `ConstantDesc` has not been completed yet, `.isSealed()` will return `false` for it, and the code will compile. This may happen when `x.length()` is removed/replaced in the example. This patch proposes to complete the Symbols before checking for sealedness.
 - it appears to be difficult to reproduce with a synthetic testcase, due to variations in ordering in maps. Hence this patch is using `String` in the testcase, as opposed to a test-only class.

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

Commit messages:
 - 8311038: Incorrect exhaustivity computation

Changes: https://git.openjdk.org/jdk/pull/14711/files
 Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=14711&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8311038
  Stats: 28 lines in 2 files changed: 26 ins; 0 del; 2 mod
  Patch: https://git.openjdk.org/jdk/pull/14711.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/14711/head:pull/14711

PR: https://git.openjdk.org/jdk/pull/14711


More information about the compiler-dev mailing list