Switch labels (null again), some tweaking

forax at univ-mlv.fr forax at univ-mlv.fr
Thu Apr 29 15:19:48 UTC 2021


----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>, "Maurizio Cimadamore" <maurizio.cimadamore at oracle.com>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Jeudi 29 Avril 2021 16:52:28
> Objet: Re: Switch labels (null again), some tweaking

> Without diving into the translation details prematurely, I just want to
> note that we *do* want the compiler to "totalize" things for us,
> because, DA.  Suppose we have A permits B, C:
> 
>     int x ;
>     switch (a) {
>         case B(int foo): x = foo; break;
>         case C(int bar): x = bar; break;
>     }
>     // HERE
> 
> There is some remainder -- null, D -- but we don't want that remainder
> to leak past the switch; when we get to HERE, we'd like x to be DA, and
> a to have matched one of the patterns.  So the compiler has to insert
> _something_ to handle the remaining cases, even if it is only:
> 
>     default: throw new TotalityException("blah blah");
> 

Not necessarily, the synthetic switch can use default for the last case

  int x;
  var carrier = invokedynamic (a);
  switch(carrier.dispatch) {
    case 0:
      int foo = carrier.foo();
      x = foo;
      break;
    default:
      int bar = carrier.bar();
      x = bar;
      break;
  } 

and let the runtime part bound to invokedynamic to take care of the remainder.  

> But, the details of code generation are not what M is asking about now
> -- he's asking "if I were the user, could I write a truly covering
> switch?"  And the answer is yes, but the shape of the remainder is
> "spiny" and can get complicated quickly.

Ok

Rémi

> 
>> Not necessarily.
>>
>> First, let's acknowledge that you are moving the discussion from the basic
>> semantics to talk about the translation strategy which touch more the binary
>> compatibility,
>> aka, what is the semantics when the view of the world at compile time and at
>> runtime are disagreeing.
>>
>> There are at least two reasons to let javac emits such cases,
>> first like with null as remainder, you have to add a lot of cases once you have
>> sub-patterns, so it will bloat the bytecode,
>> then if we insert such cases, it means that we also have a code that throw a
>> specific exception bolt into the bytecode which means that we can not change
>> the binary compatibility story later.
>>
>> The jobs to implement the binary compatibility semantics is the job of the
>> runtime not the compiler, like with lambdas, where the LambdaMetaFactory does
>> some runtime checks before generating + linking the lambda proxy,
>> I expect a SwitchMetafactory to do the same job. This is more flexible and
>> allows the runtime to be updated when the VM change by example, the
>> lambdaMetafactory code was changed with the introduction of hidden classes.
>>
>> Now, the big question is what binary compatibility semantics we want ?
>>
>> We can by example throw a subclass of LinkageError upfront during the linking
>> when we detect that the sealed type as more subclasses that the one accessible
>> at compile time, but it means that adding a new subclass to a sealed type is
>> not a binary compatible change anymore. We can delay the detection the an
>> incompatible class until we see one at runtime, so adding a new subclass is a
>> compatible change but calling a switch with an instance of such subclass is
>> not.
>>
>> There is also the question about the order of the cases, should the order of the
>> cases be validated at runtime or not, by example, if we have B < A at compile
>> time but A < B at runtime with a switch that like case B first and case A
>> second.
>>
>>> Maurizio
> > Rémi


More information about the amber-spec-experts mailing list