Rollup merge of #67318 - Centril:spdocs, r=matthewjasper
Improve typeck & lowering docs for slice patterns cc #62254 r? @matthewjasper
This commit is contained in:
@@ -2852,19 +2852,23 @@ impl<'a> LoweringContext<'a> {
|
|||||||
let mut rest = None;
|
let mut rest = None;
|
||||||
|
|
||||||
let mut iter = pats.iter().enumerate();
|
let mut iter = pats.iter().enumerate();
|
||||||
while let Some((idx, pat)) = iter.next() {
|
for (idx, pat) in iter.by_ref() {
|
||||||
// Interpret the first `..` pattern as a subtuple pattern.
|
// 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() {
|
if pat.is_rest() {
|
||||||
rest = Some((idx, pat.span));
|
rest = Some((idx, pat.span));
|
||||||
break;
|
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));
|
elems.push(self.lower_pat(pat));
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some((_, pat)) = iter.next() {
|
for (_, pat) in iter {
|
||||||
// There was a previous subtuple pattern; make sure we don't allow more.
|
// There was a previous sub-tuple pattern; make sure we don't allow more...
|
||||||
if pat.is_rest() {
|
if pat.is_rest() {
|
||||||
|
// ...but there was one again, so error.
|
||||||
self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
|
self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
|
||||||
} else {
|
} else {
|
||||||
elems.push(self.lower_pat(pat));
|
elems.push(self.lower_pat(pat));
|
||||||
@@ -2874,6 +2878,12 @@ impl<'a> LoweringContext<'a> {
|
|||||||
(elems.into(), rest.map(|(ddpos, _)| ddpos))
|
(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 {
|
fn lower_pat_slice(&mut self, pats: &[AstP<Pat>]) -> hir::PatKind {
|
||||||
let mut before = Vec::new();
|
let mut before = Vec::new();
|
||||||
let mut after = Vec::new();
|
let mut after = Vec::new();
|
||||||
@@ -2881,14 +2891,17 @@ impl<'a> LoweringContext<'a> {
|
|||||||
let mut prev_rest_span = None;
|
let mut prev_rest_span = None;
|
||||||
|
|
||||||
let mut iter = pats.iter();
|
let mut iter = pats.iter();
|
||||||
while let Some(pat) = iter.next() {
|
// Lower all the patterns until the first occurence of a sub-slice pattern.
|
||||||
// Interpret the first `((ref mut?)? x @)? ..` pattern as a subslice pattern.
|
for pat in iter.by_ref() {
|
||||||
match pat.kind {
|
match pat.kind {
|
||||||
|
// Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
|
||||||
PatKind::Rest => {
|
PatKind::Rest => {
|
||||||
prev_rest_span = Some(pat.span);
|
prev_rest_span = Some(pat.span);
|
||||||
slice = Some(self.pat_wild_with_node_id_of(pat));
|
slice = Some(self.pat_wild_with_node_id_of(pat));
|
||||||
break;
|
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() => {
|
PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
|
||||||
prev_rest_span = Some(sub.span);
|
prev_rest_span = Some(sub.span);
|
||||||
let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
|
let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
|
||||||
@@ -2896,14 +2909,13 @@ impl<'a> LoweringContext<'a> {
|
|||||||
slice = Some(self.pat_with_node_id_of(pat, node));
|
slice = Some(self.pat_with_node_id_of(pat, node));
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// It was not a subslice pattern so lower it normally.
|
// It was not a subslice pattern so lower it normally.
|
||||||
before.push(self.lower_pat(pat));
|
_ => 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.
|
// There was a previous subslice pattern; make sure we don't allow more.
|
||||||
let rest_span = match pat.kind {
|
let rest_span = match pat.kind {
|
||||||
PatKind::Rest => Some(pat.span),
|
PatKind::Rest => Some(pat.span),
|
||||||
@@ -2915,8 +2927,10 @@ impl<'a> LoweringContext<'a> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(rest_span) = rest_span {
|
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");
|
self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
|
||||||
} else {
|
} else {
|
||||||
|
// Lower the pattern normally.
|
||||||
after.push(self.lower_pat(pat));
|
after.push(self.lower_pat(pat));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1048,8 +1048,15 @@ pub enum PatKind {
|
|||||||
/// A range pattern (e.g., `1..=2` or `1..2`).
|
/// A range pattern (e.g., `1..=2` or `1..2`).
|
||||||
Range(P<Expr>, P<Expr>, RangeEnd),
|
Range(P<Expr>, P<Expr>, RangeEnd),
|
||||||
|
|
||||||
/// `[a, b, ..i, y, z]` is represented as:
|
/// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`.
|
||||||
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`.
|
///
|
||||||
|
/// 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>>),
|
Slice(HirVec<P<Pat>>, Option<P<Pat>>, HirVec<P<Pat>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5351,9 +5351,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
directly, not through a function pointer");
|
directly, not through a function pointer");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolves `typ` by a single level if `typ` is a type variable.
|
/// Resolves `typ` by a single level if `typ` is a type variable.
|
||||||
// If no resolution is possible, then an error is reported.
|
/// If no resolution is possible, then an error is reported.
|
||||||
// Numeric inference variables may be left unresolved.
|
/// Numeric inference variables may be left unresolved.
|
||||||
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
|
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
let ty = self.resolve_vars_with_obligations(ty);
|
let ty = self.resolve_vars_with_obligations(ty);
|
||||||
if !ty.is_ty_var() {
|
if !ty.is_ty_var() {
|
||||||
|
|||||||
@@ -1154,6 +1154,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
self.tcx.mk_ref(region, mt)
|
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(
|
fn check_pat_slice(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
@@ -1167,27 +1177,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let expected_ty = self.structurally_resolved_type(span, expected);
|
let expected_ty = self.structurally_resolved_type(span, expected);
|
||||||
let (inner_ty, slice_ty) = match expected_ty.kind {
|
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) => {
|
ty::Array(inner_ty, size) => {
|
||||||
let slice_ty = if let Some(size) = size.try_eval_usize(tcx, self.param_env) {
|
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;
|
let min_len = before.len() as u64 + after.len() as u64;
|
||||||
if slice.is_none() {
|
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 {
|
if min_len != size {
|
||||||
self.error_scrutinee_inconsistent_length(span, min_len, size)
|
self.error_scrutinee_inconsistent_length(span, min_len, size)
|
||||||
}
|
}
|
||||||
tcx.types.err
|
tcx.types.err
|
||||||
} else if let Some(rest) = size.checked_sub(min_len) {
|
} 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)
|
tcx.mk_array(inner_ty, rest)
|
||||||
} else {
|
} 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);
|
self.error_scrutinee_with_rest_inconsistent_length(span, min_len, size);
|
||||||
tcx.types.err
|
tcx.types.err
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
self.error_scrutinee_unfixed_length(span);
|
||||||
tcx.types.err
|
tcx.types.err
|
||||||
};
|
};
|
||||||
(inner_ty, slice_ty)
|
(inner_ty, slice_ty)
|
||||||
}
|
}
|
||||||
ty::Slice(inner_ty) => (inner_ty, expected_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() {
|
if !expected_ty.references_error() {
|
||||||
self.error_expected_array_or_slice(span, expected_ty);
|
self.error_expected_array_or_slice(span, expected_ty);
|
||||||
@@ -1196,12 +1218,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Type check all the patterns before `slice`.
|
||||||
for elt in before {
|
for elt in before {
|
||||||
self.check_pat(&elt, inner_ty, def_bm, discrim_span);
|
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 {
|
if let Some(slice) = slice {
|
||||||
self.check_pat(&slice, slice_ty, def_bm, discrim_span);
|
self.check_pat(&slice, slice_ty, def_bm, discrim_span);
|
||||||
}
|
}
|
||||||
|
// Type check the elements after `slice`, if present.
|
||||||
for elt in after {
|
for elt in after {
|
||||||
self.check_pat(&elt, inner_ty, def_bm, discrim_span);
|
self.check_pat(&elt, inner_ty, def_bm, discrim_span);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user