Skip to content

Commit 38f4d55

Browse files
committed
Rollup merge of #48500 - petrochenkov:parpat, r=nikomatsakis
Support parentheses in patterns under feature gate This is a prerequisite for any other extensions to pattern syntax - `|` with multiple patterns, type ascription, `..PAT` in slice patterns. Closes rust-lang/rfcs#554
2 parents 39d5e1c + c9aff92 commit 38f4d55

File tree

12 files changed

+166
-107
lines changed

12 files changed

+166
-107
lines changed

src/librustc/hir/lowering.rs

Lines changed: 78 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,86 +2472,88 @@ impl<'a> LoweringContext<'a> {
24722472
}
24732473

24742474
fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> {
2475-
let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id);
2475+
let node = match p.node {
2476+
PatKind::Wild => hir::PatKind::Wild,
2477+
PatKind::Ident(ref binding_mode, pth1, ref sub) => {
2478+
match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
2479+
// `None` can occur in body-less function signatures
2480+
def @ None | def @ Some(Def::Local(_)) => {
2481+
let canonical_id = match def {
2482+
Some(Def::Local(id)) => id,
2483+
_ => p.id
2484+
};
2485+
hir::PatKind::Binding(self.lower_binding_mode(binding_mode),
2486+
canonical_id,
2487+
respan(pth1.span, pth1.node.name),
2488+
sub.as_ref().map(|x| self.lower_pat(x)))
2489+
}
2490+
Some(def) => {
2491+
hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
2492+
span: pth1.span,
2493+
def,
2494+
segments: hir_vec![
2495+
hir::PathSegment::from_name(pth1.node.name)
2496+
],
2497+
})))
2498+
}
2499+
}
2500+
}
2501+
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
2502+
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
2503+
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
2504+
ImplTraitContext::Disallowed);
2505+
hir::PatKind::TupleStruct(qpath,
2506+
pats.iter().map(|x| self.lower_pat(x)).collect(),
2507+
ddpos)
2508+
}
2509+
PatKind::Path(ref qself, ref path) => {
2510+
hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional,
2511+
ImplTraitContext::Disallowed))
2512+
}
2513+
PatKind::Struct(ref path, ref fields, etc) => {
2514+
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
2515+
ImplTraitContext::Disallowed);
2516+
2517+
let fs = fields.iter()
2518+
.map(|f| {
2519+
Spanned {
2520+
span: f.span,
2521+
node: hir::FieldPat {
2522+
name: self.lower_ident(f.node.ident),
2523+
pat: self.lower_pat(&f.node.pat),
2524+
is_shorthand: f.node.is_shorthand,
2525+
},
2526+
}
2527+
})
2528+
.collect();
2529+
hir::PatKind::Struct(qpath, fs, etc)
2530+
}
2531+
PatKind::Tuple(ref elts, ddpos) => {
2532+
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
2533+
}
2534+
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
2535+
PatKind::Ref(ref inner, mutbl) => {
2536+
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
2537+
}
2538+
PatKind::Range(ref e1, ref e2, ref end) => {
2539+
hir::PatKind::Range(P(self.lower_expr(e1)),
2540+
P(self.lower_expr(e2)),
2541+
self.lower_range_end(end))
2542+
}
2543+
PatKind::Slice(ref before, ref slice, ref after) => {
2544+
hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
2545+
slice.as_ref().map(|x| self.lower_pat(x)),
2546+
after.iter().map(|x| self.lower_pat(x)).collect())
2547+
}
2548+
PatKind::Paren(ref inner) => return self.lower_pat(inner),
2549+
PatKind::Mac(_) => panic!("Shouldn't exist here"),
2550+
};
24762551

2552+
let LoweredNodeId { node_id, hir_id } = self.lower_node_id(p.id);
24772553
P(hir::Pat {
24782554
id: node_id,
24792555
hir_id,
2480-
node: match p.node {
2481-
PatKind::Wild => hir::PatKind::Wild,
2482-
PatKind::Ident(ref binding_mode, pth1, ref sub) => {
2483-
match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
2484-
// `None` can occur in body-less function signatures
2485-
def @ None | def @ Some(Def::Local(_)) => {
2486-
let canonical_id = match def {
2487-
Some(Def::Local(id)) => id,
2488-
_ => p.id
2489-
};
2490-
hir::PatKind::Binding(self.lower_binding_mode(binding_mode),
2491-
canonical_id,
2492-
respan(pth1.span, pth1.node.name),
2493-
sub.as_ref().map(|x| self.lower_pat(x)))
2494-
}
2495-
Some(def) => {
2496-
hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
2497-
span: pth1.span,
2498-
def,
2499-
segments: hir_vec![
2500-
hir::PathSegment::from_name(pth1.node.name)
2501-
],
2502-
})))
2503-
}
2504-
}
2505-
}
2506-
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
2507-
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
2508-
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
2509-
ImplTraitContext::Disallowed);
2510-
hir::PatKind::TupleStruct(qpath,
2511-
pats.iter().map(|x| self.lower_pat(x)).collect(),
2512-
ddpos)
2513-
}
2514-
PatKind::Path(ref qself, ref path) => {
2515-
hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional,
2516-
ImplTraitContext::Disallowed))
2517-
}
2518-
PatKind::Struct(ref path, ref fields, etc) => {
2519-
let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional,
2520-
ImplTraitContext::Disallowed);
2521-
2522-
let fs = fields.iter()
2523-
.map(|f| {
2524-
Spanned {
2525-
span: f.span,
2526-
node: hir::FieldPat {
2527-
name: self.lower_ident(f.node.ident),
2528-
pat: self.lower_pat(&f.node.pat),
2529-
is_shorthand: f.node.is_shorthand,
2530-
},
2531-
}
2532-
})
2533-
.collect();
2534-
hir::PatKind::Struct(qpath, fs, etc)
2535-
}
2536-
PatKind::Tuple(ref elts, ddpos) => {
2537-
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
2538-
}
2539-
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
2540-
PatKind::Ref(ref inner, mutbl) => {
2541-
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
2542-
}
2543-
PatKind::Range(ref e1, ref e2, ref end) => {
2544-
hir::PatKind::Range(P(self.lower_expr(e1)),
2545-
P(self.lower_expr(e2)),
2546-
self.lower_range_end(end))
2547-
}
2548-
PatKind::Slice(ref before, ref slice, ref after) => {
2549-
hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
2550-
slice.as_ref().map(|x| self.lower_pat(x)),
2551-
after.iter().map(|x| self.lower_pat(x)).collect())
2552-
}
2553-
PatKind::Mac(_) => panic!("Shouldn't exist here"),
2554-
},
2556+
node,
25552557
span: p.span,
25562558
})
25572559
}

src/librustc_lint/builtin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ impl EarlyLintPass for IllegalFloatLiteralPattern {
737737
PatKind::TupleStruct(..) |
738738
PatKind::Ref(..) |
739739
PatKind::Box(..) |
740+
PatKind::Paren(..) |
740741
PatKind::Slice(..) => (),
741742

742743
// Extract the expressions and check them

src/libsyntax/ast.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ impl Pat {
562562
PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
563563
s.iter().all(|p| p.walk(it))
564564
}
565-
PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
565+
PatKind::Box(ref s) | PatKind::Ref(ref s, _) | PatKind::Paren(ref s) => {
566566
s.walk(it)
567567
}
568568
PatKind::Slice(ref before, ref slice, ref after) => {
@@ -656,6 +656,8 @@ pub enum PatKind {
656656
/// `[a, b, ..i, y, z]` is represented as:
657657
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`
658658
Slice(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
659+
/// Parentheses in patters used for grouping, i.e. `(PAT)`.
660+
Paren(P<Pat>),
659661
/// A macro pattern; pre-expansion
660662
Mac(Mac),
661663
}

src/libsyntax/feature_gate.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,9 @@ declare_features! (
449449

450450
// Multiple patterns with `|` in `if let` and `while let`
451451
(active, if_while_or_patterns, "1.26.0", Some(48215)),
452+
453+
// Parentheses in patterns
454+
(active, pattern_parentheses, "1.26.0", None),
452455
);
453456

454457
declare_features! (
@@ -1663,6 +1666,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
16631666
gate_feature_post!(&self, dotdoteq_in_patterns, pattern.span,
16641667
"`..=` syntax in patterns is experimental");
16651668
}
1669+
PatKind::Paren(..) => {
1670+
gate_feature_post!(&self, pattern_parentheses, pattern.span,
1671+
"parentheses in patterns are unstable");
1672+
}
16661673
_ => {}
16671674
}
16681675
visit::walk_pat(self, pattern)

src/libsyntax/fold.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
11481148
slice.map(|x| folder.fold_pat(x)),
11491149
after.move_map(|x| folder.fold_pat(x)))
11501150
}
1151+
PatKind::Paren(inner) => PatKind::Paren(folder.fold_pat(inner)),
11511152
PatKind::Mac(mac) => PatKind::Mac(folder.fold_mac(mac))
11521153
},
11531154
span: folder.new_span(span)

src/libsyntax/parse/parser.rs

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3484,33 +3484,47 @@ impl<'a> Parser<'a> {
34843484
};
34853485
}
34863486

3487-
fn parse_pat_tuple_elements(&mut self, unary_needs_comma: bool)
3488-
-> PResult<'a, (Vec<P<Pat>>, Option<usize>)> {
3489-
let mut fields = vec![];
3490-
let mut ddpos = None;
3487+
// Parses a parenthesized list of patterns like
3488+
// `()`, `(p)`, `(p,)`, `(p, q)`, or `(p, .., q)`. Returns:
3489+
// - a vector of the patterns that were parsed
3490+
// - an option indicating the index of the `..` element
3491+
// - a boolean indicating whether a trailing comma was present.
3492+
// Trailing commas are significant because (p) and (p,) are different patterns.
3493+
fn parse_parenthesized_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
3494+
self.expect(&token::OpenDelim(token::Paren))?;
34913495

3492-
while !self.check(&token::CloseDelim(token::Paren)) {
3493-
if ddpos.is_none() && self.eat(&token::DotDot) {
3494-
ddpos = Some(fields.len());
3495-
if self.eat(&token::Comma) {
3496-
// `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
3497-
fields.push(self.parse_pat()?);
3496+
let mut fields = Vec::new();
3497+
let mut ddpos = None;
3498+
let mut trailing_comma = false;
3499+
loop {
3500+
if self.eat(&token::DotDot) {
3501+
if ddpos.is_none() {
3502+
ddpos = Some(fields.len());
3503+
} else {
3504+
// Emit a friendly error, ignore `..` and continue parsing
3505+
self.span_err(self.prev_span,
3506+
"`..` can only be used once per tuple or tuple struct pattern");
34983507
}
3499-
} else if ddpos.is_some() && self.eat(&token::DotDot) {
3500-
// Emit a friendly error, ignore `..` and continue parsing
3501-
self.span_err(self.prev_span, "`..` can only be used once per \
3502-
tuple or tuple struct pattern");
3503-
} else {
3508+
} else if !self.check(&token::CloseDelim(token::Paren)) {
35043509
fields.push(self.parse_pat()?);
3510+
} else {
3511+
break
35053512
}
35063513

3507-
if !self.check(&token::CloseDelim(token::Paren)) ||
3508-
(unary_needs_comma && fields.len() == 1 && ddpos.is_none()) {
3509-
self.expect(&token::Comma)?;
3514+
trailing_comma = self.eat(&token::Comma);
3515+
if !trailing_comma {
3516+
break
35103517
}
35113518
}
35123519

3513-
Ok((fields, ddpos))
3520+
if ddpos == Some(fields.len()) && trailing_comma {
3521+
// `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
3522+
self.span_err(self.prev_span, "trailing comma is not permitted after `..`");
3523+
}
3524+
3525+
self.expect(&token::CloseDelim(token::Paren))?;
3526+
3527+
Ok((fields, ddpos, trailing_comma))
35143528
}
35153529

35163530
fn parse_pat_vec_elements(
@@ -3714,10 +3728,12 @@ impl<'a> Parser<'a> {
37143728
}
37153729
token::OpenDelim(token::Paren) => {
37163730
// Parse (pat,pat,pat,...) as tuple pattern
3717-
self.bump();
3718-
let (fields, ddpos) = self.parse_pat_tuple_elements(true)?;
3719-
self.expect(&token::CloseDelim(token::Paren))?;
3720-
pat = PatKind::Tuple(fields, ddpos);
3731+
let (fields, ddpos, trailing_comma) = self.parse_parenthesized_pat_list()?;
3732+
pat = if fields.len() == 1 && ddpos.is_none() && !trailing_comma {
3733+
PatKind::Paren(fields.into_iter().nth(0).unwrap())
3734+
} else {
3735+
PatKind::Tuple(fields, ddpos)
3736+
};
37213737
}
37223738
token::OpenDelim(token::Bracket) => {
37233739
// Parse [pat,pat,...] as slice pattern
@@ -3807,9 +3823,7 @@ impl<'a> Parser<'a> {
38073823
return Err(self.fatal("unexpected `(` after qualified path"));
38083824
}
38093825
// Parse tuple struct or enum pattern
3810-
self.bump();
3811-
let (fields, ddpos) = self.parse_pat_tuple_elements(false)?;
3812-
self.expect(&token::CloseDelim(token::Paren))?;
3826+
let (fields, ddpos, _) = self.parse_parenthesized_pat_list()?;
38133827
pat = PatKind::TupleStruct(path, fields, ddpos)
38143828
}
38153829
_ => pat = PatKind::Path(qself, path),

src/libsyntax/print/pprust.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,11 @@ impl<'a> State<'a> {
26592659
|s, p| s.print_pat(p))?;
26602660
self.s.word("]")?;
26612661
}
2662+
PatKind::Paren(ref inner) => {
2663+
self.popen()?;
2664+
self.print_pat(inner)?;
2665+
self.pclose()?;
2666+
}
26622667
PatKind::Mac(ref m) => self.print_mac(m, token::Paren)?,
26632668
}
26642669
self.ann.post(self, NodePat(pat))

src/libsyntax/visit.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,8 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
425425
walk_list!(visitor, visit_pat, tuple_elements);
426426
}
427427
PatKind::Box(ref subpattern) |
428-
PatKind::Ref(ref subpattern, _) => {
428+
PatKind::Ref(ref subpattern, _) |
429+
PatKind::Paren(ref subpattern) => {
429430
visitor.visit_pat(subpattern)
430431
}
431432
PatKind::Ident(_, ref pth1, ref optional_subpattern) => {

src/test/parse-fail/pat-tuple-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212

1313
fn main() {
1414
match 0 {
15-
(pat, ..,) => {} //~ ERROR expected pattern, found `)`
15+
(pat, ..,) => {} //~ ERROR trailing comma is not permitted after `..`
1616
}
1717
}

src/test/parse-fail/pat-tuple-6.rs renamed to src/test/run-pass/pat-tuple-7.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-flags: -Z parse-only
11+
#![feature(pattern_parentheses)]
1212

1313
fn main() {
1414
match 0 {
15-
(pat) => {} //~ ERROR expected one of `,` or `@`, found `)`
15+
(pat) => assert_eq!(pat, 0)
1616
}
1717
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
match 0 {
13+
(pat) => {} //~ ERROR parentheses in patterns are unstable
14+
}
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0658]: parentheses in patterns are unstable
2+
--> $DIR/feature-gate-pattern_parentheses.rs:13:9
3+
|
4+
LL | (pat) => {} //~ ERROR parentheses in patterns are unstable
5+
| ^^^^^
6+
|
7+
= help: add #![feature(pattern_parentheses)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
If you want more information on this error, try using "rustc --explain E0658"

0 commit comments

Comments
 (0)