Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available
Brian Goetz
brian.goetz at oracle.com
Tue Nov 1 19:58:38 UTC 2022
> ---
>
> 5.5: There's this "downcast compatible" notion that was apparently introduced with instanceof pattern matching. It means a cast is legal and not unchecked. Ok. But—not all legal casts are downcasts, right? It's pretty confusing to use a term that suggests that they are.
I tripped over this a few times as well, until I just memorized what
"DC" meant. A better name is welcome.
> 6.3.3.1: Maybe someday we'll have expressions nested in patterns, but for now isn't it meaningless to ask whether a pattern variable is in scope within another part of the same pattern?
This may be leftover from when we had guarded patterns `P && e`, where
`e` was part of the pattern?
> - It's weird that 'null' is the one case constant that can't come after a 'default', given that 'null' is the one value that can't be handled by a 'default'. :-) Since we have to allow other case constants after 'default' anyway, I think it's best to allow 'null' as one more. ("default dominates everything except constants" is a rule I can remember.)
I suspect that this can be simplified further, and will have to be
anyway when we do underscore (since we will be allowing multiple
patterns on a single case.)
> 14.11.1: I don't think we prevent this scenario:
>
> switch (obj) { case String s: case Object o: }
>
> The rules about duplicate pattern labels apply to "a statement is labeled...". There's no labeled statement here.
>
> I think we should probably prohibit this, for the same code hygiene reason that we prohibit it before a statement group that doesn't try to use any of the variables.
>
> (This would not be a legal switch expression, because switch expressions can't fall out. But enhanced switch statements can, as long as they're exhaustive.)
Let's be clear about why we'd be prohibiting this. Arguably, this could
be OK as long as there is no use of either `s` or `o` here. But that
seems pretty useless.
The basis for disallowing should be that you can't fall into, or out of,
a case label that declares a pattern variable.
> 14.11.2: Design suggestion: rename "enhanced switch" to "pattern switch", define it as only those switches that make use of patterns, and don't worry about the remaining "switch with new features" corner cases. It's just such an important concept that I think there's a benefit to making the distinction really clean and obvious. E.g., asking someone new to Java to memorize the ad hoc set of types that don't demand exhaustiveness seems unhelpfully complicated.
>
> (Corner cases I'm thinking about: want to use a null constant but not be exhaustive? Fine. Want to have an Object input type but an empty switch body? Pointless, but fine. Etc.)
I think the motivation here is that we want to minimize the surface area
of non-exhaustive switches, by quarantining the "need not be exhaustive"
to those that would have compiled under Java 8. "Switches must be
exhaustive, except for statement switches over T1..Tn with all constant
labels."
> 14.11.3, 15.28.2: Opinionated take: we're continuing to throw ICCE when an unmatched enum constant slips through a switch, because it would be a (behaviorally) incompatible change to throw something else. Meanwhile, unmatched sealed classes get a MatchException. I think there are a tiny number of people who would notice if we changed from ICCE to MatchException for enums too, and a lot more people who will have to cope with the historical baggage going forward if we don't. We should just standardize on MatchException.
Probably better, it's a small incompatibility. No one could be relying
on the ICCE...
> 14.14.2: I'm sure there's been some discussion about this already, but the use of MatchException for nulls in 'for' loops but NPE for nulls in switches also seems like a sad historical wart. What's wrong with an NPE from a 'for' loop?
I think if the RHS of the foreach loop is null, we should NPE (as
before.) But if one of the _elements_ of the RHS array/iterable is
null, then we should ME on the record pattern. (Otherwise we have a
sharp edge between a top-level record pattern and a nested record pattern.)
> 14.30.1: Parenthesized patterns are no fun for a spec writer. :-) Are they actually useful? I'm not sure I've seen an example demonstrating what they're for. (The JEP only talks about them abstractly.)
They were added when we were doing guarded patterns and never took them
out. They will be useful again when we have & and | for patterns; right
now they're just taking up space.
> 14.30.1, 14.30.2: I'm not sold on *any patterns*, *resolved patterns*, and *executable switch blocks*.
>
> The semantics are fine—some type patterns will match null, others will not. But it seems to me that this can be a property of the type pattern, one of many properties of programs that we determine at compile time. No need to frame it as rewriting a program into something else. (Compare the handling of the '+' operator. We don't rewrite to a non-denotable "concatenation expression".)
>
> Concretely:
> - The pattern matching runtime rules can just say "the null reference matches a type pattern if the type pattern is unconditional".
> - We can make it a little more clear that a type pattern is determined to be unconditional, or not, based on its context-dependent match type (is that what we call it?)
>
> For a *compiler*, it will be useful to come up with an encoding that preserves the compile-time "unconditional" property in bytecode. But that's a compiler problem, not something JLS needs to comment on.
It is a property of the pattern *and the type being matched*. We tried
writing it the other way and it was not necessarily better....
> 14.30.3: A record pattern can't match null, but for the purpose of dominance, it's not clear to me why a record pattern can't be considered unconditional, and thus dominate the equivalent type pattern or a 'default'.
Unconditional means "no runtime checks"; it's like a static cast for
which we don't emit a checkcast. Exhaustive means "satisfies the type
system, but might have bad values" (null, novel enum constants, novel
subtypes, at any level in the tree.)
More information about the amber-spec-experts
mailing list