Switch labels (null again), some tweaking

Brian Goetz brian.goetz at oracle.com
Wed Apr 28 16:29:31 UTC 2021



> Let's say I start with this:
>
>
> switch (lunch) {
>         case Box(Soup s) {
>              if (s == null) {
>                   System.err.println("Box of null");
>              } else {
>                   System.err.println("Box of soup");
>              }
>         }
>
>         case Bag(Soup s): {
>              if (s == null) {
>                   System.err.println("Bag of null");
>              } else {
>                   System.err.println("Bag of soup");
>              }
>
>         }
>
> }
>

I assume that you are saying Box permits Soup only.  But your 
assumptions about "where do the nulls go" here are not right. Box(Soup) 
does not match Box(null); the set of patterns { Box(Soup) } is total on 
Box(Lunch) _with remainder Box(null)_.  So the null paths in this 
example are dead.  (Also missing break statements.)  So rewriting, this 
switch is really equivalent to:

     switch (lunch) {
         case Box(Soup s):
               System.err.println("Box of soup");
               break;

         case Bag(Soup s):
              System.err.println("Bag of soup");
              break;

         /* implicit */
         case Box(null), Bag(null): throw new NPE();
     }

and the switch is total on Container(Lunch) under the Lunch=Soup, 
Container=Box|Bag assumptions.


> Then Sandwich is added to the hierarchy. The switch no longer 
> compiles, I have to make it total. The following, which is, I believe, 
> the first thing that will come to mind:
>
> switch (lunch) {
>         case Box(Soup s) {  /* same as before */ }
>         case Box(Sandwich s) { ... }
>         case Bag(Soup s): { /* same as before */ }
>         case Bag(Sandwich s) { ... }
> }
>

With the correct version of the first switch, your first idea for fixing 
it to accomodate sandwiches is correct!  The switch is now total on 
Container<Lunch>, the old cases handle the same thing they did, the new 
cases handle the new cases, and the implicit cases (Box(null) etc) 
handle the same thing they did.

> But if it is considered exhaustive, then this will compile, but the 
> null handling logic will be in the wrong place, and will be 
> essentially dead code (which the user might be unaware of). 

No, because it was dead in the first place too.  Box(null) and Bag(null) 
were always treated as remainder.




More information about the amber-spec-experts mailing list