Description
Looking at various parts of the patterns feature specification, some small issues came up:
Constant vs. context type
In section Type checking and pattern required type, it is not obvious how to deal with the situation where the constant expression has a type which is unrelated to the context type, or a situation where inference fails. Do we ever conclude that "this constant pattern is wrong"? Note that this topic may overlap with #2632.
class C<X extends num> {
const C();
}
void main() {
// Context type `int` unrelated to actual type `bool` of constantPattern.
// (This might be just another case covered by #2632).
switch (1) { case true: break; }
// Context type `C<dynamic>` can't be used to infer the constant as `const C<dynamic>()`,
// because that's an error. Do we ignore the context type and infer `const C<num>()`? Emit an error?
C c = C<num>();
switch (c) { case const C(): break; }
}
Map pattern vs. context type
Section Type checking and pattern required type again: in the rule about map patterns, it is again possible for key type inference to fail (and there's again the overlap with #2632). It's the same situation as the previous section, just for map keys.
Non-exhaustive switch incurs run-time error?
In section 'Switch statement', it is stated that
If no case matches and there is no default clause, throw a runtime error.
However, I would only expect this runtime error to occur when the static type of the scrutinee is exhaustive, otherwise not.
Equality operator typing
In section Matching (refuting and destructuring), item Relational, iii, b says
This cannot happen. I believe we can just remove item b.
Match failure and dynamic error should be described as normative
In the sections about the run-time semantics, it is probably better to change the description to be all-normative at each location where it currently says 'become runtime exceptions' in a commentary sentence (in italics): There is one behavior in a refutable context, and another one in an irrefutable context, and they are both normative.
If
p
has no rest element andl
is not equal toh
then the match fails. Ifp
has a rest element andl
is less thanh + t
then the match fails. These match failures become runtime exceptions if the list pattern is in an irrefutable context.
-->
Assume that
p
has no rest element andl
is not equal toh
; orp
has a rest element andl
is less thanh + t
. In this situation, the match fails if the context is refutable, and a dynamic error occurs if the context is irrefutable.
Rest element with constant pattern will not match
This is similar to the first two sections above ("vs. context type"), but more specialized: A rest element in a list pattern can have a subpattern, and the matched object is known in even more detail than we do with constant patterns and map keys.
For instance, with case [1, ... const [2, 3], 4]
, we're expecting the sublist to be identical to a constant list, but that will never happen because the sublist is obtained by calling sublist
on the matched list, and the result from there will never be the constant list (unless it's a really weird list implementation).
So maybe we should make constant patterns an error here? Allow only list patterns and variable patterns? Basically everything else will fail.
'Wrapped in a unary pattern' too broad?
In section Record pattern we have the phrase
the subpattern must be a variable pattern which may be wrapped in a unary pattern.
Perhaps it should be spelled out:
the subpattern must be a variable pattern which may be wrapped in a cast pattern, null-check pattern, or a null-assert pattern.
The remaining unary pattern is a primary pattern, but it seems unlikely to me that primary patterns should be included here. A couple of cases might work, but I doubt that it is particularly useful:
class A { final int i; A(this.i); }
void main() {
switch (something) {
case A(: (int i)): break; // Parenthesized pattern.
case A(: [int i]): break; // List pattern; similarly for map/record.
case A(: A(var i)): break; // Object pattern.
}
}