Skip to content
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

Merged
merged 4 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is. Could inline this in a followup pr if you want 🤷‍♂️

Copy link
Contributor

Choose a reason for hiding this comment

The 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 slice_ty and inferred). At most, I think this method should be split in two, one for the slice.is_none() and one for the other bits.

(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
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);
}
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
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();
}
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
lcnr marked this conversation as resolved.
Show resolved Hide resolved
[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