Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 75e23ff

Browse files
authoredJul 28, 2019
Rollup merge of rust-lang#62550 - Centril:rest-patterns, r=petrochenkov
Implement RFC 2707 + Parser recovery for range patterns Implement rust-lang/rfcs#2707. - Add a new basic syntactic pattern form `ast::PatKind::Rest` (parsed as `..` or `DOTDOT`) and simplify `ast::PatKind::{Slice, Tuple, TupleStruct}` as a result. - Lower `ast::PatKind::Rest` in combination with the aforementioned `PatKind` variants as well as `PatKind::Ident`. The HIR remains unchanged for now (may be advisable to make slight adjustments later). - Refactor `parser.rs` wrt. parsing sequences and lists of things in the process. - Add parser recovery for range patterns of form `X..`, `X..=`, `X...`, `..Y`, `..=Y`, and `...Y`. This should make it easy to actually support these patterns semantically later if we so desire. cc rust-lang#62254 r? @petrochenkov
2 parents 9a239ef + d33696f commit 75e23ff

File tree

87 files changed

+1546
-675
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1546
-675
lines changed
 

‎src/doc/unstable-book/src/language-features/slice-patterns.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ matched against that pattern. For example:
1717
fn is_symmetric(list: &[u32]) -> bool {
1818
match list {
1919
&[] | &[_] => true,
20-
&[x, ref inside.., y] if x == y => is_symmetric(inside),
20+
&[x, ref inside @ .., y] if x == y => is_symmetric(inside),
2121
&[..] => false,
2222
}
2323
}

‎src/librustc/hir/lowering.rs

+161-39
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use std::mem;
5858
use smallvec::SmallVec;
5959
use syntax::attr;
6060
use syntax::ast;
61+
use syntax::ptr::P as AstP;
6162
use syntax::ast::*;
6263
use syntax::errors;
6364
use syntax::ext::hygiene::ExpnId;
@@ -467,7 +468,7 @@ impl<'a> LoweringContext<'a> {
467468
fn visit_pat(&mut self, p: &'tcx Pat) {
468469
match p.node {
469470
// Doesn't generate a HIR node
470-
PatKind::Paren(..) => {},
471+
PatKind::Paren(..) | PatKind::Rest => {},
471472
_ => {
472473
if let Some(owner) = self.hir_id_owner {
473474
self.lctx.lower_node_id_with_owner(p.id, owner);
@@ -1156,7 +1157,7 @@ impl<'a> LoweringContext<'a> {
11561157
&mut self,
11571158
capture_clause: CaptureBy,
11581159
closure_node_id: NodeId,
1159-
ret_ty: Option<syntax::ptr::P<Ty>>,
1160+
ret_ty: Option<AstP<Ty>>,
11601161
span: Span,
11611162
body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr,
11621163
) -> hir::ExprKind {
@@ -4171,45 +4172,20 @@ impl<'a> LoweringContext<'a> {
41714172
let node = match p.node {
41724173
PatKind::Wild => hir::PatKind::Wild,
41734174
PatKind::Ident(ref binding_mode, ident, ref sub) => {
4174-
match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
4175-
// `None` can occur in body-less function signatures
4176-
res @ None | res @ Some(Res::Local(_)) => {
4177-
let canonical_id = match res {
4178-
Some(Res::Local(id)) => id,
4179-
_ => p.id,
4180-
};
4181-
4182-
hir::PatKind::Binding(
4183-
self.lower_binding_mode(binding_mode),
4184-
self.lower_node_id(canonical_id),
4185-
ident,
4186-
sub.as_ref().map(|x| self.lower_pat(x)),
4187-
)
4188-
}
4189-
Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
4190-
None,
4191-
P(hir::Path {
4192-
span: ident.span,
4193-
res: self.lower_res(res),
4194-
segments: hir_vec![hir::PathSegment::from_ident(ident)],
4195-
}),
4196-
)),
4197-
}
4175+
let lower_sub = |this: &mut Self| sub.as_ref().map(|x| this.lower_pat(x));
4176+
self.lower_pat_ident(p, binding_mode, ident, lower_sub)
41984177
}
41994178
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
4200-
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
4179+
PatKind::TupleStruct(ref path, ref pats) => {
42014180
let qpath = self.lower_qpath(
42024181
p.id,
42034182
&None,
42044183
path,
42054184
ParamMode::Optional,
42064185
ImplTraitContext::disallowed(),
42074186
);
4208-
hir::PatKind::TupleStruct(
4209-
qpath,
4210-
pats.iter().map(|x| self.lower_pat(x)).collect(),
4211-
ddpos,
4212-
)
4187+
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
4188+
hir::PatKind::TupleStruct(qpath, pats, ddpos)
42134189
}
42144190
PatKind::Path(ref qself, ref path) => {
42154191
let qpath = self.lower_qpath(
@@ -4246,8 +4222,9 @@ impl<'a> LoweringContext<'a> {
42464222
.collect();
42474223
hir::PatKind::Struct(qpath, fs, etc)
42484224
}
4249-
PatKind::Tuple(ref elts, ddpos) => {
4250-
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
4225+
PatKind::Tuple(ref pats) => {
4226+
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
4227+
hir::PatKind::Tuple(pats, ddpos)
42514228
}
42524229
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
42534230
PatKind::Ref(ref inner, mutbl) => {
@@ -4258,22 +4235,167 @@ impl<'a> LoweringContext<'a> {
42584235
P(self.lower_expr(e2)),
42594236
self.lower_range_end(end),
42604237
),
4261-
PatKind::Slice(ref before, ref slice, ref after) => hir::PatKind::Slice(
4262-
before.iter().map(|x| self.lower_pat(x)).collect(),
4263-
slice.as_ref().map(|x| self.lower_pat(x)),
4264-
after.iter().map(|x| self.lower_pat(x)).collect(),
4265-
),
4238+
PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
4239+
PatKind::Rest => {
4240+
// If we reach here the `..` pattern is not semantically allowed.
4241+
self.ban_illegal_rest_pat(p.span)
4242+
}
42664243
PatKind::Paren(ref inner) => return self.lower_pat(inner),
42674244
PatKind::Mac(_) => panic!("Shouldn't exist here"),
42684245
};
42694246

4247+
self.pat_with_node_id_of(p, node)
4248+
}
4249+
4250+
fn lower_pat_tuple(
4251+
&mut self,
4252+
pats: &[AstP<Pat>],
4253+
ctx: &str,
4254+
) -> (HirVec<P<hir::Pat>>, Option<usize>) {
4255+
let mut elems = Vec::with_capacity(pats.len());
4256+
let mut rest = None;
4257+
4258+
let mut iter = pats.iter().enumerate();
4259+
while let Some((idx, pat)) = iter.next() {
4260+
// Interpret the first `..` pattern as a subtuple pattern.
4261+
if pat.is_rest() {
4262+
rest = Some((idx, pat.span));
4263+
break;
4264+
}
4265+
// It was not a subslice pattern so lower it normally.
4266+
elems.push(self.lower_pat(pat));
4267+
}
4268+
4269+
while let Some((_, pat)) = iter.next() {
4270+
// There was a previous subtuple pattern; make sure we don't allow more.
4271+
if pat.is_rest() {
4272+
self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
4273+
} else {
4274+
elems.push(self.lower_pat(pat));
4275+
}
4276+
}
4277+
4278+
(elems.into(), rest.map(|(ddpos, _)| ddpos))
4279+
}
4280+
4281+
fn lower_pat_slice(&mut self, pats: &[AstP<Pat>]) -> hir::PatKind {
4282+
let mut before = Vec::new();
4283+
let mut after = Vec::new();
4284+
let mut slice = None;
4285+
let mut prev_rest_span = None;
4286+
4287+
let mut iter = pats.iter();
4288+
while let Some(pat) = iter.next() {
4289+
// Interpret the first `((ref mut?)? x @)? ..` pattern as a subslice pattern.
4290+
match pat.node {
4291+
PatKind::Rest => {
4292+
prev_rest_span = Some(pat.span);
4293+
slice = Some(self.pat_wild_with_node_id_of(pat));
4294+
break;
4295+
},
4296+
PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
4297+
prev_rest_span = Some(sub.span);
4298+
let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
4299+
let node = self.lower_pat_ident(pat, bm, ident, lower_sub);
4300+
slice = Some(self.pat_with_node_id_of(pat, node));
4301+
break;
4302+
},
4303+
_ => {}
4304+
}
4305+
4306+
// It was not a subslice pattern so lower it normally.
4307+
before.push(self.lower_pat(pat));
4308+
}
4309+
4310+
while let Some(pat) = iter.next() {
4311+
// There was a previous subslice pattern; make sure we don't allow more.
4312+
let rest_span = match pat.node {
4313+
PatKind::Rest => Some(pat.span),
4314+
PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => {
4315+
// The `HirValidator` is merciless; add a `_` pattern to avoid ICEs.
4316+
after.push(self.pat_wild_with_node_id_of(pat));
4317+
Some(sub.span)
4318+
},
4319+
_ => None,
4320+
};
4321+
if let Some(rest_span) = rest_span {
4322+
self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
4323+
} else {
4324+
after.push(self.lower_pat(pat));
4325+
}
4326+
}
4327+
4328+
hir::PatKind::Slice(before.into(), slice, after.into())
4329+
}
4330+
4331+
fn lower_pat_ident(
4332+
&mut self,
4333+
p: &Pat,
4334+
binding_mode: &BindingMode,
4335+
ident: Ident,
4336+
lower_sub: impl FnOnce(&mut Self) -> Option<P<hir::Pat>>,
4337+
) -> hir::PatKind {
4338+
match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
4339+
// `None` can occur in body-less function signatures
4340+
res @ None | res @ Some(Res::Local(_)) => {
4341+
let canonical_id = match res {
4342+
Some(Res::Local(id)) => id,
4343+
_ => p.id,
4344+
};
4345+
4346+
hir::PatKind::Binding(
4347+
self.lower_binding_mode(binding_mode),
4348+
self.lower_node_id(canonical_id),
4349+
ident,
4350+
lower_sub(self),
4351+
)
4352+
}
4353+
Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
4354+
None,
4355+
P(hir::Path {
4356+
span: ident.span,
4357+
res: self.lower_res(res),
4358+
segments: hir_vec![hir::PathSegment::from_ident(ident)],
4359+
}),
4360+
)),
4361+
}
4362+
}
4363+
4364+
fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> P<hir::Pat> {
4365+
self.pat_with_node_id_of(p, hir::PatKind::Wild)
4366+
}
4367+
4368+
/// Construct a `Pat` with the `HirId` of `p.id` lowered.
4369+
fn pat_with_node_id_of(&mut self, p: &Pat, node: hir::PatKind) -> P<hir::Pat> {
42704370
P(hir::Pat {
42714371
hir_id: self.lower_node_id(p.id),
42724372
node,
42734373
span: p.span,
42744374
})
42754375
}
42764376

4377+
/// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
4378+
fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
4379+
self.diagnostic()
4380+
.struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
4381+
.span_label(sp, &format!("can only be used once per {} pattern", ctx))
4382+
.span_label(prev_sp, "previously used here")
4383+
.emit();
4384+
}
4385+
4386+
/// Used to ban the `..` pattern in places it shouldn't be semantically.
4387+
fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind {
4388+
self.diagnostic()
4389+
.struct_span_err(sp, "`..` patterns are not allowed here")
4390+
.note("only allowed in tuple, tuple struct, and slice patterns")
4391+
.emit();
4392+
4393+
// We're not in a list context so `..` can be reasonably treated
4394+
// as `_` because it should always be valid and roughly matches the
4395+
// intent of `..` (notice that the rest of a single slot is that slot).
4396+
hir::PatKind::Wild
4397+
}
4398+
42774399
fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
42784400
match *e {
42794401
RangeEnd::Included(_) => hir::RangeEnd::Included,

0 commit comments

Comments
 (0)
Please sign in to comment.