Compiler could be smarter when dealing with exhaustive switch and final variables?

David Alayachew davidalayachew at gmail.com
Sat Oct 8 14:34:10 UTC 2022


 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> wrote:

>
>
> ------------------------------
>
> *From: *"David Alayachew" <davidalayachew at gmail.com>
> *To: *"amber-dev" <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/62ebe641/attachment-0001.htm>


More information about the amber-dev mailing list