Draft JLS Spec about unnamed patterns and variables

Brian Goetz brian.goetz at oracle.com
Thu Feb 23 18:46:16 UTC 2023


The language about "fall through between patterns" was motivated by 
uncertainty about the scope of bindings.  We could have been more 
precise (allowing you to fall through if you don't use `s`) but that 
seemed to be just inviting questionable code, so it was coarsened.

With the advent of binding-less patterns, it reopened the door here to 
both fall-through and case merging, but we really wanted the case 
merging.  So the spec could now be refined to "can't fall out of 
patterns _with bindings_."

If you're asking "do we want to allow fall through here", well, while we 
are not big fans of fallthrough, we're also not big fans of "go out of 
our way to prohibit features we don't like", because that often leads to 
more complexity in the language.  So it seemed that treating these two 
things -- case merging and fall-through -- equally.

On 2/23/2023 1:26 PM, Maurizio Cimadamore wrote:
>
>> Under this JEP this code could be rewritten blindly into:
>>
>> |switch (o) {       case Number _ -> 1;       case String _, Integer 
>> _-> 2; } |
>>
>> Under the definition of dead code above, the common case that was 
>> grouped together,|-> 2|, is not dead anymore. It can be reached 
>> via|*case String _*, Integer _-> 2|. As a result, the code above is 
>> correct. It just happens that the sub-pattern|Integer _|will never be 
>> reachable. This can be a warning but the overall case is correct.
>>
>> An alternative interpretation would be to treat sub-patterns as "dead 
>> code". Under that interpretation the second|case|of the second 
>> example would be dominated because there is at least one preceding 
>> sub-pattern (or whole case label with one pattern as in this case) 
>> that dominates at least one of its sub-patterns (|Integer _|). That 
>> case could be rejected (symmetrically to the first example). This 
>> seems restrictive but also a valid direction.
>>
>> So, my question is what would be the pros and cons of each approach?
>>
> Actually, it seems to me that you can rewrite the above as follows, 
> even w/o this JEP:
>
>
> |switch (o) {       case Number n -> 1;       case String s, Integer 
> i-> 2; }|
>
> If you do, the compiler complains, but not because `Integer i` is 
> unreacheable. But simply because we have fall-through between patterns.
>
>
> This is defined in 14.11.1:
>
>> t is a compile-time error if there is a statement in a switch block 
>> that consists of switch-labeled statement groups for which both of 
>> the following are true:
>>
>> 1.
>>
>>     It is labeled with a switch label that introduces a pattern variable.
>>
>> 2.
>>
>>     There is a statement preceding it in the switch block and that
>>     statement can complete normally (14.22
>>     <https://docs.oracle.com/javase/specs/jls/se18/html/jls-14.html#jls-14.22>).
>>
>> This condition is required to exclude the possibility of a switch 
>> labeled statement being reached for which a pattern variable declared 
>> in its switch label is in scope but without the pattern matching 
>> having succeeded. For example, the statement labeled by the switch 
>> label supporting the type pattern |Integer i| could be reached from 
>> the preceding statement group, and so the pattern variable |i| will 
>> not be initialized:
>>
>> ||
> Now, you could make a case that the above restriction is unnecessary 
> if we have unnamed pattern binding variables... and _if_ you go down 
> that path, yes, you do end up with an issue when it comes to dominance.
>
>
> But do we want to change the fall-through restriction?
>
>
> Cheers
> Maurizio
>
>
>>
>> Many, thanks,
>>
>> Aggelos
>>
>>
>> ------------------------------------------------------------------------
>> *From:* Brian Goetz <brian.goetz at oracle.com>
>> *Sent:* 26 January 2023 20:33
>> *To:* Angelos Bimpoudis <angelos.bimpoudis at oracle.com>; 
>> amber-spec-experts <amber-spec-experts at openjdk.java.net>
>> *Subject:* Re: Draft JLS Spec about unnamed patterns and variables
>> Small wording nit... in "an unnamed declaration can be used in place 
>> of the following declarations"
>>
>> I'm not sure "in place of" is the right wording; I think you may just 
>> want to say "in", since the grammar permits it in all of these 
>> places.  (What you're really doing here is signalling that there are 
>> places the grammar allows it, but the semantics do not; you are going 
>> to call these out individually in the appropriate places.)
>>
>> Similar for the second "in place of" in this section.
>>
>> In 14.11.1, I might refactor the text a little further. The second 
>> sentence of the first paragraph below is about case constants only, 
>> but now comes after you talk about case patterns or case constants:
>>
>>> A|case|label has either one or more|case|constants, ora*one or 
>>> more*|case|pattern*s*. Every|case|constant must be either (1) 
>>> the|null|literal, (2) a constant expression (15.29 
>>> <https://docs.oracle.com/javase/specs/jls/se19/html/jls-15.html#jls-15.29>), 
>>> or (3) the name of an enum constant (8.9.1 
>>> <https://docs.oracle.com/javase/specs/jls/se19/html/jls-8.html#jls-8.9.1>); 
>>> otherwise a compile-time error occurs. A|case|label that has 
>>> a|null||case|constant may have an optional|default|.
>>>
>>> It is a compile-time error if for any|case|label with more than 
>>> one|case|patterns, any of its|case|patterns declares one or more 
>>> pattern variables.
>>>
>>
>> I suggest:
>>
>> A|case|label has either one or more|case|constants, ora*one or 
>> more*|case|pattern*s*.
>>
>> /For a case label with case constants, /every|case|constant must be 
>> either (1) the|null|literal, (2) a constant expression (15.29 
>> <https://docs.oracle.com/javase/specs/jls/se19/html/jls-15.html#jls-15.29>), 
>> or (3) the name of an enum constant (8.9.1 
>> <https://docs.oracle.com/javase/specs/jls/se19/html/jls-8.html#jls-8.9.1>); 
>> otherwise a compile-time error occurs. A|case|label that has 
>> a|null||case|constant may have an optional|default|.
>>
>> /For a case label with case patterns/, it is a compile-time error if 
>> any of its|case|patterns declares one or more pattern variables.
>>
>> I am not sure about the definition of dominance here.  If I have:
>>
>>     case Integer _, String _:  A;
>>     case Number _ : B;
>>
>> Number dominates Integer, but it doesn't dominate Integer|String.  I 
>> think you mean "if at least one of pi..pn dominates *all* of the 
>> patterns ri..rm, no?
>>
>> But I'm not even sure if this is the right formulation, because:
>>
>>     sealed interface I permits A, B { }
>>     record A() implements I {}
>>     record B() implements I {}
>>
>>     case A _, B _: ...
>>     case I i: ...
>>
>> The first case label dominates I.  So I think you have to appeal to 
>> exhaustiveness:
>>
>> "A case label with case patterns p1...pm dominates another case label 
>> with case patterns q1...qm if the set of patterns { p1..pm } 
>> dominates each qi", no?
>>
>> You probably have to slightly refactor the second statement about 
>> "compile time error if dominance" accordingly.
>>
>>
>>
>>
>> On 1/26/2023 5:36 AM, Angelos Bimpoudis wrote:
>>> Dear experts,
>>>
>>> The first draft of the JLS spec about unnamed patterns and variables 
>>> (https://openjdk.org/jeps/8294349 
>>> <https://openjdk.org/jeps/8294349>) is available at:
>>>
>>> https://cr.openjdk.java.net/~abimpoudis/unnamed/latest/ 
>>> <https://cr.openjdk.java.net/~abimpoudis/unnamed/latest/>
>>>
>>> Comments very much welcomed!
>>> Angelos
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20230223/2f8afba5/attachment-0001.htm>


More information about the amber-spec-observers mailing list