Skip to content

Commit

Permalink
Rollup merge of rust-lang#62550 - Centril:rest-patterns, r=petrochenkov
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Centril committed Jul 26, 2019
2 parents c5c5534 + 9cde1db commit 63ec331
Show file tree
Hide file tree
Showing 86 changed files with 1,538 additions and 667 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ matched against that pattern. For example:
fn is_symmetric(list: &[u32]) -> bool {
match list {
&[] | &[_] => true,
&[x, ref inside.., y] if x == y => is_symmetric(inside),
&[x, ref inside @ .., y] if x == y => is_symmetric(inside),
&[..] => false,
}
}
Expand Down
200 changes: 161 additions & 39 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use std::mem;
use smallvec::SmallVec;
use syntax::attr;
use syntax::ast;
use syntax::ptr::P as AstP;
use syntax::ast::*;
use syntax::errors;
use syntax::ext::hygiene::ExpnId;
Expand Down Expand Up @@ -468,7 +469,7 @@ impl<'a> LoweringContext<'a> {
fn visit_pat(&mut self, p: &'tcx Pat) {
match p.node {
// Doesn't generate a HIR node
PatKind::Paren(..) => {},
PatKind::Paren(..) | PatKind::Rest => {},
_ => {
if let Some(owner) = self.hir_id_owner {
self.lctx.lower_node_id_with_owner(p.id, owner);
Expand Down Expand Up @@ -1157,7 +1158,7 @@ impl<'a> LoweringContext<'a> {
&mut self,
capture_clause: CaptureBy,
closure_node_id: NodeId,
ret_ty: Option<syntax::ptr::P<Ty>>,
ret_ty: Option<AstP<Ty>>,
span: Span,
body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr,
) -> hir::ExprKind {
Expand Down Expand Up @@ -4172,45 +4173,20 @@ impl<'a> LoweringContext<'a> {
let node = match p.node {
PatKind::Wild => hir::PatKind::Wild,
PatKind::Ident(ref binding_mode, ident, ref sub) => {
match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
// `None` can occur in body-less function signatures
res @ None | res @ Some(Res::Local(_)) => {
let canonical_id = match res {
Some(Res::Local(id)) => id,
_ => p.id,
};

hir::PatKind::Binding(
self.lower_binding_mode(binding_mode),
self.lower_node_id(canonical_id),
ident,
sub.as_ref().map(|x| self.lower_pat(x)),
)
}
Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
None,
P(hir::Path {
span: ident.span,
res: self.lower_res(res),
segments: hir_vec![hir::PathSegment::from_ident(ident)],
}),
)),
}
let lower_sub = |this: &mut Self| sub.as_ref().map(|x| this.lower_pat(x));
self.lower_pat_ident(p, binding_mode, ident, lower_sub)
}
PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
PatKind::TupleStruct(ref path, ref pats) => {
let qpath = self.lower_qpath(
p.id,
&None,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
hir::PatKind::TupleStruct(
qpath,
pats.iter().map(|x| self.lower_pat(x)).collect(),
ddpos,
)
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
hir::PatKind::TupleStruct(qpath, pats, ddpos)
}
PatKind::Path(ref qself, ref path) => {
let qpath = self.lower_qpath(
Expand Down Expand Up @@ -4247,8 +4223,9 @@ impl<'a> LoweringContext<'a> {
.collect();
hir::PatKind::Struct(qpath, fs, etc)
}
PatKind::Tuple(ref elts, ddpos) => {
hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
PatKind::Tuple(ref pats) => {
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
hir::PatKind::Tuple(pats, ddpos)
}
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
PatKind::Ref(ref inner, mutbl) => {
Expand All @@ -4259,22 +4236,167 @@ impl<'a> LoweringContext<'a> {
P(self.lower_expr(e2)),
self.lower_range_end(end),
),
PatKind::Slice(ref before, ref slice, ref after) => hir::PatKind::Slice(
before.iter().map(|x| self.lower_pat(x)).collect(),
slice.as_ref().map(|x| self.lower_pat(x)),
after.iter().map(|x| self.lower_pat(x)).collect(),
),
PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
PatKind::Rest => {
// If we reach here the `..` pattern is not semantically allowed.
self.ban_illegal_rest_pat(p.span)
}
PatKind::Paren(ref inner) => return self.lower_pat(inner),
PatKind::Mac(_) => panic!("Shouldn't exist here"),
};

self.pat_with_node_id_of(p, node)
}

fn lower_pat_tuple(
&mut self,
pats: &[AstP<Pat>],
ctx: &str,
) -> (HirVec<P<hir::Pat>>, Option<usize>) {
let mut elems = Vec::with_capacity(pats.len());
let mut rest = None;

let mut iter = pats.iter().enumerate();
while let Some((idx, pat)) = iter.next() {
// Interpret the first `..` pattern as a subtuple pattern.
if pat.is_rest() {
rest = Some((idx, pat.span));
break;
}
// It was not a subslice pattern so lower it normally.
elems.push(self.lower_pat(pat));
}

while let Some((_, pat)) = iter.next() {
// There was a previous subtuple pattern; make sure we don't allow more.
if pat.is_rest() {
self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
} else {
elems.push(self.lower_pat(pat));
}
}

(elems.into(), rest.map(|(ddpos, _)| ddpos))
}

fn lower_pat_slice(&mut self, pats: &[AstP<Pat>]) -> hir::PatKind {
let mut before = Vec::new();
let mut after = Vec::new();
let mut slice = None;
let mut prev_rest_span = None;

let mut iter = pats.iter();
while let Some(pat) = iter.next() {
// Interpret the first `((ref mut?)? x @)? ..` pattern as a subslice pattern.
match pat.node {
PatKind::Rest => {
prev_rest_span = Some(pat.span);
slice = Some(self.pat_wild_with_node_id_of(pat));
break;
},
PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
prev_rest_span = Some(sub.span);
let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
let node = self.lower_pat_ident(pat, bm, ident, lower_sub);
slice = Some(self.pat_with_node_id_of(pat, node));
break;
},
_ => {}
}

// It was not a subslice pattern so lower it normally.
before.push(self.lower_pat(pat));
}

while let Some(pat) = iter.next() {
// There was a previous subslice pattern; make sure we don't allow more.
let rest_span = match pat.node {
PatKind::Rest => Some(pat.span),
PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => {
// The `HirValidator` is merciless; add a `_` pattern to avoid ICEs.
after.push(self.pat_wild_with_node_id_of(pat));
Some(sub.span)
},
_ => None,
};
if let Some(rest_span) = rest_span {
self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
} else {
after.push(self.lower_pat(pat));
}
}

hir::PatKind::Slice(before.into(), slice, after.into())
}

fn lower_pat_ident(
&mut self,
p: &Pat,
binding_mode: &BindingMode,
ident: Ident,
lower_sub: impl FnOnce(&mut Self) -> Option<P<hir::Pat>>,
) -> hir::PatKind {
match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
// `None` can occur in body-less function signatures
res @ None | res @ Some(Res::Local(_)) => {
let canonical_id = match res {
Some(Res::Local(id)) => id,
_ => p.id,
};

hir::PatKind::Binding(
self.lower_binding_mode(binding_mode),
self.lower_node_id(canonical_id),
ident,
lower_sub(self),
)
}
Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
None,
P(hir::Path {
span: ident.span,
res: self.lower_res(res),
segments: hir_vec![hir::PathSegment::from_ident(ident)],
}),
)),
}
}

fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> P<hir::Pat> {
self.pat_with_node_id_of(p, hir::PatKind::Wild)
}

/// Construct a `Pat` with the `HirId` of `p.id` lowered.
fn pat_with_node_id_of(&mut self, p: &Pat, node: hir::PatKind) -> P<hir::Pat> {
P(hir::Pat {
hir_id: self.lower_node_id(p.id),
node,
span: p.span,
})
}

/// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
self.diagnostic()
.struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
.span_label(sp, &format!("can only be used once per {} pattern", ctx))
.span_label(prev_sp, "previously used here")
.emit();
}

/// Used to ban the `..` pattern in places it shouldn't be semantically.
fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind {
self.diagnostic()
.struct_span_err(sp, "`..` patterns are not allowed here")
.note("only allowed in tuple, tuple struct, and slice patterns")
.emit();

// We're not in a list context so `..` can be reasonably treated
// as `_` because it should always be valid and roughly matches the
// intent of `..` (notice that the rest of a single slot is that slot).
hir::PatKind::Wild
}

fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
match *e {
RangeEnd::Included(_) => hir::RangeEnd::Included,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/hair/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ pub enum PatternKind<'tcx> {

/// Matches against a slice, checking the length and extracting elements.
/// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty.
/// e.g., `&[ref xs..]`.
/// e.g., `&[ref xs @ ..]`.
Slice {
prefix: Vec<Pattern<'tcx>>,
slice: Option<Pattern<'tcx>>,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ impl<'a> AstValidator<'a> {
// ```
fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
match expr.node {
ExprKind::Lit(..) => {}
ExprKind::Lit(..) | ExprKind::Err => {}
ExprKind::Path(..) if allow_paths => {}
ExprKind::Unary(UnOp::Neg, ref inner)
if match inner.node { ExprKind::Lit(_) => true, _ => false } => {}
Expand Down
29 changes: 22 additions & 7 deletions src/librustc_target/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,34 @@ impl TargetDataLayout {
let mut dl = TargetDataLayout::default();
let mut i128_align_src = 64;
for spec in target.data_layout.split('-') {
match spec.split(':').collect::<Vec<_>>()[..] {
let spec_parts = spec.split(':').collect::<Vec<_>>();

match &*spec_parts {
["e"] => dl.endian = Endian::Little,
["E"] => dl.endian = Endian::Big,
[p] if p.starts_with("P") => {
dl.instruction_address_space = parse_address_space(&p[1..], "P")?
}
["a", ref a..] => dl.aggregate_align = align(a, "a")?,
["f32", ref a..] => dl.f32_align = align(a, "f32")?,
["f64", ref a..] => dl.f64_align = align(a, "f64")?,
[p @ "p", s, ref a..] | [p @ "p0", s, ref a..] => {
// FIXME: Ping cfg(bootstrap) -- Use `ref a @ ..` with new bootstrap compiler.
["a", ..] => {
let a = &spec_parts[1..]; // FIXME inline into pattern.
dl.aggregate_align = align(a, "a")?
}
["f32", ..] => {
let a = &spec_parts[1..]; // FIXME inline into pattern.
dl.f32_align = align(a, "f32")?
}
["f64", ..] => {
let a = &spec_parts[1..]; // FIXME inline into pattern.
dl.f64_align = align(a, "f64")?
}
[p @ "p", s, ..] | [p @ "p0", s, ..] => {
let a = &spec_parts[2..]; // FIXME inline into pattern.
dl.pointer_size = size(s, p)?;
dl.pointer_align = align(a, p)?;
}
[s, ref a..] if s.starts_with("i") => {
[s, ..] if s.starts_with("i") => {
let a = &spec_parts[1..]; // FIXME inline into pattern.
let bits = match s[1..].parse::<u64>() {
Ok(bits) => bits,
Err(_) => {
Expand All @@ -142,7 +156,8 @@ impl TargetDataLayout {
dl.i128_align = a;
}
}
[s, ref a..] if s.starts_with("v") => {
[s, ..] if s.starts_with("v") => {
let a = &spec_parts[1..]; // FIXME inline into pattern.
let v_size = size(&s[1..], "v")?;
let a = align(a, s)?;
if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let rhs_ty = self.check_expr(end);

// Check that both end-points are of numeric or char type.
let numeric_or_char = |ty: Ty<'_>| ty.is_numeric() || ty.is_char();
let numeric_or_char = |ty: Ty<'_>| {
ty.is_numeric()
|| ty.is_char()
|| ty.references_error()
};
let lhs_compat = numeric_or_char(lhs_ty);
let rhs_compat = numeric_or_char(rhs_ty);

Expand Down
4 changes: 3 additions & 1 deletion src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1818,7 +1818,9 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: &'tcx ty::AdtDef, sp: Span, d
);
let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {}", msg);
err.span_label(sp, &msg);
if let &[ref start.., ref end] = &variant_spans[..] {
if let &[.., ref end] = &variant_spans[..] {
// FIXME: Ping cfg(bootstrap) -- Use `ref start @ ..` with new bootstrap compiler.
let start = &variant_spans[..variant_spans.len() - 1];
for variant_span in start {
err.span_label(*variant_span, "");
}
Expand Down
6 changes: 3 additions & 3 deletions src/librustc_typeck/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3497,8 +3497,8 @@ Example of erroneous code:
let r = &[1, 2];
match r {
&[a, b, c, rest..] => { // error: pattern requires at least 3
// elements but array has 2
&[a, b, c, rest @ ..] => { // error: pattern requires at least 3
// elements but array has 2
println!("a={}, b={}, c={} rest={:?}", a, b, c, rest);
}
}
Expand All @@ -3512,7 +3512,7 @@ requires. You can match an arbitrary number of remaining elements with `..`:
let r = &[1, 2, 3, 4, 5];
match r {
&[a, b, c, rest..] => { // ok!
&[a, b, c, rest @ ..] => { // ok!
// prints `a=1, b=2, c=3 rest=[4, 5]`
println!("a={}, b={}, c={} rest={:?}", a, b, c, rest);
}
Expand Down
Loading

0 comments on commit 63ec331

Please sign in to comment.