Skip to content

Commit

Permalink
Auto merge of rust-lang#61708 - dlrobertson:or-patterns-0, r=centril
Browse files Browse the repository at this point in the history
Initial implementation of or-patterns

An incomplete implementation of or-patterns (e.g. `Some(0 | 1)` as a pattern). This patch set aims to implement initial parsing of `or-patterns`.

Related to: rust-lang#54883

CC @alexreg @varkor
r? @Centril
  • Loading branch information
bors committed Aug 18, 2019
2 parents bd1da18 + 1870537 commit fc8765d
Show file tree
Hide file tree
Showing 30 changed files with 241 additions and 51 deletions.
36 changes: 36 additions & 0 deletions src/doc/unstable-book/src/language-features/or-patterns.md
Original file line number Diff line number Diff line change
@@ -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`");
}
}
}
```
5 changes: 5 additions & 0 deletions src/librustc/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,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)
}
Expand Down Expand Up @@ -975,6 +976,10 @@ pub enum PatKind {
/// `0 <= position <= subpats.len()`
TupleStruct(QPath, HirVec<P<Pat>>, Option<usize>),

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

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

Expand Down
3 changes: 3 additions & 0 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,9 @@ impl<'a> State<'a> {
self.s.space();
self.s.word("}");
}
PatKind::Or(ref pats) => {
self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(&p));
}
PatKind::Tuple(ref elts, ddpos) => {
self.popen();
if let Some(ddpos) = ddpos {
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
}
Expand Down
5 changes: 5 additions & 0 deletions src/librustc_mir/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f);
}
}
PatternKind::Or { ref pats } => {
for pat in pats {
self.visit_bindings(&pat, pattern_user_ty.clone(), f);
}
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/librustc_mir/build/matches/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
candidate.match_pairs.push(MatchPair::new(place, subpattern));
Ok(())
}

PatternKind::Or { .. } => {
Err(match_pair)
}
}
}
}
2 changes: 2 additions & 0 deletions src/librustc_mir/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
PatternKind::AscribeUserType { .. } |
PatternKind::Array { .. } |
PatternKind::Wild |
PatternKind::Or { .. } |
PatternKind::Binding { .. } |
PatternKind::Leaf { .. } |
PatternKind::Deref { .. } => {
Expand Down Expand Up @@ -130,6 +131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
PatternKind::Slice { .. } |
PatternKind::Array { .. } |
PatternKind::Wild |
PatternKind::Or { .. } |
PatternKind::Binding { .. } |
PatternKind::AscribeUserType { .. } |
PatternKind::Leaf { .. } |
Expand Down
7 changes: 7 additions & 0 deletions src/librustc_mir/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,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.");
}
}
}

Expand Down Expand Up @@ -1884,6 +1887,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);

Expand Down
48 changes: 37 additions & 11 deletions src/librustc_mir/hair/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ pub enum PatternKind<'tcx> {
slice: Option<Pattern<'tcx>>,
suffix: Vec<Pattern<'tcx>>,
},

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

#[derive(Copy, Clone, Debug, PartialEq)]
Expand All @@ -186,6 +192,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, .. } =>
Expand Down Expand Up @@ -224,9 +242,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)?;

Expand All @@ -241,12 +256,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, " }}");
Expand All @@ -257,7 +272,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) {
Expand Down Expand Up @@ -305,25 +320,29 @@ 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)?
}
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(())
}
}
}
}
Expand Down Expand Up @@ -655,6 +674,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 {
Expand Down Expand Up @@ -1436,6 +1461,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) },
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..) |
Expand Down Expand Up @@ -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, discrim_span);
}
expected_ty
}
PatKind::Tuple(ref elements, ddpos) => {
let mut expected_len = elements.len();
if ddpos.is_some() {
Expand Down
3 changes: 3 additions & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<String>>().join(" | ")
}
PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
.collect::<Vec<String>>().join(", ")),
PatKind::Box(ref p) => name_from_pat(&**p),
Expand Down
11 changes: 8 additions & 3 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -648,6 +649,10 @@ pub enum PatKind {
/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
TupleStruct(Path, Vec<P<Pat>>),

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

/// A possibly qualified path pattern.
/// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants
/// or associated constants. Qualified path patterns `<A>::B::C`/`<A as Trait>::B::C` can
Expand Down
5 changes: 5 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// -------------------------------------------------------------------------
Expand All @@ -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,
];

Expand Down Expand Up @@ -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,
Expand Down
5 changes: 3 additions & 2 deletions src/libsyntax/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,15 +1050,16 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, 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: _ }) => {
vis.visit_expr(e1);
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),
}
Expand Down
Loading

0 comments on commit fc8765d

Please sign in to comment.