about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2023-11-27 04:08:09 +0100
committerNadrieril <nadrieril+git@gmail.com>2023-12-03 12:25:46 +0100
commit70deb9a57f4c5757162f4c845ee9fe318717064f (patch)
treefc2a4b840aee4e2e43e38c20ecc740c271878861
parent06a8ed10b627d04c9e91c41064c38745374acc71 (diff)
downloadrust-70deb9a57f4c5757162f4c845ee9fe318717064f.tar.gz
rust-70deb9a57f4c5757162f4c845ee9fe318717064f.zip
Disallow arm bodies on never patterns
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl5
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs9
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs21
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.stderr2
-rw-r--r--tests/ui/never_patterns/check.rs3
-rw-r--r--tests/ui/never_patterns/check.stderr31
-rw-r--r--tests/ui/pattern/never_patterns.rs28
8 files changed, 75 insertions, 26 deletions
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index ecbe8cc6aec..6bde4f2d8fa 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -108,6 +108,11 @@ ast_lowering_misplaced_impl_trait =
 ast_lowering_misplaced_relax_trait_bound =
     `?Trait` bounds are only permitted at the point where a type parameter is declared
 
+ast_lowering_never_pattern_with_body =
+    a never pattern is always unreachable
+    .label = this will never be executed
+    .suggestion = remove this expression
+
 ast_lowering_never_pattern_with_guard =
     a guard on a never pattern will never be run
     .suggestion = remove this guard
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index c6a4166f537..11bb559719b 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -350,6 +350,15 @@ pub struct MatchArmWithNoBody {
 }
 
 #[derive(Diagnostic)]
+#[diag(ast_lowering_never_pattern_with_body)]
+pub struct NeverPatternWithBody {
+    #[primary_span]
+    #[label]
+    #[suggestion(code = "", applicability = "maybe-incorrect")]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(ast_lowering_never_pattern_with_guard)]
 pub struct NeverPatternWithGuard {
     #[primary_span]
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 4913f9cd12d..137ccb99346 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -2,7 +2,8 @@ use super::errors::{
     AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
     BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
     FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
-    NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign,
+    NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
+    UnderscoreExprLhsAssign,
 };
 use super::ResolverAstLoweringExt;
 use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
@@ -567,20 +568,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let hir_id = self.next_id();
         let span = self.lower_span(arm.span);
         self.lower_attrs(hir_id, &arm.attrs);
-        let body = if let Some(body) = &arm.body {
-            // FIXME(never_patterns): Disallow never pattern with a body or guard
+        let is_never_pattern = pat.is_never_pattern();
+        let body = if let Some(body) = &arm.body
+            && !is_never_pattern
+        {
             self.lower_expr(body)
         } else {
-            if !pat.is_never_pattern() {
-                self.tcx
-                    .sess
-                    .emit_err(MatchArmWithNoBody { span, suggestion: span.shrink_to_hi() });
+            // Either `body.is_none()` or `is_never_pattern` here.
+            if !is_never_pattern {
+                let suggestion = span.shrink_to_hi();
+                self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
+            } else if let Some(body) = &arm.body {
+                self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
+                guard = None;
             } else if let Some(g) = &arm.guard {
                 self.tcx.sess.emit_err(NeverPatternWithGuard { span: g.span });
                 guard = None;
             }
 
-            // An arm without a body, meant for never patterns.
             // We add a fake `loop {}` arm body so that it typecks to `!`.
             // FIXME(never_patterns): Desugar into a call to `unreachable_unchecked`.
             let block = self.arena.alloc(hir::Block {
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs
index 69e9f62abf0..ca5ce3b9489 100644
--- a/tests/ui/feature-gates/feature-gate-never_patterns.rs
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs
@@ -12,7 +12,7 @@ fn main() {
     unsafe {
         let ptr: *const Void = NonNull::dangling().as_ptr();
         match *ptr {
-            ! => {} //~ ERROR `!` patterns are experimental
+            ! //~ ERROR `!` patterns are experimental
         }
     }
 
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
index b7290eeb36d..2354a3b0476 100644
--- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
@@ -18,7 +18,7 @@ LL |     let (Ok(_x) | Err(&!)) = res.as_ref();
 error[E0658]: `!` patterns are experimental
   --> $DIR/feature-gate-never_patterns.rs:15:13
    |
-LL |             ! => {}
+LL |             !
    |             ^
    |
    = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
diff --git a/tests/ui/never_patterns/check.rs b/tests/ui/never_patterns/check.rs
index 1f55ef11242..e298112244a 100644
--- a/tests/ui/never_patterns/check.rs
+++ b/tests/ui/never_patterns/check.rs
@@ -12,6 +12,7 @@ macro_rules! never {
 fn no_arms_or_guards(x: Void) {
     match None::<Void> {
         Some(!) => {}
+        //~^ ERROR a never pattern is always unreachable
         None => {}
     }
     match None::<Void> {
@@ -21,10 +22,12 @@ fn no_arms_or_guards(x: Void) {
     }
     match None::<Void> {
         Some(!) if true => {}
+        //~^ ERROR a never pattern is always unreachable
         None => {}
     }
     match None::<Void> {
         Some(never!()) => {},
+        //~^ ERROR a never pattern is always unreachable
         None => {}
     }
 }
diff --git a/tests/ui/never_patterns/check.stderr b/tests/ui/never_patterns/check.stderr
index a25ee1b0a2a..bfbc7a1b534 100644
--- a/tests/ui/never_patterns/check.stderr
+++ b/tests/ui/never_patterns/check.stderr
@@ -1,8 +1,35 @@
+error: a never pattern is always unreachable
+  --> $DIR/check.rs:14:20
+   |
+LL |         Some(!) => {}
+   |                    ^^
+   |                    |
+   |                    this will never be executed
+   |                    help: remove this expression
+
 error: a guard on a never pattern will never be run
-  --> $DIR/check.rs:18:20
+  --> $DIR/check.rs:19:20
    |
 LL |         Some(!) if true,
    |                    ^^^^ help: remove this guard
 
-error: aborting due to 1 previous error
+error: a never pattern is always unreachable
+  --> $DIR/check.rs:24:28
+   |
+LL |         Some(!) if true => {}
+   |                            ^^
+   |                            |
+   |                            this will never be executed
+   |                            help: remove this expression
+
+error: a never pattern is always unreachable
+  --> $DIR/check.rs:29:27
+   |
+LL |         Some(never!()) => {},
+   |                           ^^
+   |                           |
+   |                           this will never be executed
+   |                           help: remove this expression
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/pattern/never_patterns.rs b/tests/ui/pattern/never_patterns.rs
index fdba1b8e087..915f3e75e7b 100644
--- a/tests/ui/pattern/never_patterns.rs
+++ b/tests/ui/pattern/never_patterns.rs
@@ -20,58 +20,58 @@ fn never_pattern_location(void: Void) {
     // FIXME(never_patterns): Don't accept on a non-empty type.
     match Some(0) {
         None => {}
-        Some(!) => {}
+        Some(!),
     }
     // FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
     match () {
         () => {}
-        ! => {}
+        !,
     }
     // FIXME(never_patterns): Don't accept even on an empty branch.
     match None::<Void> {
         None => {}
-        ! => {}
+        !,
     }
     // FIXME(never_patterns): Let alone if the emptiness is behind a reference.
     match None::<&Void> {
         None => {}
-        ! => {}
+        !,
     }
     // Participate in match ergonomics.
     match &void {
-        ! => {}
+        !
     }
     match &&void {
-        ! => {}
+        !
     }
     match &&void {
-        &! => {}
+        &!
     }
     match &None::<Void> {
         None => {}
-        Some(!) => {}
+        Some(!)
     }
     match None::<&Void> {
         None => {}
-        Some(!) => {}
+        Some(!),
     }
     // Accept on a composite empty type.
     match None::<&(u32, Void)> {
         None => {}
-        Some(&!) => {}
+        Some(&!),
     }
     // Accept on an simple empty type.
     match None::<Void> {
         None => {}
-        Some(!) => {}
+        Some(!),
     }
     match None::<&Void> {
         None => {}
-        Some(&!) => {}
+        Some(&!),
     }
     match None::<&(u32, Void)> {
         None => {}
-        Some(&(_, !)) => {}
+        Some(&(_, !)),
     }
 }
 
@@ -89,7 +89,7 @@ fn never_and_bindings() {
     // FIXME(never_patterns): A never pattern mustn't have bindings.
     match x {
         Ok(_) => {}
-        Err(&(_b, !)) => {}
+        Err(&(_b, !)),
     }
     match x {
         Ok(_a) | Err(&(_b, !)) => {}