----- Mail original ----- > De: "Brian Goetz" <brian.go...@oracle.com> > À: "Remi Forax" <fo...@univ-mlv.fr>, "Maurizio Cimadamore" > <maurizio.cimadam...@oracle.com> > Cc: "amber-spec-experts" <amber-spec-experts@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