[patterns-switch] Draft JLS spec for JEP 406 (Pattern Matching for Switch)

Gavin Bierman gavin.bierman at oracle.com
Mon May 17 16:48:04 UTC 2021

Dear experts,

I have amended the spec for JEP 406 (Pattern Matching for Switch) to reflect the consensus following the recent lengthy discussions on this list about two design changes, specifically:

  1.  treating total type patterns as match-everything switch labels (i.e. matching null)
  2.  requiring non-legacy switches to be total like switch expressions

The updated spec is available at:


Please let us know what you think!


PS: The details:

To capture the notion of a total type pattern matching everything, I have not followed the idea of extending the notion of pattern matching to be a three-place relation, taking a value, a compile-time type, and a pattern. This passing of a compile-time type around at *runtime* is not at all how the JLS works (which maintains a pretty strict phase distinction), and it doesn’t accurately reflect what we do in the compiler either. So, here’s how it has been specified:

1. I introduced a new pattern, called an *any pattern*. Right now, it’s a pattern that is expressible but not denotable, i.e. it is an error if it appears anywhere in the source program. It arises from a process of “resolving” a pattern...

2. There is a new notion of "resolving" a pattern with a type. It works like this:

A pattern P resolves with a type U as follows:

1. if P is T t where U <: T, P resolves to an any pattern `var t`, where the type of t is T; otherwise P resolves to P.

2. if P is var x, P resolves to an any pattern `var x` where the type of x is U;

3. if P is (Q), then P resolves to (Q'), where Q' is the result of resolving Q with U

4. if P is Q && e, then
    (i)  If Q && e is total for U then P resolves to Q' && e where Q' is the result of resolving Q with U;
    (ii.) If Q && e is not total for U then P resolves to P.

So the basic idea of resolving a pattern is that it replaces any total patterns with an any pattern.

Now, the runtime behaviour of pattern matching is specified to always be with respect to a resolved pattern.

In other words, with e instanceof P, we resolve P with the type of e to get a resolved pattern Q, and then we pattern match the value of e with the resolved pattern Q. With switches we resolve any pattern labels with the type of the selector expression before finding a switch label that matches.

Pattern matching can now be more fully specified as follows:

-   A value v (including null) matches `var x` and x is initialized to v.

-   The null reference does not match T t.

-   A non-null value v matches T t if v can be cast to T without raising a ClassCastException, and t is initialized to v; and does not match otherwise.

-   A value v matches p && e if (i) v matches p, and (ii) e evaluates to true; and does not match otherwise.

-   A value v matches (p) if v matches p; and does not match otherwise.

This means that total type patterns appearing as switch labels will now match all values *including null*, as they will be resolved to any patterns.

I have also changed the specification of the runtime semantics of switches by breaking it up into two different cases depending on whether the selector expression evaluates to `null` or not. If it does evaluate to null then we look for either a `case null` or a total pattern case label. If we find none then we NPE. Otherwise, we look at all the switch labels as before (including the legacy ones involving constants). This simplifies matters with the legacy labels so they don't need to explicitly exclude null.

I have added a new condition on switch statements that if it is a non-legacy switch then the switch block must be complete (the same condition as for switch expressions). The execution of a switch statement now has a new test: if no switch label matches in the body, then either (i) if it is a non-legacy switch then ICCE is thrown; or (ii) if it is a legacy switch then execution completes normally (as before).

Some other minor tweaks were also made.

On 23 Apr 2021, at 16:58, Gavin Bierman <gavin.bierman at oracle.com<mailto:gavin.bierman at oracle.com>> wrote:

Dear experts,

Apologies for the delay, but here is the first draft of the spec for JEP 406 (Pattern Matching for Switch):


As you will see, the design has evolved a little since we wrote the JEP (which I will update shortly). The chief changes are:

1. Instead of enumerating the special case labels, e.g. case null, default, case null, T t and so on; the notion of switch label has been made more general, with a set of restrictions to cut out the things we don’t want. In particular, this means that we get (a) symmetric versions of the compound patterns, e.g. case null, default and case default, null: and (b) we get a perfect alignment of the old : style with the new , style, e.g. we can write both `case null, default: …` and `case null: default: …` with identical treatment.

2.  I figured out a way to remove the restriction from the JEP that switches must be all pattern-matching, or all non-pattern-matching. Apart from simplifying matters, this allows for some nice new features, e.g.

   enum E { F, G, H }

   switch(...) { // A switch block with an enum constant and pattern label!
       case F -> …
       case E e -> … // Acts like a default but e is in scope!

A couple of odd extras come for the ride, just for your information:

switch(o) { /* empty */ }

is now well-typed for all types of o, as is:

switch(o) { default: … }

[Basically any type restrictions on the selector expression come from the switch labels. We still maintain that if you use a constant case label, the type of the selector must be char, byte, short, int, Character, Byte, Short, Integer, or String as in Java 16.]

As always, your opinions are welcomed!


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20210517/22178245/attachment.htm>

More information about the amber-spec-experts mailing list