Skip to content

Commit 1870537

Browse files
committed
initial implementation of or-pattern parsing
Initial implementation of parsing or-patterns e.g., `Some(Foo | Bar)`. This is a partial implementation of RFC 2535.
1 parent 1713ac4 commit 1870537

22 files changed

+142
-60
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# `or_patterns`
2+
3+
The tracking issue for this feature is: [#54883]
4+
5+
[#54883]: https://github.com/rust-lang/rust/issues/54883
6+
7+
------------------------
8+
9+
The `or_pattern` language feature allows `|` to be arbitrarily nested within
10+
a pattern, for example, `Some(A(0) | B(1 | 2))` becomes a valid pattern.
11+
12+
## Examples
13+
14+
```rust,ignore
15+
#![feature(or_patterns)]
16+
17+
pub enum Foo {
18+
Bar,
19+
Baz,
20+
Quux,
21+
}
22+
23+
pub fn example(maybe_foo: Option<Foo>) {
24+
match maybe_foo {
25+
Some(Foo::Bar | Foo::Baz) => {
26+
println!("The value contained `Bar` or `Baz`");
27+
}
28+
Some(_) => {
29+
println!("The value did not contain `Bar` or `Baz`");
30+
}
31+
None => {
32+
println!("The value was `None`");
33+
}
34+
}
35+
}
36+
```

src/librustc/hir/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,8 @@ pub enum PatKind {
978978
TupleStruct(QPath, HirVec<P<Pat>>, Option<usize>),
979979

980980
/// An or-pattern `A | B | C`.
981-
Or(Vec<P<Pat>>),
981+
/// Invariant: `pats.len() >= 2`.
982+
Or(HirVec<P<Pat>>),
982983

983984
/// A path pattern for an unit struct/variant or a (maybe-associated) constant.
984985
Path(QPath),

src/librustc/hir/print.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use syntax::source_map::{SourceMap, Spanned};
44
use syntax::parse::ParseSess;
55
use syntax::print::pp::{self, Breaks};
66
use syntax::print::pp::Breaks::{Consistent, Inconsistent};
7-
use syntax::print::pprust::{self, Comments, PrintState, SeparatorSpacing};
7+
use syntax::print::pprust::{self, Comments, PrintState};
88
use syntax::symbol::kw;
99
use syntax::util::parser::{self, AssocOp, Fixity};
1010
use syntax_pos::{self, BytePos, FileName};
@@ -1688,8 +1688,7 @@ impl<'a> State<'a> {
16881688
self.s.word("}");
16891689
}
16901690
PatKind::Or(ref pats) => {
1691-
let spacing = SeparatorSpacing::Both;
1692-
self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(&p))?;
1691+
self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(&p));
16931692
}
16941693
PatKind::Tuple(ref elts, ddpos) => {
16951694
self.popen();

src/librustc_mir/build/matches/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -658,9 +658,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
658658
}
659659
}
660660
PatternKind::Or { ref pats } => {
661-
// FIXME(#47184): extract or handle `pattern_user_ty` somehow
662661
for pat in pats {
663-
self.visit_bindings(&pat, &pattern_user_ty.clone(), f);
662+
self.visit_bindings(&pat, pattern_user_ty.clone(), f);
664663
}
665664
}
666665
}

src/librustc_mir/hair/pattern/_match.rs

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
/// D((r_1, p_(i,2), .., p_(i,n)))
7676
/// D((r_2, p_(i,2), .., p_(i,n)))
7777
///
78+
/// Note that the OR-patterns are not always used directly in Rust, but are used to derive
79+
/// the exhaustive integer matching rules, so they're written here for posterity.
80+
///
7881
/// The algorithm for computing `U`
7982
/// -------------------------------
8083
/// The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).

src/librustc_mir/hair/pattern/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ pub enum PatternKind<'tcx> {
176176
suffix: Vec<Pattern<'tcx>>,
177177
},
178178

179-
/// or-pattern
179+
/// An or-pattern, e.g. `p | q`.
180+
/// Invariant: `pats.len() >= 2`.
180181
Or {
181182
pats: Vec<Pattern<'tcx>>,
182183
},

src/librustc_typeck/check/_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
313313
PatKind::Or(ref pats) => {
314314
let expected_ty = self.structurally_resolved_type(pat.span, expected);
315315
for pat in pats {
316-
self.check_pat_walk(pat, expected, def_bm, false);
316+
self.check_pat_walk(pat, expected, def_bm, discrim_span);
317317
}
318318
expected_ty
319319
}

src/libsyntax/ast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ pub enum PatKind {
650650
TupleStruct(Path, Vec<P<Pat>>),
651651

652652
/// An or-pattern `A | B | C`.
653+
/// Invariant: `pats.len() >= 2`.
653654
Or(Vec<P<Pat>>),
654655

655656
/// A possibly qualified path pattern.

src/libsyntax/feature_gate.rs

+5
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,9 @@ declare_features! (
559559
// Allows `impl Trait` to be used inside type aliases (RFC 2515).
560560
(active, type_alias_impl_trait, "1.38.0", Some(63063), None),
561561

562+
// Allows the use of or-patterns, e.g. `0 | 1`.
563+
(active, or_patterns, "1.38.0", Some(54883), None),
564+
562565
// -------------------------------------------------------------------------
563566
// feature-group-end: actual feature gates
564567
// -------------------------------------------------------------------------
@@ -571,6 +574,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
571574
sym::impl_trait_in_bindings,
572575
sym::generic_associated_types,
573576
sym::const_generics,
577+
sym::or_patterns,
574578
sym::let_chains,
575579
];
576580

@@ -2443,6 +2447,7 @@ pub fn check_crate(krate: &ast::Crate,
24432447
gate_all!(let_chains_spans, let_chains, "`let` expressions in this position are experimental");
24442448
gate_all!(async_closure_spans, async_closure, "async closures are unstable");
24452449
gate_all!(yield_spans, generators, "yield syntax is experimental");
2450+
gate_all!(or_pattern_spans, or_patterns, "or-patterns syntax is experimental");
24462451

24472452
let visitor = &mut PostExpansionVisitor {
24482453
context: &ctx,

src/libsyntax/parse/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ pub struct ParseSess {
6666
// Places where `yield e?` exprs were used and should be feature gated.
6767
pub yield_spans: Lock<Vec<Span>>,
6868
pub injected_crate_name: Once<Symbol>,
69+
// Places where or-patterns e.g. `Some(Foo | Bar)` were used and should be feature gated.
70+
pub or_pattern_spans: Lock<Vec<Span>>,
6971
}
7072

7173
impl ParseSess {
@@ -96,6 +98,7 @@ impl ParseSess {
9698
async_closure_spans: Lock::new(Vec::new()),
9799
yield_spans: Lock::new(Vec::new()),
98100
injected_crate_name: Once::new(),
101+
or_pattern_spans: Lock::new(Vec::new()),
99102
}
100103
}
101104

src/libsyntax/parse/parser/pat.rs

+37-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ use errors::{Applicability, DiagnosticBuilder};
1414

1515
impl<'a> Parser<'a> {
1616
/// Parses a pattern.
17-
pub fn parse_pat(&mut self, expected: Option<&'static str>) -> PResult<'a, P<Pat>> {
17+
pub fn parse_pat(
18+
&mut self,
19+
expected: Option<&'static str>
20+
) -> PResult<'a, P<Pat>> {
1821
self.parse_pat_with_range_pat(true, expected)
1922
}
2023

@@ -97,6 +100,34 @@ impl<'a> Parser<'a> {
97100
Ok(())
98101
}
99102

103+
/// Parses a pattern, that may be a or-pattern (e.g. `Some(Foo | Bar)`).
104+
fn parse_pat_with_or(&mut self, expected: Option<&'static str>) -> PResult<'a, P<Pat>> {
105+
// Parse the first pattern.
106+
let first_pat = self.parse_pat(expected)?;
107+
108+
// If the next token is not a `|`, this is not an or-pattern and
109+
// we should exit here.
110+
if !self.check(&token::BinOp(token::Or)) {
111+
return Ok(first_pat)
112+
}
113+
114+
let lo = first_pat.span;
115+
116+
let mut pats = vec![first_pat];
117+
118+
while self.eat(&token::BinOp(token::Or)) {
119+
pats.push(self.parse_pat_with_range_pat(
120+
true, expected
121+
)?);
122+
}
123+
124+
let or_pattern_span = lo.to(self.prev_span);
125+
126+
self.sess.or_pattern_spans.borrow_mut().push(or_pattern_span);
127+
128+
Ok(self.mk_pat(or_pattern_span, PatKind::Or(pats)))
129+
}
130+
100131
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
101132
/// allowed).
102133
fn parse_pat_with_range_pat(
@@ -240,7 +271,9 @@ impl<'a> Parser<'a> {
240271

241272
/// Parse a tuple or parenthesis pattern.
242273
fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
243-
let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?;
274+
let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
275+
p.parse_pat_with_or(None)
276+
})?;
244277

245278
// Here, `(pat,)` is a tuple pattern.
246279
// For backward compatibility, `(..)` is a tuple pattern as well.
@@ -483,7 +516,7 @@ impl<'a> Parser<'a> {
483516
err.span_label(self.token.span, msg);
484517
return Err(err);
485518
}
486-
let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?;
519+
let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat_with_or(None))?;
487520
Ok(PatKind::TupleStruct(path, fields))
488521
}
489522

@@ -627,7 +660,7 @@ impl<'a> Parser<'a> {
627660
// Parsing a pattern of the form "fieldname: pat"
628661
let fieldname = self.parse_field_name()?;
629662
self.bump();
630-
let pat = self.parse_pat(None)?;
663+
let pat = self.parse_pat_with_or(None)?;
631664
hi = pat.span;
632665
(pat, fieldname, false)
633666
} else {

src/libsyntax/print/pprust.rs

+12-35
Original file line numberDiff line numberDiff line change
@@ -431,46 +431,33 @@ impl std::ops::DerefMut for State<'_> {
431431
}
432432
}
433433

434-
pub enum SeparatorSpacing {
435-
After,
436-
Both,
437-
}
438-
439434
pub trait PrintState<'a>: std::ops::Deref<Target=pp::Printer> + std::ops::DerefMut {
440435
fn comments(&mut self) -> &mut Option<Comments<'a>>;
441436
fn print_ident(&mut self, ident: ast::Ident);
442437
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
443438

444-
fn strsep<T, F>(
445-
&mut self,
446-
sep: &'static str,
447-
spacing: SeparatorSpacing,
448-
b: Breaks,
449-
elts: &[T],
450-
mut op: F
451-
) -> io::Result<()>
439+
fn strsep<T, F>(&mut self, sep: &'static str, space_before: bool,
440+
b: Breaks, elts: &[T], mut op: F)
452441
where F: FnMut(&mut Self, &T),
453442
{
454443
self.rbox(0, b);
455-
let mut first = true;
456-
for elt in elts {
457-
if first {
458-
first = false;
459-
} else {
460-
if let SeparatorSpacing::Both = spacing {
461-
self.writer().space();
444+
if let Some((first, rest)) = elts.split_first() {
445+
op(self, first);
446+
for elt in rest {
447+
if space_before {
448+
self.space();
462449
}
463450
self.word_space(sep);
451+
op(self, elt);
464452
}
465-
op(self, elt);
466453
}
467454
self.end();
468455
}
469456

470-
fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], mut op: F)
457+
fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
471458
where F: FnMut(&mut Self, &T),
472459
{
473-
self.strsep(",", SeparatorSpacing::After, b, elts, op)
460+
self.strsep(",", false, b, elts, op)
474461
}
475462

476463
fn maybe_print_comment(&mut self, pos: BytePos) {
@@ -2379,8 +2366,7 @@ impl<'a> State<'a> {
23792366
self.pclose();
23802367
}
23812368
PatKind::Or(ref pats) => {
2382-
let spacing = SeparatorSpacing::Both;
2383-
self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(p))?;
2369+
self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(p));
23842370
}
23852371
PatKind::Path(None, ref path) => {
23862372
self.print_path(path, true, 0);
@@ -2458,16 +2444,7 @@ impl<'a> State<'a> {
24582444
}
24592445

24602446
fn print_pats(&mut self, pats: &[P<ast::Pat>]) {
2461-
let mut first = true;
2462-
for p in pats {
2463-
if first {
2464-
first = false;
2465-
} else {
2466-
self.s.space();
2467-
self.word_space("|");
2468-
}
2469-
self.print_pat(p);
2470-
}
2447+
self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p));
24712448
}
24722449

24732450
fn print_arm(&mut self, arm: &ast::Arm) {

src/libsyntax/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
462462
visitor.visit_expr(upper_bound);
463463
}
464464
PatKind::Wild | PatKind::Rest => {},
465-
PatKind::Tuple(ref elems) => {
465+
PatKind::Tuple(ref elems)
466466
| PatKind::Slice(ref elems)
467467
| PatKind::Or(ref elems) => {
468468
walk_list!(visitor, visit_pat, elems);

src/libsyntax_pos/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ symbols! {
469469
option_env,
470470
opt_out_copy,
471471
or,
472+
or_patterns,
472473
Ord,
473474
Ordering,
474475
Output,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![crate_type="lib"]
2+
3+
pub fn example(x: Option<usize>) {
4+
match x {
5+
Some(0 | 1 | 2) => {}
6+
//~^ ERROR: or-patterns syntax is experimental
7+
_ => {}
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: or-patterns syntax is experimental
2+
--> $DIR/feature-gate-or_patterns.rs:5:14
3+
|
4+
LL | Some(0 | 1 | 2) => {}
5+
| ^^^^^^^^^
6+
|
7+
= note: for more information, see https://github.com/rust-lang/rust/issues/54883
8+
= help: add `#![feature(or_patterns)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.

src/test/ui/parser/pat-lt-bracket-6.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ fn main() {
22
struct Test(&'static u8, [u8; 0]);
33
let x = Test(&0, []);
44

5-
let Test(&desc[..]) = x; //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
6-
//~^ ERROR subslice patterns are unstable
5+
let Test(&desc[..]) = x;
6+
//~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[`
7+
//~^^ ERROR subslice patterns are unstable
78
}
89

910
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types

src/test/ui/parser/pat-lt-bracket-6.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `)`, `,`, or `@`, found `[`
1+
error: expected one of `)`, `,`, `@`, or `|`, found `[`
22
--> $DIR/pat-lt-bracket-6.rs:5:19
33
|
44
LL | let Test(&desc[..]) = x;
5-
| ^ expected one of `)`, `,`, or `@` here
5+
| ^ expected one of `)`, `,`, `@`, or `|` here
66

77
error[E0658]: subslice patterns are unstable
88
--> $DIR/pat-lt-bracket-6.rs:5:20
@@ -14,7 +14,7 @@ LL | let Test(&desc[..]) = x;
1414
= help: add `#![feature(slice_patterns)]` to the crate attributes to enable
1515

1616
error[E0308]: mismatched types
17-
--> $DIR/pat-lt-bracket-6.rs:9:30
17+
--> $DIR/pat-lt-bracket-6.rs:10:30
1818
|
1919
LL | const RECOVERY_WITNESS: () = 0;
2020
| ^ expected (), found integer

src/test/ui/parser/pat-lt-bracket-7.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ fn main() {
22
struct Thing(u8, [u8; 0]);
33
let foo = core::iter::empty();
44

5-
for Thing(x[]) in foo {} //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
5+
for Thing(x[]) in foo {}
6+
//~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[`
67
}
78

89
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types

0 commit comments

Comments
 (0)