JDK-8284528 : Compiler implementation for Pattern Matching for switch (Third Preview)
  • Type: CSR
  • Component: tools
  • Sub-Component: javac
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 19
  • Submitted: 2022-04-07
  • Updated: 2022-05-23
  • Resolved: 2022-05-10
Related Reports
CSR :  
Relates :  
Description
Summary
-------

Pattern matching for switch previewed in JDK 17 ([JEP 406](https://bugs.openjdk.java.net/browse/JDK-8213076)) and JDK 18 ([JEP 420](https://bugs.openjdk.java.net/browse/JDK-8273326)). It is proposed to preview for a third time in JDK 19, incorporating support for record patterns ([JEP 405](https://bugs.openjdk.java.net/browse/JDK-8260244)) and with adjustments based on feedback from preview rounds.

Problem
-------

More time is needed to gain experience with the pattern matching for switch feature. Following feedback from the first and second round of preview, some small amendments are being made.

Solution
--------

Pattern matching for switch will be a preview feature in JDK 19. The spec and compiler will be adjusted based on the current feedback.

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

The updated JLS draft for pattern matching for switch is attached as jep427+405-20220426.zip, and is also available for convenience [here](http://cr.openjdk.java.net/~gbierman/jep427+405/jep427+405-20220426/specs/patterns-switch-record-patterns-jls.html)

Note that the JLS draft also includes changes to support _Record Patterns_ ([JEP 405](https://openjdk.java.net/jeps/405)). An overview of how record patterns affect _Pattern Matching for switch_ is given in the [CSR for Record Patterns](https://bugs.openjdk.java.net/browse/JDK-8284529).

Other than support for record patterns, the third preview of _Pattern Matching for switch_ incorporates the following changes which are mostly informed by feedback from the second preview:

 * The guarded pattern is dropped, and is replaced with a guard on pattern-based case labels. This means that nested patterns can no longer have their own guards.
 * The guard separator is no longer '&&'. A new contextual keyword, 'when', is defined for this purpose.
 * Both switch expressions and switch statements allow unconditional patterns.
 * Null handling is now a property of the switch expression/statement, not of pattern matching.
 * A new exception, `java.lang.MatchException`, is introduced for the cases where a switch is exhaustive at compile time, but not at run time. This can arise due to anomalous separate compilation:
   * A sealed interface has a different set of permitted subtypes at run time than it had at compile time.
   * An enum class has a different set of constants at run time than it had at compile time.

For example, this previous code:

    Object o = ...;
    switch (o) {
        case null, String s && !"true".equals(s) -> {}
    }

would now be written as:

    Object o = ...;
    switch (o) {
        case null, String s when !"true".equals(s) -> {}
    }

The API changes are attached as specdiff.00.zip, and are also available here:
http://cr.openjdk.java.net/~jlahoda/8282274/specdiff.00/overview-summary.html

The API changes mostly reflect the removal of the guarded patterns, the introduction of the guards on case labels, and the addition of `MatchException`.

The changes to the specification and API are a subject of change until the CSR is finalized.
Comments
Thanks [~gbierman]; moving updated request to Approved.
10-05-2022

[~darcy] Attached updated JLS as requested.
10-05-2022

While I agree the JLS should speak about setting the cause, I wonder if it is relevant here. This is a CSR for type test patterns in switches, and there should be no calls to accessors under this CSR?
10-05-2022

Moving to Provisional with one comment to resolve. If the intention is to have MatchException have its cause set by by the abrupt completion of a record accessor, > If execution of the invocation of the accessor method completes > abruptly then pattern matching completes abruptly by throwing a > MatchException. then the JLS spec should state that. For comparison, with try-with-resources (14.20.3), the text summarizes the handling of how exception are suppressed: > An exception from the closing of one resource does not prevent the > closing of other resources. Such an exception is suppressed if an > exception was thrown previously by an initializer, the try block, or > the closing of a resource. and the desugaring in 14.20.3.1 gives the exact operational semantics of suppression.
10-05-2022

> A question not necessary only related to this proposal: does the default clause > need to be deemed reachable to avoid a compilation error? For example, let say > one writes to write code that is robust to evolution of the system around it, > including the set of enum constants. A enum-based expression switch could > include cases for all the constants (A, B, & C) that exist in the compilation > environment, but may also want to include a default clause to handle any enum > constants present in the runtime environment that are not present at compile > time. Strictly speaking, the default clause would not be reachable. Such code > with an unreachable-at-compile-time default clause compiles under JDK 19. I > assume the intention is to allow analogous code to keep compiling. I don't think we need to add anything. Reachability (14.20) doesn't look at switch _labels_. Unreachable labels are covered by _dominance_, which doesn't say that a complete set of enum constants could dominate default. The only thing that can dominate a default is an unconditional pattern. In the example for a complete set of enum constants being exhaustive, we do actually say: > Note that a default switch label is permitted, but not required in the case where all the enum constants appear in the switch labels. So we explicitly state that a default is still allowed.
28-04-2022

> Note obsolete syntax is being used in the example below: > 6.3.3.2 Record Pattern > if (o instanceof Point(int x, int y) && (x == y)) { // Not the pattern Point(int x, int x)! This isn't obsolete syntax, it's a conditional-AND where the LHS is a pattern instanceof and the RHS is an equality check. (It's a reason why we moved away from using guarded patterns - you don't need them in instanceof, only really for switch!)
28-04-2022

Regarding constructors of the MatchException, javac will need a variant with and without the cause Throwable, and while it currently uses the variants without message, I'd prefer to have an option to start using the exception message if needed without changing the library. So, if it would not be too difficult, I'd keep the constructors. Alternatively, javac would need to start to use the variants with a message (and with or without a Throwable), and we would delete the variants without a message. Please let me know if it is OK to keep the constructors. Thanks.
27-04-2022

Moving to Provisional, not Approved. I recommend making MatchException final or sealed, whichever is necessary for the implementation. While providing the four conventional constructors is the default guidance for exception types, given that this is not a general purpose exception meant to be instantiated by users, I suggest only including those constructors that would be used by the platform. Note obsolete syntax is being used in the example below: > 6.3.3.2 Record Pattern > > if (o instanceof Point(int x, int y) && (x == y)) { // Not the pattern > Point(int x, int x)! A question not necessary only related to this proposal: does the default clause need to be deemed reachable to avoid a compilation error? For example, let say one writes to write code that is robust to evolution of the system around it, including the set of enum constants. A enum-based expression switch could include cases for all the constants (A, B, & C) that exist in the compilation environment, but may also want to include a default clause to handle any enum constants present in the runtime environment that are not present at compile time. Strictly speaking, the default clause would not be reachable. Such code with an unreachable-at-compile-time default clause compiles under JDK 19. I assume the intention is to allow analogous code to keep compiling.
15-04-2022