about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2023-10-05 00:58:14 +0200
committerNadrieril <nadrieril+git@gmail.com>2023-10-05 00:58:14 +0200
commitc1b29b338dc37f96f0695e393743ce35508d3704 (patch)
tree4be3b6baeddff3695133a9271c973da92a9cff1f
parentedf6a2d3371218043e4858b81fe48c99535cabe7 (diff)
downloadrust-c1b29b338dc37f96f0695e393743ce35508d3704.tar.gz
rust-c1b29b338dc37f96f0695e393743ce35508d3704.zip
Fix handling slices of empty types
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs29
-rw-r--r--tests/ui/pattern/usefulness/slice_of_empty.rs22
-rw-r--r--tests/ui/pattern/usefulness/slice_of_empty.stderr39
3 files changed, 82 insertions, 8 deletions
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index dd00982f7f6..a7a000ba31c 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -859,6 +859,7 @@ impl<'tcx> Constructor<'tcx> {
 }
 
 /// Describes the set of all constructors for a type.
+#[derive(Debug)]
 pub(super) enum ConstructorSet {
     /// The type has a single constructor, e.g. `&T` or a struct.
     Single,
@@ -903,6 +904,7 @@ pub(super) enum ConstructorSet {
 /// - constructors in `present` and `missing` are split for the column; in other words, they are
 ///     either fully included in or disjoint from each constructor in the column. This avoids
 ///     non-trivial intersections like between `0..10` and `5..15`.
+#[derive(Debug)]
 struct SplitConstructorSet<'tcx> {
     present: SmallVec<[Constructor<'tcx>; 1]>,
     missing: Vec<Constructor<'tcx>>,
@@ -911,8 +913,8 @@ struct SplitConstructorSet<'tcx> {
 }
 
 impl ConstructorSet {
+    #[instrument(level = "debug", skip(cx), ret)]
     pub(super) fn for_ty<'p, 'tcx>(cx: &MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self {
-        debug!("ConstructorSet::for_ty({:?})", ty);
         let make_range =
             |start, end| IntRange::from_range(cx.tcx, start, end, ty, RangeEnd::Included);
         // This determines the set of all possible constructors for the type `ty`. For numbers,
@@ -1036,6 +1038,7 @@ impl ConstructorSet {
     /// This is the core logical operation of exhaustiveness checking. This analyzes a column a
     /// constructors to 1/ determine which constructors of the type (if any) are missing; 2/ split
     /// constructors to handle non-trivial intersections e.g. on ranges or slices.
+    #[instrument(level = "debug", skip(self, pcx, ctors), ret)]
     fn split<'a, 'tcx>(
         &self,
         pcx: &PatCtxt<'_, '_, 'tcx>,
@@ -1111,7 +1114,7 @@ impl ConstructorSet {
             }
             &ConstructorSet::Slice(array_len) => {
                 let seen_slices = seen.map(|c| c.as_slice().unwrap());
-                let base_slice = Slice { kind: VarLen(0, 0), array_len };
+                let base_slice = Slice::new(array_len, VarLen(0, 0));
                 for (seen, splitted_slice) in base_slice.split(seen_slices) {
                     let ctor = Slice(splitted_slice);
                     match seen {
@@ -1121,12 +1124,22 @@ impl ConstructorSet {
                 }
             }
             ConstructorSet::SliceOfEmpty => {
-                // Behaves essentially like `Single`.
-                let slice = Slice(Slice::new(None, FixedLen(0)));
-                if seen.next().is_none() {
-                    missing.push(slice);
-                } else {
-                    present.push(slice);
+                // This one is tricky because even though there's only one possible value of this
+                // type (namely `[]`), slice patterns of all lengths are allowed, they're just
+                // unreachable if length != 0.
+                // We still gather the seen constructors in `present`, but the only slice that can
+                // go in `missing` is `[]`.
+                let seen_slices = seen.map(|c| c.as_slice().unwrap());
+                let base_slice = Slice::new(None, VarLen(0, 0));
+                for (seen, splitted_slice) in base_slice.split(seen_slices) {
+                    let ctor = Slice(splitted_slice);
+                    match seen {
+                        Presence::Seen => present.push(ctor),
+                        Presence::Unseen if splitted_slice.arity() == 0 => {
+                            missing.push(Slice(Slice::new(None, FixedLen(0))))
+                        }
+                        Presence::Unseen => {}
+                    }
                 }
             }
             ConstructorSet::Unlistable => {
diff --git a/tests/ui/pattern/usefulness/slice_of_empty.rs b/tests/ui/pattern/usefulness/slice_of_empty.rs
new file mode 100644
index 00000000000..fe068871195
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice_of_empty.rs
@@ -0,0 +1,22 @@
+#![feature(never_type)]
+#![feature(exhaustive_patterns)]
+#![deny(unreachable_patterns)]
+
+fn main() {}
+
+fn foo(nevers: &[!]) {
+    match nevers {
+        &[] => (),
+    };
+
+    match nevers {
+        &[] => (),
+        &[_] => (),        //~ ERROR unreachable pattern
+        &[_, _, ..] => (), //~ ERROR unreachable pattern
+    };
+
+    match nevers {
+        //~^ ERROR non-exhaustive patterns: `&[]` not covered
+        &[_] => (), //~ ERROR unreachable pattern
+    };
+}
diff --git a/tests/ui/pattern/usefulness/slice_of_empty.stderr b/tests/ui/pattern/usefulness/slice_of_empty.stderr
new file mode 100644
index 00000000000..07bb6b3a67d
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice_of_empty.stderr
@@ -0,0 +1,39 @@
+error: unreachable pattern
+  --> $DIR/slice_of_empty.rs:14:9
+   |
+LL |         &[_] => (),
+   |         ^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice_of_empty.rs:3:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice_of_empty.rs:15:9
+   |
+LL |         &[_, _, ..] => (),
+   |         ^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice_of_empty.rs:20:9
+   |
+LL |         &[_] => (),
+   |         ^^^^
+
+error[E0004]: non-exhaustive patterns: `&[]` not covered
+  --> $DIR/slice_of_empty.rs:18:11
+   |
+LL |     match nevers {
+   |           ^^^^^^ pattern `&[]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |         &[_] => (), &[] => todo!(),
+   |                   ++++++++++++++++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0004`.