Skip to content

Commit

Permalink
Rollup merge of rust-lang#67439 - Centril:clean-hair-slice, r=matthew…
Browse files Browse the repository at this point in the history
…jasper

Cleanup `lower_pattern_unadjusted` & Improve slice pat typeck

Following up on rust-lang#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
  • Loading branch information
Centril authored Dec 22, 2019
2 parents 35ff2f1 + 7353ff2 commit 877dc9d
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 162 deletions.
9 changes: 5 additions & 4 deletions src/librustc_mir/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}

Expand Down
162 changes: 41 additions & 121 deletions src/librustc_mir/hair/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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)
}

Expand All @@ -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 {
Expand All @@ -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),
}
}

Expand Down
87 changes: 50 additions & 37 deletions src/librustc_typeck/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
};

Expand All @@ -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) {
Expand Down

0 comments on commit 877dc9d

Please sign in to comment.