Rollup merge of #67439 - Centril:clean-hair-slice, r=matthewjasper
Cleanup `lower_pattern_unadjusted` & Improve slice pat typeck Following up on https://github.com/rust-lang/rust/pull/67318, in this PR, the HAIR lowering of patterns (`lower_pattern_unadjusted`) is cleaned up with a focus on slice patterns (in particular, some dead code is removed). Moreover, `check_pat_slice` is refactored some more. r? @matthewjasper
This commit is contained in:
commit
877dc9daa8
3 changed files with 96 additions and 162 deletions
|
@ -620,10 +620,11 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
|
|||
enum SliceKind {
|
||||
/// Patterns of length `n` (`[x, y]`).
|
||||
FixedLen(u64),
|
||||
/// Patterns using the `..` notation (`[x, .., y]`). Captures any array constructor of `length
|
||||
/// >= i + j`. In the case where `array_len` is `Some(_)`, this indicates that we only care
|
||||
/// about the first `i` and the last `j` values of the array, and everything in between is a
|
||||
/// wildcard `_`.
|
||||
/// Patterns using the `..` notation (`[x, .., y]`).
|
||||
/// Captures any array constructor of `length >= i + j`.
|
||||
/// In the case where `array_len` is `Some(_)`,
|
||||
/// this indicates that we only care about the first `i` and the last `j` values of the array,
|
||||
/// and everything in between is a wildcard `_`.
|
||||
VarLen(u64, u64),
|
||||
}
|
||||
|
||||
|
|
|
@ -445,6 +445,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pat<'tcx> {
|
||||
let mut ty = self.tables.node_type(pat.hir_id);
|
||||
|
||||
if let ty::Error = ty.kind {
|
||||
// Avoid ICEs (e.g., #50577 and #50585).
|
||||
return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) };
|
||||
}
|
||||
|
||||
let kind = match pat.kind {
|
||||
hir::PatKind::Wild => PatKind::Wild,
|
||||
|
||||
|
@ -544,57 +549,19 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
hir::PatKind::Slice(ref prefix, ref slice, ref suffix) => {
|
||||
match ty.kind {
|
||||
ty::Ref(_, ty, _) =>
|
||||
PatKind::Deref {
|
||||
subpattern: Pat {
|
||||
ty,
|
||||
span: pat.span,
|
||||
kind: Box::new(self.slice_or_array_pattern(
|
||||
pat.span, ty, prefix, slice, suffix))
|
||||
},
|
||||
},
|
||||
ty::Slice(..) |
|
||||
ty::Array(..) =>
|
||||
self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix),
|
||||
ty::Error => { // Avoid ICE
|
||||
return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) };
|
||||
}
|
||||
_ =>
|
||||
span_bug!(
|
||||
pat.span,
|
||||
"unexpanded type for vector pattern: {:?}",
|
||||
ty),
|
||||
}
|
||||
self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix)
|
||||
}
|
||||
|
||||
hir::PatKind::Tuple(ref subpatterns, ddpos) => {
|
||||
match ty.kind {
|
||||
ty::Tuple(ref tys) => {
|
||||
let subpatterns =
|
||||
subpatterns.iter()
|
||||
.enumerate_and_adjust(tys.len(), ddpos)
|
||||
.map(|(i, subpattern)| FieldPat {
|
||||
field: Field::new(i),
|
||||
pattern: self.lower_pattern(subpattern)
|
||||
})
|
||||
.collect();
|
||||
|
||||
PatKind::Leaf { subpatterns }
|
||||
}
|
||||
ty::Error => { // Avoid ICE (#50577)
|
||||
return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) };
|
||||
}
|
||||
hir::PatKind::Tuple(ref pats, ddpos) => {
|
||||
let tys = match ty.kind {
|
||||
ty::Tuple(ref tys) => tys,
|
||||
_ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty),
|
||||
}
|
||||
};
|
||||
let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos);
|
||||
PatKind::Leaf { subpatterns }
|
||||
}
|
||||
|
||||
hir::PatKind::Binding(_, id, ident, ref sub) => {
|
||||
let var_ty = self.tables.node_type(pat.hir_id);
|
||||
if let ty::Error = var_ty.kind {
|
||||
// Avoid ICE
|
||||
return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) };
|
||||
};
|
||||
let bm = *self.tables.pat_binding_modes().get(pat.hir_id)
|
||||
.expect("missing binding mode");
|
||||
let (mutability, mode) = match bm {
|
||||
|
@ -609,13 +576,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
|
||||
// A ref x pattern is the same node used for x, and as such it has
|
||||
// x's type, which is &T, where we want T (the type being matched).
|
||||
let var_ty = ty;
|
||||
if let ty::BindByReference(_) = bm {
|
||||
if let ty::Ref(_, rty, _) = ty.kind {
|
||||
ty = rty;
|
||||
} else {
|
||||
bug!("`ref {}` has wrong type {}", ident, ty);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PatKind::Binding {
|
||||
mutability,
|
||||
|
@ -627,28 +595,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
hir::PatKind::TupleStruct(ref qpath, ref subpatterns, ddpos) => {
|
||||
hir::PatKind::TupleStruct(ref qpath, ref pats, ddpos) => {
|
||||
let res = self.tables.qpath_res(qpath, pat.hir_id);
|
||||
let adt_def = match ty.kind {
|
||||
ty::Adt(adt_def, _) => adt_def,
|
||||
ty::Error => { // Avoid ICE (#50585)
|
||||
return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) };
|
||||
}
|
||||
_ => span_bug!(pat.span,
|
||||
"tuple struct pattern not applied to an ADT {:?}",
|
||||
ty),
|
||||
_ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty),
|
||||
};
|
||||
let variant_def = adt_def.variant_of_res(res);
|
||||
|
||||
let subpatterns =
|
||||
subpatterns.iter()
|
||||
.enumerate_and_adjust(variant_def.fields.len(), ddpos)
|
||||
.map(|(i, field)| FieldPat {
|
||||
field: Field::new(i),
|
||||
pattern: self.lower_pattern(field),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos);
|
||||
self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
|
||||
}
|
||||
|
||||
|
@ -668,11 +622,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
|
||||
}
|
||||
|
||||
hir::PatKind::Or(ref pats) => {
|
||||
PatKind::Or {
|
||||
pats: pats.iter().map(|p| self.lower_pattern(p)).collect(),
|
||||
}
|
||||
}
|
||||
hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
|
||||
};
|
||||
|
||||
Pat {
|
||||
|
@ -682,80 +632,50 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_tuple_subpats(
|
||||
&mut self,
|
||||
pats: &'tcx [P<hir::Pat>],
|
||||
expected_len: usize,
|
||||
gap_pos: Option<usize>,
|
||||
) -> Vec<FieldPat<'tcx>> {
|
||||
pats.iter()
|
||||
.enumerate_and_adjust(expected_len, gap_pos)
|
||||
.map(|(i, subpattern)| FieldPat {
|
||||
field: Field::new(i),
|
||||
pattern: self.lower_pattern(subpattern)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn lower_patterns(&mut self, pats: &'tcx [P<hir::Pat>]) -> Vec<Pat<'tcx>> {
|
||||
pats.iter().map(|p| self.lower_pattern(p)).collect()
|
||||
}
|
||||
|
||||
fn lower_opt_pattern(&mut self, pat: &'tcx Option<P<hir::Pat>>) -> Option<Pat<'tcx>>
|
||||
{
|
||||
fn lower_opt_pattern(&mut self, pat: &'tcx Option<P<hir::Pat>>) -> Option<Pat<'tcx>> {
|
||||
pat.as_ref().map(|p| self.lower_pattern(p))
|
||||
}
|
||||
|
||||
fn flatten_nested_slice_patterns(
|
||||
&mut self,
|
||||
prefix: Vec<Pat<'tcx>>,
|
||||
slice: Option<Pat<'tcx>>,
|
||||
suffix: Vec<Pat<'tcx>>)
|
||||
-> (Vec<Pat<'tcx>>, Option<Pat<'tcx>>, Vec<Pat<'tcx>>)
|
||||
{
|
||||
let orig_slice = match slice {
|
||||
Some(orig_slice) => orig_slice,
|
||||
None => return (prefix, slice, suffix)
|
||||
};
|
||||
let orig_prefix = prefix;
|
||||
let orig_suffix = suffix;
|
||||
|
||||
// dance because of intentional borrow-checker stupidity.
|
||||
let kind = *orig_slice.kind;
|
||||
match kind {
|
||||
PatKind::Slice { prefix, slice, mut suffix } |
|
||||
PatKind::Array { prefix, slice, mut suffix } => {
|
||||
let mut orig_prefix = orig_prefix;
|
||||
|
||||
orig_prefix.extend(prefix);
|
||||
suffix.extend(orig_suffix);
|
||||
|
||||
(orig_prefix, slice, suffix)
|
||||
}
|
||||
_ => {
|
||||
(orig_prefix, Some(Pat {
|
||||
kind: box kind, ..orig_slice
|
||||
}), orig_suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn slice_or_array_pattern(
|
||||
&mut self,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>,
|
||||
prefix: &'tcx [P<hir::Pat>],
|
||||
slice: &'tcx Option<P<hir::Pat>>,
|
||||
suffix: &'tcx [P<hir::Pat>])
|
||||
-> PatKind<'tcx>
|
||||
{
|
||||
suffix: &'tcx [P<hir::Pat>],
|
||||
) -> PatKind<'tcx> {
|
||||
let prefix = self.lower_patterns(prefix);
|
||||
let slice = self.lower_opt_pattern(slice);
|
||||
let suffix = self.lower_patterns(suffix);
|
||||
let (prefix, slice, suffix) =
|
||||
self.flatten_nested_slice_patterns(prefix, slice, suffix);
|
||||
|
||||
match ty.kind {
|
||||
ty::Slice(..) => {
|
||||
// matching a slice or fixed-length array
|
||||
PatKind::Slice { prefix: prefix, slice: slice, suffix: suffix }
|
||||
}
|
||||
|
||||
// Matching a slice, `[T]`.
|
||||
ty::Slice(..) => PatKind::Slice { prefix, slice, suffix },
|
||||
// Fixed-length array, `[T; len]`.
|
||||
ty::Array(_, len) => {
|
||||
// fixed-length array
|
||||
let len = len.eval_usize(self.tcx, self.param_env);
|
||||
assert!(len >= prefix.len() as u64 + suffix.len() as u64);
|
||||
PatKind::Array { prefix: prefix, slice: slice, suffix: suffix }
|
||||
}
|
||||
|
||||
_ => {
|
||||
span_bug!(span, "bad slice pattern type {:?}", ty);
|
||||
PatKind::Array { prefix, slice, suffix }
|
||||
}
|
||||
_ => span_bug!(span, "bad slice pattern type {:?}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1174,47 +1174,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
def_bm: BindingMode,
|
||||
discrim_span: Option<Span>,
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let expected_ty = self.structurally_resolved_type(span, expected);
|
||||
let (inner_ty, slice_ty) = match expected_ty.kind {
|
||||
let err = self.tcx.types.err;
|
||||
let expected = self.structurally_resolved_type(span, expected);
|
||||
let (inner_ty, slice_ty, expected) = match expected.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::Array(inner_ty, len) => {
|
||||
let min = before.len() as u64 + after.len() as u64;
|
||||
let slice_ty = self.check_array_pat_len(span, slice, len, min)
|
||||
.map_or(err, |len| self.tcx.mk_array(inner_ty, len));
|
||||
(inner_ty, slice_ty, expected)
|
||||
}
|
||||
ty::Slice(inner_ty) => (inner_ty, expected_ty),
|
||||
ty::Slice(inner_ty) => (inner_ty, expected, expected),
|
||||
// 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);
|
||||
if !expected.references_error() {
|
||||
self.error_expected_array_or_slice(span, expected);
|
||||
}
|
||||
(tcx.types.err, tcx.types.err)
|
||||
(err, err, err)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1230,7 +1206,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
for elt in after {
|
||||
self.check_pat(&elt, inner_ty, def_bm, discrim_span);
|
||||
}
|
||||
expected_ty
|
||||
expected
|
||||
}
|
||||
|
||||
/// Type check the length of an array pattern.
|
||||
///
|
||||
/// Return the length of the variable length pattern,
|
||||
/// if it exists and there are no errors.
|
||||
fn check_array_pat_len(
|
||||
&self,
|
||||
span: Span,
|
||||
slice: Option<&'tcx Pat>,
|
||||
len: &ty::Const<'tcx>,
|
||||
min_len: u64,
|
||||
) -> Option<u64> {
|
||||
if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) {
|
||||
// Now we know the length...
|
||||
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 != len {
|
||||
self.error_scrutinee_inconsistent_length(span, min_len, len);
|
||||
}
|
||||
} else if let r @ Some(_) = len.checked_sub(min_len) {
|
||||
// The variable-length pattern was there,
|
||||
// so it has an array type with the remaining elements left as its size...
|
||||
return r;
|
||||
} 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, len);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) {
|
||||
|
|
Loading…
Add table
Reference in a new issue