Final variable initialization problem with exhaustive switch
Dimitris Paltatzidis
dcrystalmails at gmail.com
Tue Nov 30 12:49:27 UTC 2021
>
> seems like
> it should be a bug, right? But it’s not.
>
Agree, it is just playing it too safe.
We can translate the above switch statement into an if, without losing the
semantics:
if (p == Parity.ODD) {
a = 1;
} else if (p == Parity.EVEN) {
a = 0;
}
int b = a + 1; //Compile time error: variable a might not have been
initialized
The above if is exhaustive, yet no one expects last statement to compile,
and that is totally fine.
So, reducing the switch to the above if, makes it clear why the compiler
can't guarantee about the switch too.
But, maybe it should. You see, to let the above if compile is an overkill,
but with the switch, we have the notion
of its context, and especially with enums and sealed types, we can take it
a step further and make assumptions
about its exhaustiveness.
The switch expression guarantees initialization of final variables, based
on whether it compiles or not, which by
itself depends on its exhaustiveness.
The switch statement does not have to be exhaustive to compile and
therefore there isn't here that compilation
barrier that guarantees the initialization. But, that doesn't have to be
the case. Our switch statement in question
is *Effectively *exhaustive. So, the compiler doesn't need to prove that a
switch statement is exhaustive, it is
sufficient to just prove that it is effectively exhaustive. It is similar
to the effectively final variables we have with
lambdas.
Of Course, at the end of the day, it's not about exhaustiveness, it's about
having a branch that is guaranteed
to perform the initialization. That is what we are effectively trying to
prove.
Lately, the switch has been greatly enhanced, especially in the
exhaustiveness department. Getting the
switch statement on track in that aspect, seems more consistent.
Στις Τρί, 30 Νοε 2021 στις 9:20 π.μ., ο/η John Rose <john.r.rose at oracle.com>
έγραψε:
> On Nov 29, 2021, at 12:04 PM, Dimitris Paltatzidis <
> dcrystalmails at gmail.com> wrote:
>
>
> How can the compiler prove that the final variable will be initialized only
> in the second case and not in the first too?
>
>
> That differing treatment of definite assignment seems like
> it should be a bug, right? But it’s not. In fact, your switch
> expression can (under certain circumstances) execute
> correctly without executing either of its case branches.
>
> Switch expressions are inherently exhaustive across their cases.
> Switch statements are not. This is a key difference.
>
> You can see this if you take your code and comment out
> one of the cases, say for Parity.EVEN. The switch expression
> errors out at compile time, but the switch statement is a
> happy camper.
>
> Or, if someone comes along and adds a third member to Parity
> (say, NEITHER) then your switch expression will either fail to
> compile statically or (if not recompiled) will fail to terminate
> normally at runtime (throwing something).
>
> Meanwhile, the corresponding switch statement will happily
> recompile, and (whether recompiled or not) will fall through
> when Parity.NEITHER is presented. In that case, the variable
> in question will not have been initialized.
>
> This is a problem with legacy switch statements. I’m not sure
> what the solution will be, although I know various alternatives
> have been discussed at some point. The problem is that the
> language cannot read the user’s mind, when it finds that
> (a) an enum is the subject of a switch statement but (b) not
> all members of the enum are mentioned in the cases. Is it
> an error in a switch that was intended to be exhaustive?
> Or is it just that the user (that lazy user!) didn’t want to
> mention cases which needed no-op actions?
>
> — John
>
More information about the amber-dev
mailing list