|
|
@@ -10,30 +10,636 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
|
|
## Table of contents
|
|
|
|
|
|
-- [TODO](#todo)
|
|
|
- [Overview](#overview)
|
|
|
+- [Pattern Syntax and Semantics](#pattern-syntax-and-semantics)
|
|
|
+ - [Expression patterns](#expression-patterns)
|
|
|
+ - [Alternatives considered](#alternatives-considered)
|
|
|
+ - [Bindings](#bindings)
|
|
|
+ - [Name bindings](#name-bindings)
|
|
|
+ - [Unused bindings](#unused-bindings)
|
|
|
+ - [Alternatives considered](#alternatives-considered-1)
|
|
|
+ - [Generic bindings](#generic-bindings)
|
|
|
+ - [`auto` and type deduction](#auto-and-type-deduction)
|
|
|
+ - [Alternatives considered](#alternatives-considered-2)
|
|
|
+ - [`var`](#var)
|
|
|
+ - [Tuple patterns](#tuple-patterns)
|
|
|
+ - [Struct patterns](#struct-patterns)
|
|
|
+ - [Alternatives considered](#alternatives-considered-3)
|
|
|
+ - [Alternative patterns](#alternative-patterns)
|
|
|
+ - [Templates](#templates)
|
|
|
+ - [Refutability, overlap, usefulness, and exhaustiveness](#refutability-overlap-usefulness-and-exhaustiveness)
|
|
|
+ - [Alternatives considered](#alternatives-considered-4)
|
|
|
+- [Pattern usage](#pattern-usage)
|
|
|
- [Pattern match control flow](#pattern-match-control-flow)
|
|
|
+ - [Guards](#guards)
|
|
|
- [Pattern matching in local variables](#pattern-matching-in-local-variables)
|
|
|
- [Open questions](#open-questions)
|
|
|
- [Slice or array nested value pattern matching](#slice-or-array-nested-value-pattern-matching)
|
|
|
- - [Generic/template pattern matching](#generictemplate-pattern-matching)
|
|
|
- [Pattern matching as function overload resolution](#pattern-matching-as-function-overload-resolution)
|
|
|
+- [Alternatives considered](#alternatives-considered-5)
|
|
|
+- [References](#references)
|
|
|
|
|
|
<!-- tocstop -->
|
|
|
|
|
|
-## TODO
|
|
|
+## Overview
|
|
|
|
|
|
-This is a skeletal design, added to support [the overview](README.md). It should
|
|
|
-not be treated as accepted by the core team; rather, it is a placeholder until
|
|
|
-we have more time to examine this detail. Please feel welcome to rewrite and
|
|
|
-update as appropriate.
|
|
|
+A _pattern_ is an expression-like syntax that describes the structure of some
|
|
|
+value. The pattern may contain unknowns, so it can potentially match multiple
|
|
|
+values, and those unknowns may have names, in which case they are called
|
|
|
+_bindings_. When a pattern is executed by giving it a value called the
|
|
|
+_scrutinee_, it determines whether the scrutinee matches the pattern, and if so,
|
|
|
+determines the values of the bindings.
|
|
|
|
|
|
-## Overview
|
|
|
+## Pattern Syntax and Semantics
|
|
|
+
|
|
|
+Expressions are patterns, as described below. A pattern that is not an
|
|
|
+expression, because it contains pattern-specific syntax such as a binding, is a
|
|
|
+_proper pattern_. Many expression forms, such as arbitrary function calls, are
|
|
|
+not permitted as proper patterns, so cannot contain bindings.
|
|
|
+
|
|
|
+- _pattern_ ::= _proper-pattern_
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn F(n: i32) -> i32 { return n; }
|
|
|
+
|
|
|
+match (F(42)) {
|
|
|
+ // ❌ Error: binding can't appear in a function call.
|
|
|
+ case (F(n: i32)) => {}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Expression patterns
|
|
|
+
|
|
|
+An expression is a pattern.
|
|
|
+
|
|
|
+- _pattern_ ::= _expression_
|
|
|
+
|
|
|
+The pattern is compared with the expression using the `==` operator: _pattern_
|
|
|
+`==` _scrutinee_.
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn F(n: i32) {
|
|
|
+ match (n) {
|
|
|
+ // ✅ Results in an `n == 5` comparison.
|
|
|
+ // OK despite `n` and `5` having different types.
|
|
|
+ case 5 => {}
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Any `==` operations performed by a pattern match occur in lexical order, but for
|
|
|
+repeated matches against the same _pattern_, later comparisons may be skipped by
|
|
|
+reusing the result from an earlier comparison:
|
|
|
+
|
|
|
+```carbon
|
|
|
+class ChattyIntMatcher {
|
|
|
+ external impl as EqWith(i32) {
|
|
|
+ fn Eq[me: ChattyIntMatcher](other: i32) {
|
|
|
+ Print("Matching {0}", other);
|
|
|
+ return other == 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn F() {
|
|
|
+ // Prints `Matching 1` then `Matching 2`,
|
|
|
+ // may or may not then print `Matching 1` again.
|
|
|
+ match ((1, 2)) {
|
|
|
+ case ({} as ChattyIntMatcher, 0) => {}
|
|
|
+ case (1, {} as ChattyIntMatcher) => {}
|
|
|
+ case ({} as ChattyIntMatcher, 2) => {}
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### Alternatives considered
|
|
|
+
|
|
|
+- [Introducer syntax for expression patterns](/proposals/p2188.md#introducer-syntax-for-expression-patterns)
|
|
|
+
|
|
|
+### Bindings
|
|
|
+
|
|
|
+#### Name bindings
|
|
|
+
|
|
|
+A name binding is a pattern.
|
|
|
+
|
|
|
+- _binding-pattern_ ::= _identifier_ `:` _expression_
|
|
|
+- _proper-pattern_ ::= _binding-pattern_
|
|
|
+
|
|
|
+The type of the _identifier_ is specified by the _expression_. The scrutinee is
|
|
|
+implicitly converted to that type if necessary.
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn F() -> i32 {
|
|
|
+ match (5) {
|
|
|
+ // ✅ `5` is implicitly converted to `i32`.
|
|
|
+ // Returns `5 as i32`.
|
|
|
+ case n: i32 => { return n; }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+When a new object needs to be created for the binding, the lifetime of the bound
|
|
|
+value matches the scope of the binding.
|
|
|
+
|
|
|
+```carbon
|
|
|
+class NoisyDestructor {
|
|
|
+ fn Make() -> Self { return {}; }
|
|
|
+ external impl i32 as ImplicitAs(NoisyDestructor) {
|
|
|
+ fn Convert[me: i32]() -> Self { return Make(); }
|
|
|
+ }
|
|
|
+ destructor {
|
|
|
+ Print("Destroyed!");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn G() {
|
|
|
+ // Does not print "Destroyed!".
|
|
|
+ let n: NoisyDestructor = NoisyDestructor.Make();
|
|
|
+ Print("Body of G");
|
|
|
+ // Prints "Destroyed!" here.
|
|
|
+}
|
|
|
+
|
|
|
+fn H(n: i32) {
|
|
|
+ // Does not print "Destroyed!".
|
|
|
+ let (v: NoisyDestructor, w: i32) = (n, n);
|
|
|
+ Print("Body of H");
|
|
|
+ // Prints "Destroyed!" here.
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### Unused bindings
|
|
|
+
|
|
|
+A syntax like a binding but with `_` in place of an identifier, or `unused`
|
|
|
+before the name, can be used to ignore part of a value. Names that are qualified
|
|
|
+with the `unused` keyword are visible for name lookup but uses are invalid,
|
|
|
+including when they cause ambiguous name lookup errors. If attempted to be used,
|
|
|
+a compiler error will be shown to the user, instructing them to either remove
|
|
|
+the `unused` qualifier or remove the use.
|
|
|
+
|
|
|
+- _binding-pattern_ ::= `_` `:` _expression_
|
|
|
+- _binding-pattern_ ::= `unused` _identifier_ `:` _expression_
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn F(n: i32) {
|
|
|
+ match (n) {
|
|
|
+ // ✅ Matches and discards the value of `n`.
|
|
|
+ case _: i32 => {}
|
|
|
+ // ❌ Error: unreachable.
|
|
|
+ default => {}
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+As specified in [#1084](/proposals/p1084.md), function redeclarations may
|
|
|
+replace binding names with `_`s but may not use different names.
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn G(n: i32);
|
|
|
+fn H(n: i32);
|
|
|
+fn J(n: i32);
|
|
|
+
|
|
|
+// ✅ Does not use `n`.
|
|
|
+fn G(_: i32) {}
|
|
|
+// ❌ Error: name of parameter does not match declaration.
|
|
|
+fn H(m: i32) {}
|
|
|
+// ✅ Does not use `n`.
|
|
|
+fn J(unused n: i32);
|
|
|
+```
|
|
|
+
|
|
|
+##### Alternatives considered
|
|
|
+
|
|
|
+- [Commented names](/proposals/p2022.md#commented-names)
|
|
|
+- [Only short form support with `_`](/proposals/p2022.md#only-short-form-support-with-_)
|
|
|
+- [Named identifiers prefixed with `_`](/proposals/p2022.md#named-identifiers-prefixed-with-_)
|
|
|
+- [Anonymous, named identifiers](/proposals/p2022.md#anonymous-named-identifiers)
|
|
|
+- [Attributes](/proposals/p2022.md#attributes)
|
|
|
+
|
|
|
+#### Generic bindings
|
|
|
+
|
|
|
+A `:!` can be used in place of `:` for a binding that is usable at compile time.
|
|
|
+
|
|
|
+- _generic-pattern_ ::= `unused`? `template`? _identifier_ `:!` _expression_
|
|
|
+- _generic-pattern_ ::= `template`? _identifier_ `:!` _expression_
|
|
|
+- _generic-pattern_ ::= `template`? `_` `:!` _expression_
|
|
|
+- _generic-pattern_ ::= `unused` `template`? _identifier_ `:!` _expression_
|
|
|
+- _proper-pattern_ ::= _generic-pattern_
|
|
|
+
|
|
|
+```carbon
|
|
|
+// ✅ `F` takes a generic type parameter `T` and a parameter `x` of type `T`.
|
|
|
+fn F(T:! Type, x: T) {
|
|
|
+ var v: T = x;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+The `template` keyword indicates the binding is introducing a template
|
|
|
+parameter, so name lookups into the parameter should be deferred until its value
|
|
|
+is known.
|
|
|
+
|
|
|
+#### `auto` and type deduction
|
|
|
+
|
|
|
+The `auto` keyword is a placeholder for a unique deduced type.
|
|
|
+
|
|
|
+- _expression_ ::= `auto`
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn F(n: i32) {
|
|
|
+ var v: auto = SomeComplicatedExpression(n);
|
|
|
+ // Equivalent to:
|
|
|
+ var w: T = SomeComplicatedExpression(n);
|
|
|
+ // ... where `T` is the type of the initializer.
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+The `auto` keyword is only permitted in specific contexts. Currently these are:
|
|
|
+
|
|
|
+- As the return type of a function.
|
|
|
+- As the type of a binding.
|
|
|
+
|
|
|
+It is anticipated that `auto` may be permitted in more contexts in the future,
|
|
|
+for example as a generic argument in a parameterized type that appears in a
|
|
|
+context where `auto` is allowed, such as `Vector(auto)` or `auto*`.
|
|
|
+
|
|
|
+When the type of a binding requires type deduction, the type is deduced against
|
|
|
+the type of the scrutinee and deduced values are substituted back into the type
|
|
|
+before pattern matching is performed.
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn G[T:! Type](p: T*);
|
|
|
+class X { external impl as ImplicitAs(i32*); }
|
|
|
+// ✅ Deduces `T = i32` then implicitly and
|
|
|
+// trivially converts `p` to `i32*`.
|
|
|
+fn H1(p: i32*) { G(p); }
|
|
|
+// ❌ Error, can't deduce `T*` from `X`.
|
|
|
+fn H2(p: X) { G(p); }
|
|
|
+```
|
|
|
+
|
|
|
+The above is only an illustration; the behavior of type deduction is not yet
|
|
|
+specified.
|
|
|
+
|
|
|
+#### Alternatives considered
|
|
|
+
|
|
|
+- [Shorthand for `auto`](/proposals/p2188.md#shorthand-for-auto)
|
|
|
+
|
|
|
+### `var`
|
|
|
+
|
|
|
+A `var` prefix indicates that a pattern provides mutable storage for the
|
|
|
+scrutinee.
|
|
|
+
|
|
|
+- _proper-pattern_ ::= `var` _proper-pattern_
|
|
|
+
|
|
|
+A `var` pattern matches when its nested pattern matches. The type of the storage
|
|
|
+is the resolved type of the nested _pattern_. Any bindings within the nested
|
|
|
+pattern refer to portions of the corresponding storage rather than to the
|
|
|
+scrutinee.
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn F(p: i32*);
|
|
|
+fn G() {
|
|
|
+ match ((1, 2)) {
|
|
|
+ // `n` is a mutable `i32`.
|
|
|
+ case (var n: i32, 1) => { F(&n); }
|
|
|
+ // `n` and `m` are the elements of a mutable `(i32, i32)`.
|
|
|
+ case var (n: i32, m: i32) => { F(if n then &n else &m); }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Pattern matching precedes the initialization of the storage for any `var`
|
|
|
+patterns. An introduced variable is only initialized if the complete pattern
|
|
|
+matches.
|
|
|
+
|
|
|
+```carbon
|
|
|
+class X {
|
|
|
+ destructor { Print("Destroyed!"); }
|
|
|
+}
|
|
|
+fn F(x: X) {
|
|
|
+ match ((x, 1 as i32)) {
|
|
|
+ case (var y: X, 0) => {}
|
|
|
+ case (var z: X, 1) => {}
|
|
|
+ // Prints "Destroyed!" only once, when `z` is destroyed.
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+A `var` pattern cannot be nested within another `var` pattern. The declaration
|
|
|
+syntax `var` _pattern_ `=` _expresson_ `;` is equivalent to `let` `var`
|
|
|
+_pattern_ `=` _expression_ `;`.
|
|
|
+
|
|
|
+### Tuple patterns
|
|
|
|
|
|
-The most prominent mechanism to manipulate and work with types in Carbon is
|
|
|
-pattern matching. This may seem like a deviation from C++, but in fact this is
|
|
|
-largely about building a clear, coherent model for a fundamental part of C++:
|
|
|
-overload resolution.
|
|
|
+A tuple of patterns can be used as a pattern.
|
|
|
+
|
|
|
+- _tuple-pattern_ ::= `(` [_expression_ `,`]\* _proper-pattern_ [`,`
|
|
|
+ _pattern_]\* `,`? `)`
|
|
|
+- _proper-pattern_ ::= _tuple-pattern_
|
|
|
+
|
|
|
+A _tuple-pattern_ containing no commas is treated as grouping parens: the
|
|
|
+contained _proper-pattern_ is matched directly against the scrutinee. Otherwise,
|
|
|
+the behavior is as follows.
|
|
|
+
|
|
|
+A tuple pattern is matched left-to-right. The scrutinee is required to be of
|
|
|
+tuple type.
|
|
|
+
|
|
|
+Note that a tuple pattern must contain at least one _proper-pattern_. Otherwise,
|
|
|
+it is a tuple-valued expression. However, a tuple pattern and a corresponding
|
|
|
+tuple-valued expression are matched in the same way because `==` for a tuple
|
|
|
+compares fields left-to-right.
|
|
|
+
|
|
|
+### Struct patterns
|
|
|
+
|
|
|
+A struct can be matched with a struct pattern.
|
|
|
+
|
|
|
+- _proper-pattern_ ::= `{` [_field-init_ `,`]\* _proper-field-pattern_ [`,`
|
|
|
+ _field-pattern_]\* `}`
|
|
|
+- _proper-pattern_ ::= `{` [_field-pattern_ `,`]+ `_` `}`
|
|
|
+- _field-init_ ::= _designator_ `=` _expression_
|
|
|
+- _proper-field-pattern_ ::= _designator_ `=` _proper-pattern_
|
|
|
+- _proper-field-pattern_ ::= _binding-pattern_
|
|
|
+- _field-pattern_ ::= _field-init_
|
|
|
+- _field-pattern_ ::= _proper-field-pattern_
|
|
|
+
|
|
|
+A struct pattern resembles a struct literal, with at least one field initialized
|
|
|
+with a proper pattern:
|
|
|
+
|
|
|
+```carbon
|
|
|
+match ({.a = 1, .b = 2}) {
|
|
|
+ // Struct literal as an expression pattern.
|
|
|
+ case {.b = 2, .a = 1} => {}
|
|
|
+ // Struct pattern.
|
|
|
+ case {.b = n: i32, .a = m: i32} => {}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+The scrutinee is required to be of struct type, and to have the same set of
|
|
|
+field names as the pattern. The pattern is matched left-to-right, meaning that
|
|
|
+matching is performed in the field order specified in the pattern, not in the
|
|
|
+field order of the scrutinee. This is consistent with the behavior of matching
|
|
|
+against a struct-valued expression, where the expression pattern becomes the
|
|
|
+left operand of the `==` and so determines the order in which `==` comparisons
|
|
|
+for fields are performed.
|
|
|
+
|
|
|
+In the case where a field will be bound to an identifier with the same name, a
|
|
|
+shorthand syntax is available: `a: T` is synonymous with `.a = a: T`.
|
|
|
+
|
|
|
+```carbon
|
|
|
+match ({.a = 1, .b = 2}) {
|
|
|
+ case {a: i32, b: i32} => { return a + b; }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+If some fields should be ignored when matching, a trailing `, _` can be added to
|
|
|
+specify this:
|
|
|
+
|
|
|
+```carbon
|
|
|
+match ({.a = 1, .b = 2}) {
|
|
|
+ case {.a = 1, _} => { return 1; }
|
|
|
+ case {b: i32, _} => { return b; }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+This is valid even if all fields are actually named in the pattern.
|
|
|
+
|
|
|
+#### Alternatives considered
|
|
|
+
|
|
|
+- [Struct pattern syntax](/proposals/p2188.md#struct-pattern-syntax)
|
|
|
+
|
|
|
+### Alternative patterns
|
|
|
+
|
|
|
+An alternative pattern is used to match one alternative of a choice type.
|
|
|
+
|
|
|
+- _proper-pattern_ ::= _callee-expression_ _tuple-pattern_
|
|
|
+- _proper-pattern_ ::= _designator_ _tuple-pattern_?
|
|
|
+
|
|
|
+Here, _callee-expression_ is syntactically an expression that is valid as the
|
|
|
+callee in a function call expression, and an alternative pattern is
|
|
|
+syntactically a function call expression whose argument list contains at least
|
|
|
+one _proper-pattern_.
|
|
|
+
|
|
|
+If a _callee-expression_ is provided, it is required to name a choice type
|
|
|
+alternative that has a parameter list, and the scrutinee is implicitly converted
|
|
|
+to that choice type. Otherwise, the scrutinee is required to be of some choice
|
|
|
+type, and the designator is looked up in that type and is required to name an
|
|
|
+alternative with a parameter list if and only if a _tuple-pattern_ is specified.
|
|
|
+
|
|
|
+The pattern matches if the active alternative in the scrutinee is the specified
|
|
|
+alternative, and the arguments of the alternative match the given tuple pattern
|
|
|
+(if any).
|
|
|
+
|
|
|
+```carbon
|
|
|
+choice Optional(T:! Type) {
|
|
|
+ None,
|
|
|
+ Some(T)
|
|
|
+}
|
|
|
+
|
|
|
+match (Optional(i32).None) {
|
|
|
+ // ✅ `.None` resolved to `Optional(i32).None`.
|
|
|
+ case .None => {}
|
|
|
+ // ✅ `.Some` resolved to `Optional(i32).Some`.
|
|
|
+ case .Some(n: i32) => { Print("{0}", n); }
|
|
|
+ // ❌ Error, no such alternative exists.
|
|
|
+ case .Other => {}
|
|
|
+}
|
|
|
+
|
|
|
+class X {
|
|
|
+ external impl as ImplicitAs(Optional(i32));
|
|
|
+}
|
|
|
+
|
|
|
+match ({} as X) {
|
|
|
+ // ✅ OK, but expression pattern.
|
|
|
+ case Optional(i32).None => {}
|
|
|
+ // ✅ OK, implicitly converts to `Optional(i32)`.
|
|
|
+ case Optional(i32).Some(n: i32) => { Print("{0}", n); }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Note that a pattern of the form `Optional(T).None` is an expression pattern and
|
|
|
+is compared using `==`.
|
|
|
+
|
|
|
+### Templates
|
|
|
+
|
|
|
+Any checking of the type of the scrutinee against the type of the pattern that
|
|
|
+cannot be performed because the type of the scrutinee involves a template
|
|
|
+parameter is deferred until the template parameter's value is known. During
|
|
|
+instantiation, patterns that are not meaningful due to a type error are instead
|
|
|
+treated as not matching. This includes cases where an `==` fails because of a
|
|
|
+missing `EqWith` implementation.
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn TypeName[template T:! Type](x: T) -> String {
|
|
|
+ match (x) {
|
|
|
+ // ✅ OK, the type of `x` is a template parameter.
|
|
|
+ case _: i32 => { return "int"; }
|
|
|
+ case _: bool => { return "bool"; }
|
|
|
+ case _: auto* => { return "pointer"; }
|
|
|
+ default => { return "unknown"; }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Cases where the match is invalid for reasons not involving the template
|
|
|
+parameter are rejected when type-checking the template:
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn MeaninglessMatch[template T:! Type](x: T*) {
|
|
|
+ match (*x) {
|
|
|
+ // ✅ OK, `T` could be a tuple.
|
|
|
+ case (_: auto, _: auto) => {}
|
|
|
+ default => {}
|
|
|
+ }
|
|
|
+ match (x->y) {
|
|
|
+ // ✅ OK, `T.y` could be a tuple.
|
|
|
+ case (_: auto, _: auto) => {}
|
|
|
+ default => {}
|
|
|
+ }
|
|
|
+ match (x) {
|
|
|
+ // ❌ Error, tuple pattern cannot match value of non-tuple type `T*`.
|
|
|
+ case (_: auto, _: auto) => {}
|
|
|
+ default => {}
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Refutability, overlap, usefulness, and exhaustiveness
|
|
|
+
|
|
|
+Some definitions:
|
|
|
+
|
|
|
+- A pattern _P_ is _useful_ in the context of a set of patterns _C_ if there
|
|
|
+ exists a value that _P_ can match that no pattern in _C_ matches.
|
|
|
+- A set of patterns _C_ is _exhaustive_ if it matches all possible values.
|
|
|
+ Equivalently, _C_ is exhaustive if the pattern `_: auto` is not useful in
|
|
|
+ the context of _C_.
|
|
|
+- A pattern _P_ is _refutable_ if there are values that it does not match,
|
|
|
+ that is, if the pattern `_: auto` is useful in the context of {_P_}.
|
|
|
+ Equivalently, the pattern _P_ is _refutable_ if the set of patterns {_P_} is
|
|
|
+ not exhaustive.
|
|
|
+- A set of patterns _C_ is _overlapping_ if there exists any value that is
|
|
|
+ matched by more than one pattern in _C_.
|
|
|
+
|
|
|
+For the purpose of these terms, expression patterns that match a constant tuple,
|
|
|
+struct, or choice value are treated as if they were tuple, struct, or
|
|
|
+alternative patterns, respectively, and `bool` is treated like a choice type.
|
|
|
+Any expression patterns that remain after applying this rule are considered to
|
|
|
+match a single value from an infinite set of values so that a set of expression
|
|
|
+patterns is never exhaustive:
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn IsEven(n: u8) -> bool {
|
|
|
+ // Not considered exhaustive.
|
|
|
+ match (n) {
|
|
|
+ case 0 => { return true; }
|
|
|
+ case 1 => { return false; }
|
|
|
+ ...
|
|
|
+ case 255 => { return false; }
|
|
|
+ }
|
|
|
+ // Code here is considered to be reachable.
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+```carbon
|
|
|
+fn IsTrue(b: bool) -> bool {
|
|
|
+ // Considered exhaustive.
|
|
|
+ match (b) {
|
|
|
+ case false => { return false; }
|
|
|
+ case true => { return true; }
|
|
|
+ }
|
|
|
+ // Code here is considered to be unreachable.
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+When determining whether a pattern is useful, no attempt is made to determine
|
|
|
+the value of any guards, and instead a worst-case assumption is made: a guard on
|
|
|
+that pattern is assumed to evaluate to true and a guard on any pattern in the
|
|
|
+context set is assumed to evaluate to false.
|
|
|
+
|
|
|
+We will diagnose the following situations:
|
|
|
+
|
|
|
+- A pattern is not useful in the context of prior patterns. In a `match`
|
|
|
+ statement, this happens if a pattern or `default` cannot match because all
|
|
|
+ cases it could cover are handled by prior cases or a prior `default`. For
|
|
|
+ example:
|
|
|
+
|
|
|
+ ```carbon
|
|
|
+ choice Optional(T:! Type) {
|
|
|
+ None,
|
|
|
+ Some(T)
|
|
|
+ }
|
|
|
+ fn F(a: Optional(i32), b: Optional(i32)) {
|
|
|
+ match ((a, b)) {
|
|
|
+ case (.Some(a: i32), _: auto) => {}
|
|
|
+ // ✅ OK, but only matches values of the form `(None, Some)`,
|
|
|
+ // because `(Some, Some)` is matched by the previous pattern.
|
|
|
+ case (_: auto, .Some(b: i32)) => {}
|
|
|
+ // ✅ OK, matches all remaining values.
|
|
|
+ case (.None, .None) => {}
|
|
|
+ // ❌ Error, this pattern never matches.
|
|
|
+ case (_: auto, _: auto) => {}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+- A pattern match is not exhaustive and the program doesn't explicitly say
|
|
|
+ what to do when no pattern matches. For example:
|
|
|
+
|
|
|
+ - If the patterns in a `match` are not exhaustive and no `default` is
|
|
|
+ provided.
|
|
|
+
|
|
|
+ ```carbon
|
|
|
+ fn F(n: i32) -> i32 {
|
|
|
+ // ❌ Error, this `match` is not exhaustive.
|
|
|
+ match (n) {
|
|
|
+ case 0 => { return 2; }
|
|
|
+ case 1 => { return 3; }
|
|
|
+ case 2 => { return 5; }
|
|
|
+ case 3 => { return 7; }
|
|
|
+ case 4 => { return 11; }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+ - If a refutable pattern appears in a context where only one pattern can
|
|
|
+ be specified, such as a `let` or `var` declaration, and there is no
|
|
|
+ fallback behavior. This currently includes all pattern matching contexts
|
|
|
+ other than `match` statements, but the `var`/`let`-`else` feature in
|
|
|
+ [#1871](https://github.com/carbon-language/carbon-lang/pull/1871) would
|
|
|
+ introduce a second context permitting refutable matches, and overloaded
|
|
|
+ functions might introduce a third context.
|
|
|
+
|
|
|
+ ```carbon
|
|
|
+ fn F(n: i32) {
|
|
|
+ // ❌ Error, refutable expression pattern `5` used in context
|
|
|
+ // requiring an irrefutable pattern.
|
|
|
+ var 5 = n;
|
|
|
+ }
|
|
|
+ // ❌ Error, refutable expression pattern `5` used in context
|
|
|
+ // requiring an irrefutable pattern.
|
|
|
+ fn G(n: i32, 5);
|
|
|
+ ```
|
|
|
+
|
|
|
+- When a set of patterns have no ordering or tie-breaker, it is an error for
|
|
|
+ them to overlap unless there is a unique best match for any value that
|
|
|
+ matches more than one pattern. However, this situation does not apply to any
|
|
|
+ current language rule:
|
|
|
+
|
|
|
+ - For `match` statements, patterns are matched top-down, so overlap is
|
|
|
+ permitted.
|
|
|
+ - We do not yet have an approved design for overloaded functions, but it
|
|
|
+ is anticipated that declaration order will be used in that case too.
|
|
|
+ - For a set of `impl`s that match a given `impl` lookup, argument
|
|
|
+ deduction is used rather than pattern matching, but `impl`s with the
|
|
|
+ same type structure are an error unless a `match_first` declaration is
|
|
|
+ used to order the `impl`s.
|
|
|
+
|
|
|
+#### Alternatives considered
|
|
|
+
|
|
|
+- [Treat expression patterns as exhaustive if they cover all possible values](/proposals/p2188.md#treat-expression-patterns-as-exhaustive-if-they-cover-all-possible-values)
|
|
|
+- [Allow non-exhaustive `match` statements](/proposals/p2188.md#allow-non-exhaustive-match-statements)
|
|
|
+
|
|
|
+## Pattern usage
|
|
|
+
|
|
|
+This section is a skeletal design, added to support [the overview](README.md).
|
|
|
+It should not be treated as accepted by the core team; rather, it is a
|
|
|
+placeholder until we have more time to examine this detail. Please feel welcome
|
|
|
+to rewrite and update as appropriate.
|
|
|
|
|
|
### Pattern match control flow
|
|
|
|
|
|
@@ -43,7 +649,7 @@ something much more powerful, `match`. This is not a novel construct, and is
|
|
|
widely used in existing languages (Swift and Rust among others) and is currently
|
|
|
under active investigation for C++. Carbon's `match` can be used as follows:
|
|
|
|
|
|
-```
|
|
|
+```carbon
|
|
|
fn Bar() -> (i32, (f32, f32));
|
|
|
fn Foo() -> f32 {
|
|
|
match (Bar()) {
|
|
|
@@ -89,13 +695,32 @@ In order to match a value, whatever is specified in the pattern must match.
|
|
|
Using `auto` for a type will always match, making `_: auto` the wildcard
|
|
|
pattern.
|
|
|
|
|
|
+#### Guards
|
|
|
+
|
|
|
+We allow `case`s within a `match` statement to have _guards_. These are not part
|
|
|
+of pattern syntax, but instead are specific to `case` syntax:
|
|
|
+
|
|
|
+- _case_ ::= `case` _pattern_ [`if` _expression_]? `=>` _block_
|
|
|
+
|
|
|
+A guard indicates that a `case` only matches if some predicate holds. The
|
|
|
+bindings in the pattern are in scope in the guard:
|
|
|
+
|
|
|
+```carbon
|
|
|
+match (x) {
|
|
|
+ case (m: i32, n: i32) if m + n < 5 => { return m - n; }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+For consistency, this facility is also available for `default` clauses, so that
|
|
|
+`default` remains equivalent to `case _: auto`.
|
|
|
+
|
|
|
### Pattern matching in local variables
|
|
|
|
|
|
Value patterns may be used when declaring local variables to conveniently
|
|
|
destructure them and do other type manipulations. However, the patterns must
|
|
|
match at compile time, so they can't use an `if` clause.
|
|
|
|
|
|
-```
|
|
|
+```carbon
|
|
|
fn Bar() -> (i32, (f32, f32));
|
|
|
fn Foo() -> i32 {
|
|
|
var (p: i32, _: auto) = Bar();
|
|
|
@@ -113,12 +738,19 @@ a local variable named `p` which is then returned.
|
|
|
An open question is how to effectively fit a "slice" or "array" pattern into
|
|
|
nested value pattern matching, or whether we shouldn't do so.
|
|
|
|
|
|
-### Generic/template pattern matching
|
|
|
-
|
|
|
-An open question is going beyond a simple "type" to things that support generics
|
|
|
-and/or templates.
|
|
|
-
|
|
|
### Pattern matching as function overload resolution
|
|
|
|
|
|
Need to flesh out specific details of how overload selection leverages the
|
|
|
pattern matching machinery, what (if any) restrictions are imposed, etc.
|
|
|
+
|
|
|
+## Alternatives considered
|
|
|
+
|
|
|
+- [Type pattern matching](/proposals/p2188.md#type-pattern-matching)
|
|
|
+- [Allow guards on arbitrary patterns](/proposals/p2188.md#allow-guards-on-arbitrary-patterns)
|
|
|
+
|
|
|
+## References
|
|
|
+
|
|
|
+- Proposal
|
|
|
+ [#2022: Unused Pattern Bindings (Unused Function Parameters)](https://github.com/carbon-language/carbon-lang/pull/2022)
|
|
|
+- Proposal
|
|
|
+ [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188)
|