infer arr len from pattern
This commit is contained in:
parent
8926bb497d
commit
50ab77384e
7 changed files with 90 additions and 21 deletions
|
@ -1355,16 +1355,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
let err = self.tcx.types.err;
|
let err = self.tcx.types.err;
|
||||||
let expected = self.structurally_resolved_type(span, expected);
|
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];`.
|
// 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 min = before.len() as u64 + after.len() as u64;
|
||||||
let slice_ty = self
|
let (slice_ty, expected) =
|
||||||
.check_array_pat_len(span, slice, len, min)
|
self.check_array_pat_len(span, element_ty, expected, slice, len, min);
|
||||||
.map_or(err, |len| self.tcx.mk_array(inner_ty, len));
|
(element_ty, slice_ty, expected)
|
||||||
(inner_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.
|
// The expected type must be an array or slice, but was neither, so error.
|
||||||
_ => {
|
_ => {
|
||||||
if !expected.references_error() {
|
if !expected.references_error() {
|
||||||
|
@ -1376,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
// Type check all the patterns before `slice`.
|
// Type check all the patterns before `slice`.
|
||||||
for elt in before {
|
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.
|
// Type check the `slice`, if present, against its expected type.
|
||||||
if let Some(slice) = slice {
|
if let Some(slice) = slice {
|
||||||
|
@ -1384,22 +1383,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
// Type check the elements after `slice`, if present.
|
// Type check the elements after `slice`, if present.
|
||||||
for elt in after {
|
for elt in after {
|
||||||
self.check_pat(&elt, inner_ty, def_bm, ti);
|
self.check_pat(&elt, element_ty, def_bm, ti);
|
||||||
}
|
}
|
||||||
expected
|
expected
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type check the length of an array pattern.
|
/// Type check the length of an array pattern.
|
||||||
///
|
///
|
||||||
/// Return the length of the variable length pattern,
|
/// Returns both the type of the variable length pattern
|
||||||
/// if it exists and there are no errors.
|
/// (or `tcx.err` in case there is none),
|
||||||
|
/// and the potentially inferred array type.
|
||||||
fn check_array_pat_len(
|
fn check_array_pat_len(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
element_ty: Ty<'tcx>,
|
||||||
|
arr_ty: Ty<'tcx>,
|
||||||
slice: Option<&'tcx Pat<'tcx>>,
|
slice: Option<&'tcx Pat<'tcx>>,
|
||||||
len: &ty::Const<'tcx>,
|
len: &ty::Const<'tcx>,
|
||||||
min_len: u64,
|
min_len: u64,
|
||||||
) -> Option<u64> {
|
) -> (Ty<'tcx>, Ty<'tcx>) {
|
||||||
if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) {
|
if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) {
|
||||||
// Now we know the length...
|
// Now we know the length...
|
||||||
if slice.is_none() {
|
if slice.is_none() {
|
||||||
|
@ -1409,21 +1411,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
if min_len != len {
|
if min_len != len {
|
||||||
self.error_scrutinee_inconsistent_length(span, 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,
|
// The variable-length pattern was there,
|
||||||
// so it has an array type with the remaining elements left as its size...
|
// 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 {
|
} else {
|
||||||
// ...however, in this case, there were no remaining elements.
|
// ...however, in this case, there were no remaining elements.
|
||||||
// That is, the slice pattern requires more than the array type offers.
|
// That is, the slice pattern requires more than the array type offers.
|
||||||
self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len);
|
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 {
|
} else {
|
||||||
// No idea what the length is, which happens if we have e.g.,
|
// We have a variable-length pattern and don't know the array length.
|
||||||
// `let [a, b] = arr` where `arr: [T; N]` where `const N: usize`.
|
// 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.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) {
|
fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) {
|
||||||
|
|
27
src/test/ui/const-generics/infer_arg_from_pat.rs
Normal file
27
src/test/ui/const-generics/infer_arg_from_pat.rs
Normal file
|
@ -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<const N: usize> {
|
||||||
|
arr: [u8; N],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> A<N> {
|
||||||
|
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);
|
||||||
|
}
|
8
src/test/ui/const-generics/infer_arg_from_pat.stderr
Normal file
8
src/test/ui/const-generics/infer_arg_from_pat.stderr
Normal file
|
@ -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
|
||||||
|
|
13
src/test/ui/const-generics/infer_arr_len_from_pat.rs
Normal file
13
src/test/ui/const-generics/infer_arr_len_from_pat.rs
Normal file
|
@ -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<const N: usize>() -> [u8; N] {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let [_, _] = as_chunks();
|
||||||
|
}
|
8
src/test/ui/const-generics/infer_arr_len_from_pat.stderr
Normal file
8
src/test/ui/const-generics/infer_arr_len_from_pat.stderr
Normal file
|
@ -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
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
fn is_123<const N: usize>(x: [u32; N]) -> bool {
|
fn is_123<const N: usize>(x: [u32; N]) -> bool {
|
||||||
match x {
|
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
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,15 @@ LL | #![feature(const_generics)]
|
||||||
|
|
|
|
||||||
= note: `#[warn(incomplete_features)]` on by default
|
= 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
|
--> $DIR/E0730.rs:6:9
|
||||||
|
|
|
|
||||||
LL | [1, 2, 3] => true,
|
LL | [1, 2, 3] => true,
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^ expected `3usize`, found `N`
|
||||||
|
|
|
||||||
|
= note: expected array `[u32; 3]`
|
||||||
|
found array `[u32; _]`
|
||||||
|
|
||||||
error: aborting due to previous error
|
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`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue