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 28, 2019
2 parents 9a239ef + d33696f commit ef2d8c9
Show file tree
Hide file tree
Showing 87 changed files with 1,546 additions and 675 deletions.
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
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 @@ -467,7 +468,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 @@ -1156,7 +1157,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 @@ -4171,45 +4172,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 @@ -4246,8 +4222,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 @@ -4258,22 +4235,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
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
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
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
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
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
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

0 comments on commit ef2d8c9

Please sign in to comment.