From 940de707f5ae1a49ee596e55cdc875f8ff65c151 Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 3 Feb 2021 14:33:51 -0600 Subject: [PATCH 1/9] document or-patterns --- src/expressions/closure-expr.md | 2 +- src/items/functions.md | 2 +- src/patterns.md | 62 +++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/expressions/closure-expr.md b/src/expressions/closure-expr.md index 217a6a19c..680bec123 100644 --- a/src/expressions/closure-expr.md +++ b/src/expressions/closure-expr.md @@ -10,7 +10,7 @@ >    _ClosureParam_ (`,` _ClosureParam_)\* `,`? > > _ClosureParam_ :\ ->    [_OuterAttribute_]\* [_Pattern_] ( `:` [_Type_] )? +>    [_OuterAttribute_]\* [_PatternNoTopAlt_] ( `:` [_Type_] )? A _closure expression_, also know as a lambda expression or a lambda, defines a closure and denotes it as a value, in a single expression. A closure expression diff --git a/src/items/functions.md b/src/items/functions.md index b6bd15904..6c064a7d1 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -32,7 +32,7 @@ > ) > > _FunctionParamPattern_ :\ ->    [_Pattern_] `:` ( [_Type_] | `...` ) +>    [_PatternNoTopAlt_] `:` ( [_Type_] | `...` ) > > _FunctionReturnType_ :\ >    `->` [_Type_] diff --git a/src/patterns.md b/src/patterns.md index 121b825d1..ba4a25ec8 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -2,6 +2,17 @@ > **Syntax**\ > _Pattern_ :\ +>       `|`? _PatternAllowTopAltInner_ +> +> _PatternNoTopAlt_ :\ +>       `(` _Pattern_ `)` +>    | _PatternInner_ +> +> _PatternAllowTopAltInner_ :\ +>       _PatternAllowTopAltInner_ `|` _PatternAllowTopAltInner_ +>    | _Pattern_ +> +> _PatternInner_ :\ >       _PatternWithoutRange_\ >    | [_RangePattern_] > @@ -756,6 +767,57 @@ 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 either of two sub-patterns (e.g. `A | +B`). 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 function and closure arguments (represented +by the _PatternNoTopAlt_ production). Additionally, the macro `:pat` matchers +only match or-patterns in the 2021+ editions. + +### 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. + + [type coercions]: https://doc.rust-lang.org/reference/type-coercions.html + + 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)`. + + [_GroupedPattern_]: #grouped-patterns [_IdentifierPattern_]: #identifier-patterns [_LiteralPattern_]: #literal-patterns From 1be55f9994e7a9a00e1cfe1dd0aaa2666d7ff394 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 7 Feb 2021 11:15:49 -0600 Subject: [PATCH 2/9] simplify pattern grammar --- src/patterns.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/patterns.md b/src/patterns.md index ba4a25ec8..212638f3e 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -2,19 +2,13 @@ > **Syntax**\ > _Pattern_ :\ ->       `|`? _PatternAllowTopAltInner_ +>       `|`? _PatternNoTopAlt_ `|` _PatternNoTopAlt_ +>    | _PatternNoTopAlt_ > > _PatternNoTopAlt_ :\ ->       `(` _Pattern_ `)` ->    | _PatternInner_ -> -> _PatternAllowTopAltInner_ :\ ->       _PatternAllowTopAltInner_ `|` _PatternAllowTopAltInner_ ->    | _Pattern_ -> -> _PatternInner_ :\ >       _PatternWithoutRange_\ >    | [_RangePattern_] +>    | `(` _Pattern_ `)` > > _PatternWithoutRange_ :\ >       [_LiteralPattern_]\ From 4c75c0b84b61b1ddc67817ed8bcaf4f7361ffb4b Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 4 Mar 2021 10:49:36 -0600 Subject: [PATCH 3/9] let bindings disallow top-level or-patterns (for now) --- src/statements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/statements.md b/src/statements.md index 5d8e0e95d..2beabb836 100644 --- a/src/statements.md +++ b/src/statements.md @@ -53,7 +53,7 @@ fn outer() { > **Syntax**\ > _LetStatement_ :\ ->    [_OuterAttribute_]\* `let` [_Pattern_] +>    [_OuterAttribute_]\* `let` [_PatternNoTopAlt_] > ( `:` [_Type_] )? (`=` [_Expression_] )? `;` A *`let` statement* introduces a new set of [variables], given by an From 245b8336818913beafa7a35a9ad59c85f28338fb Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 28 Mar 2021 11:31:28 -0500 Subject: [PATCH 4/9] address review comments --- src/expressions/closure-expr.md | 2 +- src/items/functions.md | 2 +- src/macros-by-example.md | 12 ++++++++++-- src/patterns.md | 19 ++++++++----------- src/statements.md | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/expressions/closure-expr.md b/src/expressions/closure-expr.md index 680bec123..6fbbd4c4c 100644 --- a/src/expressions/closure-expr.md +++ b/src/expressions/closure-expr.md @@ -80,7 +80,7 @@ Attributes on closure parameters follow the same rules and restrictions as [_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 [_OuterAttribute_]: ../attributes.md diff --git a/src/items/functions.md b/src/items/functions.md index 6c064a7d1..9d6ebcde3 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -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 diff --git a/src/macros-by-example.md b/src/macros-by-example.md index e97455d0b..c2a7e885c 100644 --- a/src/macros-by-example.md +++ b/src/macros-by-example.md @@ -28,7 +28,7 @@ > > _MacroFragSpec_ :\ >       `block` | `expr` | `ident` | `item` | `lifetime` | `literal`\ ->    | `meta` | `pat` | `path` | `stmt` | `tt` | `ty` | `vis` +>    | `meta` | `pat` | `pat2015` | `pat2021` | `path` | `stmt` | `tt` | `ty` | `vis` > > _MacroRepSep_ :\ >    [_Token_]_except delimiters and repetition operators_ @@ -122,7 +122,9 @@ fragment specifiers are: * `block`: a [_BlockExpression_] * `stmt`: a [_Statement_] without the trailing semicolon (except for item statements that require semicolons) - * `pat`: a [_Pattern_] + * `pat2015`: a [_PatternNoTopAlt_] + * `pat2021`: a [_Pattern_] + * `pat`: equivalent to either `pat2015` or `pat2021`, depending on edition (see note below). * `expr`: an [_Expression_] * `ty`: a [_Type_] * `ident`: an [IDENTIFIER_OR_KEYWORD] @@ -133,6 +135,11 @@ fragment specifiers are: * `vis`: a possibly empty [_Visibility_] qualifier * `literal`: matches `-`?[_LiteralExpression_] +> **Edition Differences**: In Editions 2015 and 2018, `pat` fragment-specifiers +> do not allow top-level or-patterns (i.e. they accept [_PatternNoTopAlt_]). In +> Edition 2021, this was changed so that top-level or-patterns are accepted by +> `pat` (i.e. [_Pattern_]). + In the transcriber, metavariables are referred to simply by `$`_name_, since the fragment kind is specified in the matcher. Metavariables are replaced with the syntax element that matched them. The keyword metavariable `$crate` can be @@ -489,6 +496,7 @@ For more detail, see the [formal specification]. [_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 diff --git a/src/patterns.md b/src/patterns.md index 212638f3e..a78b8c78e 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -2,12 +2,11 @@ > **Syntax**\ > _Pattern_ :\ ->       `|`? _PatternNoTopAlt_ `|` _PatternNoTopAlt_ ->    | _PatternNoTopAlt_ +>       `|`? _PatternNoTopAlt_ ( `|` _PatternNoTopAlt_ )\* > > _PatternNoTopAlt_ :\ >       _PatternWithoutRange_\ ->    | [_RangePattern_] +>    | [_RangePattern_]\ >    | `(` _Pattern_ `)` > > _PatternWithoutRange_ :\ @@ -763,12 +762,11 @@ refer to refutable constants or enum variants for enums with multiple variants. ## Or-patterns -_Or-patterns_ are patterns that match on either of two sub-patterns (e.g. `A | -B`). 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 function and closure arguments (represented -by the _PatternNoTopAlt_ production). Additionally, the macro `:pat` matchers -only match or-patterns in the 2021+ editions. +_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 @@ -780,8 +778,6 @@ only match or-patterns in the 2021+ editions. + the type of any two bindings with the same name in `p` and `q` do not unify with respect to types or binding modes. - [type coercions]: https://doc.rust-lang.org/reference/type-coercions.html - Unification of types is in all instances aforementioned exact and implicit [type coercions] do not apply. @@ -838,3 +834,4 @@ only match or-patterns in the 2021+ editions. [structs]: items/structs.md [tuples]: types/tuple.md [scrutinee]: glossary.md#scrutinee +[type coercions]: type-coercions.md diff --git a/src/statements.md b/src/statements.md index 2beabb836..06cd9513e 100644 --- a/src/statements.md +++ b/src/statements.md @@ -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 From f567a17c02f97c6452a5f976ac03a41389b78c85 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 28 Mar 2021 11:35:18 -0500 Subject: [PATCH 5/9] replace MatchArmPatterns with Pattern --- src/expressions/if-expr.md | 4 ++-- src/expressions/loop-expr.md | 3 +-- src/expressions/match-expr.md | 5 +---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/expressions/if-expr.md b/src/expressions/if-expr.md index 5d04bb5c7..a00c823a8 100644 --- a/src/expressions/if-expr.md +++ b/src/expressions/if-expr.md @@ -44,7 +44,7 @@ assert_eq!(y, "Bigger"); > **Syntax**\ > _IfLetExpression_ :\ ->    `if` `let` [_MatchArmPatterns_] `=` [_Expression_]_except struct or lazy boolean operator expression_ +>    `if` `let` [_Pattern_] `=` [_Expression_]_except struct or lazy boolean operator expression_ > [_BlockExpression_]\ >    (`else` ( > [_BlockExpression_] @@ -155,7 +155,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 [scrutinee]: ../glossary.md#scrutinee diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 9ddefa12f..40cb90a9d 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -67,7 +67,7 @@ while i < 10 { > **Syntax**\ > [_PredicatePatternLoopExpression_] :\ ->    `while` `let` [_MatchArmPatterns_] `=` [_Expression_]_except struct or lazy boolean operator expression_ +>    `while` `let` [_Pattern_] `=` [_Expression_]_except struct or lazy boolean operator expression_ > [_BlockExpression_] A `while let` loop is semantically similar to a `while` loop but in place of a @@ -291,7 +291,6 @@ expression `()`. [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 [scrutinee]: ../glossary.md#scrutinee diff --git a/src/expressions/match-expr.md b/src/expressions/match-expr.md index 6d9f3f2af..9716d8bda 100644 --- a/src/expressions/match-expr.md +++ b/src/expressions/match-expr.md @@ -15,10 +15,7 @@ >    _MatchArm_ `=>` [_Expression_] `,`? > > _MatchArm_ :\ ->    [_OuterAttribute_]\* _MatchArmPatterns_ _MatchArmGuard_? -> -> _MatchArmPatterns_ :\ ->    `|`? [_Pattern_] ( `|` [_Pattern_] )\* +>    [_OuterAttribute_]\* [_Pattern_] _MatchArmGuard_? > > _MatchArmGuard_ :\ >    `if` [_Expression_] From 45753bee694154545f6c8c500d2e8b04e5fccf40 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 28 Mar 2021 14:23:38 -0500 Subject: [PATCH 6/9] add a small section about or-pattern precedence and delimiters --- src/patterns.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/patterns.md b/src/patterns.md index a78b8c78e..c5345da76 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -807,6 +807,15 @@ closure arguments (represented by the _PatternNoTopAlt_ production). 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 From 100fa060ce1d2db7e4d1533efd289f9c18d08d53 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 28 Mar 2021 14:31:23 -0500 Subject: [PATCH 7/9] remove redundancy --- src/patterns.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/patterns.md b/src/patterns.md index c5345da76..7676d5255 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -6,8 +6,7 @@ > > _PatternNoTopAlt_ :\ >       _PatternWithoutRange_\ ->    | [_RangePattern_]\ ->    | `(` _Pattern_ `)` +>    | [_RangePattern_] > > _PatternWithoutRange_ :\ >       [_LiteralPattern_]\ From 4b574cad3cb179692abb30880520cca11fa90a2f Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 21 Apr 2021 20:13:55 -0500 Subject: [PATCH 8/9] update pat2015/2021 parts --- src/macros-by-example.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/macros-by-example.md b/src/macros-by-example.md index c2a7e885c..3bdf9ce41 100644 --- a/src/macros-by-example.md +++ b/src/macros-by-example.md @@ -28,7 +28,7 @@ > > _MacroFragSpec_ :\ >       `block` | `expr` | `ident` | `item` | `lifetime` | `literal`\ ->    | `meta` | `pat` | `pat2015` | `pat2021` | `path` | `stmt` | `tt` | `ty` | `vis` +>    | `meta` | `pat` | `pat_param` | `path` | `stmt` | `tt` | `ty` | `vis` > > _MacroRepSep_ :\ >    [_Token_]_except delimiters and repetition operators_ @@ -122,9 +122,8 @@ fragment specifiers are: * `block`: a [_BlockExpression_] * `stmt`: a [_Statement_] without the trailing semicolon (except for item statements that require semicolons) - * `pat2015`: a [_PatternNoTopAlt_] - * `pat2021`: a [_Pattern_] - * `pat`: equivalent to either `pat2015` or `pat2021`, depending on edition (see note below). + * `pat_param`: a [_PatternNoTopAlt_] + * `pat`: equivalent to `pat_param` * `expr`: an [_Expression_] * `ty`: a [_Type_] * `ident`: an [IDENTIFIER_OR_KEYWORD] @@ -135,11 +134,6 @@ fragment specifiers are: * `vis`: a possibly empty [_Visibility_] qualifier * `literal`: matches `-`?[_LiteralExpression_] -> **Edition Differences**: In Editions 2015 and 2018, `pat` fragment-specifiers -> do not allow top-level or-patterns (i.e. they accept [_PatternNoTopAlt_]). In -> Edition 2021, this was changed so that top-level or-patterns are accepted by -> `pat` (i.e. [_Pattern_]). - In the transcriber, metavariables are referred to simply by `$`_name_, since the fragment kind is specified in the matcher. Metavariables are replaced with the syntax element that matched them. The keyword metavariable `$crate` can be @@ -495,7 +489,6 @@ 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 From 629e7df5dc6fda16af98bc40cd139de0e0fa73a2 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 28 Apr 2021 11:11:10 -0700 Subject: [PATCH 9/9] Temporarily remove pat_param. It is not clear if #83386 will merge in time for 1.53. I'd like to get the rest of this in for that release. --- src/macros-by-example.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/macros-by-example.md b/src/macros-by-example.md index 3bdf9ce41..172b9863e 100644 --- a/src/macros-by-example.md +++ b/src/macros-by-example.md @@ -28,7 +28,7 @@ > > _MacroFragSpec_ :\ >       `block` | `expr` | `ident` | `item` | `lifetime` | `literal`\ ->    | `meta` | `pat` | `pat_param` | `path` | `stmt` | `tt` | `ty` | `vis` +>    | `meta` | `pat` | `path` | `stmt` | `tt` | `ty` | `vis` > > _MacroRepSep_ :\ >    [_Token_]_except delimiters and repetition operators_ @@ -122,8 +122,7 @@ fragment specifiers are: * `block`: a [_BlockExpression_] * `stmt`: a [_Statement_] without the trailing semicolon (except for item statements that require semicolons) - * `pat_param`: a [_PatternNoTopAlt_] - * `pat`: equivalent to `pat_param` + * `pat`: a [_PatternNoTopAlt_] * `expr`: an [_Expression_] * `ty`: a [_Type_] * `ident`: an [IDENTIFIER_OR_KEYWORD]