about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-11-29 11:42:03 +0000
committerbors <bors@rust-lang.org>2023-11-29 11:42:03 +0000
commitce4727f723144aea42d66c09a0a7e75f8c51b8fd (patch)
treedea63ac4b1c641a0b2da8eb142fe4fb31877b441
parentec1f21cb0483c40329b54c10b19c0e6220f5e51d (diff)
parent6094cb5d3635b17b51e0f1cfe83e5532eada064e (diff)
downloadrust-ce4727f723144aea42d66c09a0a7e75f8c51b8fd.tar.gz
rust-ce4727f723144aea42d66c09a0a7e75f8c51b8fd.zip
Auto merge of #118443 - matthiaskrgr:rollup-mp8o3m4, r=matthiaskrgr
Rollup of 7 pull requests

Successful merges:

 - #118157 (Add `never_patterns` feature gate)
 - #118191 (Suggest `let` or `==` on typo'd let-chain)
 - #118231 (also add is_empty to const raw slices)
 - #118333 (Print list of missing target features when calling a function with target features outside an unsafe block)
 - #118426 (ConstProp: Correctly remove const if unknown value assigned to it.)
 - #118428 (rustdoc: Move `AssocItemRender` and `RenderMode` to `html::render`.)
 - #118438 (Update nto-qnx.md)

Failed merges:

 - #118268 (Pretty print `Fn<(..., ...)>` trait refs with parentheses (almost) always)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_ast/src/ast.rs4
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs2
-rw-r--r--compiler/rustc_ast/src/visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/pat.rs1
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs1
-rw-r--r--compiler/rustc_feature/src/unstable.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs7
-rw-r--r--compiler/rustc_hir/src/intravisit.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs1
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs8
-rw-r--r--compiler/rustc_lint/src/unused.rs2
-rw-r--r--compiler/rustc_middle/src/mir/query.rs13
-rw-r--r--compiler/rustc_middle/src/thir.rs11
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs2
-rw-r--r--compiler/rustc_mir_build/messages.ftl36
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs1
-rw-r--r--compiler/rustc_mir_build/src/build/matches/simplify.rs6
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs2
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs83
-rw-r--r--compiler/rustc_mir_build/src/errors.rs25
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/print.rs3
-rw-r--r--compiler/rustc_mir_transform/messages.ftl13
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs29
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs7
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs125
-rw-r--r--compiler/rustc_parse/messages.ftl4
-rw-r--r--compiler/rustc_parse/src/errors.rs26
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs48
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs6
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs17
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/ptr/const_ptr.rs18
-rw-r--r--library/core/src/ptr/mut_ptr.rs6
-rw-r--r--src/doc/rustc/src/platform-support/nto-qnx.md6
-rw-r--r--src/librustdoc/clean/utils.rs3
-rw-r--r--src/librustdoc/formats/mod.rs15
-rw-r--r--src/librustdoc/html/render/mod.rs17
-rw-r--r--src/librustdoc/html/render/print_item.rs5
-rw-r--r--src/librustdoc/html/render/write_shared.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/equatable_if_let.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs1
-rw-r--r--src/tools/rustfmt/src/patterns.rs5
-rw-r--r--tests/mir-opt/const_prop/overwrite_with_const_with_params.rs26
-rw-r--r--tests/mir-opt/const_prop/overwrite_with_const_with_params.size_of.ConstProp.diff20
-rw-r--r--tests/ui/const_prop/overwrite_with_const_with_params.rs21
-rw-r--r--tests/ui/expr/if/bad-if-let-suggestion.rs3
-rw-r--r--tests/ui/expr/if/bad-if-let-suggestion.stderr22
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.rs27
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.stderr39
-rw-r--r--tests/ui/macros/stringify.rs5
-rw-r--r--tests/ui/pattern/never_patterns.rs99
-rw-r--r--tests/ui/pattern/never_patterns.stderr51
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr78
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs20
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr78
-rw-r--r--tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr8
66 files changed, 909 insertions, 187 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 2b56e9f3a86..cba9121aa5a 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -645,6 +645,7 @@ impl Pat {
             // These patterns do not contain subpatterns, skip.
             PatKind::Wild
             | PatKind::Rest
+            | PatKind::Never
             | PatKind::Lit(_)
             | PatKind::Range(..)
             | PatKind::Ident(..)
@@ -795,6 +796,9 @@ pub enum PatKind {
     /// only one rest pattern may occur in the pattern sequences.
     Rest,
 
+    // A never pattern `!`
+    Never,
+
     /// Parentheses in patterns used for grouping (i.e., `(PAT)`).
     Paren(P<Pat>),
 
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 541b9872922..8ce86bf9ecf 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1249,7 +1249,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
     let Pat { id, kind, span, tokens } = pat.deref_mut();
     vis.visit_id(id);
     match kind {
-        PatKind::Wild | PatKind::Rest => {}
+        PatKind::Wild | PatKind::Rest | PatKind::Never => {}
         PatKind::Ident(_binding_mode, ident, sub) => {
             vis.visit_ident(ident);
             visit_opt(sub, |sub| vis.visit_pat(sub));
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 1caa39e2dd9..9dbadcb49d3 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -559,7 +559,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
             walk_list!(visitor, visit_expr, lower_bound);
             walk_list!(visitor, visit_expr, upper_bound);
         }
-        PatKind::Wild | PatKind::Rest => {}
+        PatKind::Wild | PatKind::Rest | PatKind::Never => {}
         PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
             walk_list!(visitor, visit_pat, elems);
         }
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index 4950f6e54b5..017314ee4d1 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -24,6 +24,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             let node = loop {
                 match &pattern.kind {
                     PatKind::Wild => break hir::PatKind::Wild,
+                    PatKind::Never => break hir::PatKind::Never,
                     PatKind::Ident(binding_mode, ident, sub) => {
                         let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(s));
                         break self.lower_pat_ident(pattern, *binding_mode, *ident, lower_sub);
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 8fb7c7de50c..8851fbd28f0 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -555,6 +555,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(explicit_tail_calls, "`become` expression is experimental");
     gate_all!(generic_const_items, "generic const items are experimental");
     gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
+    gate_all!(never_patterns, "`!` patterns are experimental");
 
     if !visitor.features.negative_bounds {
         for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 86aba343bc4..cbdcc683b56 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1343,6 +1343,7 @@ impl<'a> State<'a> {
         is that it doesn't matter */
         match &pat.kind {
             PatKind::Wild => self.word("_"),
+            PatKind::Never => self.word("!"),
             PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => {
                 if *by_ref == ByRef::Yes {
                     self.word_nbsp("ref");
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index f73a286e6af..8182c8779ff 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -155,7 +155,7 @@ macro_rules! declare_features {
 // was set.
 //
 // Note that the features are grouped into internal/user-facing and then
-// sorted by version inside those groups. This is enforced with tidy.
+// sorted alphabetically inside those groups. This is enforced with tidy.
 //
 // N.B., `tools/tidy/src/features.rs` parses this information directly out of the
 // source, so take care when modifying it.
@@ -520,6 +520,8 @@ declare_features! (
     (unstable, native_link_modifiers_as_needed, "1.53.0", Some(81490), None),
     /// Allow negative trait implementations.
     (unstable, negative_impls, "1.44.0", Some(68318), None),
+    /// Allows the `!` pattern.
+    (incomplete, never_patterns, "CURRENT_RUSTC_VERSION", Some(118155), None),
     /// Allows the `!` type. Does not imply 'exhaustive_patterns' (below) any more.
     (unstable, never_type, "1.13.0", Some(35121), None),
     /// Allows diverging expressions to fall back to `!` rather than `()`.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index bd6eba37547..81733d8f64e 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1002,7 +1002,7 @@ impl<'hir> Pat<'hir> {
 
         use PatKind::*;
         match self.kind {
-            Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => true,
+            Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) => true,
             Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
             Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
             TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
@@ -1029,7 +1029,7 @@ impl<'hir> Pat<'hir> {
 
         use PatKind::*;
         match self.kind {
-            Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => {}
+            Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) => {}
             Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
             Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
             TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
@@ -1142,6 +1142,9 @@ pub enum PatKind<'hir> {
     /// Invariant: `pats.len() >= 2`.
     Or(&'hir [Pat<'hir>]),
 
+    /// A never pattern `!`.
+    Never,
+
     /// A path pattern for a unit struct/variant or a (maybe-associated) constant.
     Path(QPath<'hir>),
 
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 8a672855989..963b324ca13 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -660,7 +660,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) {
             walk_list!(visitor, visit_expr, lower_bound);
             walk_list!(visitor, visit_expr, upper_bound);
         }
-        PatKind::Wild => (),
+        PatKind::Never | PatKind::Wild => (),
         PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => {
             walk_list!(visitor, visit_pat, prepatterns);
             walk_list!(visitor, visit_pat, slice_pattern);
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 9557568b387..37b308f9f88 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -662,6 +662,7 @@ fn resolve_local<'tcx>(
             PatKind::Ref(_, _)
             | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..)
             | PatKind::Wild
+            | PatKind::Never
             | PatKind::Path(_)
             | PatKind::Lit(_)
             | PatKind::Range(_, _, _) => false,
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index e9e8c7fd4fc..b7e7d258a90 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1724,6 +1724,7 @@ impl<'a> State<'a> {
         // is that it doesn't matter
         match pat.kind {
             PatKind::Wild => self.word("_"),
+            PatKind::Never => self.word("!"),
             PatKind::Binding(BindingAnnotation(by_ref, mutbl), _, ident, sub) => {
                 if by_ref == ByRef::Yes {
                     self.word_nbsp("ref");
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index d7cf6dba9aa..9991050d7b2 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -401,12 +401,17 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
             mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
                 match &pat.kind {
                     PatKind::Binding(.., opt_sub_pat) => {
-                        // If the opt_sub_pat is None, than the binding does not count as
+                        // If the opt_sub_pat is None, then the binding does not count as
                         // a wildcard for the purpose of borrowing discr.
                         if opt_sub_pat.is_none() {
                             needs_to_be_read = true;
                         }
                     }
+                    PatKind::Never => {
+                        // A never pattern reads the value.
+                        // FIXME(never_patterns): does this do what I expect?
+                        needs_to_be_read = true;
+                    }
                     PatKind::Path(qpath) => {
                         // A `Path` pattern is just a name like `Foo`. This is either a
                         // named constant or else it refers to an ADT variant
diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs
index 94da4bfcdc4..0bcb7c7b5d7 100644
--- a/compiler/rustc_hir_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs
@@ -766,6 +766,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
             | PatKind::Binding(.., None)
             | PatKind::Lit(..)
             | PatKind::Range(..)
+            | PatKind::Never
             | PatKind::Wild => {
                 // always ok
             }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index f11ffabf4d4..1ecf553d71d 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -178,6 +178,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let ty = match pat.kind {
             PatKind::Wild => expected,
+            // FIXME(never_patterns): check the type is uninhabited. If that is not possible within
+            // typeck, do that in a later phase.
+            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),
             PatKind::Binding(ba, var_id, _, sub) => {
@@ -287,9 +290,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             | PatKind::Box(_)
             | PatKind::Range(..)
             | PatKind::Slice(..) => AdjustMode::Peel,
+            // A never pattern behaves somewhat like a literal or unit variant.
+            PatKind::Never => AdjustMode::Peel,
             // String and byte-string literals result in types `&str` and `&[u8]` respectively.
             // All other literals result in non-reference types.
-            // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`.
+            // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`.
             //
             // Call `resolve_vars_if_possible` here for inline const blocks.
             PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() {
@@ -743,6 +748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         | PatKind::Slice(..) => "binding",
 
                         PatKind::Wild
+                        | PatKind::Never
                         | PatKind::Binding(..)
                         | PatKind::Path(..)
                         | PatKind::Box(..)
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index a7a4709e887..0a167b0893c 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1154,7 +1154,7 @@ impl EarlyLintPass for UnusedParens {
             // Do not lint on `(..)` as that will result in the other arms being useless.
             Paren(_)
             // The other cases do not contain sub-patterns.
-            | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {},
+            | Wild | Never | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {},
             // These are list-like patterns; parens can always be removed.
             TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
                 self.check_unused_parens_pat(cx, p, false, false, keep_space);
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index ab8fef5129b..98642adc190 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -27,7 +27,7 @@ pub enum UnsafetyViolationKind {
     UnsafeFn,
 }
 
-#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
+#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
 pub enum UnsafetyViolationDetails {
     CallToUnsafeFunction,
     UseOfInlineAssembly,
@@ -39,10 +39,17 @@ pub enum UnsafetyViolationDetails {
     AccessToUnionField,
     MutationOfLayoutConstrainedField,
     BorrowOfLayoutConstrainedField,
-    CallToFunctionWith,
+    CallToFunctionWith {
+        /// Target features enabled in callee's `#[target_feature]` but missing in
+        /// caller's `#[target_feature]`.
+        missing: Vec<Symbol>,
+        /// Target features in `missing` that are enabled at compile time
+        /// (e.g., with `-C target-feature`).
+        build_enabled: Vec<Symbol>,
+    },
 }
 
-#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
+#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
 pub struct UnsafetyViolation {
     pub source_info: SourceInfo,
     pub lint_root: hir::HirId,
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 00c33113692..c48428c713c 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -635,7 +635,12 @@ impl<'tcx> Pat<'tcx> {
 
         use PatKind::*;
         match &self.kind {
-            Wild | Range(..) | Binding { subpattern: None, .. } | Constant { .. } | Error(_) => {}
+            Wild
+            | Never
+            | Range(..)
+            | Binding { subpattern: None, .. }
+            | Constant { .. }
+            | Error(_) => {}
             AscribeUserType { subpattern, .. }
             | Binding { subpattern: Some(subpattern), .. }
             | Deref { subpattern }
@@ -809,6 +814,9 @@ pub enum PatKind<'tcx> {
         pats: Box<[Box<Pat<'tcx>>]>,
     },
 
+    /// A never pattern `!`.
+    Never,
+
     /// An error has been encountered during lowering. We probably shouldn't report more lints
     /// related to this pattern.
     Error(ErrorGuaranteed),
@@ -1069,6 +1077,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
 
         match self.kind {
             PatKind::Wild => write!(f, "_"),
+            PatKind::Never => write!(f, "!"),
             PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"),
             PatKind::Binding { mutability, name, mode, ref subpattern, .. } => {
                 let is_mut = match mode {
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index 62c3ceeab8a..4943c11848b 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -227,7 +227,7 @@ pub fn walk_pat<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, pat: &Pat<'
             is_primary: _,
             name: _,
         } => visitor.visit_pat(subpattern),
-        Binding { .. } | Wild | Error(_) => {}
+        Binding { .. } | Wild | Never | Error(_) => {}
         Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => {
             for subpattern in subpatterns {
                 visitor.visit_pat(&subpattern.pattern);
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 7dd0e7d4b92..c8d6c2114e9 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -30,12 +30,32 @@ mir_build_borrow_of_moved_value = borrow of moved value
 
 mir_build_call_to_fn_with_requires_unsafe =
     call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block
-    .note = can only be called if the required target features are available
+    .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
+        [1] feature
+        *[count] features
+        }: {$missing_target_features}
+    .note = the {$build_target_features} target {$build_target_features_count ->
+        [1] feature
+        *[count] features
+        } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
+        [1] it
+        *[count] them
+        } in `#[target_feature]`
     .label = call to function with `#[target_feature]`
 
 mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
     call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe function or block
-    .note = can only be called if the required target features are available
+    .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
+        [1] feature
+        *[count] features
+        }: {$missing_target_features}
+    .note = the {$build_target_features} target {$build_target_features_count ->
+        [1] feature
+        *[count] features
+        } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
+        [1] it
+        *[count] them
+        } in `#[target_feature]`
     .label = call to function with `#[target_feature]`
 
 mir_build_call_to_unsafe_fn_requires_unsafe =
@@ -330,7 +350,17 @@ mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_uns
 
 mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe =
     call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
-    .note = can only be called if the required target features are available
+    .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
+        [1] feature
+        *[count] features
+        }: {$missing_target_features}
+    .note = the {$build_target_features} target {$build_target_features_count ->
+        [1] feature
+        *[count] features
+        } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
+        [1] it
+        *[count] them
+        } in `#[target_feature]`
     .label = call to function with `#[target_feature]`
 
 mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe =
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 83686667a4a..90f950d59d5 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -827,6 +827,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             PatKind::Constant { .. }
             | PatKind::Range { .. }
             | PatKind::Wild
+            | PatKind::Never
             | PatKind::Error(_) => {}
 
             PatKind::Deref { ref subpattern } => {
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs
index 8a6cb26242a..a7f6f4873e3 100644
--- a/compiler/rustc_mir_build/src/build/matches/simplify.rs
+++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs
@@ -194,6 +194,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 Ok(())
             }
 
+            PatKind::Never => {
+                // A never pattern acts like a load from the place.
+                // FIXME(never_patterns): load from the place
+                Ok(())
+            }
+
             PatKind::Constant { .. } => {
                 // FIXME normalize patterns when possible
                 Err(match_pair)
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 6bd60972c8b..2e7182f261e 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -75,6 +75,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             | PatKind::Array { .. }
             | PatKind::Wild
             | PatKind::Binding { .. }
+            | PatKind::Never
             | PatKind::Leaf { .. }
             | PatKind::Deref { .. }
             | PatKind::Error(_) => self.error_simplifiable(match_pair),
@@ -107,6 +108,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             PatKind::Slice { .. }
             | PatKind::Array { .. }
             | PatKind::Wild
+            | PatKind::Never
             | PatKind::Or { .. }
             | PatKind::Binding { .. }
             | PatKind::AscribeUserType { .. }
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index bbaa02233e5..c17fdf71b55 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -1,7 +1,10 @@
+use std::borrow::Cow;
+
 use crate::build::ExprCategory;
 use crate::errors::*;
 use rustc_middle::thir::visit::{self, Visitor};
 
+use rustc_errors::DiagnosticArgValue;
 use rustc_hir as hir;
 use rustc_middle::mir::BorrowKind;
 use rustc_middle::thir::*;
@@ -247,8 +250,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                     self.requires_unsafe(pat.span, AccessToUnionField);
                     return; // we can return here since this already requires unsafe
                 }
-                // wildcard doesn't take anything
+                // wildcard/never don't take anything
                 PatKind::Wild |
+                PatKind::Never |
                 // these just wrap other patterns
                 PatKind::Or { .. } |
                 PatKind::InlineConstant { .. } |
@@ -392,15 +396,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                     // the call requires `unsafe`. Don't check this on wasm
                     // targets, though. For more information on wasm see the
                     // is_like_wasm check in hir_analysis/src/collect.rs
+                    let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
                     if !self.tcx.sess.target.options.is_like_wasm
-                        && !self
-                            .tcx
-                            .codegen_fn_attrs(func_did)
-                            .target_features
+                        && !callee_features
                             .iter()
                             .all(|feature| self.body_target_features.contains(feature))
                     {
-                        self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
+                        let missing: Vec<_> = callee_features
+                            .iter()
+                            .copied()
+                            .filter(|feature| !self.body_target_features.contains(feature))
+                            .collect();
+                        let build_enabled = self
+                            .tcx
+                            .sess
+                            .target_features
+                            .iter()
+                            .copied()
+                            .filter(|feature| missing.contains(feature))
+                            .collect();
+                        self.requires_unsafe(
+                            expr.span,
+                            CallToFunctionWith { function: func_did, missing, build_enabled },
+                        );
                     }
                 }
             }
@@ -526,7 +544,7 @@ struct UnusedUnsafeWarning {
     enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
 }
 
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, PartialEq)]
 enum UnsafeOpKind {
     CallToUnsafeFunction(Option<DefId>),
     UseOfInlineAssembly,
@@ -537,7 +555,15 @@ enum UnsafeOpKind {
     AccessToUnionField,
     MutationOfLayoutConstrainedField,
     BorrowOfLayoutConstrainedField,
-    CallToFunctionWith(DefId),
+    CallToFunctionWith {
+        function: DefId,
+        /// Target features enabled in callee's `#[target_feature]` but missing in
+        /// caller's `#[target_feature]`.
+        missing: Vec<Symbol>,
+        /// Target features in `missing` that are enabled at compile time
+        /// (e.g., with `-C target-feature`).
+        build_enabled: Vec<Symbol>,
+    },
 }
 
 use UnsafeOpKind::*;
@@ -658,13 +684,22 @@ impl UnsafeOpKind {
                     unsafe_not_inherited_note,
                 },
             ),
-            CallToFunctionWith(did) => tcx.emit_spanned_lint(
+            CallToFunctionWith { function, missing, build_enabled } => tcx.emit_spanned_lint(
                 UNSAFE_OP_IN_UNSAFE_FN,
                 hir_id,
                 span,
                 UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
                     span,
-                    function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
+                    function: &with_no_trimmed_paths!(tcx.def_path_str(*function)),
+                    missing_target_features: DiagnosticArgValue::StrListSepByAnd(
+                        missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
+                    ),
+                    missing_target_features_count: missing.len(),
+                    note: if build_enabled.is_empty() { None } else { Some(()) },
+                    build_target_features: DiagnosticArgValue::StrListSepByAnd(
+                        build_enabled.iter().map(|feature| Cow::from(feature.as_str())).collect(),
+                    ),
+                    build_target_features_count: build_enabled.len(),
                     unsafe_not_inherited_note,
                 },
             ),
@@ -821,18 +856,38 @@ impl UnsafeOpKind {
                     unsafe_not_inherited_note,
                 });
             }
-            CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => {
+            CallToFunctionWith { function, missing, build_enabled }
+                if unsafe_op_in_unsafe_fn_allowed =>
+            {
                 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
                     span,
+                    missing_target_features: DiagnosticArgValue::StrListSepByAnd(
+                        missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
+                    ),
+                    missing_target_features_count: missing.len(),
+                    note: if build_enabled.is_empty() { None } else { Some(()) },
+                    build_target_features: DiagnosticArgValue::StrListSepByAnd(
+                        build_enabled.iter().map(|feature| Cow::from(feature.as_str())).collect(),
+                    ),
+                    build_target_features_count: build_enabled.len(),
                     unsafe_not_inherited_note,
-                    function: &tcx.def_path_str(*did),
+                    function: &tcx.def_path_str(*function),
                 });
             }
-            CallToFunctionWith(did) => {
+            CallToFunctionWith { function, missing, build_enabled } => {
                 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe {
                     span,
+                    missing_target_features: DiagnosticArgValue::StrListSepByAnd(
+                        missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
+                    ),
+                    missing_target_features_count: missing.len(),
+                    note: if build_enabled.is_empty() { None } else { Some(()) },
+                    build_target_features: DiagnosticArgValue::StrListSepByAnd(
+                        build_enabled.iter().map(|feature| Cow::from(feature.as_str())).collect(),
+                    ),
+                    build_target_features_count: build_enabled.len(),
                     unsafe_not_inherited_note,
-                    function: &tcx.def_path_str(*did),
+                    function: &tcx.def_path_str(*function),
                 });
             }
         }
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 418f9bb9de9..8d2a559e73c 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -2,6 +2,7 @@ use crate::{
     fluent_generated as fluent,
     thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt},
 };
+use rustc_errors::DiagnosticArgValue;
 use rustc_errors::{
     error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
     Handler, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
@@ -124,11 +125,17 @@ pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
 
 #[derive(LintDiagnostic)]
 #[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe)]
-#[note]
+#[help]
 pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe<'a> {
     #[label]
     pub span: Span,
     pub function: &'a str,
+    pub missing_target_features: DiagnosticArgValue<'a>,
+    pub missing_target_features_count: usize,
+    #[note]
+    pub note: Option<()>,
+    pub build_target_features: DiagnosticArgValue<'a>,
+    pub build_target_features_count: usize,
     #[subdiagnostic]
     pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
 }
@@ -369,24 +376,36 @@ pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed
 
 #[derive(Diagnostic)]
 #[diag(mir_build_call_to_fn_with_requires_unsafe, code = "E0133")]
-#[note]
+#[help]
 pub struct CallToFunctionWithRequiresUnsafe<'a> {
     #[primary_span]
     #[label]
     pub span: Span,
     pub function: &'a str,
+    pub missing_target_features: DiagnosticArgValue<'a>,
+    pub missing_target_features_count: usize,
+    #[note]
+    pub note: Option<()>,
+    pub build_target_features: DiagnosticArgValue<'a>,
+    pub build_target_features_count: usize,
     #[subdiagnostic]
     pub unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
 }
 
 #[derive(Diagnostic)]
 #[diag(mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
-#[note]
+#[help]
 pub struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> {
     #[primary_span]
     #[label]
     pub span: Span,
     pub function: &'a str,
+    pub missing_target_features: DiagnosticArgValue<'a>,
+    pub missing_target_features_count: usize,
+    #[note]
+    pub note: Option<()>,
+    pub build_target_features: DiagnosticArgValue<'a>,
+    pub build_target_features_count: usize,
     #[subdiagnostic]
     pub unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index 8ddc6c924e2..31114190f07 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -1557,6 +1557,12 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
                 let pats = expand_or_pat(pat);
                 fields = Fields::from_iter(cx, pats.into_iter().map(mkpat));
             }
+            PatKind::Never => {
+                // FIXME(never_patterns): handle `!` in exhaustiveness. This is a sane default
+                // in the meantime.
+                ctor = Wildcard;
+                fields = Fields::empty();
+            }
             PatKind::Error(_) => {
                 ctor = Opaque(OpaqueId::new());
                 fields = Fields::empty();
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 8b2a96cff41..5edb2054fd4 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -251,6 +251,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         let kind = match pat.kind {
             hir::PatKind::Wild => PatKind::Wild,
 
+            hir::PatKind::Never => PatKind::Never,
+
             hir::PatKind::Lit(value) => self.lower_lit(value),
 
             hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index e449928a643..547da7e7007 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -642,6 +642,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
             PatKind::Wild => {
                 print_indented!(self, "Wild", depth_lvl + 1);
             }
+            PatKind::Never => {
+                print_indented!(self, "Never", depth_lvl + 1);
+            }
             PatKind::AscribeUserType { ascription, subpattern } => {
                 print_indented!(self, "AscribeUserType: {", depth_lvl + 1);
                 print_indented!(self, format!("ascription: {:?}", ascription), depth_lvl + 2);
diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl
index 5a99afc45b0..b8dbdf18db3 100644
--- a/compiler/rustc_mir_transform/messages.ftl
+++ b/compiler/rustc_mir_transform/messages.ftl
@@ -42,8 +42,19 @@ mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in
     }
     .not_inherited = items do not inherit unsafety from separate enclosing items
 
+mir_transform_target_feature_call_help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
+    [1] feature
+    *[count] features
+    }: {$missing_target_features}
+
 mir_transform_target_feature_call_label = call to function with `#[target_feature]`
-mir_transform_target_feature_call_note = can only be called if the required target features are available
+mir_transform_target_feature_call_note = the {$build_target_features} target {$build_target_features_count ->
+    [1] feature
+    *[count] features
+    } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
+    [1] it
+    *[count] them
+    } in `#[target_feature]`
 
 mir_transform_unaligned_packed_ref = reference to packed field is unaligned
     .note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index c63b24b194f..84e7362c3d0 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -287,19 +287,20 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
             .safety;
         match safety {
             // `unsafe` blocks are required in safe code
-            Safety::Safe => violations.into_iter().for_each(|&violation| {
+            Safety::Safe => violations.into_iter().for_each(|violation| {
                 match violation.kind {
                     UnsafetyViolationKind::General => {}
                     UnsafetyViolationKind::UnsafeFn => {
                         bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
                     }
                 }
-                if !self.violations.contains(&violation) {
-                    self.violations.push(violation)
+                if !self.violations.contains(violation) {
+                    self.violations.push(violation.clone())
                 }
             }),
             // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
-            Safety::FnUnsafe => violations.into_iter().for_each(|&(mut violation)| {
+            Safety::FnUnsafe => violations.into_iter().for_each(|violation| {
+                let mut violation = violation.clone();
                 violation.kind = UnsafetyViolationKind::UnsafeFn;
                 if !self.violations.contains(&violation) {
                     self.violations.push(violation)
@@ -367,9 +368,22 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
 
         // Is `callee_features` a subset of `calling_features`?
         if !callee_features.iter().all(|feature| self_features.contains(feature)) {
+            let missing: Vec<_> = callee_features
+                .iter()
+                .copied()
+                .filter(|feature| !self_features.contains(feature))
+                .collect();
+            let build_enabled = self
+                .tcx
+                .sess
+                .target_features
+                .iter()
+                .copied()
+                .filter(|feature| missing.contains(feature))
+                .collect();
             self.require_unsafe(
                 UnsafetyViolationKind::General,
-                UnsafetyViolationDetails::CallToFunctionWith,
+                UnsafetyViolationDetails::CallToFunctionWith { missing, build_enabled },
             )
         }
     }
@@ -528,8 +542,9 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     // Only suggest wrapping the entire function body in an unsafe block once
     let mut suggest_unsafe_block = true;
 
-    for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
-        let details = errors::RequiresUnsafeDetail { violation: details, span: source_info.span };
+    for &UnsafetyViolation { source_info, lint_root, kind, ref details } in violations.iter() {
+        let details =
+            errors::RequiresUnsafeDetail { violation: details.clone(), span: source_info.span };
 
         match kind {
             UnsafetyViolationKind::General => {
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 9a16003bdc9..b96125de95e 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -439,6 +439,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
 
         // FIXME we need to revisit this for #67176
         if rvalue.has_param() {
+            trace!("skipping, has param");
             return None;
         }
         if !rvalue
@@ -707,7 +708,11 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
     fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
         self.super_assign(place, rvalue, location);
 
-        let Some(()) = self.check_rvalue(rvalue) else { return };
+        let Some(()) = self.check_rvalue(rvalue) else {
+            trace!("rvalue check failed, removing const");
+            Self::remove_const(&mut self.ecx, place.local);
+            return;
+        };
 
         match self.ecx.machine.can_const_prop[place.local] {
             // Do nothing if the place is indirect.
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index 5879a803946..3a5270f105a 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -1,6 +1,8 @@
+use std::borrow::Cow;
+
 use rustc_errors::{
-    Applicability, DecorateLint, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
-    IntoDiagnostic,
+    Applicability, DecorateLint, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage,
+    EmissionGuarantee, Handler, IntoDiagnostic,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
@@ -9,6 +11,8 @@ use rustc_session::lint::{self, Lint};
 use rustc_span::def_id::DefId;
 use rustc_span::Span;
 
+use crate::fluent_generated as fluent;
+
 #[derive(LintDiagnostic)]
 pub(crate) enum ConstMutate {
     #[diag(mir_transform_const_modify)]
@@ -61,72 +65,105 @@ pub(crate) struct RequiresUnsafe {
 impl<'sess, G: EmissionGuarantee> IntoDiagnostic<'sess, G> for RequiresUnsafe {
     #[track_caller]
     fn into_diagnostic(self, handler: &'sess Handler) -> DiagnosticBuilder<'sess, G> {
-        let mut diag =
-            handler.struct_diagnostic(crate::fluent_generated::mir_transform_requires_unsafe);
+        let mut diag = handler.struct_diagnostic(fluent::mir_transform_requires_unsafe);
         diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string()));
         diag.set_span(self.span);
         diag.span_label(self.span, self.details.label());
-        diag.note(self.details.note());
         let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
         diag.set_arg("details", desc);
         diag.set_arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
+        self.details.add_subdiagnostics(&mut diag);
         if let Some(sp) = self.enclosing {
-            diag.span_label(sp, crate::fluent_generated::mir_transform_not_inherited);
+            diag.span_label(sp, fluent::mir_transform_not_inherited);
         }
         diag
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Clone)]
 pub(crate) struct RequiresUnsafeDetail {
     pub span: Span,
     pub violation: UnsafetyViolationDetails,
 }
 
 impl RequiresUnsafeDetail {
-    fn note(self) -> DiagnosticMessage {
+    fn add_subdiagnostics<G: EmissionGuarantee>(&self, diag: &mut DiagnosticBuilder<'_, G>) {
         use UnsafetyViolationDetails::*;
         match self.violation {
-            CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_note,
-            UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_note,
+            CallToUnsafeFunction => {
+                diag.note(fluent::mir_transform_call_to_unsafe_note);
+            }
+            UseOfInlineAssembly => {
+                diag.note(fluent::mir_transform_use_of_asm_note);
+            }
             InitializingTypeWith => {
-                crate::fluent_generated::mir_transform_initializing_valid_range_note
+                diag.note(fluent::mir_transform_initializing_valid_range_note);
+            }
+            CastOfPointerToInt => {
+                diag.note(fluent::mir_transform_const_ptr2int_note);
+            }
+            UseOfMutableStatic => {
+                diag.note(fluent::mir_transform_use_of_static_mut_note);
+            }
+            UseOfExternStatic => {
+                diag.note(fluent::mir_transform_use_of_extern_static_note);
+            }
+            DerefOfRawPointer => {
+                diag.note(fluent::mir_transform_deref_ptr_note);
+            }
+            AccessToUnionField => {
+                diag.note(fluent::mir_transform_union_access_note);
             }
-            CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_note,
-            UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_note,
-            UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_note,
-            DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_note,
-            AccessToUnionField => crate::fluent_generated::mir_transform_union_access_note,
             MutationOfLayoutConstrainedField => {
-                crate::fluent_generated::mir_transform_mutation_layout_constrained_note
+                diag.note(fluent::mir_transform_mutation_layout_constrained_note);
             }
             BorrowOfLayoutConstrainedField => {
-                crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_note
+                diag.note(fluent::mir_transform_mutation_layout_constrained_borrow_note);
+            }
+            CallToFunctionWith { ref missing, ref build_enabled } => {
+                diag.help(fluent::mir_transform_target_feature_call_help);
+                diag.set_arg(
+                    "missing_target_features",
+                    DiagnosticArgValue::StrListSepByAnd(
+                        missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
+                    ),
+                );
+                diag.set_arg("missing_target_features_count", missing.len());
+                if !build_enabled.is_empty() {
+                    diag.note(fluent::mir_transform_target_feature_call_note);
+                    diag.set_arg(
+                        "build_target_features",
+                        DiagnosticArgValue::StrListSepByAnd(
+                            build_enabled
+                                .iter()
+                                .map(|feature| Cow::from(feature.as_str()))
+                                .collect(),
+                        ),
+                    );
+                    diag.set_arg("build_target_features_count", build_enabled.len());
+                }
             }
-            CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_note,
         }
     }
 
-    fn label(self) -> DiagnosticMessage {
+    fn label(&self) -> DiagnosticMessage {
         use UnsafetyViolationDetails::*;
         match self.violation {
-            CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_label,
-            UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_label,
-            InitializingTypeWith => {
-                crate::fluent_generated::mir_transform_initializing_valid_range_label
-            }
-            CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_label,
-            UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_label,
-            UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_label,
-            DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_label,
-            AccessToUnionField => crate::fluent_generated::mir_transform_union_access_label,
+            CallToUnsafeFunction => fluent::mir_transform_call_to_unsafe_label,
+            UseOfInlineAssembly => fluent::mir_transform_use_of_asm_label,
+            InitializingTypeWith => fluent::mir_transform_initializing_valid_range_label,
+            CastOfPointerToInt => fluent::mir_transform_const_ptr2int_label,
+            UseOfMutableStatic => fluent::mir_transform_use_of_static_mut_label,
+            UseOfExternStatic => fluent::mir_transform_use_of_extern_static_label,
+            DerefOfRawPointer => fluent::mir_transform_deref_ptr_label,
+            AccessToUnionField => fluent::mir_transform_union_access_label,
             MutationOfLayoutConstrainedField => {
-                crate::fluent_generated::mir_transform_mutation_layout_constrained_label
+                fluent::mir_transform_mutation_layout_constrained_label
             }
             BorrowOfLayoutConstrainedField => {
-                crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_label
+                fluent::mir_transform_mutation_layout_constrained_borrow_label
             }
-            CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_label,
+            CallToFunctionWith { .. } => fluent::mir_transform_target_feature_call_label,
         }
     }
 }
@@ -151,12 +188,12 @@ impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
         let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
         diag.set_arg("details", desc);
         diag.span_label(self.details.span, self.details.label());
-        diag.note(self.details.note());
+        self.details.add_subdiagnostics(diag);
 
         if let Some((start, end, fn_sig)) = self.suggest_unsafe_block {
-            diag.span_note(fn_sig, crate::fluent_generated::mir_transform_note);
+            diag.span_note(fn_sig, fluent::mir_transform_note);
             diag.tool_only_multipart_suggestion(
-                crate::fluent_generated::mir_transform_suggestion,
+                fluent::mir_transform_suggestion,
                 vec![(start, " unsafe {".into()), (end, "}".into())],
                 Applicability::MaybeIncorrect,
             );
@@ -166,7 +203,7 @@ impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
     }
 
     fn msg(&self) -> DiagnosticMessage {
-        crate::fluent_generated::mir_transform_unsafe_op_in_unsafe_fn
+        fluent::mir_transform_unsafe_op_in_unsafe_fn
     }
 }
 
@@ -193,12 +230,8 @@ impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
 
     fn msg(&self) -> DiagnosticMessage {
         match self {
-            AssertLint::ArithmeticOverflow(..) => {
-                crate::fluent_generated::mir_transform_arithmetic_overflow
-            }
-            AssertLint::UnconditionalPanic(..) => {
-                crate::fluent_generated::mir_transform_operation_will_panic
-            }
+            AssertLint::ArithmeticOverflow(..) => fluent::mir_transform_arithmetic_overflow,
+            AssertLint::UnconditionalPanic(..) => fluent::mir_transform_operation_will_panic,
         }
     }
 }
@@ -255,11 +288,11 @@ impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
         self,
         diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
     ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
-        diag.span_label(self.yield_sp, crate::fluent_generated::_subdiag::label);
+        diag.span_label(self.yield_sp, fluent::_subdiag::label);
         if let Some(reason) = self.reason {
             diag.subdiagnostic(reason);
         }
-        diag.span_help(self.src_sp, crate::fluent_generated::_subdiag::help);
+        diag.span_help(self.src_sp, fluent::_subdiag::help);
         diag.set_arg("pre", self.pre);
         diag.set_arg("def_path", self.tcx.def_path_str(self.def_id));
         diag.set_arg("post", self.post);
@@ -267,7 +300,7 @@ impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
-        crate::fluent_generated::mir_transform_must_not_suspend
+        fluent::mir_transform_must_not_suspend
     }
 }
 
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 83bbfeb2805..eadd2892d93 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -492,9 +492,13 @@ parse_match_arm_body_without_braces = `match` arm body without braces
         } with a body
     .suggestion_use_comma_not_semicolon = replace `;` with `,` to end a `match` arm expression
 
+parse_maybe_comparison = you might have meant to compare for equality
+
 parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn`
     .suggestion = replace `fn` with `impl` here
 
+parse_maybe_missing_let = you might have meant to continue the let-chain
+
 parse_maybe_recover_from_bad_qpath_stage_2 =
     missing angle brackets in associated item path
     .suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 0a919240ce5..e5d4cb6f4da 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -415,6 +415,32 @@ pub(crate) struct ExpectedExpressionFoundLet {
     pub span: Span,
     #[subdiagnostic]
     pub reason: ForbiddenLetReason,
+    #[subdiagnostic]
+    pub missing_let: Option<MaybeMissingLet>,
+    #[subdiagnostic]
+    pub comparison: Option<MaybeComparison>,
+}
+
+#[derive(Subdiagnostic, Clone, Copy)]
+#[multipart_suggestion(
+    parse_maybe_missing_let,
+    applicability = "maybe-incorrect",
+    style = "verbose"
+)]
+pub(crate) struct MaybeMissingLet {
+    #[suggestion_part(code = "let ")]
+    pub span: Span,
+}
+
+#[derive(Subdiagnostic, Clone, Copy)]
+#[multipart_suggestion(
+    parse_maybe_comparison,
+    applicability = "maybe-incorrect",
+    style = "verbose"
+)]
+pub(crate) struct MaybeComparison {
+    #[suggestion_part(code = "=")]
+    pub span: Span,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 79567eb3898..e334837c1d4 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-filelength
 use super::diagnostics::SnapshotParser;
 use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma};
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
@@ -2477,7 +2478,7 @@ impl<'a> Parser<'a> {
         let mut cond =
             self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?;
 
-        CondChecker { parser: self, forbid_let_reason: None }.visit_expr(&mut cond);
+        CondChecker::new(self).visit_expr(&mut cond);
 
         if let ExprKind::Let(_, _, _, None) = cond.kind {
             // Remove the last feature gating of a `let` expression since it's stable.
@@ -2493,6 +2494,8 @@ impl<'a> Parser<'a> {
             let err = errors::ExpectedExpressionFoundLet {
                 span: self.token.span,
                 reason: ForbiddenLetReason::OtherForbidden,
+                missing_let: None,
+                comparison: None,
             };
             if self.prev_token.kind == token::BinOp(token::Or) {
                 // This was part of a closure, the that part of the parser recover.
@@ -2876,7 +2879,7 @@ impl<'a> Parser<'a> {
                 let if_span = this.prev_token.span;
                 let mut cond = this.parse_match_guard_condition()?;
 
-                CondChecker { parser: this, forbid_let_reason: None }.visit_expr(&mut cond);
+                CondChecker::new(this).visit_expr(&mut cond);
 
                 let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
                 if has_let_expr {
@@ -3552,6 +3555,14 @@ pub(crate) enum ForbiddenLetReason {
 struct CondChecker<'a> {
     parser: &'a Parser<'a>,
     forbid_let_reason: Option<ForbiddenLetReason>,
+    missing_let: Option<errors::MaybeMissingLet>,
+    comparison: Option<errors::MaybeComparison>,
+}
+
+impl<'a> CondChecker<'a> {
+    fn new(parser: &'a Parser<'a>) -> Self {
+        CondChecker { parser, forbid_let_reason: None, missing_let: None, comparison: None }
+    }
 }
 
 impl MutVisitor for CondChecker<'_> {
@@ -3562,11 +3573,13 @@ impl MutVisitor for CondChecker<'_> {
         match e.kind {
             ExprKind::Let(_, _, _, ref mut is_recovered @ None) => {
                 if let Some(reason) = self.forbid_let_reason {
-                    *is_recovered = Some(
-                        self.parser
-                            .sess
-                            .emit_err(errors::ExpectedExpressionFoundLet { span, reason }),
-                    );
+                    *is_recovered =
+                        Some(self.parser.sess.emit_err(errors::ExpectedExpressionFoundLet {
+                            span,
+                            reason,
+                            missing_let: self.missing_let,
+                            comparison: self.comparison,
+                        }));
                 } else {
                     self.parser.sess.gated_spans.gate(sym::let_chains, span);
                 }
@@ -3590,9 +3603,28 @@ impl MutVisitor for CondChecker<'_> {
                 noop_visit_expr(e, self);
                 self.forbid_let_reason = forbid_let_reason;
             }
+            ExprKind::Assign(ref lhs, _, span) => {
+                let forbid_let_reason = self.forbid_let_reason;
+                self.forbid_let_reason = Some(OtherForbidden);
+                let missing_let = self.missing_let;
+                if let ExprKind::Binary(_, _, rhs) = &lhs.kind
+                    && let ExprKind::Path(_, _)
+                    | ExprKind::Struct(_)
+                    | ExprKind::Call(_, _)
+                    | ExprKind::Array(_) = rhs.kind
+                {
+                    self.missing_let =
+                        Some(errors::MaybeMissingLet { span: rhs.span.shrink_to_lo() });
+                }
+                let comparison = self.comparison;
+                self.comparison = Some(errors::MaybeComparison { span: span.shrink_to_hi() });
+                noop_visit_expr(e, self);
+                self.forbid_let_reason = forbid_let_reason;
+                self.missing_let = missing_let;
+                self.comparison = comparison;
+            }
             ExprKind::Unary(_, _)
             | ExprKind::Await(_, _)
-            | ExprKind::Assign(_, _, _)
             | ExprKind::AssignOp(_, _, _)
             | ExprKind::Range(_, _, _)
             | ExprKind::Try(_)
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 8f5f4459d64..b05868e235a 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -368,8 +368,12 @@ impl<'a> Parser<'a> {
             self.recover_dotdotdot_rest_pat(lo)
         } else if let Some(form) = self.parse_range_end() {
             self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`.
+        } else if self.eat(&token::Not) {
+            // Parse `!`
+            self.sess.gated_spans.gate(sym::never_patterns, self.prev_token.span);
+            PatKind::Never
         } else if self.eat_keyword(kw::Underscore) {
-            // Parse _
+            // Parse `_`
             PatKind::Wild
         } else if self.eat_keyword(kw::Mut) {
             self.parse_pat_ident_mut(syntax_loc)?
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index f915c1057d6..28354ab0986 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -286,7 +286,21 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_pat(&mut self, p: &'v hir::Pat<'v>) {
         record_variants!(
             (self, p, p.kind, Id::Node(p.hir_id), hir, Pat, PatKind),
-            [Wild, Binding, Struct, TupleStruct, Or, Path, Tuple, Box, Ref, Lit, Range, Slice]
+            [
+                Wild,
+                Binding,
+                Struct,
+                TupleStruct,
+                Or,
+                Never,
+                Path,
+                Tuple,
+                Box,
+                Ref,
+                Lit,
+                Range,
+                Slice
+            ]
         );
         hir_visit::walk_pat(self, p)
     }
@@ -554,6 +568,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
                 Range,
                 Slice,
                 Rest,
+                Never,
                 Paren,
                 MacCall
             ]
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 40b03874242..9a2fe01229d 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1097,6 +1097,7 @@ symbols! {
         negative_impls,
         neon,
         never,
+        never_patterns,
         never_type,
         never_type_fallback,
         new,
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 36685f756d0..c8a0eb4ffc2 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -1644,6 +1644,24 @@ impl<T> *const [T] {
         metadata(self)
     }
 
+    /// Returns `true` if the raw slice has a length of 0.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_ptr_len)]
+    /// use std::ptr;
+    ///
+    /// let slice: *const [i8] = ptr::slice_from_raw_parts(ptr::null(), 3);
+    /// assert!(!slice.is_empty());
+    /// ```
+    #[inline(always)]
+    #[unstable(feature = "slice_ptr_len", issue = "71146")]
+    #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")]
+    pub const fn is_empty(self) -> bool {
+        self.len() == 0
+    }
+
     /// Returns a raw pointer to the slice's buffer.
     ///
     /// This is equivalent to casting `self` to `*const T`, but more type-safe.
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index bc362fb627f..ce0e6d6f297 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -1920,10 +1920,10 @@ impl<T> *mut [T] {
     ///
     /// ```
     /// #![feature(slice_ptr_len)]
+    /// use std::ptr;
     ///
-    /// let mut a = [1, 2, 3];
-    /// let ptr = &mut a as *mut [_];
-    /// assert!(!ptr.is_empty());
+    /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3);
+    /// assert!(!slice.is_empty());
     /// ```
     #[inline(always)]
     #[unstable(feature = "slice_ptr_len", issue = "71146")]
diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md
index 9f0662783a4..2a741fa3d02 100644
--- a/src/doc/rustc/src/platform-support/nto-qnx.md
+++ b/src/doc/rustc/src/platform-support/nto-qnx.md
@@ -121,10 +121,8 @@ export build_env='
 
 env $build_env \
     ./x.py build \
-        --target aarch64-unknown-nto-qnx710 \
-        --target x86_64-pc-nto-qnx710 \
-        --target x86_64-unknown-linux-gnu \
-        rustc library/core library/alloc
+        --target aarch64-unknown-nto-qnx710,x86_64-pc-nto-qnx710,x86_64-unknown-linux-gnu \
+        rustc library/core library/alloc library/std
 ```
 
 ## Running the Rust test suite
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index c22ad43e049..82f4a38384d 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -303,7 +303,8 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
     debug!("trying to get a name from pattern: {p:?}");
 
     Symbol::intern(&match p.kind {
-        PatKind::Wild | PatKind::Struct(..) => return kw::Underscore,
+        // FIXME(never_patterns): does this make sense?
+        PatKind::Wild | PatKind::Never | PatKind::Struct(..) => return kw::Underscore,
         PatKind::Binding(_, _, ident, _) => return ident.name,
         PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p),
         PatKind::Or(pats) => {
diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs
index e607a16ad54..0056fb48530 100644
--- a/src/librustdoc/formats/mod.rs
+++ b/src/librustdoc/formats/mod.rs
@@ -9,21 +9,6 @@ pub(crate) use renderer::{run_format, FormatRenderer};
 use crate::clean::{self, ItemId};
 use crate::html::render::Context;
 
-/// Specifies whether rendering directly implemented trait items or ones from a certain Deref
-/// impl.
-pub(crate) enum AssocItemRender<'a> {
-    All,
-    DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
-}
-
-/// For different handling of associated items from the Deref target of a type rather than the type
-/// itself.
-#[derive(Copy, Clone, PartialEq)]
-pub(crate) enum RenderMode {
-    Normal,
-    ForDeref { mut_: bool },
-}
-
 /// Metadata about implementations for a type or trait.
 #[derive(Clone, Debug)]
 pub(crate) struct Impl {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index ff5c761208b..348d546c236 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -66,7 +66,7 @@ use crate::clean::{self, ItemId, RenderedLink, SelfTy};
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
-use crate::formats::{AssocItemRender, Impl, RenderMode};
+use crate::formats::Impl;
 use crate::html::escape::Escape;
 use crate::html::format::{
     display_fn, href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
@@ -89,6 +89,21 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
     })
 }
 
+/// Specifies whether rendering directly implemented trait items or ones from a certain Deref
+/// impl.
+pub(crate) enum AssocItemRender<'a> {
+    All,
+    DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
+}
+
+/// For different handling of associated items from the Deref target of a type rather than the type
+/// itself.
+#[derive(Copy, Clone, PartialEq)]
+pub(crate) enum RenderMode {
+    Normal,
+    ForDeref { mut_: bool },
+}
+
 // Helper structs for rendering items/sidebars and carrying along contextual
 // information
 
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 14387e0d45d..2dac943f695 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -22,12 +22,13 @@ use super::{
     item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
     render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
     render_impl, render_rightside, render_stability_since_raw,
-    render_stability_since_raw_with_extra, AssocItemLink, Context, ImplRenderingParameters,
+    render_stability_since_raw_with_extra, AssocItemLink, AssocItemRender, Context,
+    ImplRenderingParameters, RenderMode,
 };
 use crate::clean;
 use crate::config::ModuleSorting;
 use crate::formats::item_type::ItemType;
-use crate::formats::{AssocItemRender, Impl, RenderMode};
+use crate::formats::Impl;
 use crate::html::escape::Escape;
 use crate::html::format::{
     display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space,
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index d2c7c578c08..b04776e91dc 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -15,14 +15,14 @@ use rustc_span::Symbol;
 use serde::ser::SerializeSeq;
 use serde::{Serialize, Serializer};
 
-use super::{collect_paths_for_type, ensure_trailing_slash, Context};
+use super::{collect_paths_for_type, ensure_trailing_slash, Context, RenderMode};
 use crate::clean::{Crate, Item, ItemId, ItemKind};
 use crate::config::{EmitType, RenderOptions};
 use crate::docfs::PathError;
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
-use crate::formats::{Impl, RenderMode};
+use crate::formats::Impl;
 use crate::html::format::Buffer;
 use crate::html::render::{AssocItemLink, ImplRenderingParameters};
 use crate::html::{layout, static_files};
diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
index 575fead5bf3..630df9a84f5 100644
--- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
+++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
@@ -46,7 +46,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
         pats.iter().all(unary_pattern)
     }
     match &pat.kind {
-        PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => {
+        PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Never | PatKind::Or(_) => {
             false
         },
         PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index 745be671b86..cbf478620ec 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -150,6 +150,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
 #[derive(Clone, Copy)]
 enum NormalizedPat<'a> {
     Wild,
+    Never,
     Struct(Option<DefId>, &'a [(Symbol, Self)]),
     Tuple(Option<DefId>, &'a [Self]),
     Or(&'a [Self]),
@@ -229,6 +230,7 @@ impl<'a> NormalizedPat<'a> {
             PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => {
                 Self::from_pat(cx, arena, pat)
             },
+            PatKind::Never => Self::Never,
             PatKind::Struct(ref path, fields, _) => {
                 let fields =
                     arena.alloc_from_iter(fields.iter().map(|f| (f.ident.name, Self::from_pat(cx, arena, f.pat))));
@@ -333,6 +335,7 @@ impl<'a> NormalizedPat<'a> {
     fn has_overlapping_values(&self, other: &Self) -> bool {
         match (*self, *other) {
             (Self::Wild, _) | (_, Self::Wild) => true,
+            (Self::Never, Self::Never) => true,
             (Self::Or(pats), ref other) | (ref other, Self::Or(pats)) => {
                 pats.iter().any(|pat| pat.has_overlapping_values(other))
             },
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index 8ff088a208f..952c0dc72b1 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -226,7 +226,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
         // Therefore they are not some form of constructor `C`,
         // with which a pattern `C(p_0)` may be formed,
         // which we would want to join with other `C(p_j)`s.
-        Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
+        Ident(.., None) | Lit(_) | Wild | Never | Path(..) | Range(..) | Rest | MacCall(_)
         // Skip immutable refs, as grouping them saves few characters,
         // and almost always requires adding parens (increasing noisiness).
         // In the case of only two patterns, replacement adds net characters.
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index e8842ce10f8..e83c04eda20 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -629,6 +629,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
 
         match pat.value.kind {
             PatKind::Wild => kind!("Wild"),
+            PatKind::Never => kind!("Never"),
             PatKind::Binding(ann, _, name, sub) => {
                 bind!(self, name);
                 opt_bind!(self, sub);
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 8031e6faa74..34ec83709ff 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -1017,6 +1017,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 }
                 e.hash(&mut self.s);
             },
+            PatKind::Never => {},
             PatKind::Wild => {},
         }
     }
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 172b063df31..fd37713fc60 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1707,6 +1707,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 
     match pat.kind {
         PatKind::Wild => false,
+        PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.
         PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
         PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
         PatKind::Lit(..) | PatKind::Range(..) => true,
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index 33f3b4b8a21..8504999b8ff 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -40,7 +40,9 @@ pub(crate) fn is_short_pattern(pat: &ast::Pat, pat_str: &str) -> bool {
 
 fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
     match pat.kind {
-        ast::PatKind::Rest | ast::PatKind::Wild | ast::PatKind::Lit(_) => true,
+        ast::PatKind::Rest | ast::PatKind::Never | ast::PatKind::Wild | ast::PatKind::Lit(_) => {
+            true
+        }
         ast::PatKind::Ident(_, _, ref pat) => pat.is_none(),
         ast::PatKind::Struct(..)
         | ast::PatKind::MacCall(..)
@@ -193,6 +195,7 @@ impl Rewrite for Pat {
                     None
                 }
             }
+            PatKind::Never => None,
             PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
                 let infix = match end_kind.node {
                     RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
diff --git a/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs b/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs
new file mode 100644
index 00000000000..4cf6d7c1396
--- /dev/null
+++ b/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs
@@ -0,0 +1,26 @@
+// unit-test: ConstProp
+// compile-flags: -O
+
+// Regression test for https://github.com/rust-lang/rust/issues/118328
+
+#![allow(unused_assignments)]
+
+struct SizeOfConst<T>(std::marker::PhantomData<T>);
+impl<T> SizeOfConst<T> {
+    const SIZE: usize = std::mem::size_of::<T>();
+}
+
+// EMIT_MIR overwrite_with_const_with_params.size_of.ConstProp.diff
+fn size_of<T>() -> usize {
+    // CHECK-LABEL: fn size_of(
+    // CHECK: _1 = const 0_usize;
+    // CHECK-NEXT: _1 = const _;
+    // CHECK-NEXT: _0 = _1;
+    let mut a = 0;
+    a = SizeOfConst::<T>::SIZE;
+    a
+}
+
+fn main() {
+    assert_eq!(size_of::<u32>(), std::mem::size_of::<u32>());
+}
diff --git a/tests/mir-opt/const_prop/overwrite_with_const_with_params.size_of.ConstProp.diff b/tests/mir-opt/const_prop/overwrite_with_const_with_params.size_of.ConstProp.diff
new file mode 100644
index 00000000000..ad8318832d6
--- /dev/null
+++ b/tests/mir-opt/const_prop/overwrite_with_const_with_params.size_of.ConstProp.diff
@@ -0,0 +1,20 @@
+- // MIR for `size_of` before ConstProp
++ // MIR for `size_of` after ConstProp
+  
+  fn size_of() -> usize {
+      let mut _0: usize;
+      let mut _1: usize;
+      scope 1 {
+          debug a => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = const 0_usize;
+          _1 = const _;
+          _0 = _1;
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/ui/const_prop/overwrite_with_const_with_params.rs b/tests/ui/const_prop/overwrite_with_const_with_params.rs
new file mode 100644
index 00000000000..6f533919a47
--- /dev/null
+++ b/tests/ui/const_prop/overwrite_with_const_with_params.rs
@@ -0,0 +1,21 @@
+// compile-flags: -O
+// run-pass
+
+// Regression test for https://github.com/rust-lang/rust/issues/118328
+
+#![allow(unused_assignments)]
+
+struct SizeOfConst<T>(std::marker::PhantomData<T>);
+impl<T> SizeOfConst<T> {
+    const SIZE: usize = std::mem::size_of::<T>();
+}
+
+fn size_of<T>() -> usize {
+    let mut a = 0;
+    a = SizeOfConst::<T>::SIZE;
+    a
+}
+
+fn main() {
+    assert_eq!(size_of::<u32>(), std::mem::size_of::<u32>());
+}
diff --git a/tests/ui/expr/if/bad-if-let-suggestion.rs b/tests/ui/expr/if/bad-if-let-suggestion.rs
index 99d584ac7a5..b0d0676e1ea 100644
--- a/tests/ui/expr/if/bad-if-let-suggestion.rs
+++ b/tests/ui/expr/if/bad-if-let-suggestion.rs
@@ -1,6 +1,3 @@
-// FIXME(compiler-errors): This really should suggest `let` on the RHS of the
-// `&&` operator, but that's kinda hard to do because of precedence.
-// Instead, for now we just make sure not to suggest `if let let`.
 fn a() {
     if let x = 1 && i = 2 {}
     //~^ ERROR cannot find value `i` in this scope
diff --git a/tests/ui/expr/if/bad-if-let-suggestion.stderr b/tests/ui/expr/if/bad-if-let-suggestion.stderr
index 20ac9ca76ba..0d1f895bd82 100644
--- a/tests/ui/expr/if/bad-if-let-suggestion.stderr
+++ b/tests/ui/expr/if/bad-if-let-suggestion.stderr
@@ -1,19 +1,27 @@
 error: expected expression, found `let` statement
-  --> $DIR/bad-if-let-suggestion.rs:5:8
+  --> $DIR/bad-if-let-suggestion.rs:2:8
    |
 LL |     if let x = 1 && i = 2 {}
    |        ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
+help: you might have meant to continue the let-chain
+   |
+LL |     if let x = 1 && let i = 2 {}
+   |                     +++
+help: you might have meant to compare for equality
+   |
+LL |     if let x = 1 && i == 2 {}
+   |                        +
 
 error[E0425]: cannot find value `i` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:5:21
+  --> $DIR/bad-if-let-suggestion.rs:2:21
    |
 LL |     if let x = 1 && i = 2 {}
    |                     ^ not found in this scope
 
 error[E0425]: cannot find value `i` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:12:9
+  --> $DIR/bad-if-let-suggestion.rs:9:9
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -22,7 +30,7 @@ LL |     if (i + j) = i {}
    |         ^ help: a function with a similar name exists: `a`
 
 error[E0425]: cannot find value `j` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:12:13
+  --> $DIR/bad-if-let-suggestion.rs:9:13
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -31,7 +39,7 @@ LL |     if (i + j) = i {}
    |             ^ help: a function with a similar name exists: `a`
 
 error[E0425]: cannot find value `i` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:12:18
+  --> $DIR/bad-if-let-suggestion.rs:9:18
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -40,7 +48,7 @@ LL |     if (i + j) = i {}
    |                  ^ help: a function with a similar name exists: `a`
 
 error[E0425]: cannot find value `x` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:19:8
+  --> $DIR/bad-if-let-suggestion.rs:16:8
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -49,7 +57,7 @@ LL |     if x[0] = 1 {}
    |        ^ help: a function with a similar name exists: `a`
 
 error[E0308]: mismatched types
-  --> $DIR/bad-if-let-suggestion.rs:5:8
+  --> $DIR/bad-if-let-suggestion.rs:2:8
    |
 LL |     if let x = 1 && i = 2 {}
    |        ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs
new file mode 100644
index 00000000000..69e9f62abf0
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs
@@ -0,0 +1,27 @@
+// Check that never patterns require the feature gate.
+use std::ptr::NonNull;
+
+enum Void {}
+
+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();
+        match *ptr {
+            ! => {} //~ ERROR `!` patterns are experimental
+        }
+    }
+
+    // Check that the gate operates even behind `cfg`.
+    #[cfg(FALSE)]
+    unsafe {
+        let ptr: *const Void = NonNull::dangling().as_ptr();
+        match *ptr {
+            ! => {} //~ 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
new file mode 100644
index 00000000000..b7290eeb36d
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
@@ -0,0 +1,39 @@
+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
+   |
+LL |     let (Ok(_x) | Err(&!)) = res.as_ref();
+   |                        ^
+   |
+   = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
+   = help: add `#![feature(never_patterns)]` to the crate attributes to enable
+
+error[E0658]: `!` patterns are experimental
+  --> $DIR/feature-gate-never_patterns.rs:15:13
+   |
+LL |             ! => {}
+   |             ^
+   |
+   = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
+   = help: add `#![feature(never_patterns)]` to the crate attributes to enable
+
+error[E0658]: `!` patterns are experimental
+  --> $DIR/feature-gate-never_patterns.rs:24:13
+   |
+LL |             ! => {}
+   |             ^
+   |
+   = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
+   = help: add `#![feature(never_patterns)]` to the crate attributes to enable
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0408, E0658.
+For more information about an error, try `rustc --explain E0408`.
diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs
index 70ca00285c4..c0eb7f01fdb 100644
--- a/tests/ui/macros/stringify.rs
+++ b/tests/ui/macros/stringify.rs
@@ -11,6 +11,7 @@
 #![feature(decl_macro)]
 #![feature(explicit_tail_calls)]
 #![feature(more_qualified_paths)]
+#![feature(never_patterns)]
 #![feature(raw_ref_op)]
 #![feature(trait_alias)]
 #![feature(try_blocks)]
@@ -635,6 +636,10 @@ fn test_pat() {
     // PatKind::Rest
     c1!(pat, [ .. ], "..");
 
+    // PatKind::Never
+    c1!(pat, [ Some(!) ], "Some(!)");
+    c1!(pat, [ None | Some(!) ], "None | Some(!)");
+
     // PatKind::Paren
     c1!(pat, [ (pat) ], "(pat)");
 
diff --git a/tests/ui/pattern/never_patterns.rs b/tests/ui/pattern/never_patterns.rs
new file mode 100644
index 00000000000..e2e17e0e9a7
--- /dev/null
+++ b/tests/ui/pattern/never_patterns.rs
@@ -0,0 +1,99 @@
+#![feature(never_patterns)]
+#![allow(incomplete_features)]
+
+enum Void {}
+
+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) | 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.
+fn never_pattern_location(void: Void) {
+    // FIXME(never_patterns): Don't accept on a non-empty type.
+    match Some(0) {
+        None => {}
+        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(!) => {}
+    }
+    match None::<&Void> {
+        None => {}
+        Some(!) => {}
+    }
+    // Accept on a composite empty type.
+    match None::<&(u32, Void)> {
+        None => {}
+        Some(&!) => {}
+    }
+    // Accept on an simple empty type.
+    match None::<Void> {
+        None => {}
+        Some(!) => {}
+    }
+    match None::<&Void> {
+        None => {}
+        Some(&!) => {}
+    }
+    match None::<&(u32, Void)> {
+        None => {}
+        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
new file mode 100644
index 00000000000..11e50debfd3
--- /dev/null
+++ b/tests/ui/pattern/never_patterns.stderr
@@ -0,0 +1,51 @@
+error[E0408]: variable `_x` is not bound in all patterns
+  --> $DIR/never_patterns.rs:12:19
+   |
+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 |         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
+   |
+LL |         Ok(_a) | Err(&(_b, !)) => {}
+   |            --    ^^^^^^^^^^^^^ pattern doesn't bind `_a`
+   |            |
+   |            variable not in all patterns
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0408`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
index 0ef7b8b09f1..cabc475fa61 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr
@@ -1,83 +1,115 @@
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:23:5
+  --> $DIR/safe-calls.rs:28:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:26:5
+  --> $DIR/safe-calls.rs:31:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:29:5
+  --> $DIR/safe-calls.rs:34:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:36:5
+  --> $DIR/safe-calls.rs:41:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:39:5
+  --> $DIR/safe-calls.rs:44:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:46:5
+  --> $DIR/safe-calls.rs:51:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:49:5
+  --> $DIR/safe-calls.rs:54:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: bmi2
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:52:5
+  --> $DIR/safe-calls.rs:57:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: bmi2
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:60:5
+  --> $DIR/safe-calls.rs:65:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
 
 error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:65:18
+  --> $DIR/safe-calls.rs:70:15
    |
-LL | const name: () = sse2();
-   |                  ^^^^^^ call to function with `#[target_feature]`
+LL | const _: () = sse2();
+   |               ^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
 
-error: aborting due to 10 previous errors
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:74:15
+   |
+LL | const _: () = sse2_and_fxsr();
+   |               ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
+   |
+   = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr
+   = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
+
+error: call to function with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
+  --> $DIR/safe-calls.rs:82:5
+   |
+LL |     sse2();
+   |     ^^^^^^ call to function with `#[target_feature]`
+   |
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
+note: an unsafe function restricts its caller, but its body is safe by default
+  --> $DIR/safe-calls.rs:81:1
+   |
+LL | unsafe fn needs_unsafe_block() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: the lint level is defined here
+  --> $DIR/safe-calls.rs:78:8
+   |
+LL | #[deny(unsafe_op_in_unsafe_fn)]
+   |        ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs
index cebc6f94784..f17dab269bc 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs
@@ -7,6 +7,11 @@
 #[target_feature(enable = "sse2")]
 const fn sse2() {}
 
+#[target_feature(enable = "sse2")]
+#[target_feature(enable = "fxsr")]
+const fn sse2_and_fxsr() {}
+
+
 #[target_feature(enable = "avx")]
 #[target_feature(enable = "bmi2")]
 fn avx_bmi2() {}
@@ -62,8 +67,21 @@ fn qux() {
     //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
 }
 
-const name: () = sse2();
+const _: () = sse2();
 //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
 //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
 
+const _: () = sse2_and_fxsr();
+//[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+//[thir]~^^ ERROR call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe
+
+#[deny(unsafe_op_in_unsafe_fn)]
+#[target_feature(enable = "avx")]
+#[target_feature(enable = "bmi2")]
+unsafe fn needs_unsafe_block() {
+    sse2();
+    //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+    //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
+}
+
 fn main() {}
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr
index c75ac6e8b9a..13b58fde862 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr
@@ -1,83 +1,115 @@
 error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:23:5
+  --> $DIR/safe-calls.rs:28:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
 
 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:26:5
+  --> $DIR/safe-calls.rs:31:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
 
 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:29:5
+  --> $DIR/safe-calls.rs:34:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
 
 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:36:5
+  --> $DIR/safe-calls.rs:41:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
 
 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:39:5
+  --> $DIR/safe-calls.rs:44:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2
 
 error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:46:5
+  --> $DIR/safe-calls.rs:51:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
 
 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:49:5
+  --> $DIR/safe-calls.rs:54:5
    |
 LL |     avx_bmi2();
    |     ^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: bmi2
 
 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:52:5
+  --> $DIR/safe-calls.rs:57:5
    |
 LL |     Quux.avx_bmi2();
    |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: bmi2
 
 error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:60:5
+  --> $DIR/safe-calls.rs:65:5
    |
 LL |     sse2();
    |     ^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
 
 error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
-  --> $DIR/safe-calls.rs:65:18
+  --> $DIR/safe-calls.rs:70:15
    |
-LL | const name: () = sse2();
-   |                  ^^^^^^ call to function with `#[target_feature]`
+LL | const _: () = sse2();
+   |               ^^^^^^ call to function with `#[target_feature]`
    |
-   = note: can only be called if the required target features are available
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
 
-error: aborting due to 10 previous errors
+error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:74:15
+   |
+LL | const _: () = sse2_and_fxsr();
+   |               ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
+   |
+   = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr
+   = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
+
+error: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
+  --> $DIR/safe-calls.rs:82:5
+   |
+LL |     sse2();
+   |     ^^^^^^ call to function with `#[target_feature]`
+   |
+   = help: in order for the call to be safe, the context requires the following additional target feature: sse2
+   = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
+note: an unsafe function restricts its caller, but its body is safe by default
+  --> $DIR/safe-calls.rs:81:1
+   |
+LL | unsafe fn needs_unsafe_block() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: the lint level is defined here
+  --> $DIR/safe-calls.rs:78:8
+   |
+LL | #[deny(unsafe_op_in_unsafe_fn)]
+   |        ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr
index 247fad2e992..f3726166eba 100644
--- a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr
+++ b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr
@@ -29,6 +29,10 @@ LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(
    |            ^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
+help: you might have meant to compare for equality
+   |
+LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] == 1 {
+   |                                                                                  +
 
 error: expected expression, found `let` statement
   --> $DIR/invalid-let-in-a-valid-let-context.rs:24:23
@@ -53,6 +57,10 @@ LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 {
    |            ^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
+help: you might have meant to compare for equality
+   |
+LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = ()] == 1 {
+   |                                                            +
 
 error: expected expression, found `let` statement
   --> $DIR/invalid-let-in-a-valid-let-context.rs:42:21