Compiler could be smarter when dealing with exhaustive switch and final variables?
Jan Lahoda
jan.lahoda at oracle.com
Sat Oct 8 16:47:39 UTC 2022
Hi David,
Would you please have an exact reproducible testcase and the exact error that is produced?
Based on your e-mail, I tried:
---
class Test {
sealed interface SomeInterface {}
record First() implements SomeInterface {}
record Second() implements SomeInterface {}
private void exhaustiveSwitchWithFinalUninitialized()
{
final int numA;
final int numB;
SomeInterface abc = new First();
switch (abc) //compiler error unless we uncomment the default
{
case First f ->
{
numA = 1;
numB = 0;
}
case Second s ->
{
numA = 0;
numB = 1;
}
/*
default ->
{
numA = 2;
numB = 2;
}
*/
}
System.out.println(numA + "");
System.out.println(numB + "");
}
}
---
Which seems to build fine on a (custom build of) JDK 19b36 (jdk-19+36):
---
$ javac --enable-preview -source 19 /tmp/Test.java
Note: /tmp/Test.java uses preview features of Java SE 19.
Note: Recompile with -Xlint:preview for details.
---
I checked both the JLS and javac, at it seems that as long as the switch is exhaustive, the variables should be definitely assigned.
Thanks,
Jan
________________________________
From: amber-dev <amber-dev-retn at openjdk.org> on behalf of David Alayachew <davidalayachew at gmail.com>
Sent: Saturday, October 8, 2022 4:34 PM
To: Remi Forax <forax at univ-mlv.fr>
Cc: amber-dev <amber-dev at openjdk.org>
Subject: Re: Compiler could be smarter when dealing with exhaustive switch and final variables?
Hello Rémi,
First off, apologies for double sending - I sent to just you instead of to the mailing list too.
> Hi David,
> i think this is similar to
> https://bugs.openjdk.org/browse/JDK-8294670
> that i've reported recently.
Whoops, apologies. I even see linked in the bug report that the issue was even reported on the same mailing list barely a week ago. I've been falling behind on reading the mailing lists. I'll be sure to catch up before posting bug reports in the future.
> You can even abuse of the switch + record pattern
> to extract numA and numB :)
What a clever idea lol. Expressiveness be damned, but your solution actually works.
I have been trying to use local records more frequently, but there's this one annoying edge case that has made me shy away from using them as much (https://bugs.openjdk.org/browse/JDK-8287885). But if there's flexibility like this to be found, then it sounds like I should lean more into local records.
> BTW, this kind of code is less unreadable in languages like Scala where
> the switch is postfix to the value switched upon and not prefix.
I'm still digesting the comment about type classes that you mentioned last time. Seems like Scala has a lot to teach me as a language.
Thank you for your response and its insight!
David Alayachew
On Sat, Oct 8, 2022 at 6:22 AM Remi Forax <forax at univ-mlv.fr<mailto:forax at univ-mlv.fr>> wrote:
________________________________
From: "David Alayachew" <davidalayachew at gmail.com<mailto:davidalayachew at gmail.com>>
To: "amber-dev" <amber-dev at openjdk.org<mailto:amber-dev at openjdk.org>>
Sent: Saturday, October 8, 2022 9:07:31 AM
Subject: Compiler could be smarter when dealing with exhaustive switch and final variables?
Hello Amber Dev Team,
When if/else statements and final variables combine, the compiler can make some helpful assertions. Consider the following code.
sealed interface SomeInterface {}
record First() implements SomeInterface {}
record Second() implements SomeInterface {}
private void exhaustiveIfWithFinalUninitialized()
{
SomeInterface abc = new First();
final int numA;
final int numB;
if (abc instanceof First)
{
numA = -1;
numB = -1;
}
else if (abc instanceof Second)
{
numA = -2;
numB = -2;
}
else
{
throw new IllegalStateException("This shouldn't be possible!");
}
System.out.println(numA + "");
System.out.println(numB + "");
}
The above code compiles with no errors whatsoever. The compiler is smart enough to realize that, no matter what, numA and numB will most certainly be populated by the time we reach the print statements below the if statements.
However, the compiler does not make the same realization when working with exhaustive switches. But since the switch statement is exhaustive, shouldn't the compiler be able to make the same realization here? If anything, the switch should be capable of all of the above and more, correct?
private void exhaustiveSwitchWithFinalUninitialized()
{
final int numA;
final int numB;
SomeInterface abc = new First();
switch (abc) //compiler error unless we uncomment the default
{
case First f ->
{
numA = 1;
numB = 0;
}
case Second s ->
{
numA = 0;
numB = 1;
}
/*
default ->
{
numA = 2;
numB = 2;
}
*/
}
System.out.println(numA + "");
System.out.println(numB + "");
}
I would like it if the compiler could make the same realizations it did for the if statement. I should also add, this seems to apply to all forms of exhaustive switch except for those with default clauses.
Hi David,
i think this is similar to
https://bugs.openjdk.org/browse/JDK-8294670
that i've reported recently.
Obviously, this is easy to work around - I could just make a switch expression for each variable, and then just populate them individually. Alternatively, I could just put a default clause any time I need that compiler realization, though I give up some benefits from exhaustive switch as a result.
As a workaround, you can also group the values into a record (that can be local to the method) and use an expression switch and initialize the local variable after the switch.
You can even abuse of the switch + record pattern to extract numA and numB :)
record Pair(int numA, int numB) {}
switch(switch(abc) {
case First -> new Pair(1, 0);
case Second -> new Pair(0, 1);
}) {
case Pair(int numA, int numB) -> {
System.out.println(numA);
System.out.println(numB);
}
}
BTW, this kind of code is less unreadable in languages like Scala where the switch is postfix to the value switched upon and not prefix.
Thank you all for your time and help!
David Alayachew
regards,
Rémi
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20221008/b48e0ed3/attachment-0001.htm>
More information about the amber-dev
mailing list