Auto merge of #61708 - dlrobertson:or-patterns-0, r=centril
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: #54883 CC @alexreg @varkor r? @Centril
This commit is contained in:
commit
fc8765d6d8
30 changed files with 241 additions and 51 deletions
36
src/doc/unstable-book/src/language-features/or-patterns.md
Normal file
36
src/doc/unstable-book/src/language-features/or-patterns.md
Normal 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`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -140,6 +140,11 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
|
||||||
self.add_ast_node(pat.hir_id.local_id, &[pats_exit])
|
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) => {
|
PatKind::Slice(ref pre, ref vec, ref post) => {
|
||||||
let pre_exit = self.pats_all(pre.iter(), pred);
|
let pre_exit = self.pats_all(pre.iter(), pred);
|
||||||
let vec_exit = self.pats_all(vec.iter(), pre_exit);
|
let vec_exit = self.pats_all(vec.iter(), pre_exit);
|
||||||
|
|
|
@ -709,6 +709,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
|
||||||
visitor.visit_pat(&field.pat)
|
visitor.visit_pat(&field.pat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PatKind::Or(ref pats) => walk_list!(visitor, visit_pat, pats),
|
||||||
PatKind::Tuple(ref tuple_elements, _) => {
|
PatKind::Tuple(ref tuple_elements, _) => {
|
||||||
walk_list!(visitor, visit_pat, tuple_elements);
|
walk_list!(visitor, visit_pat, tuple_elements);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2669,6 +2669,9 @@ impl<'a> LoweringContext<'a> {
|
||||||
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
|
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
|
||||||
hir::PatKind::TupleStruct(qpath, pats, ddpos)
|
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) => {
|
PatKind::Path(ref qself, ref path) => {
|
||||||
let qpath = self.lower_qpath(
|
let qpath = self.lower_qpath(
|
||||||
p.id,
|
p.id,
|
||||||
|
|
|
@ -881,6 +881,7 @@ impl Pat {
|
||||||
PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
|
PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
|
||||||
s.iter().all(|p| p.walk_(it))
|
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, _) => {
|
PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
|
||||||
s.walk_(it)
|
s.walk_(it)
|
||||||
}
|
}
|
||||||
|
@ -975,6 +976,10 @@ pub enum PatKind {
|
||||||
/// `0 <= position <= subpats.len()`
|
/// `0 <= position <= subpats.len()`
|
||||||
TupleStruct(QPath, HirVec<P<Pat>>, Option<usize>),
|
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.
|
/// A path pattern for an unit struct/variant or a (maybe-associated) constant.
|
||||||
Path(QPath),
|
Path(QPath),
|
||||||
|
|
||||||
|
|
|
@ -1687,6 +1687,9 @@ impl<'a> State<'a> {
|
||||||
self.s.space();
|
self.s.space();
|
||||||
self.s.word("}");
|
self.s.word("}");
|
||||||
}
|
}
|
||||||
|
PatKind::Or(ref pats) => {
|
||||||
|
self.strsep("|", true, Inconsistent, &pats[..], |s, p| s.print_pat(&p));
|
||||||
|
}
|
||||||
PatKind::Tuple(ref elts, ddpos) => {
|
PatKind::Tuple(ref elts, ddpos) => {
|
||||||
self.popen();
|
self.popen();
|
||||||
if let Some(ddpos) = ddpos {
|
if let Some(ddpos) = ddpos {
|
||||||
|
|
|
@ -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)) => {
|
PatKind::Binding(.., Some(ref subpat)) => {
|
||||||
self.cat_pattern_(cmt, &subpat, op)?;
|
self.cat_pattern_(cmt, &subpat, op)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -657,6 +657,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
self.visit_bindings(&subpattern.pattern, subpattern_user_ty, f);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,6 +195,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
candidate.match_pairs.push(MatchPair::new(place, subpattern));
|
candidate.match_pairs.push(MatchPair::new(place, subpattern));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PatternKind::Or { .. } => {
|
||||||
|
Err(match_pair)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
PatternKind::AscribeUserType { .. } |
|
PatternKind::AscribeUserType { .. } |
|
||||||
PatternKind::Array { .. } |
|
PatternKind::Array { .. } |
|
||||||
PatternKind::Wild |
|
PatternKind::Wild |
|
||||||
|
PatternKind::Or { .. } |
|
||||||
PatternKind::Binding { .. } |
|
PatternKind::Binding { .. } |
|
||||||
PatternKind::Leaf { .. } |
|
PatternKind::Leaf { .. } |
|
||||||
PatternKind::Deref { .. } => {
|
PatternKind::Deref { .. } => {
|
||||||
|
@ -130,6 +131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
PatternKind::Slice { .. } |
|
PatternKind::Slice { .. } |
|
||||||
PatternKind::Array { .. } |
|
PatternKind::Array { .. } |
|
||||||
PatternKind::Wild |
|
PatternKind::Wild |
|
||||||
|
PatternKind::Or { .. } |
|
||||||
PatternKind::Binding { .. } |
|
PatternKind::Binding { .. } |
|
||||||
PatternKind::AscribeUserType { .. } |
|
PatternKind::AscribeUserType { .. } |
|
||||||
PatternKind::Leaf { .. } |
|
PatternKind::Leaf { .. } |
|
||||||
|
|
|
@ -1359,6 +1359,9 @@ fn pat_constructors<'tcx>(cx: &mut MatchCheckCtxt<'_, 'tcx>,
|
||||||
Some(vec![Slice(pat_len)])
|
Some(vec![Slice(pat_len)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PatternKind::Or { .. } => {
|
||||||
|
bug!("support for or-patterns has not been fully implemented yet.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1884,6 +1887,10 @@ fn specialize<'p, 'a: 'p, 'tcx>(
|
||||||
"unexpected ctor {:?} for slice pat", constructor)
|
"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);
|
debug!("specialize({:#?}, {:#?}) = {:#?}", r[0], wild_patterns, head);
|
||||||
|
|
||||||
|
|
|
@ -175,6 +175,12 @@ pub enum PatternKind<'tcx> {
|
||||||
slice: Option<Pattern<'tcx>>,
|
slice: Option<Pattern<'tcx>>,
|
||||||
suffix: Vec<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)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
@ -186,6 +192,18 @@ pub struct PatternRange<'tcx> {
|
||||||
|
|
||||||
impl<'tcx> fmt::Display for Pattern<'tcx> {
|
impl<'tcx> fmt::Display for Pattern<'tcx> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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 {
|
match *self.kind {
|
||||||
PatternKind::Wild => write!(f, "_"),
|
PatternKind::Wild => write!(f, "_"),
|
||||||
PatternKind::AscribeUserType { ref subpattern, .. } =>
|
PatternKind::AscribeUserType { ref subpattern, .. } =>
|
||||||
|
@ -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 {
|
if let Some(variant) = variant {
|
||||||
write!(f, "{}", variant.ident)?;
|
write!(f, "{}", variant.ident)?;
|
||||||
|
|
||||||
|
@ -241,12 +256,12 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let name = variant.fields[p.field.index()].ident;
|
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;
|
printed += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if printed < variant.fields.len() {
|
if printed < variant.fields.len() {
|
||||||
write!(f, "{}..", start_or_continue())?;
|
write!(f, "{}..", start_or_comma())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
return write!(f, " }}");
|
return write!(f, " }}");
|
||||||
|
@ -257,7 +272,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
|
||||||
if num_fields != 0 || variant.is_none() {
|
if num_fields != 0 || variant.is_none() {
|
||||||
write!(f, "(")?;
|
write!(f, "(")?;
|
||||||
for i in 0..num_fields {
|
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.
|
// Common case: the field is where we expect it.
|
||||||
if let Some(p) = subpatterns.get(i) {
|
if let Some(p) = subpatterns.get(i) {
|
||||||
|
@ -305,14 +320,12 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
|
||||||
}
|
}
|
||||||
PatternKind::Slice { ref prefix, ref slice, ref suffix } |
|
PatternKind::Slice { ref prefix, ref slice, ref suffix } |
|
||||||
PatternKind::Array { 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, "[")?;
|
write!(f, "[")?;
|
||||||
for p in prefix {
|
for p in prefix {
|
||||||
write!(f, "{}{}", start_or_continue(), p)?;
|
write!(f, "{}{}", start_or_comma(), p)?;
|
||||||
}
|
}
|
||||||
if let Some(ref slice) = *slice {
|
if let Some(ref slice) = *slice {
|
||||||
write!(f, "{}", start_or_continue())?;
|
write!(f, "{}", start_or_comma())?;
|
||||||
match *slice.kind {
|
match *slice.kind {
|
||||||
PatternKind::Wild => {}
|
PatternKind::Wild => {}
|
||||||
_ => write!(f, "{}", slice)?
|
_ => write!(f, "{}", slice)?
|
||||||
|
@ -320,10 +333,16 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
|
||||||
write!(f, "..")?;
|
write!(f, "..")?;
|
||||||
}
|
}
|
||||||
for p in suffix {
|
for p in suffix {
|
||||||
write!(f, "{}{}", start_or_continue(), p)?;
|
write!(f, "{}{}", start_or_comma(), p)?;
|
||||||
}
|
}
|
||||||
write!(f, "]")
|
write!(f, "]")
|
||||||
}
|
}
|
||||||
|
PatternKind::Or { ref pats } => {
|
||||||
|
for pat in pats {
|
||||||
|
write!(f, "{}{}", start_or_continue(" | "), pat)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -655,6 +674,12 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
||||||
|
|
||||||
self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
|
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 {
|
Pattern {
|
||||||
|
@ -1436,6 +1461,7 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
|
||||||
slice: slice.fold_with(folder),
|
slice: slice.fold_with(folder),
|
||||||
suffix: suffix.fold_with(folder)
|
suffix: suffix.fold_with(folder)
|
||||||
},
|
},
|
||||||
|
PatternKind::Or { ref pats } => PatternKind::Or { pats: pats.fold_with(folder) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let is_non_ref_pat = match pat.node {
|
let is_non_ref_pat = match pat.node {
|
||||||
PatKind::Struct(..) |
|
PatKind::Struct(..) |
|
||||||
PatKind::TupleStruct(..) |
|
PatKind::TupleStruct(..) |
|
||||||
|
PatKind::Or(_) |
|
||||||
PatKind::Tuple(..) |
|
PatKind::Tuple(..) |
|
||||||
PatKind::Box(_) |
|
PatKind::Box(_) |
|
||||||
PatKind::Range(..) |
|
PatKind::Range(..) |
|
||||||
|
@ -309,6 +310,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
PatKind::Struct(ref qpath, ref fields, etc) => {
|
PatKind::Struct(ref qpath, ref fields, etc) => {
|
||||||
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, discrim_span)
|
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) => {
|
PatKind::Tuple(ref elements, ddpos) => {
|
||||||
let mut expected_len = elements.len();
|
let mut expected_len = elements.len();
|
||||||
if ddpos.is_some() {
|
if ddpos.is_some() {
|
||||||
|
|
|
@ -4107,6 +4107,9 @@ fn name_from_pat(p: &hir::Pat) -> String {
|
||||||
if etc { ", .." } else { "" }
|
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))
|
PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
|
||||||
.collect::<Vec<String>>().join(", ")),
|
.collect::<Vec<String>>().join(", ")),
|
||||||
PatKind::Box(ref p) => name_from_pat(&**p),
|
PatKind::Box(ref p) => name_from_pat(&**p),
|
||||||
|
|
|
@ -572,9 +572,10 @@ impl Pat {
|
||||||
match &self.node {
|
match &self.node {
|
||||||
PatKind::Ident(_, _, Some(p)) => p.walk(it),
|
PatKind::Ident(_, _, Some(p)) => p.walk(it),
|
||||||
PatKind::Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk(it)),
|
PatKind::Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk(it)),
|
||||||
PatKind::TupleStruct(_, s) | PatKind::Tuple(s) | PatKind::Slice(s) => {
|
PatKind::TupleStruct(_, s)
|
||||||
s.iter().all(|p| p.walk(it))
|
| 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::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it),
|
||||||
PatKind::Wild
|
PatKind::Wild
|
||||||
| PatKind::Rest
|
| PatKind::Rest
|
||||||
|
@ -648,6 +649,10 @@ pub enum PatKind {
|
||||||
/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
|
/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
|
||||||
TupleStruct(Path, Vec<P<Pat>>),
|
TupleStruct(Path, Vec<P<Pat>>),
|
||||||
|
|
||||||
|
/// An or-pattern `A | B | C`.
|
||||||
|
/// Invariant: `pats.len() >= 2`.
|
||||||
|
Or(Vec<P<Pat>>),
|
||||||
|
|
||||||
/// A possibly qualified path pattern.
|
/// A possibly qualified path pattern.
|
||||||
/// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants
|
/// 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
|
/// or associated constants. Qualified path patterns `<A>::B::C`/`<A as Trait>::B::C` can
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -1050,7 +1050,6 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
|
||||||
vis.visit_span(span);
|
vis.visit_span(span);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
PatKind::Tuple(elems) => visit_vec(elems, |elem| vis.visit_pat(elem)),
|
|
||||||
PatKind::Box(inner) => vis.visit_pat(inner),
|
PatKind::Box(inner) => vis.visit_pat(inner),
|
||||||
PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
|
PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
|
||||||
PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
|
PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
|
||||||
|
@ -1058,7 +1057,9 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
|
||||||
vis.visit_expr(e2);
|
vis.visit_expr(e2);
|
||||||
vis.visit_span(span);
|
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::Paren(inner) => vis.visit_pat(inner),
|
||||||
PatKind::Mac(mac) => vis.visit_mac(mac),
|
PatKind::Mac(mac) => vis.visit_mac(mac),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -436,18 +436,30 @@ pub trait PrintState<'a>: std::ops::Deref<Target=pp::Printer> + std::ops::DerefM
|
||||||
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 commasep<T, F>(&mut self, b: Breaks, elts: &[T], mut op: F)
|
fn strsep<T, F>(&mut self, sep: &'static str, space_before: bool,
|
||||||
|
b: Breaks, elts: &[T], mut op: F)
|
||||||
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 { first = false; } else { self.word_space(","); }
|
for elt in rest {
|
||||||
op(self, elt);
|
if space_before {
|
||||||
|
self.space();
|
||||||
|
}
|
||||||
|
self.word_space(sep);
|
||||||
|
op(self, elt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.end();
|
self.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
|
||||||
|
where F: FnMut(&mut Self, &T),
|
||||||
|
{
|
||||||
|
self.strsep(",", false, b, elts, op)
|
||||||
|
}
|
||||||
|
|
||||||
fn maybe_print_comment(&mut self, pos: BytePos) {
|
fn maybe_print_comment(&mut self, pos: BytePos) {
|
||||||
while let Some(ref cmnt) = self.next_comment() {
|
while let Some(ref cmnt) = self.next_comment() {
|
||||||
if cmnt.pos < pos {
|
if cmnt.pos < pos {
|
||||||
|
@ -2353,6 +2365,9 @@ impl<'a> State<'a> {
|
||||||
self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
|
self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
|
||||||
self.pclose();
|
self.pclose();
|
||||||
}
|
}
|
||||||
|
PatKind::Or(ref pats) => {
|
||||||
|
self.strsep("|", true, 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);
|
||||||
}
|
}
|
||||||
|
@ -2429,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) {
|
||||||
|
|
|
@ -447,9 +447,6 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
|
||||||
visitor.visit_pat(&field.pat)
|
visitor.visit_pat(&field.pat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PatKind::Tuple(ref elems) => {
|
|
||||||
walk_list!(visitor, visit_pat, elems);
|
|
||||||
}
|
|
||||||
PatKind::Box(ref subpattern) |
|
PatKind::Box(ref subpattern) |
|
||||||
PatKind::Ref(ref subpattern, _) |
|
PatKind::Ref(ref subpattern, _) |
|
||||||
PatKind::Paren(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);
|
visitor.visit_expr(upper_bound);
|
||||||
}
|
}
|
||||||
PatKind::Wild | PatKind::Rest => {},
|
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);
|
walk_list!(visitor, visit_pat, elems);
|
||||||
}
|
}
|
||||||
PatKind::Mac(ref mac) => visitor.visit_mac(mac),
|
PatKind::Mac(ref mac) => visitor.visit_mac(mac),
|
||||||
|
|
|
@ -470,6 +470,7 @@ symbols! {
|
||||||
option_env,
|
option_env,
|
||||||
opt_out_copy,
|
opt_out_copy,
|
||||||
or,
|
or,
|
||||||
|
or_patterns,
|
||||||
Ord,
|
Ord,
|
||||||
Ordering,
|
Ordering,
|
||||||
Output,
|
Output,
|
||||||
|
|
9
src/test/ui/feature-gate/feature-gate-or_patterns.rs
Normal file
9
src/test/ui/feature-gate/feature-gate-or_patterns.rs
Normal 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
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
12
src/test/ui/feature-gate/feature-gate-or_patterns.stderr
Normal file
12
src/test/ui/feature-gate/feature-gate-or_patterns.stderr
Normal 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`.
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue