Skip to content

Commit f665d9d

Browse files
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 5b8be1a + 9cde1db commit f665d9d

File tree

86 files changed

+1538
-667
lines changed

Some content is hidden

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

86 files changed

+1538
-667
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;
@@ -468,7 +469,7 @@ impl<'a> LoweringContext<'a> {
468469
fn visit_pat(&mut self, p: &'tcx Pat) {
469470
match p.node {
470471
// Doesn't generate a HIR node
471-
PatKind::Paren(..) => {},
472+
PatKind::Paren(..) | PatKind::Rest => {},
472473
_ => {
473474
if let Some(owner) = self.hir_id_owner {
474475
self.lctx.lower_node_id_with_owner(p.id, owner);
@@ -1157,7 +1158,7 @@ impl<'a> LoweringContext<'a> {
11571158
&mut self,
11581159
capture_clause: CaptureBy,
11591160
closure_node_id: NodeId,
1160-
ret_ty: Option<syntax::ptr::P<Ty>>,
1161+
ret_ty: Option<AstP<Ty>>,
11611162
span: Span,
11621163
body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr,
11631164
) -> hir::ExprKind {
@@ -4173,45 +4174,20 @@ impl<'a> LoweringContext<'a> {
41734174
let node = match p.node {
41744175
PatKind::Wild => hir::PatKind::Wild,
41754176
PatKind::Ident(ref binding_mode, ident, ref sub) => {
4176-
match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
4177-
// `None` can occur in body-less function signatures
4178-
res @ None | res @ Some(Res::Local(_)) => {
4179-
let canonical_id = match res {
4180-
Some(Res::Local(id)) => id,
4181-
_ => p.id,
4182-
};
4183-
4184-
hir::PatKind::Binding(
4185-
self.lower_binding_mode(binding_mode),
4186-
self.lower_node_id(canonical_id),
4187-
ident,
4188-
sub.as_ref().map(|x| self.lower_pat(x)),
4189-
)
4190-
}
4191-
Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
4192-
None,
4193-
P(hir::Path {
4194-
span: ident.span,
4195-
res: self.lower_res(res),
4196-
segments: hir_vec![hir::PathSegment::from_ident(ident)],
4197-
}),
4198-
)),
4199-
}
4177+
let lower_sub = |this: &mut Self| sub.as_ref().map(|x| this.lower_pat(x));
4178+
self.lower_pat_ident(p, binding_mode, ident, lower_sub)
42004179
}
42014180
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
4202-
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
4181+
PatKind::TupleStruct(ref path, ref pats) => {
42034182
let qpath = self.lower_qpath(
42044183
p.id,
42054184
&None,
42064185
path,
42074186
ParamMode::Optional,
42084187
ImplTraitContext::disallowed(),
42094188
);
4210-
hir::PatKind::TupleStruct(
4211-
qpath,
4212-
pats.iter().map(|x| self.lower_pat(x)).collect(),
4213-
ddpos,
4214-
)
4189+
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
4190+
hir::PatKind::TupleStruct(qpath, pats, ddpos)
42154191
}
42164192
PatKind::Path(ref qself, ref path) => {
42174193
let qpath = self.lower_qpath(
@@ -4248,8 +4224,9 @@ impl<'a> LoweringContext<'a> {
42484224
.collect();
42494225
hir::PatKind::Struct(qpath, fs, etc)
42504226
}
4251-
PatKind::Tuple(ref elts, ddpos) => {
4252-
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
4227+
PatKind::Tuple(ref pats) => {
4228+
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
4229+
hir::PatKind::Tuple(pats, ddpos)
42534230
}
42544231
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
42554232
PatKind::Ref(ref inner, mutbl) => {
@@ -4260,22 +4237,167 @@ impl<'a> LoweringContext<'a> {
42604237
P(self.lower_expr(e2)),
42614238
self.lower_range_end(end),
42624239
),
4263-
PatKind::Slice(ref before, ref slice, ref after) => hir::PatKind::Slice(
4264-
before.iter().map(|x| self.lower_pat(x)).collect(),
4265-
slice.as_ref().map(|x| self.lower_pat(x)),
4266-
after.iter().map(|x| self.lower_pat(x)).collect(),
4267-
),
4240+
PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
4241+
PatKind::Rest => {
4242+
// If we reach here the `..` pattern is not semantically allowed.
4243+
self.ban_illegal_rest_pat(p.span)
4244+
}
42684245
PatKind::Paren(ref inner) => return self.lower_pat(inner),
42694246
PatKind::Mac(_) => panic!("Shouldn't exist here"),
42704247
};
42714248

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

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

src/librustc_mir/hair/pattern/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ pub enum PatternKind<'tcx> {
162162

163163
/// Matches against a slice, checking the length and extracting elements.
164164
/// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty.
165-
/// e.g., `&[ref xs..]`.
165+
/// e.g., `&[ref xs @ ..]`.
166166
Slice {
167167
prefix: Vec<Pattern<'tcx>>,
168168
slice: Option<Pattern<'tcx>>,

src/librustc_passes/ast_validation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ impl<'a> AstValidator<'a> {
287287
// ```
288288
fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
289289
match expr.node {
290-
ExprKind::Lit(..) => {}
290+
ExprKind::Lit(..) | ExprKind::Err => {}
291291
ExprKind::Path(..) if allow_paths => {}
292292
ExprKind::Unary(UnOp::Neg, ref inner)
293293
if match inner.node { ExprKind::Lit(_) => true, _ => false } => {}

src/librustc_target/abi/mod.rs

+22-7
Original file line numberDiff line numberDiff line change
@@ -105,20 +105,34 @@ impl TargetDataLayout {
105105
let mut dl = TargetDataLayout::default();
106106
let mut i128_align_src = 64;
107107
for spec in target.data_layout.split('-') {
108-
match spec.split(':').collect::<Vec<_>>()[..] {
108+
let spec_parts = spec.split(':').collect::<Vec<_>>();
109+
110+
match &*spec_parts {
109111
["e"] => dl.endian = Endian::Little,
110112
["E"] => dl.endian = Endian::Big,
111113
[p] if p.starts_with("P") => {
112114
dl.instruction_address_space = parse_address_space(&p[1..], "P")?
113115
}
114-
["a", ref a..] => dl.aggregate_align = align(a, "a")?,
115-
["f32", ref a..] => dl.f32_align = align(a, "f32")?,
116-
["f64", ref a..] => dl.f64_align = align(a, "f64")?,
117-
[p @ "p", s, ref a..] | [p @ "p0", s, ref a..] => {
116+
// FIXME: Ping cfg(bootstrap) -- Use `ref a @ ..` with new bootstrap compiler.
117+
["a", ..] => {
118+
let a = &spec_parts[1..]; // FIXME inline into pattern.
119+
dl.aggregate_align = align(a, "a")?
120+
}
121+
["f32", ..] => {
122+
let a = &spec_parts[1..]; // FIXME inline into pattern.
123+
dl.f32_align = align(a, "f32")?
124+
}
125+
["f64", ..] => {
126+
let a = &spec_parts[1..]; // FIXME inline into pattern.
127+
dl.f64_align = align(a, "f64")?
128+
}
129+
[p @ "p", s, ..] | [p @ "p0", s, ..] => {
130+
let a = &spec_parts[2..]; // FIXME inline into pattern.
118131
dl.pointer_size = size(s, p)?;
119132
dl.pointer_align = align(a, p)?;
120133
}
121-
[s, ref a..] if s.starts_with("i") => {
134+
[s, ..] if s.starts_with("i") => {
135+
let a = &spec_parts[1..]; // FIXME inline into pattern.
122136
let bits = match s[1..].parse::<u64>() {
123137
Ok(bits) => bits,
124138
Err(_) => {
@@ -142,7 +156,8 @@ impl TargetDataLayout {
142156
dl.i128_align = a;
143157
}
144158
}
145-
[s, ref a..] if s.starts_with("v") => {
159+
[s, ..] if s.starts_with("v") => {
160+
let a = &spec_parts[1..]; // FIXME inline into pattern.
146161
let v_size = size(&s[1..], "v")?;
147162
let a = align(a, s)?;
148163
if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {

src/librustc_typeck/check/_match.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
196196
let rhs_ty = self.check_expr(end);
197197

198198
// Check that both end-points are of numeric or char type.
199-
let numeric_or_char = |ty: Ty<'_>| ty.is_numeric() || ty.is_char();
199+
let numeric_or_char = |ty: Ty<'_>| {
200+
ty.is_numeric()
201+
|| ty.is_char()
202+
|| ty.references_error()
203+
};
200204
let lhs_compat = numeric_or_char(lhs_ty);
201205
let rhs_compat = numeric_or_char(rhs_ty);
202206

src/librustc_typeck/check/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1818,7 +1818,9 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: &'tcx ty::AdtDef, sp: Span, d
18181818
);
18191819
let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {}", msg);
18201820
err.span_label(sp, &msg);
1821-
if let &[ref start.., ref end] = &variant_spans[..] {
1821+
if let &[.., ref end] = &variant_spans[..] {
1822+
// FIXME: Ping cfg(bootstrap) -- Use `ref start @ ..` with new bootstrap compiler.
1823+
let start = &variant_spans[..variant_spans.len() - 1];
18221824
for variant_span in start {
18231825
err.span_label(*variant_span, "");
18241826
}

src/librustc_typeck/error_codes.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3497,8 +3497,8 @@ Example of erroneous code:
34973497
34983498
let r = &[1, 2];
34993499
match r {
3500-
&[a, b, c, rest..] => { // error: pattern requires at least 3
3501-
// elements but array has 2
3500+
&[a, b, c, rest @ ..] => { // error: pattern requires at least 3
3501+
// elements but array has 2
35023502
println!("a={}, b={}, c={} rest={:?}", a, b, c, rest);
35033503
}
35043504
}
@@ -3512,7 +3512,7 @@ requires. You can match an arbitrary number of remaining elements with `..`:
35123512
35133513
let r = &[1, 2, 3, 4, 5];
35143514
match r {
3515-
&[a, b, c, rest..] => { // ok!
3515+
&[a, b, c, rest @ ..] => { // ok!
35163516
// prints `a=1, b=2, c=3 rest=[4, 5]`
35173517
println!("a={}, b={}, c={} rest={:?}", a, b, c, rest);
35183518
}

0 commit comments

Comments
 (0)