Skip to content

Commit 3ef70fe

Browse files
authored
Rollup merge of #70562 - lcnr:const-arr_len, r=Centril
infer array len from pattern closes #70529 This still errors in the following case ```rust #![feature(const_generics)] fn arr<const N: usize>() -> [u8; N] { todo!() } fn main() { match arr() { [5, ..] => (), //~^ ERROR cannot pattern-match on an array without a fixed length [_, _] => (), } } ``` Considering that this should be rare and is harder to implement I would merge this PR without *fixing* the above.
2 parents 38cd294 + a3df1db commit 3ef70fe

File tree

12 files changed

+151
-23
lines changed

12 files changed

+151
-23
lines changed

src/librustc_error_codes/error_codes/E0730.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ Example of erroneous code:
77
88
fn is_123<const N: usize>(x: [u32; N]) -> bool {
99
match x {
10-
[1, 2, 3] => true, // error: cannot pattern-match on an
11-
// array without a fixed length
10+
[1, 2, ..] => true, // error: cannot pattern-match on an
11+
// array without a fixed length
1212
_ => false
1313
}
1414
}

src/librustc_typeck/check/pat.rs

+27-18
Original file line numberDiff line numberDiff line change
@@ -1355,16 +1355,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13551355
) -> Ty<'tcx> {
13561356
let err = self.tcx.types.err;
13571357
let expected = self.structurally_resolved_type(span, expected);
1358-
let (inner_ty, slice_ty, expected) = match expected.kind {
1358+
let (element_ty, slice_ty, inferred) = match expected.kind {
13591359
// An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
1360-
ty::Array(inner_ty, len) => {
1360+
ty::Array(element_ty, len) => {
13611361
let min = before.len() as u64 + after.len() as u64;
1362-
let slice_ty = self
1363-
.check_array_pat_len(span, slice, len, min)
1364-
.map_or(err, |len| self.tcx.mk_array(inner_ty, len));
1365-
(inner_ty, slice_ty, expected)
1362+
let (slice_ty, expected) =
1363+
self.check_array_pat_len(span, element_ty, expected, slice, len, min);
1364+
(element_ty, slice_ty, expected)
13661365
}
1367-
ty::Slice(inner_ty) => (inner_ty, expected, expected),
1366+
ty::Slice(element_ty) => (element_ty, expected, expected),
13681367
// The expected type must be an array or slice, but was neither, so error.
13691368
_ => {
13701369
if !expected.references_error() {
@@ -1376,30 +1375,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13761375

13771376
// Type check all the patterns before `slice`.
13781377
for elt in before {
1379-
self.check_pat(&elt, inner_ty, def_bm, ti);
1378+
self.check_pat(&elt, element_ty, def_bm, ti);
13801379
}
13811380
// Type check the `slice`, if present, against its expected type.
13821381
if let Some(slice) = slice {
13831382
self.check_pat(&slice, slice_ty, def_bm, ti);
13841383
}
13851384
// Type check the elements after `slice`, if present.
13861385
for elt in after {
1387-
self.check_pat(&elt, inner_ty, def_bm, ti);
1386+
self.check_pat(&elt, element_ty, def_bm, ti);
13881387
}
1389-
expected
1388+
inferred
13901389
}
13911390

13921391
/// Type check the length of an array pattern.
13931392
///
1394-
/// Return the length of the variable length pattern,
1395-
/// if it exists and there are no errors.
1393+
/// Returns both the type of the variable length pattern
1394+
/// (or `tcx.err` in case there is none),
1395+
/// and the potentially inferred array type.
13961396
fn check_array_pat_len(
13971397
&self,
13981398
span: Span,
1399+
element_ty: Ty<'tcx>,
1400+
arr_ty: Ty<'tcx>,
13991401
slice: Option<&'tcx Pat<'tcx>>,
14001402
len: &ty::Const<'tcx>,
14011403
min_len: u64,
1402-
) -> Option<u64> {
1404+
) -> (Ty<'tcx>, Ty<'tcx>) {
14031405
if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) {
14041406
// Now we know the length...
14051407
if slice.is_none() {
@@ -1409,21 +1411,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14091411
if min_len != len {
14101412
self.error_scrutinee_inconsistent_length(span, min_len, len);
14111413
}
1412-
} else if let r @ Some(_) = len.checked_sub(min_len) {
1414+
} else if let Some(pat_len) = len.checked_sub(min_len) {
14131415
// The variable-length pattern was there,
14141416
// so it has an array type with the remaining elements left as its size...
1415-
return r;
1417+
return (self.tcx.mk_array(element_ty, pat_len), arr_ty);
14161418
} else {
14171419
// ...however, in this case, there were no remaining elements.
14181420
// That is, the slice pattern requires more than the array type offers.
14191421
self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len);
14201422
}
1423+
} else if slice.is_none() {
1424+
// We have a pattern with a fixed length,
1425+
// which we can use to infer the length of the array.
1426+
let updated_arr_ty = self.tcx.mk_array(element_ty, min_len);
1427+
self.demand_eqtype(span, updated_arr_ty, arr_ty);
1428+
return (self.tcx.types.err, updated_arr_ty);
14211429
} else {
1422-
// No idea what the length is, which happens if we have e.g.,
1423-
// `let [a, b] = arr` where `arr: [T; N]` where `const N: usize`.
1430+
// We have a variable-length pattern and don't know the array length.
1431+
// This happens if we have e.g.,
1432+
// `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`.
14241433
self.error_scrutinee_unfixed_length(span);
14251434
}
1426-
None
1435+
(self.tcx.types.err, arr_ty)
14271436
}
14281437

14291438
fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// see issue #70529
2+
struct A;
3+
4+
impl From<A> for [u8; 2] {
5+
fn from(a: A) -> Self {
6+
[0; 2]
7+
}
8+
}
9+
10+
impl From<A> for [u8; 3] {
11+
fn from(a: A) -> Self {
12+
[0; 3]
13+
}
14+
}
15+
16+
17+
fn main() {
18+
let a = A;
19+
let [_, _] = a.into();
20+
//~^ ERROR type annotations needed
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0282]: type annotations needed
2+
--> $DIR/infer_array_len.rs:19:9
3+
|
4+
LL | let [_, _] = a.into();
5+
| ^^^^^^ consider giving this pattern a type
6+
|
7+
= note: type must be known at this point
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0282`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(const_generics)]
2+
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
3+
4+
fn is_123<const N: usize>(x: [u32; N]) -> bool {
5+
match x {
6+
[1, 2] => true, //~ ERROR mismatched types
7+
_ => false
8+
}
9+
}
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
2+
--> $DIR/match_arr_unknown_len.rs:1:12
3+
|
4+
LL | #![feature(const_generics)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
9+
error[E0308]: mismatched types
10+
--> $DIR/match_arr_unknown_len.rs:6:9
11+
|
12+
LL | [1, 2] => true,
13+
| ^^^^^^ expected `2usize`, found `N`
14+
|
15+
= note: expected array `[u32; 2]`
16+
found array `[u32; _]`
17+
18+
error: aborting due to previous error
19+
20+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// run-pass
2+
//
3+
// see issue #70529
4+
#![feature(const_generics)]
5+
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
6+
7+
struct A<const N: usize> {
8+
arr: [u8; N],
9+
}
10+
11+
impl<const N: usize> A<N> {
12+
fn new() -> Self {
13+
A {
14+
arr: [0; N],
15+
}
16+
}
17+
18+
fn value(&self) -> usize {
19+
N
20+
}
21+
}
22+
23+
fn main() {
24+
let a = A::new();
25+
let [_, _] = a.arr;
26+
assert_eq!(a.value(), 2);
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
2+
--> $DIR/infer_arg_from_pat.rs:4:12
3+
|
4+
LL | #![feature(const_generics)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// check-pass
2+
//
3+
// see issue #70529
4+
#![feature(const_generics)]
5+
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
6+
7+
fn as_chunks<const N: usize>() -> [u8; N] {
8+
loop {}
9+
}
10+
11+
fn main() {
12+
let [_, _] = as_chunks();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
2+
--> $DIR/infer_arr_len_from_pat.rs:4:12
3+
|
4+
LL | #![feature(const_generics)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+

src/test/ui/error-codes/E0730.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
fn is_123<const N: usize>(x: [u32; N]) -> bool {
55
match x {
6-
[1, 2, 3] => true, //~ ERROR cannot pattern-match on an array without a fixed length
6+
[1, 2, ..] => true, //~ ERROR cannot pattern-match on an array without a fixed length
77
_ => false
88
}
99
}

src/test/ui/error-codes/E0730.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ LL | #![feature(const_generics)]
99
error[E0730]: cannot pattern-match on an array without a fixed length
1010
--> $DIR/E0730.rs:6:9
1111
|
12-
LL | [1, 2, 3] => true,
13-
| ^^^^^^^^^
12+
LL | [1, 2, ..] => true,
13+
| ^^^^^^^^^^
1414

1515
error: aborting due to previous error
1616

0 commit comments

Comments
 (0)