about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2020-11-12 17:28:55 +0000
committerNadrieril <nadrieril+git@gmail.com>2020-11-12 17:41:36 +0000
commitb025813f03033380f8761c1b0183fbe33884d3ec (patch)
tree080f7fe026c993821991d4bb530f50a72e1be224
parent257ed899c86acf6b4ee0f6dc9d6cfc9eb98e35a5 (diff)
downloadrust-b025813f03033380f8761c1b0183fbe33884d3ec.tar.gz
rust-b025813f03033380f8761c1b0183fbe33884d3ec.zip
Handle empty matches cleanly
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/_match.rs36
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs18
-rw-r--r--src/test/ui/pattern/usefulness/match-empty.rs10
-rw-r--r--src/test/ui/pattern/usefulness/match-empty.stderr69
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs2
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr14
6 files changed, 78 insertions, 71 deletions
diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
index 5e7e81eba62..f3fa0ec5fb7 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
@@ -1392,13 +1392,12 @@ impl<'tcx> Usefulness<'tcx> {
         pcx: PatCtxt<'_, 'p, 'tcx>,
         ctor: &Constructor<'tcx>,
         ctor_wild_subpatterns: &Fields<'p, 'tcx>,
-        is_top_level: bool,
     ) -> Self {
         match self {
             UsefulWithWitness(witnesses) => {
                 let new_witnesses = if ctor.is_wildcard() {
                     let missing_ctors = MissingConstructors::new(pcx);
-                    let new_patterns = missing_ctors.report_patterns(pcx, is_top_level);
+                    let new_patterns = missing_ctors.report_patterns(pcx);
                     witnesses
                         .into_iter()
                         .flat_map(|witness| {
@@ -1454,6 +1453,9 @@ struct PatCtxt<'a, 'p, 'tcx> {
     ty: Ty<'tcx>,
     /// Span of the current pattern under investigation.
     span: Span,
+    /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
+    /// subpattern.
+    is_top_level: bool,
 }
 
 /// A witness of non-exhaustiveness for error reporting, represented
@@ -1585,11 +1587,12 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
             let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
 
             // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
-            // as though it had an "unknown" constructor to avoid exposing its emptyness. Note that
-            // an empty match will still be considered exhaustive because that case is handled
-            // separately in `check_match`.
-            let is_secretly_empty =
-                def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns;
+            // as though it had an "unknown" constructor to avoid exposing its emptyness. The
+            // exception is if the pattern is at the top level, because we want empty matches to be
+            // considered exhaustive.
+            let is_secretly_empty = def.variants.is_empty()
+                && !cx.tcx.features().exhaustive_patterns
+                && !pcx.is_top_level;
 
             if is_secretly_empty || is_declared_nonexhaustive {
                 vec![NonExhaustive]
@@ -1635,6 +1638,13 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec<Constructor<'tc
             let max = size.truncate(u128::MAX);
             vec![make_range(0, max)]
         }
+        // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot
+        // expose its emptyness. The exception is if the pattern is at the top level, because we
+        // want empty matches to be considered exhaustive.
+        ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => {
+            vec![NonExhaustive]
+        }
+        ty::Never => vec![],
         _ if cx.is_uninhabited(pcx.ty) => vec![],
         ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single],
         // This type is one for which we cannot list constructors, like `str` or `f64`.
@@ -2012,11 +2022,7 @@ impl<'tcx> MissingConstructors<'tcx> {
 
     /// List the patterns corresponding to the missing constructors. In some cases, instead of
     /// listing all constructors of a given type, we prefer to simply report a wildcard.
-    fn report_patterns<'p>(
-        &self,
-        pcx: PatCtxt<'_, 'p, 'tcx>,
-        is_top_level: bool,
-    ) -> SmallVec<[Pat<'tcx>; 1]> {
+    fn report_patterns<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Pat<'tcx>; 1]> {
         // There are 2 ways we can report a witness here.
         // Commonly, we can report all the "free"
         // constructors as witnesses, e.g., if we have:
@@ -2044,7 +2050,7 @@ impl<'tcx> MissingConstructors<'tcx> {
         // `used_ctors` is empty.
         // The exception is: if we are at the top-level, for example in an empty match, we
         // sometimes prefer reporting the list of constructors instead of just `_`.
-        let report_when_all_missing = is_top_level && !IntRange::is_integral(pcx.ty);
+        let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty);
         if self.used_ctors.is_empty() && !report_when_all_missing {
             // All constructors are unused. Report only a wildcard
             // rather than each individual constructor.
@@ -2200,7 +2206,7 @@ crate fn is_useful<'p, 'tcx>(
 
     // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
     let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty);
-    let pcx = PatCtxt { cx, matrix, ty, span: v.head().span };
+    let pcx = PatCtxt { cx, matrix, ty, span: v.head().span, is_top_level };
 
     debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head());
 
@@ -2215,7 +2221,7 @@ crate fn is_useful<'p, 'tcx>(
             let v = v.pop_head_constructor(&ctor_wild_subpatterns);
             let usefulness =
                 is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
-            usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns, is_top_level)
+            usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns)
         })
         .find(|result| result.is_useful())
         .unwrap_or(NotUseful);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 205ad850c0c..c6fd7bcbf90 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -439,24 +439,6 @@ fn check_exhaustive<'p, 'tcx>(
     hir_id: HirId,
     is_empty_match: bool,
 ) {
-    // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
-    // `is_useful` to exhaustively match uninhabited types, so we manually check here.
-    if is_empty_match && !cx.tcx.features().exhaustive_patterns {
-        let scrutinee_is_visibly_uninhabited = match scrut_ty.kind() {
-            ty::Never => true,
-            ty::Adt(def, _) => {
-                def.is_enum()
-                    && def.variants.is_empty()
-                    && !cx.is_foreign_non_exhaustive_enum(scrut_ty)
-            }
-            _ => false,
-        };
-        if scrutinee_is_visibly_uninhabited {
-            // If the type *is* uninhabited, an empty match is vacuously exhaustive.
-            return;
-        }
-    }
-
     let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
         Ok(_) => return,
         Err(err) => err,
diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs
index 609660a88f9..835df1f551b 100644
--- a/src/test/ui/pattern/usefulness/match-empty.rs
+++ b/src/test/ui/pattern/usefulness/match-empty.rs
@@ -44,22 +44,20 @@ macro_rules! match_false {
 fn foo(x: Foo) {
     match x {} // ok
     match x {
-        _ => {}, // Not detected as unreachable, see #55123.
+        _ => {}, //~ ERROR unreachable pattern
     }
     match x {
-    //~^ ERROR non-exhaustive patterns: `_` not covered
-        _ if false => {}, // Not detected as unreachable nor exhaustive.
+        _ if false => {}, //~ ERROR unreachable pattern
     }
 }
 
 fn never(x: !) {
     match x {} // ok
     match x {
-        _ => {}, // Not detected as unreachable.
+        _ => {}, //~ ERROR unreachable pattern
     }
     match x {
-    //~^ ERROR non-exhaustive patterns: `_` not covered
-        _ if false => {}, // Not detected as unreachable nor exhaustive.
+        _ if false => {}, //~ ERROR unreachable pattern
     }
 }
 
diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr
index 0218b6fda50..af666b3a921 100644
--- a/src/test/ui/pattern/usefulness/match-empty.stderr
+++ b/src/test/ui/pattern/usefulness/match-empty.stderr
@@ -1,26 +1,35 @@
-error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/match-empty.rs:49:11
+error: unreachable pattern
+  --> $DIR/match-empty.rs:47:9
    |
-LL | enum Foo {}
-   | ----------- `Foo` defined here
-...
-LL |     match x {
-   |           ^ pattern `_` not covered
+LL |         _ => {},
+   |         ^
    |
-   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
-   = note: the matched value is of type `Foo`
+note: the lint level is defined here
+  --> $DIR/match-empty.rs:3:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
 
-error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/match-empty.rs:60:11
+error: unreachable pattern
+  --> $DIR/match-empty.rs:50:9
    |
-LL |     match x {
-   |           ^ pattern `_` not covered
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty.rs:57:9
    |
-   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
-   = note: the matched value is of type `!`
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty.rs:60:9
+   |
+LL |         _ if false => {},
+   |         ^
 
 error[E0004]: non-exhaustive patterns: type `u8` is non-empty
-  --> $DIR/match-empty.rs:77:18
+  --> $DIR/match-empty.rs:75:18
    |
 LL |     match_empty!(0u8);
    |                  ^^^
@@ -29,7 +38,7 @@ LL |     match_empty!(0u8);
    = note: the matched value is of type `u8`
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty
-  --> $DIR/match-empty.rs:79:18
+  --> $DIR/match-empty.rs:77:18
    |
 LL | struct NonEmptyStruct(bool);
    | ---------------------------- `NonEmptyStruct` defined here
@@ -41,7 +50,7 @@ LL |     match_empty!(NonEmptyStruct(true));
    = note: the matched value is of type `NonEmptyStruct`
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
-  --> $DIR/match-empty.rs:81:18
+  --> $DIR/match-empty.rs:79:18
    |
 LL | / union NonEmptyUnion1 {
 LL | |     foo: (),
@@ -55,7 +64,7 @@ LL |       match_empty!((NonEmptyUnion1 { foo: () }));
    = note: the matched value is of type `NonEmptyUnion1`
 
 error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
-  --> $DIR/match-empty.rs:83:18
+  --> $DIR/match-empty.rs:81:18
    |
 LL | / union NonEmptyUnion2 {
 LL | |     foo: (),
@@ -70,7 +79,7 @@ LL |       match_empty!((NonEmptyUnion2 { foo: () }));
    = note: the matched value is of type `NonEmptyUnion2`
 
 error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
-  --> $DIR/match-empty.rs:85:18
+  --> $DIR/match-empty.rs:83:18
    |
 LL | / enum NonEmptyEnum1 {
 LL | |     Foo(bool),
@@ -87,7 +96,7 @@ LL |       match_empty!(NonEmptyEnum1::Foo(true));
    = note: the matched value is of type `NonEmptyEnum1`
 
 error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
-  --> $DIR/match-empty.rs:87:18
+  --> $DIR/match-empty.rs:85:18
    |
 LL | / enum NonEmptyEnum2 {
 LL | |     Foo(bool),
@@ -108,7 +117,7 @@ LL |       match_empty!(NonEmptyEnum2::Foo(true));
    = note: the matched value is of type `NonEmptyEnum2`
 
 error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
-  --> $DIR/match-empty.rs:89:18
+  --> $DIR/match-empty.rs:87:18
    |
 LL | / enum NonEmptyEnum5 {
 LL | |     V1, V2, V3, V4, V5,
@@ -122,7 +131,7 @@ LL |       match_empty!(NonEmptyEnum5::V1);
    = note: the matched value is of type `NonEmptyEnum5`
 
 error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/match-empty.rs:92:18
+  --> $DIR/match-empty.rs:90:18
    |
 LL |     match_false!(0u8);
    |                  ^^^ pattern `_` not covered
@@ -131,7 +140,7 @@ LL |     match_false!(0u8);
    = note: the matched value is of type `u8`
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered
-  --> $DIR/match-empty.rs:94:18
+  --> $DIR/match-empty.rs:92:18
    |
 LL | struct NonEmptyStruct(bool);
    | ---------------------------- `NonEmptyStruct` defined here
@@ -143,7 +152,7 @@ LL |     match_false!(NonEmptyStruct(true));
    = note: the matched value is of type `NonEmptyStruct`
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
-  --> $DIR/match-empty.rs:96:18
+  --> $DIR/match-empty.rs:94:18
    |
 LL | / union NonEmptyUnion1 {
 LL | |     foo: (),
@@ -157,7 +166,7 @@ LL |       match_false!((NonEmptyUnion1 { foo: () }));
    = note: the matched value is of type `NonEmptyUnion1`
 
 error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
-  --> $DIR/match-empty.rs:98:18
+  --> $DIR/match-empty.rs:96:18
    |
 LL | / union NonEmptyUnion2 {
 LL | |     foo: (),
@@ -172,7 +181,7 @@ LL |       match_false!((NonEmptyUnion2 { foo: () }));
    = note: the matched value is of type `NonEmptyUnion2`
 
 error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
-  --> $DIR/match-empty.rs:100:18
+  --> $DIR/match-empty.rs:98:18
    |
 LL | / enum NonEmptyEnum1 {
 LL | |     Foo(bool),
@@ -189,7 +198,7 @@ LL |       match_false!(NonEmptyEnum1::Foo(true));
    = note: the matched value is of type `NonEmptyEnum1`
 
 error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
-  --> $DIR/match-empty.rs:102:18
+  --> $DIR/match-empty.rs:100:18
    |
 LL | / enum NonEmptyEnum2 {
 LL | |     Foo(bool),
@@ -210,7 +219,7 @@ LL |       match_false!(NonEmptyEnum2::Foo(true));
    = note: the matched value is of type `NonEmptyEnum2`
 
 error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
-  --> $DIR/match-empty.rs:104:18
+  --> $DIR/match-empty.rs:102:18
    |
 LL | / enum NonEmptyEnum5 {
 LL | |     V1, V2, V3, V4, V5,
@@ -223,6 +232,6 @@ LL |       match_false!(NonEmptyEnum5::V1);
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `NonEmptyEnum5`
 
-error: aborting due to 16 previous errors
+error: aborting due to 18 previous errors
 
 For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs
index afd6d996c15..9b52c5b5991 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs
+++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs
@@ -25,7 +25,7 @@ pub enum EmptyNonExhaustiveEnum {}
 fn empty_non_exhaustive(x: EmptyNonExhaustiveEnum) {
     match x {}
     match x {
-        _ => {} // not detected as unreachable
+        _ => {}, //~ ERROR unreachable pattern
     }
 }
 
diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr
index 752b08b2b65..07582e0ce32 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr
+++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr
@@ -1,3 +1,15 @@
+error: unreachable pattern
+  --> $DIR/enum_same_crate_empty_match.rs:28:9
+   |
+LL |         _ => {},
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/enum_same_crate_empty_match.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
 error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered
   --> $DIR/enum_same_crate_empty_match.rs:33:11
    |
@@ -42,6 +54,6 @@ LL |       match NormalEnum::Unit {}
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `NormalEnum`
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0004`.