From 50ab77384e8f990d64c7b7fd478b19da724d6e07 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 30 Mar 2020 18:13:14 +0200 Subject: [PATCH 1/4] infer arr len from pattern --- src/librustc_typeck/check/pat.rs | 44 ++++++++++++------- .../ui/const-generics/infer_arg_from_pat.rs | 27 ++++++++++++ .../const-generics/infer_arg_from_pat.stderr | 8 ++++ .../const-generics/infer_arr_len_from_pat.rs | 13 ++++++ .../infer_arr_len_from_pat.stderr | 8 ++++ src/test/ui/error-codes/E0730.rs | 2 +- src/test/ui/error-codes/E0730.stderr | 9 ++-- 7 files changed, 90 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/const-generics/infer_arg_from_pat.rs create mode 100644 src/test/ui/const-generics/infer_arg_from_pat.stderr create mode 100644 src/test/ui/const-generics/infer_arr_len_from_pat.rs create mode 100644 src/test/ui/const-generics/infer_arr_len_from_pat.stderr diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index fc91142dd7da3..b3e8569f37293 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 0000000000000..a4e3d3dee4a82 --- /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 0000000000000..ad6bf3e235aeb --- /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 0000000000000..70633bbb141d4 --- /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 0000000000000..6f5b601e14ca1 --- /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 e5048d6e6e320..45fc7e13d1782 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 9309ee99064c9..834a3e9687059 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`. From 7f12561135e48fce7b23d58fa6ca4970d74523e3 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 30 Mar 2020 19:09:59 +0200 Subject: [PATCH 2/4] dedup docs Co-Authored-By: Mazdak Farrokhzad --- src/librustc_typeck/check/pat.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index b3e8569f37293..9dbf0489470f7 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -1423,7 +1423,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } 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); From 40c5eefdcde3a8696881643faeb7e2619ea0322f Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 30 Mar 2020 19:13:20 +0200 Subject: [PATCH 3/4] add test for array len inference --- .../ui/array-slice-vec/infer_array_len.rs | 21 +++++++++++++++++++ .../ui/array-slice-vec/infer_array_len.stderr | 11 ++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/test/ui/array-slice-vec/infer_array_len.rs create mode 100644 src/test/ui/array-slice-vec/infer_array_len.stderr diff --git a/src/test/ui/array-slice-vec/infer_array_len.rs b/src/test/ui/array-slice-vec/infer_array_len.rs new file mode 100644 index 0000000000000..22fe7cb883888 --- /dev/null +++ b/src/test/ui/array-slice-vec/infer_array_len.rs @@ -0,0 +1,21 @@ +// see issue #70529 +struct A; + +impl From for [u8; 2] { + fn from(a: A) -> Self { + [0; 2] + } +} + +impl From for [u8; 3] { + fn from(a: A) -> Self { + [0; 3] + } +} + + +fn main() { + let a = A; + let [_, _] = a.into(); + //~^ ERROR type annotations needed +} diff --git a/src/test/ui/array-slice-vec/infer_array_len.stderr b/src/test/ui/array-slice-vec/infer_array_len.stderr new file mode 100644 index 0000000000000..6eed4ce4f0c01 --- /dev/null +++ b/src/test/ui/array-slice-vec/infer_array_len.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/infer_array_len.rs:19:9 + | +LL | let [_, _] = a.into(); + | ^^^^^^ consider giving this pattern a type + | + = note: type must be known at this point + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. From a3df1db8ee40f8c5dc520a5d0a37adc5a70a15be Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Mon, 30 Mar 2020 19:34:16 +0200 Subject: [PATCH 4/4] update tests, improve variable names --- src/librustc_error_codes/error_codes/E0730.md | 4 ++-- src/librustc_typeck/check/pat.rs | 4 ++-- .../array-slice-vec/match_arr_unknown_len.rs | 11 ++++++++++ .../match_arr_unknown_len.stderr | 20 +++++++++++++++++++ src/test/ui/error-codes/E0730.rs | 2 +- src/test/ui/error-codes/E0730.stderr | 11 ++++------ 6 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/array-slice-vec/match_arr_unknown_len.rs create mode 100644 src/test/ui/array-slice-vec/match_arr_unknown_len.stderr diff --git a/src/librustc_error_codes/error_codes/E0730.md b/src/librustc_error_codes/error_codes/E0730.md index bf1f72be32589..c2a71ca5669a1 100644 --- a/src/librustc_error_codes/error_codes/E0730.md +++ b/src/librustc_error_codes/error_codes/E0730.md @@ -7,8 +7,8 @@ Example of erroneous code: 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, ..] => true, // error: cannot pattern-match on an + // array without a fixed length _ => false } } diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index 9dbf0489470f7..b3cace8298a92 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -1355,7 +1355,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let err = self.tcx.types.err; let expected = self.structurally_resolved_type(span, expected); - let (element_ty, slice_ty, expected) = match expected.kind { + let (element_ty, 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; @@ -1385,7 +1385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for elt in after { self.check_pat(&elt, element_ty, def_bm, ti); } - expected + inferred } /// Type check the length of an array pattern. diff --git a/src/test/ui/array-slice-vec/match_arr_unknown_len.rs b/src/test/ui/array-slice-vec/match_arr_unknown_len.rs new file mode 100644 index 0000000000000..7f3da75ddcbe8 --- /dev/null +++ b/src/test/ui/array-slice-vec/match_arr_unknown_len.rs @@ -0,0 +1,11 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn is_123(x: [u32; N]) -> bool { + match x { + [1, 2] => true, //~ ERROR mismatched types + _ => false + } +} + +fn main() {} diff --git a/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr b/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr new file mode 100644 index 0000000000000..9edb139028b72 --- /dev/null +++ b/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr @@ -0,0 +1,20 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/match_arr_unknown_len.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/match_arr_unknown_len.rs:6:9 + | +LL | [1, 2] => true, + | ^^^^^^ expected `2usize`, found `N` + | + = note: expected array `[u32; 2]` + found array `[u32; _]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/error-codes/E0730.rs b/src/test/ui/error-codes/E0730.rs index 45fc7e13d1782..66a6e1c817a37 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 mismatched types + [1, 2, ..] => true, //~ ERROR cannot pattern-match on an array without a fixed length _ => false } } diff --git a/src/test/ui/error-codes/E0730.stderr b/src/test/ui/error-codes/E0730.stderr index 834a3e9687059..fb53ae31c0b42 100644 --- a/src/test/ui/error-codes/E0730.stderr +++ b/src/test/ui/error-codes/E0730.stderr @@ -6,15 +6,12 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default -error[E0308]: mismatched types +error[E0730]: cannot pattern-match on an array without a fixed length --> $DIR/E0730.rs:6:9 | -LL | [1, 2, 3] => true, - | ^^^^^^^^^ expected `3usize`, found `N` - | - = note: expected array `[u32; 3]` - found array `[u32; _]` +LL | [1, 2, ..] => true, + | ^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0730`.