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

Cleanup lower_pattern_unadjusted & Improve slice pat typeck #67439

Merged
merged 12 commits into from
Dec 22, 2019
Merged
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 `_`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line breaks on punctuation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, @SimonSapin introduced me to https://sembr.org/ -- I find it produces quite readable text.

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 @@ -612,13 +579,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 @@ -630,28 +598,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 @@ -671,11 +625,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 @@ -685,80 +635,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