Skip to content

Commit

Permalink
Rollup merge of rust-lang#70932 - mark-i-m:de-abuse-err-2, r=Centril
Browse files Browse the repository at this point in the history
De-abuse TyKind::Error in pattern type checking

r? @eddyb

cc rust-lang#70866

In particular, I would appreciate extra scrutiny over the soundness of these changes.

Also, this will go a bit slowly because I'm going to use my other PR (rust-lang#70551) to check if I missed anything.
  • Loading branch information
Centril authored Apr 10, 2020
2 parents 74e93bb + f2e4709 commit 5cd1599
Showing 1 changed file with 22 additions and 16 deletions.
38 changes: 22 additions & 16 deletions src/librustc_typeck/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1353,23 +1353,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
def_bm: BindingMode,
ti: TopInfo<'tcx>,
) -> Ty<'tcx> {
let err = self.tcx.types.err;
let expected = self.structurally_resolved_type(span, expected);
let (element_ty, slice_ty, inferred) = match expected.kind {
let (element_ty, opt_slice_ty, inferred) = match expected.kind {
// An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
ty::Array(element_ty, len) => {
let min = before.len() as u64 + after.len() as u64;
let (slice_ty, expected) =
let (opt_slice_ty, expected) =
self.check_array_pat_len(span, element_ty, expected, slice, len, min);
(element_ty, slice_ty, expected)
// `opt_slice_ty.is_none()` => `slice.is_none()`.
// Note, though, that opt_slice_ty could be `Some(error_ty)`.
assert!(opt_slice_ty.is_some() || slice.is_none());
(element_ty, opt_slice_ty, expected)
}
ty::Slice(element_ty) => (element_ty, expected, expected),
ty::Slice(element_ty) => (element_ty, Some(expected), expected),
// The expected type must be an array or slice, but was neither, so error.
_ => {
if !expected.references_error() {
self.error_expected_array_or_slice(span, expected);
}
(err, err, err)
let err = self.tcx.types.err;
(err, Some(err), err)
}
};

Expand All @@ -1379,7 +1382,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
// Type check the `slice`, if present, against its expected type.
if let Some(slice) = slice {
self.check_pat(&slice, slice_ty, def_bm, ti);
self.check_pat(&slice, opt_slice_ty.unwrap(), def_bm, ti);
}
// Type check the elements after `slice`, if present.
for elt in after {
Expand All @@ -1390,9 +1393,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

/// Type check the length of an array pattern.
///
/// Returns both the type of the variable length pattern
/// (or `tcx.err` in case there is none),
/// and the potentially inferred array type.
/// Returns both the type of the variable length pattern (or `None`), and the potentially
/// inferred array type. We only return `None` for the slice type if `slice.is_none()`.
fn check_array_pat_len(
&self,
span: Span,
Expand All @@ -1401,20 +1403,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
slice: Option<&'tcx Pat<'tcx>>,
len: &ty::Const<'tcx>,
min_len: u64,
) -> (Ty<'tcx>, Ty<'tcx>) {
) -> (Option<Ty<'tcx>>, Ty<'tcx>) {
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);
if min_len == len {
return (None, arr_ty);
}

self.error_scrutinee_inconsistent_length(span, min_len, len);
} else if let Some(pat_len) = 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 (self.tcx.mk_array(element_ty, pat_len), arr_ty);
return (Some(self.tcx.mk_array(element_ty, pat_len)), arr_ty);
} else {
// ...however, in this case, there were no remaining elements.
// That is, the slice pattern requires more than the array type offers.
Expand All @@ -1425,14 +1429,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// which we can use to infer the length of the array.
let updated_arr_ty = self.tcx.mk_array(element_ty, min_len);
self.demand_eqtype(span, updated_arr_ty, arr_ty);
return (self.tcx.types.err, updated_arr_ty);
return (None, updated_arr_ty);
} else {
// We have a variable-length pattern and don't know the array length.
// This 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.tcx.types.err, arr_ty)

// If we get here, we must have emitted an error.
(Some(self.tcx.types.err), arr_ty)
}

fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) {
Expand Down

0 comments on commit 5cd1599

Please sign in to comment.