about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-11-18 21:24:40 +0000
committerbors <bors@rust-lang.org>2020-11-18 21:24:40 +0000
commit8256379832b5ecb7f71e8c5e2018446482223c12 (patch)
tree1d5e866b6b006d84b3d8ce102a1022234ed2e4ae
parent8d2d0014922e9f541694bfe87642749239990e0e (diff)
parent69821cf8df86c8f4366e0c16a0a7de8d0135b90f (diff)
downloadrust-8256379832b5ecb7f71e8c5e2018446482223c12.tar.gz
rust-8256379832b5ecb7f71e8c5e2018446482223c12.zip
Auto merge of #78995 - Nadrieril:clean-empty-match, r=varkor
Handle empty matches cleanly in exhaustiveness checking

This removes the special-casing of empty matches that was done in `check_match`. This fixes most of https://github.com/rust-lang/rust/issues/55123.
Somewhat unrelatedly, I also made `_match.rs` more self-contained, because I think it's cleaner.

r? `@varkor`
`@rustbot` modify labels: +A-exhaustiveness-checking
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/_match.rs115
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs137
-rw-r--r--src/test/ui/pattern/usefulness/auxiliary/empty.rs2
-rw-r--r--src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs33
-rw-r--r--src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr64
-rw-r--r--src/test/ui/pattern/usefulness/match-empty.rs40
-rw-r--r--src/test/ui/pattern/usefulness/match-empty.stderr78
-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
9 files changed, 317 insertions, 168 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..6bea2381862 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
@@ -364,14 +364,14 @@ impl<'tcx> Pat<'tcx> {
 /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
 /// works well.
 #[derive(Debug, Clone)]
-crate struct PatStack<'p, 'tcx> {
+struct PatStack<'p, 'tcx> {
     pats: SmallVec<[&'p Pat<'tcx>; 2]>,
     /// Cache for the constructor of the head
     head_ctor: OnceCell<Constructor<'tcx>>,
 }
 
 impl<'p, 'tcx> PatStack<'p, 'tcx> {
-    crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
+    fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
         Self::from_vec(smallvec![pat])
     }
 
@@ -455,17 +455,17 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
 
 /// A 2D matrix.
 #[derive(Clone, PartialEq)]
-crate struct Matrix<'p, 'tcx> {
+struct Matrix<'p, 'tcx> {
     patterns: Vec<PatStack<'p, 'tcx>>,
 }
 
 impl<'p, 'tcx> Matrix<'p, 'tcx> {
-    crate fn empty() -> Self {
+    fn empty() -> Self {
         Matrix { patterns: vec![] }
     }
 
     /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
-    crate fn push(&mut self, row: PatStack<'p, 'tcx>) {
+    fn push(&mut self, row: PatStack<'p, 'tcx>) {
         if let Some(rows) = row.expand_or_pat() {
             for row in rows {
                 // We recursively expand the or-patterns of the new rows.
@@ -588,7 +588,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
     }
 
     /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
-    crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
+    fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
         match ty.kind() {
             ty::Adt(def, ..) => {
                 def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local()
@@ -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| {
@@ -1440,7 +1439,7 @@ impl<'tcx> Usefulness<'tcx> {
 }
 
 #[derive(Copy, Clone, Debug)]
-crate enum WitnessPreference {
+enum WitnessPreference {
     ConstructWitness,
     LeaveOutWitness,
 }
@@ -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
@@ -1493,7 +1495,8 @@ struct PatCtxt<'a, 'p, 'tcx> {
 crate struct Witness<'tcx>(Vec<Pat<'tcx>>);
 
 impl<'tcx> Witness<'tcx> {
-    crate fn single_pattern(self) -> Pat<'tcx> {
+    /// Asserts that the witness contains a single pattern, and returns it.
+    fn single_pattern(self) -> Pat<'tcx> {
         assert_eq!(self.0.len(), 1);
         self.0.into_iter().next().unwrap()
     }
@@ -1585,11 +1588,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 emptiness. 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 +1639,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 emptiness. 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 +2023,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 +2051,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.
@@ -2086,7 +2093,7 @@ impl<'tcx> MissingConstructors<'tcx> {
 /// `is_under_guard` is used to inform if the pattern has a guard. If it
 /// has one it must not be inserted into the matrix. This shouldn't be
 /// relied on for soundness.
-crate fn is_useful<'p, 'tcx>(
+fn is_useful<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     matrix: &Matrix<'p, 'tcx>,
     v: &PatStack<'p, 'tcx>,
@@ -2200,7 +2207,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 +2222,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);
@@ -2283,3 +2290,63 @@ fn pat_constructor<'p, 'tcx>(
         PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
     }
 }
+
+/// The arm of a match expression.
+#[derive(Clone, Copy)]
+crate struct MatchArm<'p, 'tcx> {
+    /// The pattern must have been lowered through `MatchVisitor::lower_pattern`.
+    crate pat: &'p super::Pat<'tcx>,
+    crate hir_id: HirId,
+    crate has_guard: bool,
+}
+
+/// The output of checking a match for exhaustiveness and arm reachability.
+crate struct UsefulnessReport<'p, 'tcx> {
+    /// For each arm of the input, whether that arm is reachable after the arms above it.
+    crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Usefulness<'tcx>)>,
+    /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
+    /// exhaustiveness.
+    crate non_exhaustiveness_witnesses: Vec<super::Pat<'tcx>>,
+}
+
+/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which
+/// of its arms are reachable.
+///
+/// Note: the input patterns must have been lowered through `MatchVisitor::lower_pattern`.
+crate fn compute_match_usefulness<'p, 'tcx>(
+    cx: &MatchCheckCtxt<'p, 'tcx>,
+    arms: &[MatchArm<'p, 'tcx>],
+    scrut_hir_id: HirId,
+    scrut_ty: Ty<'tcx>,
+) -> UsefulnessReport<'p, 'tcx> {
+    let mut matrix = Matrix::empty();
+    let arm_usefulness: Vec<_> = arms
+        .iter()
+        .copied()
+        .map(|arm| {
+            let v = PatStack::from_pattern(arm.pat);
+            let usefulness =
+                is_useful(cx, &matrix, &v, LeaveOutWitness, arm.hir_id, arm.has_guard, true);
+            if !arm.has_guard {
+                matrix.push(v);
+            }
+            (arm, usefulness)
+        })
+        .collect();
+
+    let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(scrut_ty));
+    let v = PatStack::from_pattern(wild_pattern);
+    let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true);
+    let non_exhaustiveness_witnesses = match usefulness {
+        NotUseful => vec![], // Wildcard pattern isn't useful, so the match is exhaustive.
+        UsefulWithWitness(pats) => {
+            if pats.is_empty() {
+                bug!("Exhaustiveness check returned no witnesses")
+            } else {
+                pats.into_iter().map(|w| w.single_pattern()).collect()
+            }
+        }
+        Useful(_) => bug!(),
+    };
+    UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
+}
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 d395ae4ca2a..f9fe261bcee 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -1,6 +1,7 @@
 use super::_match::Usefulness::*;
-use super::_match::WitnessPreference::*;
-use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
+use super::_match::{
+    compute_match_usefulness, expand_pattern, MatchArm, MatchCheckCtxt, UsefulnessReport,
+};
 use super::{PatCtxt, PatKind, PatternError};
 
 use rustc_arena::TypedArena;
@@ -169,39 +170,50 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
 
         let mut have_errors = false;
 
-        let inlined_arms: Vec<_> = arms
+        let arms: Vec<_> = arms
             .iter()
-            .map(|hir::Arm { pat, guard, .. }| {
-                (self.lower_pattern(&mut cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some())
+            .map(|hir::Arm { pat, guard, .. }| MatchArm {
+                pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0,
+                hir_id: pat.hir_id,
+                has_guard: guard.is_some(),
             })
             .collect();
 
-        // Bail out early if inlining failed.
+        // Bail out early if lowering failed.
         if have_errors {
             return;
         }
 
-        // Fourth, check for unreachable arms.
-        let matrix = check_arms(&mut cx, &inlined_arms, source);
+        let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
+        let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
+
+        // Report unreachable arms.
+        report_arm_reachability(&cx, &report, source);
 
-        // Fifth, check if the match is exhaustive.
+        // Check if the match is exhaustive.
         // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
         // since an empty matrix can occur when there are arms, if those arms all have guards.
-        let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
-        let is_empty_match = inlined_arms.is_empty();
-        check_exhaustive(&mut cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
+        let is_empty_match = arms.is_empty();
+        let witnesses = report.non_exhaustiveness_witnesses;
+        if !witnesses.is_empty() {
+            non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
+        }
     }
 
     fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
         let mut cx = self.new_cx(pat.hir_id);
 
         let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false);
-        let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
-
-        let witnesses = match check_not_useful(&mut cx, pattern_ty, &pats, pat.hir_id) {
-            Ok(_) => return,
-            Err(err) => err,
-        };
+        let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
+        let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
+
+        // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
+        // only care about exhaustiveness here.
+        let witnesses = report.non_exhaustiveness_witnesses;
+        if witnesses.is_empty() {
+            // The pattern is irrefutable.
+            return;
+        }
 
         let joined_patterns = joined_uncovered_patterns(&witnesses);
         let mut err = struct_span_err!(
@@ -354,17 +366,15 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::
     });
 }
 
-/// Check for unreachable patterns.
-fn check_arms<'p, 'tcx>(
-    cx: &mut MatchCheckCtxt<'p, 'tcx>,
-    arms: &[(&'p super::Pat<'tcx>, HirId, bool)],
+/// Report unreachable arms, if any.
+fn report_arm_reachability<'p, 'tcx>(
+    cx: &MatchCheckCtxt<'p, 'tcx>,
+    report: &UsefulnessReport<'p, 'tcx>,
     source: hir::MatchSource,
-) -> Matrix<'p, 'tcx> {
-    let mut seen = Matrix::empty();
+) {
     let mut catchall = None;
-    for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() {
-        let v = PatStack::from_pattern(pat);
-        match is_useful(cx, &seen, &v, LeaveOutWitness, id, has_guard, true) {
+    for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
+        match is_useful {
             NotUseful => {
                 match source {
                     hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
@@ -373,15 +383,15 @@ fn check_arms<'p, 'tcx>(
                         // Check which arm we're on.
                         match arm_index {
                             // The arm with the user-specified pattern.
-                            0 => unreachable_pattern(cx.tcx, pat.span, id, None),
+                            0 => unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None),
                             // The arm with the wildcard pattern.
-                            1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source),
+                            1 => irrefutable_let_pattern(cx.tcx, arm.pat.span, arm.hir_id, source),
                             _ => bug!(),
                         }
                     }
 
                     hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
-                        unreachable_pattern(cx.tcx, pat.span, id, catchall);
+                        unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
                     }
 
                     // Unreachable patterns in try and await expressions occur when one of
@@ -389,79 +399,32 @@ fn check_arms<'p, 'tcx>(
                     hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
                 }
             }
+            Useful(unreachables) if unreachables.is_empty() => {}
+            // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
             Useful(unreachables) => {
-                let mut unreachables: Vec<_> = unreachables.into_iter().flatten().collect();
+                let mut unreachables: Vec<_> = unreachables.iter().flatten().copied().collect();
                 // Emit lints in the order in which they occur in the file.
                 unreachables.sort_unstable();
                 for span in unreachables {
-                    unreachable_pattern(cx.tcx, span, id, None);
+                    unreachable_pattern(cx.tcx, span, arm.hir_id, None);
                 }
             }
             UsefulWithWitness(_) => bug!(),
         }
-        if !has_guard {
-            seen.push(v);
-            if catchall.is_none() && pat_is_catchall(pat) {
-                catchall = Some(pat.span);
-            }
+        if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
+            catchall = Some(arm.pat.span);
         }
     }
-    seen
 }
 
-fn check_not_useful<'p, 'tcx>(
-    cx: &mut MatchCheckCtxt<'p, 'tcx>,
-    ty: Ty<'tcx>,
-    matrix: &Matrix<'p, 'tcx>,
-    hir_id: HirId,
-) -> Result<(), Vec<super::Pat<'tcx>>> {
-    let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
-    let v = PatStack::from_pattern(wild_pattern);
-
-    // false is given for `is_under_guard` argument due to the wildcard
-    // pattern not having a guard
-    match is_useful(cx, matrix, &v, ConstructWitness, hir_id, false, true) {
-        NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
-        UsefulWithWitness(pats) => Err(if pats.is_empty() {
-            bug!("Exhaustiveness check returned no witnesses")
-        } else {
-            pats.into_iter().map(|w| w.single_pattern()).collect()
-        }),
-        Useful(_) => bug!(),
-    }
-}
-
-fn check_exhaustive<'p, 'tcx>(
-    cx: &mut MatchCheckCtxt<'p, 'tcx>,
+/// Report that a match is not exhaustive.
+fn non_exhaustive_match<'p, 'tcx>(
+    cx: &MatchCheckCtxt<'p, 'tcx>,
     scrut_ty: Ty<'tcx>,
     sp: Span,
-    matrix: &Matrix<'p, 'tcx>,
-    hir_id: HirId,
+    witnesses: Vec<super::Pat<'tcx>>,
     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,
-    };
-
     let non_empty_enum = match scrut_ty.kind() {
         ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
         _ => false,
diff --git a/src/test/ui/pattern/usefulness/auxiliary/empty.rs b/src/test/ui/pattern/usefulness/auxiliary/empty.rs
new file mode 100644
index 00000000000..0b0719f48ee
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/auxiliary/empty.rs
@@ -0,0 +1,2 @@
+#![crate_type = "rlib"]
+pub enum EmptyForeignEnum {}
diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs
index 57b6b910ca1..c5c3a214f9a 100644
--- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs
+++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs
@@ -1,7 +1,12 @@
+// aux-build:empty.rs
 #![feature(never_type)]
+#![feature(never_type_fallback)]
 #![feature(exhaustive_patterns)]
 #![deny(unreachable_patterns)]
-enum Foo {}
+
+extern crate empty;
+
+enum EmptyEnum {}
 
 struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here
 union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here
@@ -41,8 +46,28 @@ macro_rules! match_false {
     };
 }
 
-fn foo(x: Foo) {
-    match_empty!(x); // ok
+fn empty_enum(x: EmptyEnum) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {}, //~ ERROR unreachable pattern
+    }
+}
+
+fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {}, //~ ERROR unreachable pattern
+    }
+}
+
+fn never(x: !) {
+    match x {} // ok
     match x {
         _ => {}, //~ ERROR unreachable pattern
     }
@@ -56,7 +81,7 @@ fn main() {
         None => {}
         Some(_) => {} //~ ERROR unreachable pattern
     }
-    match None::<Foo> {
+    match None::<EmptyEnum> {
         None => {}
         Some(_) => {} //~ ERROR unreachable pattern
     }
diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr
index 1f6503e3e9c..9d8b5f38e8c 100644
--- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr
+++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr
@@ -1,35 +1,59 @@
 error: unreachable pattern
-  --> $DIR/match-empty-exhaustive_patterns.rs:47:9
+  --> $DIR/match-empty-exhaustive_patterns.rs:52:9
    |
 LL |         _ => {},
    |         ^
    |
 note: the lint level is defined here
-  --> $DIR/match-empty-exhaustive_patterns.rs:3:9
+  --> $DIR/match-empty-exhaustive_patterns.rs:5:9
    |
 LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/match-empty-exhaustive_patterns.rs:50:9
+  --> $DIR/match-empty-exhaustive_patterns.rs:55:9
    |
 LL |         _ if false => {},
    |         ^
 
 error: unreachable pattern
-  --> $DIR/match-empty-exhaustive_patterns.rs:57:9
+  --> $DIR/match-empty-exhaustive_patterns.rs:62:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty-exhaustive_patterns.rs:65:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty-exhaustive_patterns.rs:72:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty-exhaustive_patterns.rs:75:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty-exhaustive_patterns.rs:82:9
    |
 LL |         Some(_) => {}
    |         ^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/match-empty-exhaustive_patterns.rs:61:9
+  --> $DIR/match-empty-exhaustive_patterns.rs:86:9
    |
 LL |         Some(_) => {}
    |         ^^^^^^^
 
 error[E0004]: non-exhaustive patterns: type `u8` is non-empty
-  --> $DIR/match-empty-exhaustive_patterns.rs:64:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:89:18
    |
 LL |     match_empty!(0u8);
    |                  ^^^
@@ -38,7 +62,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-exhaustive_patterns.rs:66:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:91:18
    |
 LL | struct NonEmptyStruct(bool);
    | ---------------------------- `NonEmptyStruct` defined here
@@ -50,7 +74,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-exhaustive_patterns.rs:68:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:93:18
    |
 LL | / union NonEmptyUnion1 {
 LL | |     foo: (),
@@ -64,7 +88,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-exhaustive_patterns.rs:70:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:95:18
    |
 LL | / union NonEmptyUnion2 {
 LL | |     foo: (),
@@ -79,7 +103,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-exhaustive_patterns.rs:72:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:97:18
    |
 LL | / enum NonEmptyEnum1 {
 LL | |     Foo(bool),
@@ -96,7 +120,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-exhaustive_patterns.rs:74:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:99:18
    |
 LL | / enum NonEmptyEnum2 {
 LL | |     Foo(bool),
@@ -117,7 +141,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-exhaustive_patterns.rs:76:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:101:18
    |
 LL | / enum NonEmptyEnum5 {
 LL | |     V1, V2, V3, V4, V5,
@@ -131,7 +155,7 @@ LL |       match_empty!(NonEmptyEnum5::V1);
    = note: the matched value is of type `NonEmptyEnum5`
 
 error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/match-empty-exhaustive_patterns.rs:79:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:104:18
    |
 LL |     match_false!(0u8);
    |                  ^^^ pattern `_` not covered
@@ -140,7 +164,7 @@ LL |     match_false!(0u8);
    = note: the matched value is of type `u8`
 
 error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered
-  --> $DIR/match-empty-exhaustive_patterns.rs:81:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:106:18
    |
 LL | struct NonEmptyStruct(bool);
    | ---------------------------- `NonEmptyStruct` defined here
@@ -152,7 +176,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-exhaustive_patterns.rs:83:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:108:18
    |
 LL | / union NonEmptyUnion1 {
 LL | |     foo: (),
@@ -166,7 +190,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-exhaustive_patterns.rs:85:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:110:18
    |
 LL | / union NonEmptyUnion2 {
 LL | |     foo: (),
@@ -181,7 +205,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-exhaustive_patterns.rs:87:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:112:18
    |
 LL | / enum NonEmptyEnum1 {
 LL | |     Foo(bool),
@@ -198,7 +222,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-exhaustive_patterns.rs:89:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:114:18
    |
 LL | / enum NonEmptyEnum2 {
 LL | |     Foo(bool),
@@ -219,7 +243,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-exhaustive_patterns.rs:91:18
+  --> $DIR/match-empty-exhaustive_patterns.rs:116:18
    |
 LL | / enum NonEmptyEnum5 {
 LL | |     V1, V2, V3, V4, V5,
@@ -232,6 +256,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 18 previous errors
+error: aborting due to 22 previous errors
 
 For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs
index f7577125d8a..10ea2a10406 100644
--- a/src/test/ui/pattern/usefulness/match-empty.rs
+++ b/src/test/ui/pattern/usefulness/match-empty.rs
@@ -1,6 +1,11 @@
+// aux-build:empty.rs
 #![feature(never_type)]
+#![feature(never_type_fallback)]
 #![deny(unreachable_patterns)]
-enum Foo {}
+
+extern crate empty;
+
+enum EmptyEnum {}
 
 struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here
 union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here
@@ -40,12 +45,33 @@ macro_rules! match_false {
     };
 }
 
-fn foo(x: Foo) {
-    match_empty!(x); // ok
-    match_false!(x); // Not detected as unreachable nor exhaustive.
-    //~^ ERROR non-exhaustive patterns: `_` not covered
+fn empty_enum(x: EmptyEnum) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {}, //~ ERROR unreachable pattern
+    }
+}
+
+fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {}, //~ ERROR unreachable pattern
+    }
+}
+
+fn never(x: !) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
     match x {
-        _ => {}, // Not detected as unreachable, see #55123.
+        _ if false => {}, //~ ERROR unreachable pattern
     }
 }
 
@@ -55,7 +81,7 @@ fn main() {
         None => {}
         Some(_) => {}
     }
-    match None::<Foo> {
+    match None::<EmptyEnum> {
         None => {}
         Some(_) => {}
     }
diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr
index 08095f6e7fb..6065c552390 100644
--- a/src/test/ui/pattern/usefulness/match-empty.stderr
+++ b/src/test/ui/pattern/usefulness/match-empty.stderr
@@ -1,17 +1,47 @@
-error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/match-empty.rs:45:18
+error: unreachable pattern
+  --> $DIR/match-empty.rs:51:9
    |
-LL | enum Foo {}
-   | ----------- `Foo` defined here
-...
-LL |     match_false!(x); // Not detected as unreachable nor exhaustive.
-   |                  ^ 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:4:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-empty.rs:54:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty.rs:61:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty.rs:64:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty.rs:71:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/match-empty.rs:74:9
+   |
+LL |         _ if false => {},
+   |         ^
 
 error[E0004]: non-exhaustive patterns: type `u8` is non-empty
-  --> $DIR/match-empty.rs:63:18
+  --> $DIR/match-empty.rs:89:18
    |
 LL |     match_empty!(0u8);
    |                  ^^^
@@ -20,7 +50,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:65:18
+  --> $DIR/match-empty.rs:91:18
    |
 LL | struct NonEmptyStruct(bool);
    | ---------------------------- `NonEmptyStruct` defined here
@@ -32,7 +62,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:67:18
+  --> $DIR/match-empty.rs:93:18
    |
 LL | / union NonEmptyUnion1 {
 LL | |     foo: (),
@@ -46,7 +76,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:69:18
+  --> $DIR/match-empty.rs:95:18
    |
 LL | / union NonEmptyUnion2 {
 LL | |     foo: (),
@@ -61,7 +91,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:71:18
+  --> $DIR/match-empty.rs:97:18
    |
 LL | / enum NonEmptyEnum1 {
 LL | |     Foo(bool),
@@ -78,7 +108,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:73:18
+  --> $DIR/match-empty.rs:99:18
    |
 LL | / enum NonEmptyEnum2 {
 LL | |     Foo(bool),
@@ -99,7 +129,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:75:18
+  --> $DIR/match-empty.rs:101:18
    |
 LL | / enum NonEmptyEnum5 {
 LL | |     V1, V2, V3, V4, V5,
@@ -113,7 +143,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:78:18
+  --> $DIR/match-empty.rs:104:18
    |
 LL |     match_false!(0u8);
    |                  ^^^ pattern `_` not covered
@@ -122,7 +152,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:80:18
+  --> $DIR/match-empty.rs:106:18
    |
 LL | struct NonEmptyStruct(bool);
    | ---------------------------- `NonEmptyStruct` defined here
@@ -134,7 +164,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:82:18
+  --> $DIR/match-empty.rs:108:18
    |
 LL | / union NonEmptyUnion1 {
 LL | |     foo: (),
@@ -148,7 +178,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:84:18
+  --> $DIR/match-empty.rs:110:18
    |
 LL | / union NonEmptyUnion2 {
 LL | |     foo: (),
@@ -163,7 +193,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:86:18
+  --> $DIR/match-empty.rs:112:18
    |
 LL | / enum NonEmptyEnum1 {
 LL | |     Foo(bool),
@@ -180,7 +210,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:88:18
+  --> $DIR/match-empty.rs:114:18
    |
 LL | / enum NonEmptyEnum2 {
 LL | |     Foo(bool),
@@ -201,7 +231,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:90:18
+  --> $DIR/match-empty.rs:116:18
    |
 LL | / enum NonEmptyEnum5 {
 LL | |     V1, V2, V3, V4, V5,
@@ -214,6 +244,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 15 previous errors
+error: aborting due to 20 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..70253a4fc90 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..966f3a2e414 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`.