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

Improve typeck & lowering docs for slice patterns #67318

Merged
merged 4 commits into from
Dec 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 26 additions & 12 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2852,19 +2852,23 @@ impl<'a> LoweringContext<'a> {
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.
for (idx, pat) in iter.by_ref() {
// Interpret the first `..` pattern as a sub-tuple pattern.
// Note that unlike for slice patterns,
// where `xs @ ..` is a legal sub-slice pattern,
// it is not a legal sub-tuple pattern.
if pat.is_rest() {
rest = Some((idx, pat.span));
break;
}
// It was not a subslice pattern so lower it normally.
// It was not a sub-tuple 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.
for (_, pat) in iter {
// There was a previous sub-tuple pattern; make sure we don't allow more...
if pat.is_rest() {
// ...but there was one again, so error.
self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
} else {
elems.push(self.lower_pat(pat));
Expand All @@ -2874,36 +2878,44 @@ impl<'a> LoweringContext<'a> {
(elems.into(), rest.map(|(ddpos, _)| ddpos))
}

/// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
/// `hir::PatKind::Slice(before, slice, after)`.
///
/// When encountering `($binding_mode $ident @)? ..` (`slice`),
/// this is interpreted as a sub-slice pattern semantically.
/// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
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.
// Lower all the patterns until the first occurence of a sub-slice pattern.
for pat in iter.by_ref() {
match pat.kind {
// Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
PatKind::Rest => {
prev_rest_span = Some(pat.span);
slice = Some(self.pat_wild_with_node_id_of(pat));
break;
},
// Found a sub-slice pattern `$binding_mode $ident @ ..`.
// Record, lower it to `$binding_mode $ident @ _`, and stop here.
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)),
}

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

while let Some(pat) = iter.next() {
// Lower all the patterns after the first sub-slice pattern.
for pat in iter {
// There was a previous subslice pattern; make sure we don't allow more.
let rest_span = match pat.kind {
PatKind::Rest => Some(pat.span),
Expand All @@ -2915,8 +2927,10 @@ impl<'a> LoweringContext<'a> {
_ => None,
};
if let Some(rest_span) = rest_span {
// We have e.g., `[a, .., b, ..]`. That's no good, error!
self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
} else {
// Lower the pattern normally.
after.push(self.lower_pat(pat));
}
}
Expand Down
11 changes: 9 additions & 2 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1048,8 +1048,15 @@ pub enum PatKind {
/// A range pattern (e.g., `1..=2` or `1..2`).
Range(P<Expr>, P<Expr>, RangeEnd),

/// `[a, b, ..i, y, z]` is represented as:
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`.
/// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`.
///
/// Here, `slice` is lowered from the syntax `($binding_mode $ident @)? ..`.
/// If `slice` exists, then `after` can be non-empty.
///
/// The representation for e.g., `[a, b, .., c, d]` is:
/// ```
/// PatKind::Slice([Binding(a), Binding(b)], Some(Wild), [Binding(c), Binding(d)])
/// ```
Slice(HirVec<P<Pat>>, Option<P<Pat>>, HirVec<P<Pat>>),
}

Expand Down
6 changes: 3 additions & 3 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5351,9 +5351,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
directly, not through a function pointer");
}

// Resolves `typ` by a single level if `typ` is a type variable.
// If no resolution is possible, then an error is reported.
// Numeric inference variables may be left unresolved.
/// Resolves `typ` by a single level if `typ` is a type variable.
/// If no resolution is possible, then an error is reported.
/// Numeric inference variables may be left unresolved.
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
let ty = self.resolve_vars_with_obligations(ty);
if !ty.is_ty_var() {
Expand Down
25 changes: 25 additions & 0 deletions src/librustc_typeck/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.mk_ref(region, mt)
}

/// Type check a slice pattern.
///
/// Syntactically, these look like `[pat_0, ..., pat_n]`.
/// Semantically, we are type checking a pattern with structure:
/// ```
/// [before_0, ..., before_n, (slice, after_0, ... after_n)?]
/// ```
/// The type of `slice`, if it is present, depends on the `expected` type.
/// If `slice` is missing, then so is `after_i`.
/// If `slice` is present, it can still represent 0 elements.
fn check_pat_slice(
&self,
span: Span,
Expand All @@ -1167,27 +1177,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
let expected_ty = self.structurally_resolved_type(span, expected);
let (inner_ty, slice_ty) = match expected_ty.kind {
// An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
ty::Array(inner_ty, size) => {
let slice_ty = if let Some(size) = size.try_eval_usize(tcx, self.param_env) {
// Now we know the length...
let min_len = before.len() as u64 + after.len() as u64;
if slice.is_none() {
// ...and since there is no variable-length pattern,
// we require an exact match between the number of elements
// in the array pattern and as provided by the matched type.
if min_len != size {
self.error_scrutinee_inconsistent_length(span, min_len, size)
}
tcx.types.err
} else if let Some(rest) = size.checked_sub(min_len) {
// The variable-length pattern was there,
// so it has an array type with the remaining elements left as its size...
tcx.mk_array(inner_ty, rest)
} else {
// ...however, in this case, there were no remaining elements.
// That is, the slice pattern requires more than the array type offers.
self.error_scrutinee_with_rest_inconsistent_length(span, min_len, size);
tcx.types.err
}
} else {
// No idea what the length is, which happens if we have e.g.,
// `let [a, b] = arr` where `arr: [T; N]` where `const N: usize`.
self.error_scrutinee_unfixed_length(span);
tcx.types.err
};
(inner_ty, slice_ty)
}
ty::Slice(inner_ty) => (inner_ty, expected_ty),
// The expected type must be an array or slice, but was neither, so error.
_ => {
if !expected_ty.references_error() {
self.error_expected_array_or_slice(span, expected_ty);
Expand All @@ -1196,12 +1218,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};

// Type check all the patterns before `slice`.
for elt in before {
self.check_pat(&elt, inner_ty, def_bm, discrim_span);
}
// Type check the `slice`, if present, against its expected type.
if let Some(slice) = slice {
self.check_pat(&slice, slice_ty, def_bm, discrim_span);
}
// Type check the elements after `slice`, if present.
for elt in after {
self.check_pat(&elt, inner_ty, def_bm, discrim_span);
}
Expand Down