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");

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.

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

Reply via email to