1
Fork 0

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.
This commit is contained in:
Dan Robertson 2019-07-14 01:05:52 +00:00
parent 1713ac4bf5
commit 1870537f27
No known key found for this signature in database
GPG key ID: 45C4A652C47E42A5
22 changed files with 142 additions and 60 deletions

View file

@ -0,0 +1,36 @@
# `or_patterns`
The tracking issue for this feature is: [#54883]
[#54883]: https://github.com/rust-lang/rust/issues/54883
------------------------
The `or_pattern` language feature allows `|` to be arbitrarily nested within
a pattern, for example, `Some(A(0) | B(1 | 2))` becomes a valid pattern.
## Examples
```rust,ignore
#![feature(or_patterns)]
pub enum Foo {
Bar,
Baz,
Quux,
}
pub fn example(maybe_foo: Option<Foo>) {
match maybe_foo {
Some(Foo::Bar | Foo::Baz) => {
println!("The value contained `Bar` or `Baz`");
}
Some(_) => {
println!("The value did not contain `Bar` or `Baz`");
}
None => {
println!("The value was `None`");
}
}
}
```

View file

@ -978,7 +978,8 @@ pub enum PatKind {
TupleStruct(QPath, HirVec<P<Pat>>, Option<usize>), TupleStruct(QPath, HirVec<P<Pat>>, Option<usize>),
/// An or-pattern `A | B | C`. /// An or-pattern `A | B | C`.
Or(Vec<P<Pat>>), /// Invariant: `pats.len() >= 2`.
Or(HirVec<P<Pat>>),
/// A path pattern for an unit struct/variant or a (maybe-associated) constant. /// A path pattern for an unit struct/variant or a (maybe-associated) constant.
Path(QPath), Path(QPath),

View file

@ -4,7 +4,7 @@ use syntax::source_map::{SourceMap, Spanned};
use syntax::parse::ParseSess; use syntax::parse::ParseSess;
use syntax::print::pp::{self, Breaks}; use syntax::print::pp::{self, Breaks};
use syntax::print::pp::Breaks::{Consistent, Inconsistent}; use syntax::print::pp::Breaks::{Consistent, Inconsistent};
use syntax::print::pprust::{self, Comments, PrintState, SeparatorSpacing}; use syntax::print::pprust::{self, Comments, PrintState};
use syntax::symbol::kw; use syntax::symbol::kw;
use syntax::util::parser::{self, AssocOp, Fixity}; use syntax::util::parser::{self, AssocOp, Fixity};
use syntax_pos::{self, BytePos, FileName}; use syntax_pos::{self, BytePos, FileName};
@ -1688,8 +1688,7 @@ impl<'a> State<'a> {
self.s.word("}"); self.s.word("}");
} }
PatKind::Or(ref pats) => { PatKind::Or(ref pats) => {
let spacing = SeparatorSpacing::Both; self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(&p));
self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(&p))?;
} }
PatKind::Tuple(ref elts, ddpos) => { PatKind::Tuple(ref elts, ddpos) => {
self.popen(); self.popen();

View file

@ -658,9 +658,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
PatternKind::Or { ref pats } => { PatternKind::Or { ref pats } => {
// FIXME(#47184): extract or handle `pattern_user_ty` somehow
for pat in pats { for pat in pats {
self.visit_bindings(&pat, &pattern_user_ty.clone(), f); self.visit_bindings(&pat, pattern_user_ty.clone(), f);
} }
} }
} }

View file

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

View file

@ -176,7 +176,8 @@ pub enum PatternKind<'tcx> {
suffix: Vec<Pattern<'tcx>>, suffix: Vec<Pattern<'tcx>>,
}, },
/// or-pattern /// An or-pattern, e.g. `p | q`.
/// Invariant: `pats.len() >= 2`.
Or { Or {
pats: Vec<Pattern<'tcx>>, pats: Vec<Pattern<'tcx>>,
}, },

View file

@ -313,7 +313,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PatKind::Or(ref pats) => { PatKind::Or(ref pats) => {
let expected_ty = self.structurally_resolved_type(pat.span, expected); let expected_ty = self.structurally_resolved_type(pat.span, expected);
for pat in pats { for pat in pats {
self.check_pat_walk(pat, expected, def_bm, false); self.check_pat_walk(pat, expected, def_bm, discrim_span);
} }
expected_ty expected_ty
} }

View file

@ -650,6 +650,7 @@ pub enum PatKind {
TupleStruct(Path, Vec<P<Pat>>), TupleStruct(Path, Vec<P<Pat>>),
/// An or-pattern `A | B | C`. /// An or-pattern `A | B | C`.
/// Invariant: `pats.len() >= 2`.
Or(Vec<P<Pat>>), Or(Vec<P<Pat>>),
/// A possibly qualified path pattern. /// A possibly qualified path pattern.

View file

@ -559,6 +559,9 @@ declare_features! (
// Allows `impl Trait` to be used inside type aliases (RFC 2515). // Allows `impl Trait` to be used inside type aliases (RFC 2515).
(active, type_alias_impl_trait, "1.38.0", Some(63063), None), (active, type_alias_impl_trait, "1.38.0", Some(63063), None),
// Allows the use of or-patterns, e.g. `0 | 1`.
(active, or_patterns, "1.38.0", Some(54883), None),
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// feature-group-end: actual feature gates // feature-group-end: actual feature gates
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -571,6 +574,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
sym::impl_trait_in_bindings, sym::impl_trait_in_bindings,
sym::generic_associated_types, sym::generic_associated_types,
sym::const_generics, sym::const_generics,
sym::or_patterns,
sym::let_chains, sym::let_chains,
]; ];
@ -2443,6 +2447,7 @@ pub fn check_crate(krate: &ast::Crate,
gate_all!(let_chains_spans, let_chains, "`let` expressions in this position are experimental"); gate_all!(let_chains_spans, let_chains, "`let` expressions in this position are experimental");
gate_all!(async_closure_spans, async_closure, "async closures are unstable"); gate_all!(async_closure_spans, async_closure, "async closures are unstable");
gate_all!(yield_spans, generators, "yield syntax is experimental"); gate_all!(yield_spans, generators, "yield syntax is experimental");
gate_all!(or_pattern_spans, or_patterns, "or-patterns syntax is experimental");
let visitor = &mut PostExpansionVisitor { let visitor = &mut PostExpansionVisitor {
context: &ctx, context: &ctx,

View file

@ -66,6 +66,8 @@ pub struct ParseSess {
// Places where `yield e?` exprs were used and should be feature gated. // Places where `yield e?` exprs were used and should be feature gated.
pub yield_spans: Lock<Vec<Span>>, pub yield_spans: Lock<Vec<Span>>,
pub injected_crate_name: Once<Symbol>, pub injected_crate_name: Once<Symbol>,
// Places where or-patterns e.g. `Some(Foo | Bar)` were used and should be feature gated.
pub or_pattern_spans: Lock<Vec<Span>>,
} }
impl ParseSess { impl ParseSess {
@ -96,6 +98,7 @@ impl ParseSess {
async_closure_spans: Lock::new(Vec::new()), async_closure_spans: Lock::new(Vec::new()),
yield_spans: Lock::new(Vec::new()), yield_spans: Lock::new(Vec::new()),
injected_crate_name: Once::new(), injected_crate_name: Once::new(),
or_pattern_spans: Lock::new(Vec::new()),
} }
} }

View file

@ -14,7 +14,10 @@ use errors::{Applicability, DiagnosticBuilder};
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/// Parses a pattern. /// Parses a pattern.
pub fn parse_pat(&mut self, expected: Option<&'static str>) -> PResult<'a, P<Pat>> { pub fn parse_pat(
&mut self,
expected: Option<&'static str>
) -> PResult<'a, P<Pat>> {
self.parse_pat_with_range_pat(true, expected) self.parse_pat_with_range_pat(true, expected)
} }
@ -97,6 +100,34 @@ impl<'a> Parser<'a> {
Ok(()) Ok(())
} }
/// Parses a pattern, that may be a or-pattern (e.g. `Some(Foo | Bar)`).
fn parse_pat_with_or(&mut self, expected: Option<&'static str>) -> PResult<'a, P<Pat>> {
// Parse the first pattern.
let first_pat = self.parse_pat(expected)?;
// If the next token is not a `|`, this is not an or-pattern and
// we should exit here.
if !self.check(&token::BinOp(token::Or)) {
return Ok(first_pat)
}
let lo = first_pat.span;
let mut pats = vec![first_pat];
while self.eat(&token::BinOp(token::Or)) {
pats.push(self.parse_pat_with_range_pat(
true, expected
)?);
}
let or_pattern_span = lo.to(self.prev_span);
self.sess.or_pattern_spans.borrow_mut().push(or_pattern_span);
Ok(self.mk_pat(or_pattern_span, PatKind::Or(pats)))
}
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
/// allowed). /// allowed).
fn parse_pat_with_range_pat( fn parse_pat_with_range_pat(
@ -240,7 +271,9 @@ impl<'a> Parser<'a> {
/// Parse a tuple or parenthesis pattern. /// Parse a tuple or parenthesis pattern.
fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> { fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?; let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
p.parse_pat_with_or(None)
})?;
// Here, `(pat,)` is a tuple pattern. // Here, `(pat,)` is a tuple pattern.
// For backward compatibility, `(..)` is a tuple pattern as well. // For backward compatibility, `(..)` is a tuple pattern as well.
@ -483,7 +516,7 @@ impl<'a> Parser<'a> {
err.span_label(self.token.span, msg); err.span_label(self.token.span, msg);
return Err(err); return Err(err);
} }
let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?; let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat_with_or(None))?;
Ok(PatKind::TupleStruct(path, fields)) Ok(PatKind::TupleStruct(path, fields))
} }
@ -627,7 +660,7 @@ impl<'a> Parser<'a> {
// Parsing a pattern of the form "fieldname: pat" // Parsing a pattern of the form "fieldname: pat"
let fieldname = self.parse_field_name()?; let fieldname = self.parse_field_name()?;
self.bump(); self.bump();
let pat = self.parse_pat(None)?; let pat = self.parse_pat_with_or(None)?;
hi = pat.span; hi = pat.span;
(pat, fieldname, false) (pat, fieldname, false)
} else { } else {

View file

@ -431,46 +431,33 @@ impl std::ops::DerefMut for State<'_> {
} }
} }
pub enum SeparatorSpacing {
After,
Both,
}
pub trait PrintState<'a>: std::ops::Deref<Target=pp::Printer> + std::ops::DerefMut { pub trait PrintState<'a>: std::ops::Deref<Target=pp::Printer> + std::ops::DerefMut {
fn comments(&mut self) -> &mut Option<Comments<'a>>; fn comments(&mut self) -> &mut Option<Comments<'a>>;
fn print_ident(&mut self, ident: ast::Ident); fn print_ident(&mut self, ident: ast::Ident);
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool); fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
fn strsep<T, F>( fn strsep<T, F>(&mut self, sep: &'static str, space_before: bool,
&mut self, b: Breaks, elts: &[T], mut op: F)
sep: &'static str,
spacing: SeparatorSpacing,
b: Breaks,
elts: &[T],
mut op: F
) -> io::Result<()>
where F: FnMut(&mut Self, &T), where F: FnMut(&mut Self, &T),
{ {
self.rbox(0, b); self.rbox(0, b);
let mut first = true; if let Some((first, rest)) = elts.split_first() {
for elt in elts { op(self, first);
if first { for elt in rest {
first = false; if space_before {
} else { self.space();
if let SeparatorSpacing::Both = spacing {
self.writer().space();
} }
self.word_space(sep); self.word_space(sep);
op(self, elt);
} }
op(self, elt);
} }
self.end(); self.end();
} }
fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], mut op: F) fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
where F: FnMut(&mut Self, &T), where F: FnMut(&mut Self, &T),
{ {
self.strsep(",", SeparatorSpacing::After, b, elts, op) self.strsep(",", false, b, elts, op)
} }
fn maybe_print_comment(&mut self, pos: BytePos) { fn maybe_print_comment(&mut self, pos: BytePos) {
@ -2379,8 +2366,7 @@ impl<'a> State<'a> {
self.pclose(); self.pclose();
} }
PatKind::Or(ref pats) => { PatKind::Or(ref pats) => {
let spacing = SeparatorSpacing::Both; self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(p));
self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(p))?;
} }
PatKind::Path(None, ref path) => { PatKind::Path(None, ref path) => {
self.print_path(path, true, 0); self.print_path(path, true, 0);
@ -2458,16 +2444,7 @@ impl<'a> State<'a> {
} }
fn print_pats(&mut self, pats: &[P<ast::Pat>]) { fn print_pats(&mut self, pats: &[P<ast::Pat>]) {
let mut first = true; self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p));
for p in pats {
if first {
first = false;
} else {
self.s.space();
self.word_space("|");
}
self.print_pat(p);
}
} }
fn print_arm(&mut self, arm: &ast::Arm) { fn print_arm(&mut self, arm: &ast::Arm) {

View file

@ -462,7 +462,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
visitor.visit_expr(upper_bound); visitor.visit_expr(upper_bound);
} }
PatKind::Wild | PatKind::Rest => {}, PatKind::Wild | PatKind::Rest => {},
PatKind::Tuple(ref elems) => { PatKind::Tuple(ref elems)
| PatKind::Slice(ref elems) | PatKind::Slice(ref elems)
| PatKind::Or(ref elems) => { | PatKind::Or(ref elems) => {
walk_list!(visitor, visit_pat, elems); walk_list!(visitor, visit_pat, elems);

View file

@ -469,6 +469,7 @@ symbols! {
option_env, option_env,
opt_out_copy, opt_out_copy,
or, or,
or_patterns,
Ord, Ord,
Ordering, Ordering,
Output, Output,

View file

@ -0,0 +1,9 @@
#![crate_type="lib"]
pub fn example(x: Option<usize>) {
match x {
Some(0 | 1 | 2) => {}
//~^ ERROR: or-patterns syntax is experimental
_ => {}
}
}

View file

@ -0,0 +1,12 @@
error[E0658]: or-patterns syntax is experimental
--> $DIR/feature-gate-or_patterns.rs:5:14
|
LL | Some(0 | 1 | 2) => {}
| ^^^^^^^^^
|
= note: for more information, see https://github.com/rust-lang/rust/issues/54883
= help: add `#![feature(or_patterns)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View file

@ -2,8 +2,9 @@ fn main() {
struct Test(&'static u8, [u8; 0]); struct Test(&'static u8, [u8; 0]);
let x = Test(&0, []); let x = Test(&0, []);
let Test(&desc[..]) = x; //~ ERROR: expected one of `)`, `,`, or `@`, found `[` let Test(&desc[..]) = x;
//~^ ERROR subslice patterns are unstable //~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[`
//~^^ ERROR subslice patterns are unstable
} }
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types

View file

@ -1,8 +1,8 @@
error: expected one of `)`, `,`, or `@`, found `[` error: expected one of `)`, `,`, `@`, or `|`, found `[`
--> $DIR/pat-lt-bracket-6.rs:5:19 --> $DIR/pat-lt-bracket-6.rs:5:19
| |
LL | let Test(&desc[..]) = x; LL | let Test(&desc[..]) = x;
| ^ expected one of `)`, `,`, or `@` here | ^ expected one of `)`, `,`, `@`, or `|` here
error[E0658]: subslice patterns are unstable error[E0658]: subslice patterns are unstable
--> $DIR/pat-lt-bracket-6.rs:5:20 --> $DIR/pat-lt-bracket-6.rs:5:20
@ -14,7 +14,7 @@ LL | let Test(&desc[..]) = x;
= help: add `#![feature(slice_patterns)]` to the crate attributes to enable = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/pat-lt-bracket-6.rs:9:30 --> $DIR/pat-lt-bracket-6.rs:10:30
| |
LL | const RECOVERY_WITNESS: () = 0; LL | const RECOVERY_WITNESS: () = 0;
| ^ expected (), found integer | ^ expected (), found integer

View file

@ -2,7 +2,8 @@ fn main() {
struct Thing(u8, [u8; 0]); struct Thing(u8, [u8; 0]);
let foo = core::iter::empty(); let foo = core::iter::empty();
for Thing(x[]) in foo {} //~ ERROR: expected one of `)`, `,`, or `@`, found `[` for Thing(x[]) in foo {}
//~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[`
} }
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types

View file

@ -1,11 +1,11 @@
error: expected one of `)`, `,`, or `@`, found `[` error: expected one of `)`, `,`, `@`, or `|`, found `[`
--> $DIR/pat-lt-bracket-7.rs:5:16 --> $DIR/pat-lt-bracket-7.rs:5:16
| |
LL | for Thing(x[]) in foo {} LL | for Thing(x[]) in foo {}
| ^ expected one of `)`, `,`, or `@` here | ^ expected one of `)`, `,`, `@`, or `|` here
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/pat-lt-bracket-7.rs:8:30 --> $DIR/pat-lt-bracket-7.rs:9:30
| |
LL | const RECOVERY_WITNESS: () = 0; LL | const RECOVERY_WITNESS: () = 0;
| ^ expected (), found integer | ^ expected (), found integer

View file

@ -8,7 +8,7 @@ fn main() {
let vec = vec![1, 2, 3]; let vec = vec![1, 2, 3];
for ( elem in vec ) { for ( elem in vec ) {
//~^ ERROR expected one of `)`, `,`, or `@`, found `in` //~^ ERROR expected one of `)`, `,`, `@`, or `|`, found `in`
//~| ERROR unexpected closing `)` //~| ERROR unexpected closing `)`
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
} }

View file

@ -1,8 +1,8 @@
error: expected one of `)`, `,`, or `@`, found `in` error: expected one of `)`, `,`, `@`, or `|`, found `in`
--> $DIR/recover-for-loop-parens-around-head.rs:10:16 --> $DIR/recover-for-loop-parens-around-head.rs:10:16
| |
LL | for ( elem in vec ) { LL | for ( elem in vec ) {
| ^^ expected one of `)`, `,`, or `@` here | ^^ expected one of `)`, `,`, `@`, or `|` here
error: unexpected closing `)` error: unexpected closing `)`
--> $DIR/recover-for-loop-parens-around-head.rs:10:23 --> $DIR/recover-for-loop-parens-around-head.rs:10:23