Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RFC 2707 + Parser recovery for range patterns #62550

Merged
merged 34 commits into from
Jul 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f7c75cc
Add 'ast::PatKind::Rest'.
Centril Jul 6, 2019
ff77ef2
Introduce 'ast::Pat::is_rest(&self) -> bool'.
Centril Jul 9, 2019
633c997
Adjust 'ast::PatKind::{TupleStruct,Tuple,Slice}'.
Centril Jul 9, 2019
12250a2
Adjust feature gating of subslice patterns accordingly.
Centril Jul 9, 2019
b02941f
Adjust pretty printing accordingly.
Centril Jul 9, 2019
8ba5f49
Adjust and document 'Pat::to_ty' accordingly.
Centril Jul 9, 2019
d5df1e0
Adjust lowering of Tuple/TupleStruct patterns.
Centril Jul 9, 2019
694b3c3
Adjust lowering of Slice patterns.
Centril Jul 9, 2019
0a40ef2
Cleanup parse_seq_* methods + record trailing separators.
Centril Jul 9, 2019
7aeb4b7
Add more parse_*_seq methods for code reuse.
Centril Jul 9, 2019
7e1b671
Cleanup using the new parse_*_seq methods.
Centril Jul 9, 2019
62b29a1
Adjust parsing of Slice, Tuple, TupleStruct patterns.
Centril Jul 9, 2019
974413f
Recover on '..X' / '..=X' / '...X' range patterns.
Centril Jul 9, 2019
2f55354
Recover on 'X..' / 'X..=' / 'X...' range patterns.
Centril Jul 9, 2019
2411134
Update tests wrt. recovery of range patterns.
Centril Jul 9, 2019
f6c8234
And also --bless those recovery tests.
Centril Jul 9, 2019
891a736
Test parsing and recovery of all sorts of range patterns.
Centril Jul 9, 2019
75da43d
Use new 'p @ ..' syntax in tests.
Centril Jul 7, 2019
91c8b53
--bless tests due to new subslice syntax.
Centril Jul 7, 2019
e725ea2
Intersection patterns 'p1 @ p2' are not supported.
Centril Jul 8, 2019
06e5ae5
Account for better recovery in two cases.
Centril Jul 9, 2019
e3cdadd
(pat, ..,) is now syntactically legal.
Centril Jul 8, 2019
7c0b1da
Win some lose some; Unfortunately we lost recovery in one case.
Centril Jul 8, 2019
cec8649
Update unstable book wrt. subslice patterns.
Centril Jul 10, 2019
984f9db
Adjust documentation in HAIR.
Centril Jul 10, 2019
1060513
Get out of bootstrapping pickle.
Centril Jul 10, 2019
acc6a6d
--bless tests after rebase.
Centril Jul 10, 2019
59b5dae
Update error_codes.rs with new subslice syntax.
Centril Jul 10, 2019
397a027
Use AstP more in lowering.
Centril Jul 11, 2019
becdba8
Address comments in lowering + parsing.
Centril Jul 24, 2019
5f4dd1d
Address comments re. off-topic errors.
Centril Jul 24, 2019
18ccd6a
Add exceptions for ExprKind::Err/TyKind::Error.
Centril Jul 24, 2019
8774207
And --bless tests accordingly for those exceptions.
Centril Jul 24, 2019
d33696f
borrowck-describe-lvalue: --bless --compare-mode=nll.
Centril Jul 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Centril marked this conversation as resolved.
Show resolved Hide resolved
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