[pattern-switch] Exhaustiveness

Brian Goetz brian.goetz at oracle.com
Fri Aug 21 15:14:57 UTC 2020


Yes, this is the sort of ordering I was aiming at.

> If the user does not want such implicit handling of an optimistically total situation in a statement switch, then it is always possible to provide explicit clauses “case null: break;” and “default: break;”.

Indeed, and this is why I was trying to break it down into a set of 
cases, to ensure that there always is a pattern the user can denote if 
they want to catch some part of the residue.  Where we are now is:

  - In a total switch (currently just switch expressions), any residue 
involving novel values gets ICCE, a null gets NPE, and any residue not 
in the above categories gets (something, maybe NPE, maybe something else.)

  - If the user explicitly wants Box(null), they have two choices: 
explicitly match Box(null), or, more likely, use some total pattern on 
Box (`Box(var x)`, `Box b`, etc.)  Similarly, if they want (for whatever 
reason) Box(novel), they can similarly use totality.  (I hope people are 
beginning to see why totality in nesting is so critical.)


So, next sub-subject (sub-ject?): when, and under what conditions, do we 
get NPE from non-total switches?  I said this yesterday:

> Separately (but not really separately), I'd like to refine my claim 
> that `switch` is null-hostile.  In reality, `switch` NPEs on null in 
> three cases: a null enum, String, or primitive box.  And, in each of 
> these cases, it NPEs because (the implementation) really does 
> dereference the target!  For a `String`, it calls `hashCode()`.  For 
> an `enum`, it calls `ordinal()`.  And for a box, it calls 
> `xxxValue()`.  It is _those_ methods that NPE, not the switch.  (Yes, 
> we could have designed it so that the implementation did a null check 
> before calling those things.)

I bring this up because these situations cause current switch to NPE 
even when the switch is not total, and this muddies the story a lot.  We 
can refine this behavior by saying: "If a switch *on enums, strings, or 
boxes* has no nullable cases, then there is an implicit `case null: NPE` 
at the beginning".

In other words, I am proposing to treat this "preemptive throwing" as an 
artifact of switching over these special types (which is fair because 
the language already gives these types special treatment.)  Then, we are 
free to treat residue-handling as a consequence of totality, not a 
general null-hostility of switch.


Let me repeat that, because it's a big deal.

     Switch is *not* null-hostile.  We were just extrapolating from too 
few data points to
     see it.

     Switches on _enums, strings, and boxes_, that do not explicitly 
have null-handling cases,
     are null-hostile, because switching on these involves calling 
methods on Enum, String,
     or {Integer,Long,...}.

     If you put a `case null` in a switch on strings/etc, it doesn't 
throw, it's just matching
     a value.

     In all other cases, null is just a value that can be matched, or 
not, and if the
     switch ignores its residue, the nulls leak out just like the rest 
of it.

     In the general case, switches throw only when they are total; for 
partial switches
     (e.g. statement switches), null is just another value that didn't 
get matched.

I believe this restores us to sanity.


Next up (separate topic): letting statement switches opt into totality.






Assuming that we're on the right track, and drilling into the next 
level, we now have to bring this back to totality.

On 8/20/2020 9:02 PM, Guy Steele wrote:
>
>> On Aug 20, 2020, at 6:14 PM, Brian Goetz <brian.goetz at oracle.com> wrote:
>>
>> I suspect there are other orderings too, such as "any nulls beat any novels" or vice versa, which would also be deterministic and potentially more natural to the user.  But before we go there, I want to make sure we have something where users can understand the exceptions that are thrown without too much head-scratching.
>>
>> If a user had:
>>
>>      case Box(Head)
>>      case Box(Tail)
>>
>> and a Box(null) arrived unexpectedly at the switch, would NPE really be what they expect?  An NPE happens when you _dereference_ a null.  But no one is deferencing anything here; it's just that Box(null) fell into that middle space of "well, you didn't really cover it, but it's such a silly case that I didn't want to make you cover it either, but here we are and we have to do something."  So maybe want some sort of SillyCaseException (perhaps with a less silly name) for     at least the null residue.
> I believe that if Head and Tail exhaustively cover an enum or sealed type (as was the intended implication of my example)—more generally, in a situation that is optimistically total---then the user would be very happy to have some sort of error signaled if some other value shows up unexpectedly in a statement switch, whether that value is “Ankle" or “null”.  Maybe a new error name would be appropriate, such as UnexpectedNull.
>
> If the user does not want such implicit handling of an optimistically total situation in a statement switch, then it is always possible to provide explicit clauses “case null: break;” and “default: break;”.
>
>> On the other hand, ICCE for Box(novel) does seem reasonable because the world really has changed in an incompatible way since the user wrote the code, and they probably do want to be alerted to the fact that their code is out of sync with the world.
> Yep.
>
>> Separately (but not really separately), I'd like to refine my claim that `switch` is null-hostile.  In reality, `switch` NPEs on null in three cases: a null enum, String, or primitive box.  And, in each of these cases, it NPEs because (the implementation) really does dereference the target!  For a `String`, it calls `hashCode()`.  For an `enum`, it calls `ordinal()`.  And for a box, it calls `xxxValue()`.  It is _those_ methods that NPE, not the switch.  (Yes, we could have designed it so that the implementation did a null check before calling those things.)

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


More information about the amber-spec-experts mailing list