diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index c372032850695..f267be812c3d2 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -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), } diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 7f15b3de5efc0..9d0a67151fb60 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -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], + expected_len: usize, + gap_pos: Option, + ) -> Vec> { + 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]) -> Vec> { pats.iter().map(|p| self.lower_pattern(p)).collect() } - fn lower_opt_pattern(&mut self, pat: &'tcx Option>) -> Option> - { + fn lower_opt_pattern(&mut self, pat: &'tcx Option>) -> Option> { pat.as_ref().map(|p| self.lower_pattern(p)) } - fn flatten_nested_slice_patterns( - &mut self, - prefix: Vec>, - slice: Option>, - suffix: Vec>) - -> (Vec>, Option>, Vec>) - { - 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], slice: &'tcx Option>, - suffix: &'tcx [P]) - -> PatKind<'tcx> - { + suffix: &'tcx [P], + ) -> 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), } } diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index 11f744e97614c..ec6439c6d7729 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -1174,47 +1174,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_bm: BindingMode, discrim_span: Option, ) -> 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 { + 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) {