-
Notifications
You must be signed in to change notification settings - Fork 13.4k
infer array len from pattern #70562
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
infer array len from pattern #70562
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the only caller? It feels like a bunch of stuff is muddled by the fact that it's a separate function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is. Could inline this in a followup pr if you want 🤷♂️ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't. I disagree with eddy here and I think the method provides separation of concerns from the rest of type checking of slice patterns (the structural parts of checking before/slice/after once you've extracted the |
||
(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,30 +1375,33 @@ 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 { | ||
self.check_pat(&slice, slice_ty, def_bm, ti); | ||
} | ||
// 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<u64> { | ||
) -> (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,28 @@ 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. | ||
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) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// run-pass | ||
lcnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// check-pass | ||
lcnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
Uh oh!
There was an error while loading. Please reload this page.