about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_resolve/messages.ftl4
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs3
-rw-r--r--compiler/rustc_resolve/src/errors.rs9
-rw-r--r--compiler/rustc_resolve/src/late.rs126
-rw-r--r--compiler/rustc_resolve/src/lib.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.rs1
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.stderr37
-rw-r--r--tests/ui/pattern/never_patterns.rs28
-rw-r--r--tests/ui/pattern/never_patterns.stderr58
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/bindings.rs46
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/bindings.stderr50
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/parse.rs1
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr8
13 files changed, 243 insertions, 130 deletions
diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl
index 3f8df16e03f..c8ec10cad17 100644
--- a/compiler/rustc_resolve/messages.ftl
+++ b/compiler/rustc_resolve/messages.ftl
@@ -36,6 +36,10 @@ resolve_attempt_to_use_non_constant_value_in_constant_with_suggestion =
 resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion =
     this would need to be a `{$suggestion}`
 
+resolve_binding_in_never_pattern =
+    never patterns cannot contain variable bindings
+    .suggestion = use a wildcard `_` instead
+
 resolve_binding_shadows_something_unacceptable =
     {$shadowing_binding}s cannot shadow {$shadowed_binding}s
     .label = cannot be named the same as {$article} {$shadowed_binding}
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index b5700661385..4a0c522b6ec 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -959,6 +959,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 .create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }),
             ResolutionError::InvalidAsmSym => self.dcx().create_err(errs::InvalidAsmSym { span }),
             ResolutionError::LowercaseSelf => self.dcx().create_err(errs::LowercaseSelf { span }),
+            ResolutionError::BindingInNeverPattern => {
+                self.dcx().create_err(errs::BindingInNeverPattern { span })
+            }
         }
     }
 
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index 1fdb193e571..821b1e946f3 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -486,6 +486,15 @@ pub(crate) struct LowercaseSelf {
     pub(crate) span: Span,
 }
 
+#[derive(Debug)]
+#[derive(Diagnostic)]
+#[diag(resolve_binding_in_never_pattern)]
+pub(crate) struct BindingInNeverPattern {
+    #[primary_span]
+    #[suggestion(code = "_", applicability = "machine-applicable", style = "short")]
+    pub(crate) span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(resolve_trait_impl_duplicate, code = "E0201")]
 pub(crate) struct TraitImplDuplicate {
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 82f50227911..5102c096d4b 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -65,6 +65,8 @@ enum IsRepeatExpr {
     Yes,
 }
 
+struct IsNeverPattern;
+
 /// Describes whether an `AnonConst` is a type level const arg or
 /// some other form of anon const (i.e. inline consts or enum discriminants)
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -3246,12 +3248,31 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         self.resolve_pattern_top(&local.pat, PatternSource::Let);
     }
 
-    /// build a map from pattern identifiers to binding-info's.
-    /// this is done hygienically. This could arise for a macro
-    /// that expands into an or-pattern where one 'x' was from the
-    /// user and one 'x' came from the macro.
-    fn binding_mode_map(&mut self, pat: &Pat) -> FxIndexMap<Ident, BindingInfo> {
+    /// Build a map from pattern identifiers to binding-info's, and check the bindings are
+    /// consistent when encountering or-patterns and never patterns.
+    /// This is done hygienically: this could arise for a macro that expands into an or-pattern
+    /// where one 'x' was from the user and one 'x' came from the macro.
+    ///
+    /// A never pattern by definition indicates an unreachable case. For example, matching on
+    /// `Result<T, &!>` could look like:
+    /// ```rust
+    /// # #![feature(never_type)]
+    /// # #![feature(never_patterns)]
+    /// # fn bar(_x: u32) {}
+    /// let foo: Result<u32, &!> = Ok(0);
+    /// match foo {
+    ///     Ok(x) => bar(x),
+    ///     Err(&!),
+    /// }
+    /// ```
+    /// This extends to product types: `(x, !)` is likewise unreachable. So it doesn't make sense to
+    /// have a binding here, and we tell the user to use `_` instead.
+    fn compute_and_check_binding_map(
+        &mut self,
+        pat: &Pat,
+    ) -> Result<FxIndexMap<Ident, BindingInfo>, IsNeverPattern> {
         let mut binding_map = FxIndexMap::default();
+        let mut is_never_pat = false;
 
         pat.walk(&mut |pat| {
             match pat.kind {
@@ -3263,18 +3284,27 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                 PatKind::Or(ref ps) => {
                     // Check the consistency of this or-pattern and
                     // then add all bindings to the larger map.
-                    for bm in self.check_consistent_bindings(ps) {
-                        binding_map.extend(bm);
+                    match self.compute_and_check_or_pat_binding_map(ps) {
+                        Ok(bm) => binding_map.extend(bm),
+                        Err(IsNeverPattern) => is_never_pat = true,
                     }
                     return false;
                 }
+                PatKind::Never => is_never_pat = true,
                 _ => {}
             }
 
             true
         });
 
-        binding_map
+        if is_never_pat {
+            for (_, binding) in binding_map {
+                self.report_error(binding.span, ResolutionError::BindingInNeverPattern);
+            }
+            Err(IsNeverPattern)
+        } else {
+            Ok(binding_map)
+        }
     }
 
     fn is_base_res_local(&self, nid: NodeId) -> bool {
@@ -3284,33 +3314,52 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         )
     }
 
-    /// Checks that all of the arms in an or-pattern have exactly the
-    /// same set of bindings, with the same binding modes for each.
-    fn check_consistent_bindings(
+    /// Compute the binding map for an or-pattern. Checks that all of the arms in the or-pattern
+    /// have exactly the same set of bindings, with the same binding modes for each.
+    /// Returns the computed binding map and a boolean indicating whether the pattern is a never
+    /// pattern.
+    ///
+    /// A never pattern by definition indicates an unreachable case. For example, destructuring a
+    /// `Result<T, &!>` could look like:
+    /// ```rust
+    /// # #![feature(never_type)]
+    /// # #![feature(never_patterns)]
+    /// # fn foo() -> Result<bool, &'static !> { Ok(true) }
+    /// let (Ok(x) | Err(&!)) = foo();
+    /// # let _ = x;
+    /// ```
+    /// Because the `Err(&!)` branch is never reached, it does not need to have the same bindings as
+    /// the other branches of the or-pattern. So we must ignore never pattern when checking the
+    /// bindings of an or-pattern.
+    /// Moreover, if all the subpatterns are never patterns (e.g. `Ok(!) | Err(!)`), then the
+    /// pattern as a whole counts as a never pattern (since it's definitionallly unreachable).
+    fn compute_and_check_or_pat_binding_map(
         &mut self,
         pats: &[P<Pat>],
-    ) -> Vec<FxIndexMap<Ident, BindingInfo>> {
-        // pats is consistent.
+    ) -> Result<FxIndexMap<Ident, BindingInfo>, IsNeverPattern> {
         let mut missing_vars = FxIndexMap::default();
         let mut inconsistent_vars = FxIndexMap::default();
 
-        // 1) Compute the binding maps of all arms.
-        let maps = pats.iter().map(|pat| self.binding_mode_map(pat)).collect::<Vec<_>>();
+        // 1) Compute the binding maps of all arms; we must ignore never patterns here.
+        let not_never_pats = pats
+            .iter()
+            .filter_map(|pat| {
+                let binding_map = self.compute_and_check_binding_map(pat).ok()?;
+                Some((binding_map, pat))
+            })
+            .collect::<Vec<_>>();
 
         // 2) Record any missing bindings or binding mode inconsistencies.
-        for (map_outer, pat_outer) in maps.iter().zip(pats.iter()) {
+        for (map_outer, pat_outer) in not_never_pats.iter() {
             // Check against all arms except for the same pattern which is always self-consistent.
-            let inners = maps
+            let inners = not_never_pats
                 .iter()
-                .zip(pats.iter())
                 .filter(|(_, pat)| pat.id != pat_outer.id)
-                .flat_map(|(map, _)| map)
-                .map(|(key, binding)| (key.name, map_outer.get(key), binding));
-
-            let inners = inners.collect::<Vec<_>>();
+                .flat_map(|(map, _)| map);
 
-            for (name, info, &binding_inner) in inners {
-                match info {
+            for (key, binding_inner) in inners {
+                let name = key.name;
+                match map_outer.get(key) {
                     None => {
                         // The inner binding is missing in the outer.
                         let binding_error =
@@ -3351,19 +3400,32 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             self.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(name, v.1));
         }
 
-        // 5) Finally bubble up all the binding maps.
-        maps
+        // 5) Bubble up the final binding map.
+        if not_never_pats.is_empty() {
+            // All the patterns are never patterns, so the whole or-pattern is one too.
+            Err(IsNeverPattern)
+        } else {
+            let mut binding_map = FxIndexMap::default();
+            for (bm, _) in not_never_pats {
+                binding_map.extend(bm);
+            }
+            Ok(binding_map)
+        }
     }
 
-    /// Check the consistency of the outermost or-patterns.
-    fn check_consistent_bindings_top(&mut self, pat: &'ast Pat) {
+    /// Check the consistency of bindings wrt or-patterns and never patterns.
+    fn check_consistent_bindings(&mut self, pat: &'ast Pat) {
+        let mut is_or_or_never = false;
         pat.walk(&mut |pat| match pat.kind {
-            PatKind::Or(ref ps) => {
-                self.check_consistent_bindings(ps);
+            PatKind::Or(..) | PatKind::Never => {
+                is_or_or_never = true;
                 false
             }
             _ => true,
-        })
+        });
+        if is_or_or_never {
+            let _ = self.compute_and_check_binding_map(pat);
+        }
     }
 
     fn resolve_arm(&mut self, arm: &'ast Arm) {
@@ -3392,7 +3454,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         visit::walk_pat(self, pat);
         self.resolve_pattern_inner(pat, pat_src, bindings);
         // This has to happen *after* we determine which pat_idents are variants:
-        self.check_consistent_bindings_top(pat);
+        self.check_consistent_bindings(pat);
     }
 
     /// Resolve bindings in a pattern. This is a helper to `resolve_pattern`.
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 90aa7d79bf0..313a6b6c683 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -265,6 +265,8 @@ enum ResolutionError<'a> {
     InvalidAsmSym,
     /// `self` used instead of `Self` in a generic parameter
     LowercaseSelf,
+    /// A never pattern has a binding.
+    BindingInNeverPattern,
 }
 
 enum VisResolutionError<'a> {
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs
index f3910622313..d23405ada2d 100644
--- a/tests/ui/feature-gates/feature-gate-never_patterns.rs
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs
@@ -7,7 +7,6 @@ fn main() {
     let res: Result<u32, Void> = Ok(0);
     let (Ok(_x) | Err(&!)) = res.as_ref();
     //~^ ERROR `!` patterns are experimental
-    //~| ERROR: is not bound in all patterns
 
     unsafe {
         let ptr: *const Void = NonNull::dangling().as_ptr();
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
index 1b5485d22fb..9f94e962035 100644
--- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
@@ -1,5 +1,5 @@
 error: unexpected `,` in pattern
-  --> $DIR/feature-gate-never_patterns.rs:34:16
+  --> $DIR/feature-gate-never_patterns.rs:33:16
    |
 LL |         Some(_),
    |                ^
@@ -13,14 +13,6 @@ help: ...or a vertical bar to match on multiple alternatives
 LL |         Some(_) |
    |
 
-error[E0408]: variable `_x` is not bound in all patterns
-  --> $DIR/feature-gate-never_patterns.rs:8:19
-   |
-LL |     let (Ok(_x) | Err(&!)) = res.as_ref();
-   |             --    ^^^^^^^ pattern doesn't bind `_x`
-   |             |
-   |             variable not in all patterns
-
 error[E0658]: `!` patterns are experimental
   --> $DIR/feature-gate-never_patterns.rs:8:24
    |
@@ -32,7 +24,7 @@ LL |     let (Ok(_x) | Err(&!)) = res.as_ref();
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: `!` patterns are experimental
-  --> $DIR/feature-gate-never_patterns.rs:15:13
+  --> $DIR/feature-gate-never_patterns.rs:14:13
    |
 LL |             !
    |             ^
@@ -42,7 +34,7 @@ LL |             !
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: `!` patterns are experimental
-  --> $DIR/feature-gate-never_patterns.rs:21:13
+  --> $DIR/feature-gate-never_patterns.rs:20:13
    |
 LL |             !
    |             ^
@@ -52,7 +44,7 @@ LL |             !
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: `!` patterns are experimental
-  --> $DIR/feature-gate-never_patterns.rs:26:13
+  --> $DIR/feature-gate-never_patterns.rs:25:13
    |
 LL |             ! => {}
    |             ^
@@ -62,25 +54,25 @@ LL |             ! => {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error: `match` arm with no body
-  --> $DIR/feature-gate-never_patterns.rs:39:9
+  --> $DIR/feature-gate-never_patterns.rs:38:9
    |
 LL |         Some(_)
    |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error: `match` arm with no body
-  --> $DIR/feature-gate-never_patterns.rs:44:9
+  --> $DIR/feature-gate-never_patterns.rs:43:9
    |
 LL |         Some(_) if false,
    |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error: `match` arm with no body
-  --> $DIR/feature-gate-never_patterns.rs:46:9
+  --> $DIR/feature-gate-never_patterns.rs:45:9
    |
 LL |         Some(_) if false
    |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error[E0658]: `!` patterns are experimental
-  --> $DIR/feature-gate-never_patterns.rs:51:13
+  --> $DIR/feature-gate-never_patterns.rs:50:13
    |
 LL |         Err(!),
    |             ^
@@ -90,7 +82,7 @@ LL |         Err(!),
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: `!` patterns are experimental
-  --> $DIR/feature-gate-never_patterns.rs:55:13
+  --> $DIR/feature-gate-never_patterns.rs:54:13
    |
 LL |         Err(!) if false,
    |             ^
@@ -100,24 +92,23 @@ LL |         Err(!) if false,
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error: `match` arm with no body
-  --> $DIR/feature-gate-never_patterns.rs:65:9
+  --> $DIR/feature-gate-never_patterns.rs:64:9
    |
 LL |         Some(_)
    |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error: `match` arm with no body
-  --> $DIR/feature-gate-never_patterns.rs:71:9
+  --> $DIR/feature-gate-never_patterns.rs:70:9
    |
 LL |         Some(_) if false
    |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
 
 error: a guard on a never pattern will never be run
-  --> $DIR/feature-gate-never_patterns.rs:55:19
+  --> $DIR/feature-gate-never_patterns.rs:54:19
    |
 LL |         Err(!) if false,
    |                   ^^^^^ help: remove this guard
 
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
 
-Some errors have detailed explanations: E0408, E0658.
-For more information about an error, try `rustc --explain E0408`.
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/pattern/never_patterns.rs b/tests/ui/pattern/never_patterns.rs
index 915f3e75e7b..8f44f8a6559 100644
--- a/tests/ui/pattern/never_patterns.rs
+++ b/tests/ui/pattern/never_patterns.rs
@@ -7,12 +7,9 @@ fn main() {}
 
 // The classic use for empty types.
 fn safe_unwrap_result<T>(res: Result<T, Void>) {
-    let Ok(_x) = res;
-    // FIXME(never_patterns): These should be allowed
+    let Ok(_x) = res; //~ ERROR refutable pattern in local binding
     let (Ok(_x) | Err(!)) = &res;
-    //~^ ERROR: is not bound in all patterns
     let (Ok(_x) | Err(&!)) = res.as_ref();
-    //~^ ERROR: is not bound in all patterns
 }
 
 // Check we only accept `!` where we want to.
@@ -74,26 +71,3 @@ fn never_pattern_location(void: Void) {
         Some(&(_, !)),
     }
 }
-
-fn never_and_bindings() {
-    let x: Result<bool, &(u32, Void)> = Ok(false);
-
-    // FIXME(never_patterns): Never patterns in or-patterns don't need to share the same bindings.
-    match x {
-        Ok(_x) | Err(&!) => {}
-        //~^ ERROR: is not bound in all patterns
-    }
-    let (Ok(_x) | Err(&!)) = x;
-    //~^ ERROR: is not bound in all patterns
-
-    // FIXME(never_patterns): A never pattern mustn't have bindings.
-    match x {
-        Ok(_) => {}
-        Err(&(_b, !)),
-    }
-    match x {
-        Ok(_a) | Err(&(_b, !)) => {}
-        //~^ ERROR: is not bound in all patterns
-        //~| ERROR: is not bound in all patterns
-    }
-}
diff --git a/tests/ui/pattern/never_patterns.stderr b/tests/ui/pattern/never_patterns.stderr
index 11e50debfd3..20eeb01cf71 100644
--- a/tests/ui/pattern/never_patterns.stderr
+++ b/tests/ui/pattern/never_patterns.stderr
@@ -1,51 +1,17 @@
-error[E0408]: variable `_x` is not bound in all patterns
-  --> $DIR/never_patterns.rs:12:19
+error[E0005]: refutable pattern in local binding
+  --> $DIR/never_patterns.rs:10:9
    |
-LL |     let (Ok(_x) | Err(!)) = &res;
-   |             --    ^^^^^^ pattern doesn't bind `_x`
-   |             |
-   |             variable not in all patterns
-
-error[E0408]: variable `_x` is not bound in all patterns
-  --> $DIR/never_patterns.rs:14:19
-   |
-LL |     let (Ok(_x) | Err(&!)) = res.as_ref();
-   |             --    ^^^^^^^ pattern doesn't bind `_x`
-   |             |
-   |             variable not in all patterns
-
-error[E0408]: variable `_x` is not bound in all patterns
-  --> $DIR/never_patterns.rs:83:18
-   |
-LL |         Ok(_x) | Err(&!) => {}
-   |            --    ^^^^^^^ pattern doesn't bind `_x`
-   |            |
-   |            variable not in all patterns
-
-error[E0408]: variable `_x` is not bound in all patterns
-  --> $DIR/never_patterns.rs:86:19
-   |
-LL |     let (Ok(_x) | Err(&!)) = x;
-   |             --    ^^^^^^^ pattern doesn't bind `_x`
-   |             |
-   |             variable not in all patterns
-
-error[E0408]: variable `_b` is not bound in all patterns
-  --> $DIR/never_patterns.rs:95:9
+LL |     let Ok(_x) = res;
+   |         ^^^^^^ pattern `Err(_)` not covered
    |
-LL |         Ok(_a) | Err(&(_b, !)) => {}
-   |         ^^^^^^         -- variable not in all patterns
-   |         |
-   |         pattern doesn't bind `_b`
-
-error[E0408]: variable `_a` is not bound in all patterns
-  --> $DIR/never_patterns.rs:95:18
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `Result<T, Void>`
+help: you might want to use `let else` to handle the variant that isn't matched
    |
-LL |         Ok(_a) | Err(&(_b, !)) => {}
-   |            --    ^^^^^^^^^^^^^ pattern doesn't bind `_a`
-   |            |
-   |            variable not in all patterns
+LL |     let Ok(_x) = res else { todo!() };
+   |                      ++++++++++++++++
 
-error: aborting due to 6 previous errors
+error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0408`.
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/bindings.rs b/tests/ui/rfcs/rfc-0000-never_patterns/bindings.rs
new file mode 100644
index 00000000000..f2e238ecb13
--- /dev/null
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/bindings.rs
@@ -0,0 +1,46 @@
+#![feature(never_patterns)]
+#![allow(incomplete_features)]
+
+enum Void {}
+
+fn main() {
+    let x: Result<bool, &(u32, u32, Void)> = Ok(false);
+
+    match x {
+        Ok(_x) | Err(&!) => {}
+    }
+    let (Ok(_x) | Err(&!)) = x;
+
+    match x {
+        Ok(_) => {}
+        Err(&(_a, _b, !)),
+        //~^ ERROR: never patterns cannot contain variable bindings
+        //~| ERROR: never patterns cannot contain variable bindings
+    }
+    match x {
+        Ok(_ok) | Err(&(_a, _b, !)) => {}
+        //~^ ERROR: never patterns cannot contain variable bindings
+        //~| ERROR: never patterns cannot contain variable bindings
+    }
+}
+
+fn void(void: Void) {
+    let (_a | !) = void;
+    let (! | _a) = void;
+    let ((_a, _) | (_a, _ | !)) = (true, void);
+    let (_a | (! | !,)) = (void,);
+    let ((_a,) | (!,)) = (void,);
+
+    let (_a, (! | !)) = (true, void);
+    //~^ ERROR: never patterns cannot contain variable bindings
+    let (_a, (_b | !)) = (true, void);
+
+    let _a @ ! = void;
+    //~^ ERROR: never patterns cannot contain variable bindings
+    let _a @ (_b | !) = void;
+    let (_a @ (), !) = ((), void);
+    //~^ ERROR: never patterns cannot contain variable bindings
+    let (_a |
+            (_b @ (_, !))) = (true, void);
+    //~^ ERROR: never patterns cannot contain variable bindings
+}
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/bindings.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/bindings.stderr
new file mode 100644
index 00000000000..b69ba80af88
--- /dev/null
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/bindings.stderr
@@ -0,0 +1,50 @@
+error: never patterns cannot contain variable bindings
+  --> $DIR/bindings.rs:16:15
+   |
+LL |         Err(&(_a, _b, !)),
+   |               ^^ help: use a wildcard `_` instead
+
+error: never patterns cannot contain variable bindings
+  --> $DIR/bindings.rs:16:19
+   |
+LL |         Err(&(_a, _b, !)),
+   |                   ^^ help: use a wildcard `_` instead
+
+error: never patterns cannot contain variable bindings
+  --> $DIR/bindings.rs:21:25
+   |
+LL |         Ok(_ok) | Err(&(_a, _b, !)) => {}
+   |                         ^^ help: use a wildcard `_` instead
+
+error: never patterns cannot contain variable bindings
+  --> $DIR/bindings.rs:21:29
+   |
+LL |         Ok(_ok) | Err(&(_a, _b, !)) => {}
+   |                             ^^ help: use a wildcard `_` instead
+
+error: never patterns cannot contain variable bindings
+  --> $DIR/bindings.rs:34:10
+   |
+LL |     let (_a, (! | !)) = (true, void);
+   |          ^^ help: use a wildcard `_` instead
+
+error: never patterns cannot contain variable bindings
+  --> $DIR/bindings.rs:38:9
+   |
+LL |     let _a @ ! = void;
+   |         ^^ help: use a wildcard `_` instead
+
+error: never patterns cannot contain variable bindings
+  --> $DIR/bindings.rs:41:10
+   |
+LL |     let (_a @ (), !) = ((), void);
+   |          ^^ help: use a wildcard `_` instead
+
+error: never patterns cannot contain variable bindings
+  --> $DIR/bindings.rs:44:14
+   |
+LL |             (_b @ (_, !))) = (true, void);
+   |              ^^ help: use a wildcard `_` instead
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/parse.rs b/tests/ui/rfcs/rfc-0000-never_patterns/parse.rs
index f254b9c201c..566bb071646 100644
--- a/tests/ui/rfcs/rfc-0000-never_patterns/parse.rs
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/parse.rs
@@ -71,6 +71,7 @@ fn parse(x: Void) {
 
     let ! = x;
     let y @ ! = x;
+    //~^ ERROR: never patterns cannot contain variable bindings
 }
 
 fn foo(!: Void) {}
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr
index e81a13a3967..17d1b7e0d43 100644
--- a/tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr
@@ -22,6 +22,12 @@ error: top-level or-patterns are not allowed in `let` bindings
 LL |     let Ok(_) | Err(!) = &res; // Disallowed; see #82048.
    |         ^^^^^^^^^^^^^^ help: wrap the pattern in parentheses: `(Ok(_) | Err(!))`
 
+error: never patterns cannot contain variable bindings
+  --> $DIR/parse.rs:73:9
+   |
+LL |     let y @ ! = x;
+   |         ^ help: use a wildcard `_` instead
+
 error: a guard on a never pattern will never be run
   --> $DIR/parse.rs:31:20
    |
@@ -40,5 +46,5 @@ error: a guard on a never pattern will never be run
 LL |         never!() if true,
    |                     ^^^^ help: remove this guard
 
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors