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),