Skip to content

Commit c1b29b3

Browse files
committed
Fix handling slices of empty types
1 parent edf6a2d commit c1b29b3

File tree

3 files changed

+82
-8
lines changed

3 files changed

+82
-8
lines changed

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

+21-8
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,7 @@ impl<'tcx> Constructor<'tcx> {
859859
}
860860

861861
/// Describes the set of all constructors for a type.
862+
#[derive(Debug)]
862863
pub(super) enum ConstructorSet {
863864
/// The type has a single constructor, e.g. `&T` or a struct.
864865
Single,
@@ -903,6 +904,7 @@ pub(super) enum ConstructorSet {
903904
/// - constructors in `present` and `missing` are split for the column; in other words, they are
904905
/// either fully included in or disjoint from each constructor in the column. This avoids
905906
/// non-trivial intersections like between `0..10` and `5..15`.
907+
#[derive(Debug)]
906908
struct SplitConstructorSet<'tcx> {
907909
present: SmallVec<[Constructor<'tcx>; 1]>,
908910
missing: Vec<Constructor<'tcx>>,
@@ -911,8 +913,8 @@ struct SplitConstructorSet<'tcx> {
911913
}
912914

913915
impl ConstructorSet {
916+
#[instrument(level = "debug", skip(cx), ret)]
914917
pub(super) fn for_ty<'p, 'tcx>(cx: &MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self {
915-
debug!("ConstructorSet::for_ty({:?})", ty);
916918
let make_range =
917919
|start, end| IntRange::from_range(cx.tcx, start, end, ty, RangeEnd::Included);
918920
// This determines the set of all possible constructors for the type `ty`. For numbers,
@@ -1036,6 +1038,7 @@ impl ConstructorSet {
10361038
/// This is the core logical operation of exhaustiveness checking. This analyzes a column a
10371039
/// constructors to 1/ determine which constructors of the type (if any) are missing; 2/ split
10381040
/// constructors to handle non-trivial intersections e.g. on ranges or slices.
1041+
#[instrument(level = "debug", skip(self, pcx, ctors), ret)]
10391042
fn split<'a, 'tcx>(
10401043
&self,
10411044
pcx: &PatCtxt<'_, '_, 'tcx>,
@@ -1111,7 +1114,7 @@ impl ConstructorSet {
11111114
}
11121115
&ConstructorSet::Slice(array_len) => {
11131116
let seen_slices = seen.map(|c| c.as_slice().unwrap());
1114-
let base_slice = Slice { kind: VarLen(0, 0), array_len };
1117+
let base_slice = Slice::new(array_len, VarLen(0, 0));
11151118
for (seen, splitted_slice) in base_slice.split(seen_slices) {
11161119
let ctor = Slice(splitted_slice);
11171120
match seen {
@@ -1121,12 +1124,22 @@ impl ConstructorSet {
11211124
}
11221125
}
11231126
ConstructorSet::SliceOfEmpty => {
1124-
// Behaves essentially like `Single`.
1125-
let slice = Slice(Slice::new(None, FixedLen(0)));
1126-
if seen.next().is_none() {
1127-
missing.push(slice);
1128-
} else {
1129-
present.push(slice);
1127+
// This one is tricky because even though there's only one possible value of this
1128+
// type (namely `[]`), slice patterns of all lengths are allowed, they're just
1129+
// unreachable if length != 0.
1130+
// We still gather the seen constructors in `present`, but the only slice that can
1131+
// go in `missing` is `[]`.
1132+
let seen_slices = seen.map(|c| c.as_slice().unwrap());
1133+
let base_slice = Slice::new(None, VarLen(0, 0));
1134+
for (seen, splitted_slice) in base_slice.split(seen_slices) {
1135+
let ctor = Slice(splitted_slice);
1136+
match seen {
1137+
Presence::Seen => present.push(ctor),
1138+
Presence::Unseen if splitted_slice.arity() == 0 => {
1139+
missing.push(Slice(Slice::new(None, FixedLen(0))))
1140+
}
1141+
Presence::Unseen => {}
1142+
}
11301143
}
11311144
}
11321145
ConstructorSet::Unlistable => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![feature(never_type)]
2+
#![feature(exhaustive_patterns)]
3+
#![deny(unreachable_patterns)]
4+
5+
fn main() {}
6+
7+
fn foo(nevers: &[!]) {
8+
match nevers {
9+
&[] => (),
10+
};
11+
12+
match nevers {
13+
&[] => (),
14+
&[_] => (), //~ ERROR unreachable pattern
15+
&[_, _, ..] => (), //~ ERROR unreachable pattern
16+
};
17+
18+
match nevers {
19+
//~^ ERROR non-exhaustive patterns: `&[]` not covered
20+
&[_] => (), //~ ERROR unreachable pattern
21+
};
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error: unreachable pattern
2+
--> $DIR/slice_of_empty.rs:14:9
3+
|
4+
LL | &[_] => (),
5+
| ^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/slice_of_empty.rs:3:9
9+
|
10+
LL | #![deny(unreachable_patterns)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error: unreachable pattern
14+
--> $DIR/slice_of_empty.rs:15:9
15+
|
16+
LL | &[_, _, ..] => (),
17+
| ^^^^^^^^^^^
18+
19+
error: unreachable pattern
20+
--> $DIR/slice_of_empty.rs:20:9
21+
|
22+
LL | &[_] => (),
23+
| ^^^^
24+
25+
error[E0004]: non-exhaustive patterns: `&[]` not covered
26+
--> $DIR/slice_of_empty.rs:18:11
27+
|
28+
LL | match nevers {
29+
| ^^^^^^ pattern `&[]` not covered
30+
|
31+
= note: the matched value is of type `&[!]`
32+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
33+
|
34+
LL | &[_] => (), &[] => todo!(),
35+
| ++++++++++++++++
36+
37+
error: aborting due to 4 previous errors
38+
39+
For more information about this error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)