about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs3
-rw-r--r--compiler/rustc_mir_build/messages.ftl5
-rw-r--r--compiler/rustc_mir_build/src/errors.rs10
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs16
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs17
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/typeck.stderr66
6 files changed, 110 insertions, 7 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index fe8c36dbe06..2060e01e1d6 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -178,8 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let ty = match pat.kind {
             PatKind::Wild | PatKind::Err(_) => expected,
-            // FIXME(never_patterns): check the type is uninhabited. If that is not possible within
-            // typeck, do that in a later phase.
+            // We allow any type here; we ensure that the type is uninhabited during match checking.
             PatKind::Never => expected,
             PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
             PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 615b553434f..2f11cb123ee 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -234,6 +234,11 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
 
 mir_build_non_const_path = runtime values cannot be referenced in patterns
 
+mir_build_non_empty_never_pattern =
+    mismatched types
+    .label = a never pattern must be used on an uninhabited type
+    .note = the matched value is of type `{$ty}`
+
 mir_build_non_exhaustive_match_all_arms_guarded =
     match arms with guards don't count towards exhaustivity
 
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 61ad99acf38..e3cc21cef11 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -788,6 +788,16 @@ pub struct FloatPattern;
 #[diag(mir_build_pointer_pattern)]
 pub struct PointerPattern;
 
+#[derive(Diagnostic)]
+#[diag(mir_build_non_empty_never_pattern)]
+#[note]
+pub struct NonEmptyNeverPattern<'tcx> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub ty: Ty<'tcx>,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(mir_build_indirect_structural_match)]
 #[note(mir_build_type_not_structural_tip)]
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 f6c5e4a5cd6..6b08f6eb7c5 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -276,10 +276,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
         } else {
             // Check the pattern for some things unrelated to exhaustiveness.
             let refutable = if cx.refutable { Refutable } else { Irrefutable };
+            let mut err = Ok(());
             pat.walk_always(|pat| {
                 check_borrow_conflicts_in_at_patterns(self, pat);
                 check_for_bindings_named_same_as_variants(self, pat, refutable);
+                err = err.and(check_never_pattern(cx, pat));
             });
+            err?;
             Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
         }
     }
@@ -811,6 +814,19 @@ fn check_for_bindings_named_same_as_variants(
     }
 }
 
+/// Check that never patterns are only used on inhabited types.
+fn check_never_pattern<'tcx>(
+    cx: &MatchCheckCtxt<'_, 'tcx>,
+    pat: &Pat<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+    if let PatKind::Never = pat.kind {
+        if !cx.is_uninhabited(pat.ty) {
+            return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
+        }
+    }
+    Ok(())
+}
+
 fn report_irrefutable_let_patterns(
     tcx: TyCtxt<'_>,
     id: HirId,
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
index 90ff34e2f02..333108e92c0 100644
--- a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
@@ -1,4 +1,3 @@
-// check-pass
 #![feature(never_patterns)]
 #![feature(exhaustive_patterns)]
 #![allow(incomplete_features)]
@@ -17,40 +16,48 @@ fn safe_unwrap_result<T: Copy>(res: Result<T, Void>) {
 
 // Check we only accept `!` where we want to.
 fn never_pattern_typeck(void: Void) {
-    // FIXME(never_patterns): Don't accept on a non-empty type.
+    // Don't accept on a non-empty type.
     match () {
         !,
+        //~^ ERROR: mismatched types
     }
     match (0, false) {
         !,
+        //~^ ERROR: mismatched types
     }
     match (0, false) {
         (_, !),
+        //~^ ERROR: mismatched types
     }
     match Some(0) {
         None => {}
         Some(!),
+        //~^ ERROR: mismatched types
     }
 
-    // FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
+    // Don't accept on an arbitrary type, even if there are no more branches.
     match () {
         () => {}
         !,
+        //~^ ERROR: mismatched types
     }
 
-    // FIXME(never_patterns): Don't accept even on an empty branch.
+    // Don't accept even on an empty branch.
     match None::<Void> {
         None => {}
         !,
+        //~^ ERROR: mismatched types
     }
     match (&[] as &[Void]) {
         [] => {}
         !,
+        //~^ ERROR: mismatched types
     }
-    // FIXME(never_patterns): Let alone if the emptiness is behind a reference.
+    // Let alone if the emptiness is behind a reference.
     match None::<&Void> {
         None => {}
         !,
+        //~^ ERROR: mismatched types
     }
 
     // Participate in match ergonomics.
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.stderr
new file mode 100644
index 00000000000..8c0475894de
--- /dev/null
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.stderr
@@ -0,0 +1,66 @@
+error: mismatched types
+  --> $DIR/typeck.rs:21:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `()`
+
+error: mismatched types
+  --> $DIR/typeck.rs:25:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `(i32, bool)`
+
+error: mismatched types
+  --> $DIR/typeck.rs:29:13
+   |
+LL |         (_, !),
+   |             ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `bool`
+
+error: mismatched types
+  --> $DIR/typeck.rs:34:14
+   |
+LL |         Some(!),
+   |              ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `i32`
+
+error: mismatched types
+  --> $DIR/typeck.rs:41:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `()`
+
+error: mismatched types
+  --> $DIR/typeck.rs:48:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `Option<Void>`
+
+error: mismatched types
+  --> $DIR/typeck.rs:53:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `[Void]`
+
+error: mismatched types
+  --> $DIR/typeck.rs:59:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `Option<&Void>`
+
+error: aborting due to 8 previous errors
+