JDK-8304394 : Compiler Implementation for Pattern Matching for switch
  • Type: CSR
  • Component: tools
  • Sub-Component: javac
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 21
  • Submitted: 2023-03-17
  • Updated: 2023-07-13
  • Resolved: 2023-05-12
Related Reports
CSR :  
Description
Summary
-------

Based on experiences with the several rounds of preview of pattern matching for switch, we propose a few improvements and simplifications. We also propose to make the feature final and permanent.

Problem
-------

Based on the experiences with the previous round of preview of the feature, we currently propose four changes:

 - parenthesized patterns should be dropped because they don't bring significant value in the current state of the feature; hence we propose to drop them.
 - qualified enum constants should be allowed as case labels. This also impacts switch exhaustivity.
 - the exhaustivity is newly defined to fix problems identified in the previous versions.
 - inside a guard, variables declared outside of the guard cannot be assigned

The feature will be made final and permanent.

Solution
--------

<h3>Parenthesized Patterns</h3>

The parenthesized patterns will be dropped. They will be removed from:

 - the specification
 - the implementation in javac
 - the Trees API. The ParenthesizedPatternTree API interface has been marked as a preview feature since introduction, and therefore can be removed.

<h3>Qualified Enum Constants</h3>

Qualified enum constants will be allowed as case labels:

    enum E1 { A, B, C; }
    enum E2 { C, D, E; }
    ...
    Object o = ...;
    switch (o) {
        case E1.A, E1.B, E1.C -> {}
        case E2.C, E2.D -> {}
    }

The existing unqualified enum constants will be preserved, in the existing context of an enum switch selector.

The exhaustivity determination will be naturally extended to include qualified enum constants: if all enum constants of an enum type are used in a set of switch constants, the set of switch constants is exhaustive for the given enum type.

<h3>New Definition of Exhaustivity</h3>

The current rules for switch exhaustivity see some obviously not-exhaustive switches as exhaustive. For example, code like:

    public class Test {

        private int test(R r) {
            return switch (r) {
                case R(A a, A b) -> 0;
                case R(A a, B b) -> 0;
                case R(B a, A b) -> 0;
                case R(B(String s), B b) -> 0;
            };
        }

        public sealed interface S permits A, B {}

        public final class A implements S {}

        public record B(Object o) implements S {}

        public record R(S a, S b) {}

    }

is considered to be exhaustive by the current rules, although the switch is obviously not exhaustive (does not cover `R` with nested `B` in the first component,  where the nested component of the `B` is not `String`). New rules are proposed which are solving this problem.

<h3>Variable Assignment in Guards</h3>

The current specification allows to effectively re-assign final variable inside guards (please see [here](https://mail.openjdk.org/pipermail/amber-dev/2023-March/007901.html)). The proposal is to disallow assignment to any variable inside case guards, unless the variable has been declared inside the guard.

<h3>Finalization of the Feature</h3>

The feature has been a preview feature for several releases. The proposal is to finalize it, making it final and permanent.

Specification
-------------

The specification draft is available for convenience [here](https://cr.openjdk.org/~gbierman/jep440%2B441/jep440%2B441-20230509/specs/patterns-switch-record-patterns-jls.html) and is attached as" 20230509 Pattern Matching for switch and Record Patterns.pdf". Please note that, for technical reasons, the specification draft also includes changes for JEP-440 (https://openjdk.org/jeps/440), which are reviewed under CSR JDK-8304401.

The specdiff of API changes is available [here](https://cr.openjdk.org/~jlahoda/8300543/specdiff.00/overview-summary.html) and is attached as specdiff.00.zip. Please note that, for technical reasons, the specdiff also includes changes for JEP-440 (https://openjdk.org/jeps/440), which are reviewed under CSR JDK-8304401.

The feature will be made final and permanent.

Comments
[~darcy] There is a small missing item from the spec. The definition of "can complete normally" for a switch statement needs to be changed [14.22] to account for the new possibility of a switch statement being exhaustive (if it is "enhanced"). The fourth bullet point for the "can complete normally" clause reads currently: - The switch block does not contain a default label. and needs to be amended to: - The *switch statement is not enhanced (14.11.2) and its* switch block does not contain a default label. Otherwise the definition does not spot that the following (enhanced) switch statement can not complete normally and so the following return statement is unreachable: static int test(String s) { switch (s) { // exhaustive case String s -> return -1; } return 0; // unreachable! } This change was already made to the compiler in October 2022 (https://bugs.openjdk.org/browse/JDK-8294670), so is purely a spec issue.
13-07-2023

Thanks [~gbierman], reaffirming Approval with the amended spec, pending successful attachment of the pdf.
13-07-2023

Moving to Approved.
12-05-2023

I've uploaded the specdiff as per the current PR, and Finalized the request. Looking forward to any feedback!
10-05-2023

[~gbierman], from a process perspective, is this CSR ready to be Finalized? Thanks.
09-05-2023

[~darcy] I uploaded the latest spec. Two main changes: 1. An improved treatment of type inference for record patterns. This is already implemented in the compiler. 2. Removed any patterns and the process of resolving patterns in favour of a compile-time property of type patterns. (This is a purely specification improvement and has no semantic content.) Hopefully makes the spec more readable.
09-05-2023

Moving to Provisional.
12-04-2023

The following program contains a switch that is triply exhaustive. But the specification doesn't require the implementation to diagnose this. That is a shame, as this probably occurs as a result of an error. package tryjava; sealed interface Bool permits True, False {} final class True implements Bool {} final class False implements Bool {} record SB2(Bool x1, Bool x2) {} class Program { public static void main(String args[]) { Bool t = new True(); Bool f = new False(); var sb2 = new SB2(t, f); switch (sb2) { case SB2(True x1, True x2): break; case SB2(False x1, False x2): break; case SB2(True x1, False x2): break; case SB2(False x1, True x2): break; case SB2(var x, var y): break; default: break; } } }
11-04-2023