Skip to content

Commit

Permalink
Merge pull request #957 from mark-i-m/or-patterns
Browse files Browse the repository at this point in the history
Document or-patterns
  • Loading branch information
ehuss authored Apr 28, 2021
2 parents 095b2d0 + 629e7df commit d23f9da
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/expressions/closure-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
> &nbsp;&nbsp; _ClosureParam_ (`,` _ClosureParam_)<sup>\*</sup> `,`<sup>?</sup>
>
> _ClosureParam_ :\
> &nbsp;&nbsp; [_OuterAttribute_]<sup>\*</sup> [_Pattern_]&nbsp;( `:` [_Type_] )<sup>?</sup>
> &nbsp;&nbsp; [_OuterAttribute_]<sup>\*</sup> [_PatternNoTopAlt_]&nbsp;( `:` [_Type_] )<sup>?</sup>
A *closure expression*, also know as a lambda expression or a lambda, defines a [closure type] and evaluates to a value of that type.
The syntax for a closure expression is an optional `move` keyword, then a pipe-symbol-delimited (`|`) comma-separated list of [patterns], called the *closure parameters* each optionally followed by a `:` and a type, then an optional `->` and type, called the *return type*, and then an expression, called the *closure body operand*.
Expand Down Expand Up @@ -59,7 +59,7 @@ Attributes on closure parameters follow the same rules and restrictions as [regu
[_Expression_]: ../expressions.md
[_BlockExpression_]: block-expr.md
[_TypeNoBounds_]: ../types.md#type-expressions
[_Pattern_]: ../patterns.md
[_PatternNoTopAlt_]: ../patterns.md
[_Type_]: ../types.md#type-expressions
[`let` binding]: ../statements.md#let-statements
[`Send`]: ../special-types-and-traits.md#send
Expand Down
4 changes: 2 additions & 2 deletions src/expressions/if-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ assert_eq!(y, "Bigger");

> **<sup>Syntax</sup>**\
> _IfLetExpression_ :\
> &nbsp;&nbsp; `if` `let` [_MatchArmPatterns_] `=` [_Expression_]<sub>_except struct or lazy boolean operator expression_</sub>
> &nbsp;&nbsp; `if` `let` [_Pattern_] `=` [_Expression_]<sub>_except struct or lazy boolean operator expression_</sub>
> [_BlockExpression_]\
> &nbsp;&nbsp; (`else` (
> [_BlockExpression_]
Expand Down Expand Up @@ -147,7 +147,7 @@ if let PAT = ( EXPR || EXPR ) { .. }
[_BlockExpression_]: block-expr.md
[_Expression_]: ../expressions.md
[_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators
[_MatchArmPatterns_]: match-expr.md
[_Pattern_]: ../patterns.md
[_eRFCIfLetChain_]: /~https://github.com/rust-lang/rfcs/blob/master/text/2497-if-let-chains.md#rollout-plan-and-transitioning-to-rust-2018
[`match` expression]: match-expr.md
[boolean type]: ../types/boolean.md
Expand Down
3 changes: 1 addition & 2 deletions src/expressions/loop-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ while i < 10 {

> **<sup>Syntax</sup>**\
> [_PredicatePatternLoopExpression_] :\
> &nbsp;&nbsp; `while` `let` [_MatchArmPatterns_] `=` [_Expression_]<sub>_except struct or lazy boolean operator expression_</sub>
> &nbsp;&nbsp; `while` `let` [_Pattern_] `=` [_Expression_]<sub>_except struct or lazy boolean operator expression_</sub>
> [_BlockExpression_]
A `while let` loop is semantically similar to a `while` loop but in place of a condition expression it expects the keyword `let` followed by a pattern, an `=`, a [scrutinee] expression and a block expression.
Expand Down Expand Up @@ -263,7 +263,6 @@ In the case a `loop` has an associated `break`, it is not considered diverging,
[LIFETIME_OR_LABEL]: ../tokens.md#lifetimes-and-loop-labels
[_BlockExpression_]: block-expr.md
[_Expression_]: ../expressions.md
[_MatchArmPatterns_]: match-expr.md
[_Pattern_]: ../patterns.md
[`match` expression]: match-expr.md
[boolean]: ../types/boolean.md
Expand Down
5 changes: 1 addition & 4 deletions src/expressions/match-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
> &nbsp;&nbsp; _MatchArm_ `=>` [_Expression_] `,`<sup>?</sup>
>
> _MatchArm_ :\
> &nbsp;&nbsp; [_OuterAttribute_]<sup>\*</sup> _MatchArmPatterns_ _MatchArmGuard_<sup>?</sup>
>
> _MatchArmPatterns_ :\
> &nbsp;&nbsp; `|`<sup>?</sup> [_Pattern_] ( `|` [_Pattern_] )<sup>\*</sup>
> &nbsp;&nbsp; [_OuterAttribute_]<sup>\*</sup> [_Pattern_] _MatchArmGuard_<sup>?</sup>
>
> _MatchArmGuard_ :\
> &nbsp;&nbsp; `if` [_Expression_]
Expand Down
4 changes: 2 additions & 2 deletions src/items/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
> )
>
> _FunctionParamPattern_ :\
> &nbsp;&nbsp; [_Pattern_] `:` ( [_Type_] | `...` )
> &nbsp;&nbsp; [_PatternNoTopAlt_] `:` ( [_Type_] | `...` )
>
> _FunctionReturnType_ :\
> &nbsp;&nbsp; `->` [_Type_]
Expand Down Expand Up @@ -378,7 +378,7 @@ fn foo_oof(#[some_inert_attribute] arg: u8) {
[_BlockExpression_]: ../expressions/block-expr.md
[_GenericParams_]: generics.md
[_Lifetime_]: ../trait-bounds.md
[_Pattern_]: ../patterns.md
[_PatternNoTopAlt_]: ../patterns.md
[_Type_]: ../types.md#type-expressions
[_WhereClause_]: generics.md#where-clauses
[_OuterAttribute_]: ../attributes.md
Expand Down
4 changes: 2 additions & 2 deletions src/macros-by-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fragment specifiers are:
* `block`: a [_BlockExpression_]
* `stmt`: a [_Statement_] without the trailing semicolon (except for item
statements that require semicolons)
* `pat`: a [_Pattern_]
* `pat`: a [_PatternNoTopAlt_]
* `expr`: an [_Expression_]
* `ty`: a [_Type_]
* `ident`: an [IDENTIFIER_OR_KEYWORD]
Expand Down Expand Up @@ -488,7 +488,7 @@ For more detail, see the [formal specification].
[_Item_]: items.md
[_LiteralExpression_]: expressions/literal-expr.md
[_MetaListIdents_]: attributes.md#meta-item-attribute-syntax
[_Pattern_]: patterns.md
[_PatternNoTopAlt_]: patterns.md
[_Statement_]: statements.md
[_TokenTree_]: macros.md#macro-invocation
[_Token_]: tokens.md
Expand Down
61 changes: 61 additions & 0 deletions src/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

> **<sup>Syntax</sup>**\
> _Pattern_ :\
> &nbsp;&nbsp; &nbsp;&nbsp; `|`<sup>?</sup> _PatternNoTopAlt_ ( `|` _PatternNoTopAlt_ )<sup>\*</sup>
>
> _PatternNoTopAlt_ :\
> &nbsp;&nbsp; &nbsp;&nbsp; _PatternWithoutRange_\
> &nbsp;&nbsp; | [_RangePattern_]
>
Expand Down Expand Up @@ -756,6 +759,63 @@ Path patterns are irrefutable when they refer to structs or an enum variant when
has only one variant or a constant whose type is irrefutable. They are refutable when they
refer to refutable constants or enum variants for enums with multiple variants.

## Or-patterns

_Or-patterns_ are patterns that match on one of two or more sub-patterns (e.g.
`A | B | C`). They can nest arbitrarily. Syntactically, or-patterns are allowed
in any of the places where other patterns are allowed (represented by the
_Pattern_ production), with the exceptions of `let`-bindings and function and
closure arguments (represented by the _PatternNoTopAlt_ production).

### Static semantics

1. Given a pattern `p | q` at some depth for some arbitrary patterns `p` and `q`,
the pattern is considered ill-formed if:

+ the type inferred for `p` does not unify with the type inferred for `q`, or
+ the same set of bindings are not introduced in `p` and `q`, or
+ the type of any two bindings with the same name in `p` and `q` do not unify
with respect to types or binding modes.

Unification of types is in all instances aforementioned exact and
implicit [type coercions] do not apply.

2. When type checking an expression `match e_s { a_1 => e_1, ... a_n => e_n }`,
for each match arm `a_i` which contains a pattern of form `p_i | q_i`,
the pattern `p_i | q_i` is considered ill formed if,
at the depth `d` where it exists the fragment of `e_s` at depth `d`,
the type of the expression fragment does not unify with `p_i | q_i`.

3. With respect to exhaustiveness checking, a pattern `p | q` is
considered to cover `p` as well as `q`. For some constructor `c(x, ..)`
the distributive law applies such that `c(p | q, ..rest)` covers the same
set of value as `c(p, ..rest) | c(q, ..rest)` does. This can be applied
recursively until there are no more nested patterns of form `p | q` other
than those that exist at the top level.

Note that by *"constructor"* we do not refer to tuple struct patterns,
but rather we refer to a pattern for any product type.
This includes enum variants, tuple structs, structs with named fields,
arrays, tuples, and slices.

### Dynamic semantics

1. The dynamic semantics of pattern matching a scrutinee expression `e_s`
against a pattern `c(p | q, ..rest)` at depth `d` where `c` is some constructor,
`p` and `q` are arbitrary patterns, and `rest` is optionally any remaining
potential factors in `c`, is defined as being the same as that of
`c(p, ..rest) | c(q, ..rest)`.

### Precedence with other undelimited patterns

As shown elsewhere in this chapter, there are several types of patterns that
are syntactically undelimited, including identifier patterns, reference
patterns, and or-patterns. Or-patterns always have the lowest-precedence. This
allows us to reserve syntactic space for a possible future type ascription
feature and also to reduce ambiguity. For example, `x @ A(..) | B(..)` will
result in an error that `x` is not bound in all patterns, `&A(x) | B(x)` will
result in a type mismatch between `x` in the different subpatterns.

[_GroupedPattern_]: #grouped-patterns
[_IdentifierPattern_]: #identifier-patterns
[_LiteralPattern_]: #literal-patterns
Expand All @@ -782,3 +842,4 @@ refer to refutable constants or enum variants for enums with multiple variants.
[structs]: items/structs.md
[tuples]: types/tuple.md
[scrutinee]: glossary.md#scrutinee
[type coercions]: type-coercions.md
4 changes: 2 additions & 2 deletions src/statements.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fn outer() {

> **<sup>Syntax</sup>**\
> _LetStatement_ :\
> &nbsp;&nbsp; [_OuterAttribute_]<sup>\*</sup> `let` [_Pattern_]
> &nbsp;&nbsp; [_OuterAttribute_]<sup>\*</sup> `let` [_PatternNoTopAlt_]
> ( `:` [_Type_] )<sup>?</sup> (`=` [_Expression_] )<sup>?</sup> `;`
A *`let` statement* introduces a new set of [variables], given by an
Expand Down Expand Up @@ -133,5 +133,5 @@ statement are [`cfg`], and [the lint check attributes].
[_LetStatement_]: #let-statements
[_MacroInvocationSemi_]: macros.md#macro-invocation
[_OuterAttribute_]: attributes.md
[_Pattern_]: patterns.md
[_PatternNoTopAlt_]: patterns.md
[_Type_]: types.md

0 comments on commit d23f9da

Please sign in to comment.