Skip to content

Some small issues about patterns #2698

Closed
@eernstg

Description

@eernstg

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 can happen if operator == on v's type returns dynamic.

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 and l is not equal to h then the match fails. If p has a rest element and l is less than h + 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 and l is not equal to h; or p has a rest element and l is less than h + 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.
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    patternsIssues related to pattern matching.questionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions