From 1713ac4bf5c992f40d667c929c1e1ce9c3a51204 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 19 Oct 2018 15:40:07 +0100 Subject: [PATCH 1/2] Initial implementation of or patterns --- src/librustc/cfg/construct.rs | 5 +++ src/librustc/hir/intravisit.rs | 1 + src/librustc/hir/lowering.rs | 3 ++ src/librustc/hir/mod.rs | 4 ++ src/librustc/hir/print.rs | 6 ++- src/librustc/middle/mem_categorization.rs | 6 +++ src/librustc_mir/build/matches/mod.rs | 6 +++ src/librustc_mir/build/matches/simplify.rs | 4 ++ src/librustc_mir/build/matches/test.rs | 2 + src/librustc_mir/hair/pattern/_match.rs | 10 +++-- src/librustc_mir/hair/pattern/mod.rs | 47 +++++++++++++++++----- src/librustc_typeck/check/_match.rs | 8 ++++ src/librustdoc/clean/mod.rs | 3 ++ src/libsyntax/ast.rs | 10 +++-- src/libsyntax/mut_visit.rs | 5 ++- src/libsyntax/print/pprust.rs | 33 ++++++++++++++- src/libsyntax/visit.rs | 7 ++-- 17 files changed, 134 insertions(+), 26 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 7ada56cfa7611..0dad2dda837b5 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -140,6 +140,11 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { self.add_ast_node(pat.hir_id.local_id, &[pats_exit]) } + PatKind::Or(ref pats) => { + let branches: Vec<_> = pats.iter().map(|p| self.pat(p, pred)).collect(); + self.add_ast_node(pat.hir_id.local_id, &branches) + } + PatKind::Slice(ref pre, ref vec, ref post) => { let pre_exit = self.pats_all(pre.iter(), pred); let vec_exit = self.pats_all(vec.iter(), pre_exit); diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 99fe9f1682f16..2c6373bdfa40d 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -709,6 +709,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { visitor.visit_pat(&field.pat) } } + PatKind::Or(ref pats) => walk_list!(visitor, visit_pat, pats), PatKind::Tuple(ref tuple_elements, _) => { walk_list!(visitor, visit_pat, tuple_elements); } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 0f6e834ca26df..d2ea485b5db82 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2669,6 +2669,9 @@ impl<'a> LoweringContext<'a> { let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct"); hir::PatKind::TupleStruct(qpath, pats, ddpos) } + PatKind::Or(ref pats) => { + hir::PatKind::Or(pats.iter().map(|x| self.lower_pat(x)).collect()) + } PatKind::Path(ref qself, ref path) => { let qpath = self.lower_qpath( p.id, diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 57fd0be77ecff..2ae08568b7f7d 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -882,6 +882,7 @@ impl Pat { PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => { s.iter().all(|p| p.walk_(it)) } + PatKind::Or(ref pats) => pats.iter().all(|p| p.walk_(it)), PatKind::Box(ref s) | PatKind::Ref(ref s, _) => { s.walk_(it) } @@ -976,6 +977,9 @@ pub enum PatKind { /// `0 <= position <= subpats.len()` TupleStruct(QPath, HirVec>, Option), + /// An or-pattern `A | B | C`. + Or(Vec>), + /// A path pattern for an unit struct/variant or a (maybe-associated) constant. Path(QPath), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 2fd683ed83c54..157b7c07a9b38 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -4,7 +4,7 @@ use syntax::source_map::{SourceMap, Spanned}; use syntax::parse::ParseSess; use syntax::print::pp::{self, Breaks}; use syntax::print::pp::Breaks::{Consistent, Inconsistent}; -use syntax::print::pprust::{self, Comments, PrintState}; +use syntax::print::pprust::{self, Comments, PrintState, SeparatorSpacing}; use syntax::symbol::kw; use syntax::util::parser::{self, AssocOp, Fixity}; use syntax_pos::{self, BytePos, FileName}; @@ -1687,6 +1687,10 @@ impl<'a> State<'a> { self.s.space(); self.s.word("}"); } + PatKind::Or(ref pats) => { + let spacing = SeparatorSpacing::Both; + self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(&p))?; + } PatKind::Tuple(ref elts, ddpos) => { self.popen(); if let Some(ddpos) = ddpos { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index a55803e255bf6..73ca981bbe868 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -1290,6 +1290,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } + PatKind::Or(ref pats) => { + for pat in pats { + self.cat_pattern_(cmt.clone(), &pat, op)?; + } + } + PatKind::Binding(.., Some(ref subpat)) => { self.cat_pattern_(cmt, &subpat, op)?; } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index d72b0addae915..0dec7ef4f0061 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -657,6 +657,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f); } } + PatternKind::Or { ref pats } => { + // FIXME(#47184): extract or handle `pattern_user_ty` somehow + for pat in pats { + self.visit_bindings(&pat, &pattern_user_ty.clone(), f); + } + } } } } diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 3473155a3ea3e..8d049b53988a9 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -195,6 +195,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.match_pairs.push(MatchPair::new(place, subpattern)); Ok(()) } + + PatternKind::Or { .. } => { + Err(match_pair) + } } } } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 65e92d422b022..ec85daccd476e 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -87,6 +87,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PatternKind::AscribeUserType { .. } | PatternKind::Array { .. } | PatternKind::Wild | + PatternKind::Or { .. } | PatternKind::Binding { .. } | PatternKind::Leaf { .. } | PatternKind::Deref { .. } => { @@ -130,6 +131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PatternKind::Slice { .. } | PatternKind::Array { .. } | PatternKind::Wild | + PatternKind::Or { .. } | PatternKind::Binding { .. } | PatternKind::AscribeUserType { .. } | PatternKind::Leaf { .. } | diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 1833ee30624bb..ae59244d37f51 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -75,9 +75,6 @@ /// D((r_1, 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 is inductive (on the number of columns: i.e., components of tuple patterns). @@ -1359,6 +1356,9 @@ fn pat_constructors<'tcx>(cx: &mut MatchCheckCtxt<'_, 'tcx>, Some(vec![Slice(pat_len)]) } } + PatternKind::Or { .. } => { + bug!("support for or-patterns has not been fully implemented yet."); + } } } @@ -1884,6 +1884,10 @@ fn specialize<'p, 'a: 'p, 'tcx>( "unexpected ctor {:?} for slice pat", constructor) } } + + PatternKind::Or { .. } => { + bug!("support for or-patterns has not been fully implemented yet."); + } }; debug!("specialize({:#?}, {:#?}) = {:#?}", r[0], wild_patterns, head); diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index bebb0719af808..d2a5793e70363 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -175,6 +175,11 @@ pub enum PatternKind<'tcx> { slice: Option>, suffix: Vec>, }, + + /// or-pattern + Or { + pats: Vec>, + }, } #[derive(Copy, Clone, Debug, PartialEq)] @@ -186,6 +191,18 @@ pub struct PatternRange<'tcx> { impl<'tcx> fmt::Display for Pattern<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Printing lists is a chore. + let mut first = true; + let mut start_or_continue = |s| { + if first { + first = false; + "" + } else { + s + } + }; + let mut start_or_comma = || start_or_continue(", "); + match *self.kind { PatternKind::Wild => write!(f, "_"), PatternKind::AscribeUserType { ref subpattern, .. } => @@ -224,9 +241,6 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { } }; - let mut first = true; - let mut start_or_continue = || if first { first = false; "" } else { ", " }; - if let Some(variant) = variant { write!(f, "{}", variant.ident)?; @@ -241,12 +255,12 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { continue; } let name = variant.fields[p.field.index()].ident; - write!(f, "{}{}: {}", start_or_continue(), name, p.pattern)?; + write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; printed += 1; } if printed < variant.fields.len() { - write!(f, "{}..", start_or_continue())?; + write!(f, "{}..", start_or_comma())?; } return write!(f, " }}"); @@ -257,7 +271,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { if num_fields != 0 || variant.is_none() { write!(f, "(")?; for i in 0..num_fields { - write!(f, "{}", start_or_continue())?; + write!(f, "{}", start_or_comma())?; // Common case: the field is where we expect it. if let Some(p) = subpatterns.get(i) { @@ -305,14 +319,12 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { } PatternKind::Slice { ref prefix, ref slice, ref suffix } | PatternKind::Array { ref prefix, ref slice, ref suffix } => { - let mut first = true; - let mut start_or_continue = || if first { first = false; "" } else { ", " }; write!(f, "[")?; for p in prefix { - write!(f, "{}{}", start_or_continue(), p)?; + write!(f, "{}{}", start_or_comma(), p)?; } if let Some(ref slice) = *slice { - write!(f, "{}", start_or_continue())?; + write!(f, "{}", start_or_comma())?; match *slice.kind { PatternKind::Wild => {} _ => write!(f, "{}", slice)? @@ -320,10 +332,16 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { write!(f, "..")?; } for p in suffix { - write!(f, "{}{}", start_or_continue(), p)?; + write!(f, "{}{}", start_or_comma(), p)?; } write!(f, "]") } + PatternKind::Or { ref pats } => { + for pat in pats { + write!(f, "{}{}", start_or_continue(" | "), pat)?; + } + Ok(()) + } } } } @@ -655,6 +673,12 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns) } + + PatKind::Or(ref pats) => { + PatternKind::Or { + pats: pats.iter().map(|p| self.lower_pattern(p)).collect(), + } + } }; Pattern { @@ -1436,6 +1460,7 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { slice: slice.fold_with(folder), suffix: suffix.fold_with(folder) }, + PatternKind::Or { ref pats } => PatternKind::Or { pats: pats.fold_with(folder) }, } } } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 99ae777bb631b..2e22fb766751a 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -53,6 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_non_ref_pat = match pat.node { PatKind::Struct(..) | PatKind::TupleStruct(..) | + PatKind::Or(_) | PatKind::Tuple(..) | PatKind::Box(_) | PatKind::Range(..) | @@ -309,6 +310,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Struct(ref qpath, ref fields, etc) => { self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, discrim_span) } + PatKind::Or(ref pats) => { + let expected_ty = self.structurally_resolved_type(pat.span, expected); + for pat in pats { + self.check_pat_walk(pat, expected, def_bm, false); + } + expected_ty + } PatKind::Tuple(ref elements, ddpos) => { let mut expected_len = elements.len(); if ddpos.is_some() { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index fede9e9301012..023d22861defa 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -4107,6 +4107,9 @@ fn name_from_pat(p: &hir::Pat) -> String { if etc { ", .." } else { "" } ) } + PatKind::Or(ref pats) => { + pats.iter().map(|p| name_from_pat(&**p)).collect::>().join(" | ") + } PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p)) .collect::>().join(", ")), PatKind::Box(ref p) => name_from_pat(&**p), diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 3ae37f734b77e..0136c4ff5f936 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -572,9 +572,10 @@ impl Pat { match &self.node { PatKind::Ident(_, _, Some(p)) => p.walk(it), PatKind::Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk(it)), - PatKind::TupleStruct(_, s) | PatKind::Tuple(s) | PatKind::Slice(s) => { - s.iter().all(|p| p.walk(it)) - } + PatKind::TupleStruct(_, s) + | PatKind::Tuple(s) + | PatKind::Slice(s) + | PatKind::Or(s) => s.iter().all(|p| p.walk(it)), PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it), PatKind::Wild | PatKind::Rest @@ -648,6 +649,9 @@ pub enum PatKind { /// A tuple struct/variant pattern (`Variant(x, y, .., z)`). TupleStruct(Path, Vec>), + /// An or-pattern `A | B | C`. + Or(Vec>), + /// A possibly qualified path pattern. /// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants /// or associated constants. Qualified path patterns `::B::C`/`::B::C` can diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index acafe327640d0..b67b4619d7f21 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -1050,7 +1050,6 @@ pub fn noop_visit_pat(pat: &mut P, vis: &mut T) { vis.visit_span(span); }; } - PatKind::Tuple(elems) => visit_vec(elems, |elem| vis.visit_pat(elem)), PatKind::Box(inner) => vis.visit_pat(inner), PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner), PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => { @@ -1058,7 +1057,9 @@ pub fn noop_visit_pat(pat: &mut P, vis: &mut T) { vis.visit_expr(e2); vis.visit_span(span); } - PatKind::Slice(elems) => visit_vec(elems, |elem| vis.visit_pat(elem)), + PatKind::Tuple(elems) + | PatKind::Slice(elems) + | PatKind::Or(elems) => visit_vec(elems, |elem| vis.visit_pat(elem)), PatKind::Paren(inner) => vis.visit_pat(inner), PatKind::Mac(mac) => vis.visit_mac(mac), } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 8a7009828bc44..8dcb7ecf881d0 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -431,23 +431,48 @@ impl std::ops::DerefMut for State<'_> { } } +pub enum SeparatorSpacing { + After, + Both, +} + pub trait PrintState<'a>: std::ops::Deref + std::ops::DerefMut { fn comments(&mut self) -> &mut Option>; fn print_ident(&mut self, ident: ast::Ident); fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool); - fn commasep(&mut self, b: Breaks, elts: &[T], mut op: F) + fn strsep( + &mut self, + sep: &'static str, + spacing: SeparatorSpacing, + b: Breaks, + elts: &[T], + mut op: F + ) -> io::Result<()> where F: FnMut(&mut Self, &T), { self.rbox(0, b); let mut first = true; for elt in elts { - if first { first = false; } else { self.word_space(","); } + if first { + first = false; + } else { + if let SeparatorSpacing::Both = spacing { + self.writer().space(); + } + self.word_space(sep); + } op(self, elt); } self.end(); } + fn commasep(&mut self, b: Breaks, elts: &[T], mut op: F) + where F: FnMut(&mut Self, &T), + { + self.strsep(",", SeparatorSpacing::After, b, elts, op) + } + fn maybe_print_comment(&mut self, pos: BytePos) { while let Some(ref cmnt) = self.next_comment() { if cmnt.pos < pos { @@ -2353,6 +2378,10 @@ impl<'a> State<'a> { self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p)); self.pclose(); } + PatKind::Or(ref pats) => { + let spacing = SeparatorSpacing::Both; + self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(p))?; + } PatKind::Path(None, ref path) => { self.print_path(path, true, 0); } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 6648347d4aef5..ce679a5db63ff 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -447,9 +447,6 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { visitor.visit_pat(&field.pat) } } - PatKind::Tuple(ref elems) => { - walk_list!(visitor, visit_pat, elems); - } PatKind::Box(ref subpattern) | PatKind::Ref(ref subpattern, _) | PatKind::Paren(ref subpattern) => { @@ -465,7 +462,9 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { visitor.visit_expr(upper_bound); } PatKind::Wild | PatKind::Rest => {}, - PatKind::Slice(ref elems) => { + PatKind::Tuple(ref elems) => { + | PatKind::Slice(ref elems) + | PatKind::Or(ref elems) => { walk_list!(visitor, visit_pat, elems); } PatKind::Mac(ref mac) => visitor.visit_mac(mac), From 1870537f2701e5aa47080a879b63a4d6b391553b Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Sun, 14 Jul 2019 01:05:52 +0000 Subject: [PATCH 2/2] 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. --- .../src/language-features/or-patterns.md | 36 ++++++++++++++ src/librustc/hir/mod.rs | 3 +- src/librustc/hir/print.rs | 5 +- src/librustc_mir/build/matches/mod.rs | 3 +- src/librustc_mir/hair/pattern/_match.rs | 3 ++ src/librustc_mir/hair/pattern/mod.rs | 3 +- src/librustc_typeck/check/_match.rs | 2 +- src/libsyntax/ast.rs | 1 + src/libsyntax/feature_gate.rs | 5 ++ src/libsyntax/parse/mod.rs | 3 ++ src/libsyntax/parse/parser/pat.rs | 41 ++++++++++++++-- src/libsyntax/print/pprust.rs | 47 +++++-------------- src/libsyntax/visit.rs | 2 +- src/libsyntax_pos/symbol.rs | 1 + .../feature-gate/feature-gate-or_patterns.rs | 9 ++++ .../feature-gate-or_patterns.stderr | 12 +++++ src/test/ui/parser/pat-lt-bracket-6.rs | 5 +- src/test/ui/parser/pat-lt-bracket-6.stderr | 6 +-- src/test/ui/parser/pat-lt-bracket-7.rs | 3 +- src/test/ui/parser/pat-lt-bracket-7.stderr | 6 +-- .../recover-for-loop-parens-around-head.rs | 2 +- ...recover-for-loop-parens-around-head.stderr | 4 +- 22 files changed, 142 insertions(+), 60 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/or-patterns.md create mode 100644 src/test/ui/feature-gate/feature-gate-or_patterns.rs create mode 100644 src/test/ui/feature-gate/feature-gate-or_patterns.stderr diff --git a/src/doc/unstable-book/src/language-features/or-patterns.md b/src/doc/unstable-book/src/language-features/or-patterns.md new file mode 100644 index 0000000000000..8ebacb44d37cc --- /dev/null +++ b/src/doc/unstable-book/src/language-features/or-patterns.md @@ -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) { + 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`"); + } + } +} +``` diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 2ae08568b7f7d..5b15cf9a6c90f 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -978,7 +978,8 @@ pub enum PatKind { TupleStruct(QPath, HirVec>, Option), /// An or-pattern `A | B | C`. - Or(Vec>), + /// Invariant: `pats.len() >= 2`. + Or(HirVec>), /// A path pattern for an unit struct/variant or a (maybe-associated) constant. Path(QPath), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 157b7c07a9b38..632a13f9183b2 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -4,7 +4,7 @@ use syntax::source_map::{SourceMap, Spanned}; use syntax::parse::ParseSess; use syntax::print::pp::{self, Breaks}; 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::util::parser::{self, AssocOp, Fixity}; use syntax_pos::{self, BytePos, FileName}; @@ -1688,8 +1688,7 @@ impl<'a> State<'a> { self.s.word("}"); } PatKind::Or(ref pats) => { - let spacing = SeparatorSpacing::Both; - self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(&p))?; + self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(&p)); } PatKind::Tuple(ref elts, ddpos) => { self.popen(); diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 0dec7ef4f0061..94323b15b696f 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -658,9 +658,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } PatternKind::Or { ref pats } => { - // FIXME(#47184): extract or handle `pattern_user_ty` somehow for pat in pats { - self.visit_bindings(&pat, &pattern_user_ty.clone(), f); + self.visit_bindings(&pat, pattern_user_ty.clone(), f); } } } diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index ae59244d37f51..222750e602df9 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -75,6 +75,9 @@ /// D((r_1, 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 is inductive (on the number of columns: i.e., components of tuple patterns). diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index d2a5793e70363..6caccfddfa422 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -176,7 +176,8 @@ pub enum PatternKind<'tcx> { suffix: Vec>, }, - /// or-pattern + /// An or-pattern, e.g. `p | q`. + /// Invariant: `pats.len() >= 2`. Or { pats: Vec>, }, diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 2e22fb766751a..fc25eb44cbd88 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -313,7 +313,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Or(ref pats) => { let expected_ty = self.structurally_resolved_type(pat.span, expected); 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 } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0136c4ff5f936..3d15782df34e2 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -650,6 +650,7 @@ pub enum PatKind { TupleStruct(Path, Vec>), /// An or-pattern `A | B | C`. + /// Invariant: `pats.len() >= 2`. Or(Vec>), /// A possibly qualified path pattern. diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1a87a903156d2..bbc3ae2822558 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -559,6 +559,9 @@ declare_features! ( // Allows `impl Trait` to be used inside type aliases (RFC 2515). (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 // ------------------------------------------------------------------------- @@ -571,6 +574,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::impl_trait_in_bindings, sym::generic_associated_types, sym::const_generics, + sym::or_patterns, 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!(async_closure_spans, async_closure, "async closures are unstable"); 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 { context: &ctx, diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 9088f929372c9..b1f3612a839a2 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -66,6 +66,8 @@ pub struct ParseSess { // Places where `yield e?` exprs were used and should be feature gated. pub yield_spans: Lock>, pub injected_crate_name: Once, + // Places where or-patterns e.g. `Some(Foo | Bar)` were used and should be feature gated. + pub or_pattern_spans: Lock>, } impl ParseSess { @@ -96,6 +98,7 @@ impl ParseSess { async_closure_spans: Lock::new(Vec::new()), yield_spans: Lock::new(Vec::new()), injected_crate_name: Once::new(), + or_pattern_spans: Lock::new(Vec::new()), } } diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs index c3079d2da0ce7..fd458aec74331 100644 --- a/src/libsyntax/parse/parser/pat.rs +++ b/src/libsyntax/parse/parser/pat.rs @@ -14,7 +14,10 @@ use errors::{Applicability, DiagnosticBuilder}; impl<'a> Parser<'a> { /// Parses a pattern. - pub fn parse_pat(&mut self, expected: Option<&'static str>) -> PResult<'a, P> { + pub fn parse_pat( + &mut self, + expected: Option<&'static str> + ) -> PResult<'a, P> { self.parse_pat_with_range_pat(true, expected) } @@ -97,6 +100,34 @@ impl<'a> Parser<'a> { 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> { + // 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 /// allowed). fn parse_pat_with_range_pat( @@ -240,7 +271,9 @@ impl<'a> Parser<'a> { /// Parse a tuple or parenthesis pattern. 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. // For backward compatibility, `(..)` is a tuple pattern as well. @@ -483,7 +516,7 @@ impl<'a> Parser<'a> { err.span_label(self.token.span, msg); 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)) } @@ -627,7 +660,7 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form "fieldname: pat" let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat(None)?; + let pat = self.parse_pat_with_or(None)?; hi = pat.span; (pat, fieldname, false) } else { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 8dcb7ecf881d0..4dc00af486013 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -431,46 +431,33 @@ impl std::ops::DerefMut for State<'_> { } } -pub enum SeparatorSpacing { - After, - Both, -} - pub trait PrintState<'a>: std::ops::Deref + std::ops::DerefMut { fn comments(&mut self) -> &mut Option>; fn print_ident(&mut self, ident: ast::Ident); fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool); - fn strsep( - &mut self, - sep: &'static str, - spacing: SeparatorSpacing, - b: Breaks, - elts: &[T], - mut op: F - ) -> io::Result<()> + fn strsep(&mut self, sep: &'static str, space_before: bool, + b: Breaks, elts: &[T], mut op: F) where F: FnMut(&mut Self, &T), { self.rbox(0, b); - let mut first = true; - for elt in elts { - if first { - first = false; - } else { - if let SeparatorSpacing::Both = spacing { - self.writer().space(); + if let Some((first, rest)) = elts.split_first() { + op(self, first); + for elt in rest { + if space_before { + self.space(); } self.word_space(sep); + op(self, elt); } - op(self, elt); } self.end(); } - fn commasep(&mut self, b: Breaks, elts: &[T], mut op: F) + fn commasep(&mut self, b: Breaks, elts: &[T], op: F) 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) { @@ -2379,8 +2366,7 @@ impl<'a> State<'a> { self.pclose(); } PatKind::Or(ref pats) => { - let spacing = SeparatorSpacing::Both; - self.strsep("|", spacing, Inconsistent, &pats[..], |s, p| s.print_pat(p))?; + self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(p)); } PatKind::Path(None, ref path) => { self.print_path(path, true, 0); @@ -2458,16 +2444,7 @@ impl<'a> State<'a> { } fn print_pats(&mut self, pats: &[P]) { - let mut first = true; - for p in pats { - if first { - first = false; - } else { - self.s.space(); - self.word_space("|"); - } - self.print_pat(p); - } + self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p)); } fn print_arm(&mut self, arm: &ast::Arm) { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index ce679a5db63ff..91b92d84a811f 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -462,7 +462,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { visitor.visit_expr(upper_bound); } PatKind::Wild | PatKind::Rest => {}, - PatKind::Tuple(ref elems) => { + PatKind::Tuple(ref elems) | PatKind::Slice(ref elems) | PatKind::Or(ref elems) => { walk_list!(visitor, visit_pat, elems); diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 27fc66d3b09e6..361e01781b1ad 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -469,6 +469,7 @@ symbols! { option_env, opt_out_copy, or, + or_patterns, Ord, Ordering, Output, diff --git a/src/test/ui/feature-gate/feature-gate-or_patterns.rs b/src/test/ui/feature-gate/feature-gate-or_patterns.rs new file mode 100644 index 0000000000000..036a6095965bd --- /dev/null +++ b/src/test/ui/feature-gate/feature-gate-or_patterns.rs @@ -0,0 +1,9 @@ +#![crate_type="lib"] + +pub fn example(x: Option) { + match x { + Some(0 | 1 | 2) => {} + //~^ ERROR: or-patterns syntax is experimental + _ => {} + } +} diff --git a/src/test/ui/feature-gate/feature-gate-or_patterns.stderr b/src/test/ui/feature-gate/feature-gate-or_patterns.stderr new file mode 100644 index 0000000000000..aaabb54c1f017 --- /dev/null +++ b/src/test/ui/feature-gate/feature-gate-or_patterns.stderr @@ -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`. diff --git a/src/test/ui/parser/pat-lt-bracket-6.rs b/src/test/ui/parser/pat-lt-bracket-6.rs index 7b9721830993e..f27caa5d78c8e 100644 --- a/src/test/ui/parser/pat-lt-bracket-6.rs +++ b/src/test/ui/parser/pat-lt-bracket-6.rs @@ -2,8 +2,9 @@ fn main() { struct Test(&'static u8, [u8; 0]); let x = Test(&0, []); - let Test(&desc[..]) = x; //~ ERROR: expected one of `)`, `,`, or `@`, found `[` - //~^ ERROR subslice patterns are unstable + let Test(&desc[..]) = x; + //~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[` + //~^^ ERROR subslice patterns are unstable } const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types diff --git a/src/test/ui/parser/pat-lt-bracket-6.stderr b/src/test/ui/parser/pat-lt-bracket-6.stderr index 201465b2c850c..6f08f0a9d95ef 100644 --- a/src/test/ui/parser/pat-lt-bracket-6.stderr +++ b/src/test/ui/parser/pat-lt-bracket-6.stderr @@ -1,8 +1,8 @@ -error: expected one of `)`, `,`, or `@`, found `[` +error: expected one of `)`, `,`, `@`, or `|`, found `[` --> $DIR/pat-lt-bracket-6.rs:5:19 | LL | let Test(&desc[..]) = x; - | ^ expected one of `)`, `,`, or `@` here + | ^ expected one of `)`, `,`, `@`, or `|` here error[E0658]: subslice patterns are unstable --> $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 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; | ^ expected (), found integer diff --git a/src/test/ui/parser/pat-lt-bracket-7.rs b/src/test/ui/parser/pat-lt-bracket-7.rs index 020fdb845e8b5..327aef5ad1570 100644 --- a/src/test/ui/parser/pat-lt-bracket-7.rs +++ b/src/test/ui/parser/pat-lt-bracket-7.rs @@ -2,7 +2,8 @@ fn main() { struct Thing(u8, [u8; 0]); 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 diff --git a/src/test/ui/parser/pat-lt-bracket-7.stderr b/src/test/ui/parser/pat-lt-bracket-7.stderr index 17557efa49e80..196f1c0ae914f 100644 --- a/src/test/ui/parser/pat-lt-bracket-7.stderr +++ b/src/test/ui/parser/pat-lt-bracket-7.stderr @@ -1,11 +1,11 @@ -error: expected one of `)`, `,`, or `@`, found `[` +error: expected one of `)`, `,`, `@`, or `|`, found `[` --> $DIR/pat-lt-bracket-7.rs:5:16 | LL | for Thing(x[]) in foo {} - | ^ expected one of `)`, `,`, or `@` here + | ^ expected one of `)`, `,`, `@`, or `|` here 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; | ^ expected (), found integer diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.rs b/src/test/ui/parser/recover-for-loop-parens-around-head.rs index e6c59fcf22dea..c6be2c90667c2 100644 --- a/src/test/ui/parser/recover-for-loop-parens-around-head.rs +++ b/src/test/ui/parser/recover-for-loop-parens-around-head.rs @@ -8,7 +8,7 @@ fn main() { let vec = vec![1, 2, 3]; for ( elem in vec ) { - //~^ ERROR expected one of `)`, `,`, or `@`, found `in` + //~^ ERROR expected one of `)`, `,`, `@`, or `|`, found `in` //~| ERROR unexpected closing `)` const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types } diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr index c160e646c28b3..1b5b6cca09243 100644 --- a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr +++ b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr @@ -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 | LL | for ( elem in vec ) { - | ^^ expected one of `)`, `,`, or `@` here + | ^^ expected one of `)`, `,`, `@`, or `|` here error: unexpected closing `)` --> $DIR/recover-for-loop-parens-around-head.rs:10:23