[deconstruction-patterns] Unconditional patterns and null (again)
Brian Goetz
brian.goetz at oracle.com
Tue Oct 25 15:08:57 UTC 2022
There is a (deliberate) gap between an unconditional pattern and a set
of patterns that is exhaustive at a type. `R r` is unconditional on R,
but `R(var x)` is not, because null -- but the set of patterns { R(var
x) } is _exhaustive_ at R. Null is in the _remainder_ of { R(var x) }
on R. So the spec says what it means and the compiler is implementing
that, but ... we are not necessarily doing the user a favor here.
The question is how much analysis the compiler should do to catch errors
surrounding remainder. Because a switch without a `case null` already
rejects null, we could conclude that the null covered by switch and the
values covered by { R(var x) } together cover everything, and therefore
the default is dead. But with the spec we have, since we don't compute
the remainder explicitly, this would likely have to be a special case
for "can we prove that null is the only value in the remainder set".
Note that a similar analysis would be needed to make this exhaustive
when we get to constant patterns:
Boolean b = ...
switch (b) {
case TRUE: ...
case FALSE: ...
}
For now, I think it would be prudent for the compiler to issue a warning
here, and we'll take a note to look into tightening the analysis for
"null \union everything but null" cases.
On 10/25/2022 5:59 AM, Tagir Valeev wrote:
> Hello!
>
> I've just discovered that recent builds of JDK 19 and JDK 20 accept
> this code, which fails with NPE at runtime:
>
> class Test {
> record R(int x) {}
>
> static void test(R r) {
> switch (r) {
> case R(int x) -> System.out.println("matched");
> default -> System.out.println("not matched");
> }
> }
>
> public static void main(String[] args) {
> test(null);
> }
> }
>
> If I remember correctly, it was rejected previously with error similar
> to this (you can emulate the error replacing `R(int x)` with `R r1`
>> Test.java:7: error: switch has both an unconditional pattern and a default label
> I see that in a recent spec draft [1] it's said that:
>> Note that record patterns are not unconditional at any type because the null reference does not match any record pattern.
> Which makes sense. And it looks like that compiler follows the spec.
> However, we all know that the default branch is not reachable in this
> switch, so we effectively accept unreachable code. Is this intended or
> should be changed somehow?
>
> With best regards,
> Tagir Valeev
>
> [1] https://docs.oracle.com/javase/specs/jls/se19/preview/specs/patterns-switch-record-patterns-jls.html#jls-14.30.3
More information about the amber-spec-observers
mailing list