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 {
|
enum SliceKind {
|
||||||
/// Patterns of length `n` (`[x, y]`).
|
/// Patterns of length `n` (`[x, y]`).
|
||||||
FixedLen(u64),
|
FixedLen(u64),
|
||||||
/// Patterns using the `..` notation (`[x, .., y]`). Captures any array constructor of `length
|
/// Patterns using the `..` notation (`[x, .., y]`).
|
||||||
/// >= i + j`. In the case where `array_len` is `Some(_)`, this indicates that we only care
|
/// Captures any array constructor of `length >= i + j`.
|
||||||
/// about the first `i` and the last `j` values of the array, and everything in between is a
|
/// In the case where `array_len` is `Some(_)`,
|
||||||
/// wildcard `_`.
|
/// 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),
|
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> {
|
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pat<'tcx> {
|
||||||
let mut ty = self.tables.node_type(pat.hir_id);
|
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 {
|
let kind = match pat.kind {
|
||||||
hir::PatKind::Wild => PatKind::Wild,
|
hir::PatKind::Wild => PatKind::Wild,
|
||||||
|
|
||||||
|
@ -544,57 +549,19 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::PatKind::Slice(ref prefix, ref slice, ref suffix) => {
|
hir::PatKind::Slice(ref prefix, ref slice, ref suffix) => {
|
||||||
match ty.kind {
|
self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix)
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::PatKind::Tuple(ref subpatterns, ddpos) => {
|
hir::PatKind::Tuple(ref pats, ddpos) => {
|
||||||
match ty.kind {
|
let tys = match ty.kind {
|
||||||
ty::Tuple(ref tys) => {
|
ty::Tuple(ref tys) => 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) };
|
|
||||||
}
|
|
||||||
_ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty),
|
_ => 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) => {
|
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)
|
let bm = *self.tables.pat_binding_modes().get(pat.hir_id)
|
||||||
.expect("missing binding mode");
|
.expect("missing binding mode");
|
||||||
let (mutability, mode) = match bm {
|
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
|
// 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).
|
// 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::BindByReference(_) = bm {
|
||||||
if let ty::Ref(_, rty, _) = ty.kind {
|
if let ty::Ref(_, rty, _) = ty.kind {
|
||||||
ty = rty;
|
ty = rty;
|
||||||
} else {
|
} else {
|
||||||
bug!("`ref {}` has wrong type {}", ident, ty);
|
bug!("`ref {}` has wrong type {}", ident, ty);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
PatKind::Binding {
|
PatKind::Binding {
|
||||||
mutability,
|
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 res = self.tables.qpath_res(qpath, pat.hir_id);
|
||||||
let adt_def = match ty.kind {
|
let adt_def = match ty.kind {
|
||||||
ty::Adt(adt_def, _) => adt_def,
|
ty::Adt(adt_def, _) => adt_def,
|
||||||
ty::Error => { // Avoid ICE (#50585)
|
_ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty),
|
||||||
return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) };
|
|
||||||
}
|
|
||||||
_ => span_bug!(pat.span,
|
|
||||||
"tuple struct pattern not applied to an ADT {:?}",
|
|
||||||
ty),
|
|
||||||
};
|
};
|
||||||
let variant_def = adt_def.variant_of_res(res);
|
let variant_def = adt_def.variant_of_res(res);
|
||||||
|
let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos);
|
||||||
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();
|
|
||||||
|
|
||||||
self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
|
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)
|
self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns)
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::PatKind::Or(ref pats) => {
|
hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
|
||||||
PatKind::Or {
|
|
||||||
pats: pats.iter().map(|p| self.lower_pattern(p)).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Pat {
|
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>> {
|
fn lower_patterns(&mut self, pats: &'tcx [P<hir::Pat>]) -> Vec<Pat<'tcx>> {
|
||||||
pats.iter().map(|p| self.lower_pattern(p)).collect()
|
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))
|
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(
|
fn slice_or_array_pattern(
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
prefix: &'tcx [P<hir::Pat>],
|
prefix: &'tcx [P<hir::Pat>],
|
||||||
slice: &'tcx Option<P<hir::Pat>>,
|
slice: &'tcx Option<P<hir::Pat>>,
|
||||||
suffix: &'tcx [P<hir::Pat>])
|
suffix: &'tcx [P<hir::Pat>],
|
||||||
-> PatKind<'tcx>
|
) -> PatKind<'tcx> {
|
||||||
{
|
|
||||||
let prefix = self.lower_patterns(prefix);
|
let prefix = self.lower_patterns(prefix);
|
||||||
let slice = self.lower_opt_pattern(slice);
|
let slice = self.lower_opt_pattern(slice);
|
||||||
let suffix = self.lower_patterns(suffix);
|
let suffix = self.lower_patterns(suffix);
|
||||||
let (prefix, slice, suffix) =
|
|
||||||
self.flatten_nested_slice_patterns(prefix, slice, suffix);
|
|
||||||
|
|
||||||
match ty.kind {
|
match ty.kind {
|
||||||
ty::Slice(..) => {
|
// Matching a slice, `[T]`.
|
||||||
// matching a slice or fixed-length array
|
ty::Slice(..) => PatKind::Slice { prefix, slice, suffix },
|
||||||
PatKind::Slice { prefix: prefix, slice: slice, suffix: suffix }
|
// Fixed-length array, `[T; len]`.
|
||||||
}
|
|
||||||
|
|
||||||
ty::Array(_, len) => {
|
ty::Array(_, len) => {
|
||||||
// fixed-length array
|
|
||||||
let len = len.eval_usize(self.tcx, self.param_env);
|
let len = len.eval_usize(self.tcx, self.param_env);
|
||||||
assert!(len >= prefix.len() as u64 + suffix.len() as u64);
|
assert!(len >= prefix.len() as u64 + suffix.len() as u64);
|
||||||
PatKind::Array { prefix: prefix, slice: slice, suffix: suffix }
|
PatKind::Array { prefix, slice, suffix }
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
span_bug!(span, "bad slice pattern type {:?}", ty);
|
|
||||||
}
|
}
|
||||||
|
_ => span_bug!(span, "bad slice pattern type {:?}", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1174,47 +1174,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
def_bm: BindingMode,
|
def_bm: BindingMode,
|
||||||
discrim_span: Option<Span>,
|
discrim_span: Option<Span>,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
let tcx = self.tcx;
|
let err = self.tcx.types.err;
|
||||||
let expected_ty = self.structurally_resolved_type(span, expected);
|
let expected = self.structurally_resolved_type(span, expected);
|
||||||
let (inner_ty, slice_ty) = match expected_ty.kind {
|
let (inner_ty, slice_ty, expected) = match expected.kind {
|
||||||
// An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
|
// An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
|
||||||
ty::Array(inner_ty, size) => {
|
ty::Array(inner_ty, len) => {
|
||||||
let slice_ty = if let Some(size) = size.try_eval_usize(tcx, self.param_env) {
|
let min = before.len() as u64 + after.len() as u64;
|
||||||
// Now we know the length...
|
let slice_ty = self.check_array_pat_len(span, slice, len, min)
|
||||||
let min_len = before.len() as u64 + after.len() as u64;
|
.map_or(err, |len| self.tcx.mk_array(inner_ty, len));
|
||||||
if slice.is_none() {
|
(inner_ty, slice_ty, expected)
|
||||||
// ...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),
|
ty::Slice(inner_ty) => (inner_ty, expected, expected),
|
||||||
// The expected type must be an array or slice, but was neither, so error.
|
// The expected type must be an array or slice, but was neither, so error.
|
||||||
_ => {
|
_ => {
|
||||||
if !expected_ty.references_error() {
|
if !expected.references_error() {
|
||||||
self.error_expected_array_or_slice(span, expected_ty);
|
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 {
|
for elt in after {
|
||||||
self.check_pat(&elt, inner_ty, def_bm, discrim_span);
|
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) {
|
fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue