Skip to content

Commit

Permalink
Rollup merge of #70562 - lcnr:const-arr_len, r=Centril
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Centril authored Mar 31, 2020
2 parents 38cd294 + a3df1db commit 3ef70fe
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 23 deletions.
4 changes: 2 additions & 2 deletions src/librustc_error_codes/error_codes/E0730.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ Example of erroneous code:
fn is_123<const N: usize>(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
}
}
Expand Down
45 changes: 27 additions & 18 deletions src/librustc_typeck/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, inferred) = 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() {
Expand All @@ -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
inferred
}

/// 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() {
Expand All @@ -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) {
Expand Down
21 changes: 21 additions & 0 deletions src/test/ui/array-slice-vec/infer_array_len.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// see issue #70529
struct A;

impl From<A> for [u8; 2] {
fn from(a: A) -> Self {
[0; 2]
}
}

impl From<A> for [u8; 3] {
fn from(a: A) -> Self {
[0; 3]
}
}


fn main() {
let a = A;
let [_, _] = a.into();
//~^ ERROR type annotations needed
}
11 changes: 11 additions & 0 deletions src/test/ui/array-slice-vec/infer_array_len.stderr
Original file line number Diff line number Diff line change
@@ -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`.
11 changes: 11 additions & 0 deletions src/test/ui/array-slice-vec/match_arr_unknown_len.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash

fn is_123<const N: usize>(x: [u32; N]) -> bool {
match x {
[1, 2] => true, //~ ERROR mismatched types
_ => false
}
}

fn main() {}
20 changes: 20 additions & 0 deletions src/test/ui/array-slice-vec/match_arr_unknown_len.stderr
Original file line number Diff line number Diff line change
@@ -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`.
27 changes: 27 additions & 0 deletions src/test/ui/const-generics/infer_arg_from_pat.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions src/test/ui/const-generics/infer_arg_from_pat.stderr
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

13 changes: 13 additions & 0 deletions src/test/ui/const-generics/infer_arr_len_from_pat.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions src/test/ui/const-generics/infer_arr_len_from_pat.stderr
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

2 changes: 1 addition & 1 deletion src/test/ui/error-codes/E0730.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

fn is_123<const N: usize>(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
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/error-codes/E0730.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ LL | #![feature(const_generics)]
error[E0730]: cannot pattern-match on an array without a fixed length
--> $DIR/E0730.rs:6:9
|
LL | [1, 2, 3] => true,
| ^^^^^^^^^
LL | [1, 2, ..] => true,
| ^^^^^^^^^^

error: aborting due to previous error

Expand Down

0 comments on commit 3ef70fe

Please sign in to comment.