diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index fc91142dd7d..b3e8569f372 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -1355,16 +1355,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let err = self.tcx.types.err; let expected = self.structurally_resolved_type(span, expected); - let (inner_ty, slice_ty, expected) = match expected.kind { + let (element_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, len) => { + ty::Array(element_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) + let (slice_ty, expected) = + self.check_array_pat_len(span, element_ty, expected, slice, len, min); + (element_ty, slice_ty, expected) } - ty::Slice(inner_ty) => (inner_ty, expected, expected), + ty::Slice(element_ty) => (element_ty, expected, expected), // The expected type must be an array or slice, but was neither, so error. _ => { if !expected.references_error() { @@ -1376,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type check all the patterns before `slice`. for elt in before { - self.check_pat(&elt, inner_ty, def_bm, ti); + self.check_pat(&elt, element_ty, def_bm, ti); } // Type check the `slice`, if present, against its expected type. if let Some(slice) = slice { @@ -1384,22 +1383,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Type check the elements after `slice`, if present. for elt in after { - self.check_pat(&elt, inner_ty, def_bm, ti); + self.check_pat(&elt, element_ty, def_bm, ti); } 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. + /// Returns both the type of the variable length pattern + /// (or `tcx.err` in case there is none), + /// and the potentially inferred array type. fn check_array_pat_len( &self, span: Span, + element_ty: Ty<'tcx>, + arr_ty: Ty<'tcx>, slice: Option<&'tcx Pat<'tcx>>, len: &ty::Const<'tcx>, min_len: u64, - ) -> 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() { @@ -1409,21 +1411,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if min_len != len { self.error_scrutinee_inconsistent_length(span, min_len, len); } - } else if let r @ Some(_) = len.checked_sub(min_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 r; + return (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. self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len); } + } else if slice.is_none() { + // We have a pattern with a fixed length, + // which we can use to infer the length of the array. + // 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); } 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`. + // 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); } - None + (self.tcx.types.err, arr_ty) } fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { diff --git a/src/test/ui/const-generics/infer_arg_from_pat.rs b/src/test/ui/const-generics/infer_arg_from_pat.rs new file mode 100644 index 00000000000..a4e3d3dee4a --- /dev/null +++ b/src/test/ui/const-generics/infer_arg_from_pat.rs @@ -0,0 +1,27 @@ +// run-pass +// +// see issue #70529 +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +struct A { + arr: [u8; N], +} + +impl A { + fn new() -> Self { + A { + arr: [0; N], + } + } + + fn value(&self) -> usize { + N + } +} + +fn main() { + let a = A::new(); + let [_, _] = a.arr; + assert_eq!(a.value(), 2); +} diff --git a/src/test/ui/const-generics/infer_arg_from_pat.stderr b/src/test/ui/const-generics/infer_arg_from_pat.stderr new file mode 100644 index 00000000000..ad6bf3e235a --- /dev/null +++ b/src/test/ui/const-generics/infer_arg_from_pat.stderr @@ -0,0 +1,8 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/infer_arg_from_pat.rs:4:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.rs b/src/test/ui/const-generics/infer_arr_len_from_pat.rs new file mode 100644 index 00000000000..70633bbb141 --- /dev/null +++ b/src/test/ui/const-generics/infer_arr_len_from_pat.rs @@ -0,0 +1,13 @@ +// check-pass +// +// see issue #70529 +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn as_chunks() -> [u8; N] { + loop {} +} + +fn main() { + let [_, _] = as_chunks(); +} diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.stderr b/src/test/ui/const-generics/infer_arr_len_from_pat.stderr new file mode 100644 index 00000000000..6f5b601e14c --- /dev/null +++ b/src/test/ui/const-generics/infer_arr_len_from_pat.stderr @@ -0,0 +1,8 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/infer_arr_len_from_pat.rs:4:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + diff --git a/src/test/ui/error-codes/E0730.rs b/src/test/ui/error-codes/E0730.rs index e5048d6e6e3..45fc7e13d17 100644 --- a/src/test/ui/error-codes/E0730.rs +++ b/src/test/ui/error-codes/E0730.rs @@ -3,7 +3,7 @@ fn is_123(x: [u32; N]) -> bool { match x { - [1, 2, 3] => true, //~ ERROR cannot pattern-match on an array without a fixed length + [1, 2, 3] => true, //~ ERROR mismatched types _ => false } } diff --git a/src/test/ui/error-codes/E0730.stderr b/src/test/ui/error-codes/E0730.stderr index 9309ee99064..834a3e96870 100644 --- a/src/test/ui/error-codes/E0730.stderr +++ b/src/test/ui/error-codes/E0730.stderr @@ -6,12 +6,15 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default -error[E0730]: cannot pattern-match on an array without a fixed length +error[E0308]: mismatched types --> $DIR/E0730.rs:6:9 | LL | [1, 2, 3] => true, - | ^^^^^^^^^ + | ^^^^^^^^^ expected `3usize`, found `N` + | + = note: expected array `[u32; 3]` + found array `[u32; _]` error: aborting due to previous error -For more information about this error, try `rustc --explain E0730`. +For more information about this error, try `rustc --explain E0308`.