From bc6a5c71b635a9a5ea52c0b5f4ff297b0f611ac7 Mon Sep 17 00:00:00 2001
From: David Carlier <devnexen@gmail.com>
Date: Fri, 8 Dec 2023 15:26:16 +0000
Subject: [PATCH 01/17] std: getrandom simplification for freebsd.

it is in the libcs' crate too now.
---
 library/std/src/sys/unix/rand.rs | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index 2825d16774272..cf0fe0f47c53f 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -64,17 +64,7 @@ mod imp {
 
     #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd"))]
     fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
-        #[cfg(not(target_os = "freebsd"))]
-        use libc::getrandom;
-        #[cfg(target_os = "freebsd")]
-        extern "C" {
-            fn getrandom(
-                buf: *mut libc::c_void,
-                buflen: libc::size_t,
-                flags: libc::c_uint,
-            ) -> libc::ssize_t;
-        }
-        unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
+        unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
     }
 
     #[cfg(not(any(

From 2c088f95202400084987ed874822b4969f678d20 Mon Sep 17 00:00:00 2001
From: Obei Sideg <obei.sideg@gmail.com>
Date: Fri, 22 Dec 2023 14:56:20 +0300
Subject: [PATCH 02/17] Disallow reference to `static mut` for expressions

Add `E0796` error code.
Add `static_mut_ref` lint.

This is the idea for the 2024 edition.
---
 Cargo.lock                                    |  1 +
 compiler/rustc_error_codes/src/error_codes.rs |  1 +
 .../src/error_codes/E0796.md                  | 22 +++++
 compiler/rustc_hir_analysis/Cargo.toml        |  1 +
 compiler/rustc_hir_analysis/messages.ftl      | 14 +++
 compiler/rustc_hir_analysis/src/check/errs.rs | 78 ++++++++++++++++
 compiler/rustc_hir_analysis/src/check/mod.rs  |  1 +
 compiler/rustc_hir_analysis/src/errors.rs     | 91 +++++++++++++++++++
 compiler/rustc_lint_defs/src/builtin.rs       | 52 +++++++++++
 9 files changed, 261 insertions(+)
 create mode 100644 compiler/rustc_error_codes/src/error_codes/E0796.md
 create mode 100644 compiler/rustc_hir_analysis/src/check/errs.rs

diff --git a/Cargo.lock b/Cargo.lock
index b8192e333fe91..1d6f7ea1e27ce 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3877,6 +3877,7 @@ dependencies = [
  "rustc_feature",
  "rustc_fluent_macro",
  "rustc_hir",
+ "rustc_hir_pretty",
  "rustc_index",
  "rustc_infer",
  "rustc_lint_defs",
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 1028d43f9c5ba..a1391cceb7128 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -515,6 +515,7 @@ E0792: include_str!("./error_codes/E0792.md"),
 E0793: include_str!("./error_codes/E0793.md"),
 E0794: include_str!("./error_codes/E0794.md"),
 E0795: include_str!("./error_codes/E0795.md"),
+E0796: include_str!("./error_codes/E0796.md"),
 }
 
 // Undocumented removed error codes. Note that many removed error codes are kept in the list above
diff --git a/compiler/rustc_error_codes/src/error_codes/E0796.md b/compiler/rustc_error_codes/src/error_codes/E0796.md
new file mode 100644
index 0000000000000..cea18f8db851f
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0796.md
@@ -0,0 +1,22 @@
+Reference of mutable static.
+
+Erroneous code example:
+
+```compile_fail,edition2024,E0796
+static mut X: i32 = 23;
+static mut Y: i32 = 24;
+
+unsafe {
+  let y = &X;
+  let ref x = X;
+  let (x, y) = (&X, &Y);
+  foo(&X);
+}
+
+fn foo<'a>(_x: &'a i32) {}
+```
+
+Mutable statics can be written to by multiple threads: aliasing violations or
+data races will cause undefined behavior.
+
+Reference of mutable static is a hard error from 2024 edition.
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index b671bebeb0503..b5ebc1fab765d 100644
--- a/compiler/rustc_hir_analysis/Cargo.toml
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
 rustc_hir = { path = "../rustc_hir" }
+rustc_hir_pretty = { path = "../rustc_hir_pretty" }
 rustc_index = { path = "../rustc_index" }
 rustc_infer = { path = "../rustc_infer" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index d8b6b9a1272fb..6a17668ad17f8 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -346,6 +346,20 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha
 hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]`
     .label = `#[start]` function is not allowed to be `#[track_caller]`
 
+hir_analysis_static_mut_ref = reference of mutable static is disallowed
+    .label = reference of mutable static
+    .note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+    .suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+    .suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+
+hir_analysis_static_mut_ref_lint = {$shared}reference of mutable static is discouraged
+    .label = shared reference of mutable static
+    .label_mut = mutable reference of mutable static
+    .suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+    .suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+    .note = reference of mutable static is a hard error from 2024 edition
+    .why_note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+
 hir_analysis_static_specialize = cannot specialize on `'static` lifetime
 
 hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl
diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs
new file mode 100644
index 0000000000000..5862643c42f80
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/errs.rs
@@ -0,0 +1,78 @@
+use rustc_hir as hir;
+use rustc_hir_pretty::qpath_to_string;
+use rustc_lint_defs::builtin::STATIC_MUT_REF;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::Span;
+use rustc_type_ir::Mutability;
+
+use crate::errors;
+
+/// Check for shared or mutable references of `static mut` inside expression
+pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
+    let span = expr.span;
+    let hir_id = expr.hir_id;
+    if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind
+        && matches!(borrow_kind, hir::BorrowKind::Ref)
+        && let Some(var) = is_path_static_mut(*expr)
+    {
+        handle_static_mut_ref(
+            tcx,
+            span,
+            var,
+            span.edition().at_least_rust_2024(),
+            matches!(m, Mutability::Mut),
+            hir_id,
+        );
+    }
+}
+
+fn is_path_static_mut(expr: hir::Expr<'_>) -> Option<String> {
+    if let hir::ExprKind::Path(qpath) = expr.kind
+        && let hir::QPath::Resolved(_, path) = qpath
+        && let hir::def::Res::Def(def_kind, _) = path.res
+        && let hir::def::DefKind::Static(mt) = def_kind
+        && matches!(mt, Mutability::Mut)
+    {
+        return Some(qpath_to_string(&qpath));
+    }
+    None
+}
+
+fn handle_static_mut_ref(
+    tcx: TyCtxt<'_>,
+    span: Span,
+    var: String,
+    e2024: bool,
+    mutable: bool,
+    hir_id: hir::HirId,
+) {
+    if e2024 {
+        let sugg = if mutable {
+            errors::StaticMutRefSugg::Mut { span, var }
+        } else {
+            errors::StaticMutRefSugg::Shared { span, var }
+        };
+        tcx.sess.parse_sess.dcx.emit_err(errors::StaticMutRef { span, sugg });
+        return;
+    }
+
+    let (label, sugg, shared) = if mutable {
+        (
+            errors::RefOfMutStaticLabel::Mut { span },
+            errors::RefOfMutStaticSugg::Mut { span, var },
+            "mutable ",
+        )
+    } else {
+        (
+            errors::RefOfMutStaticLabel::Shared { span },
+            errors::RefOfMutStaticSugg::Shared { span, var },
+            "shared ",
+        )
+    };
+    tcx.emit_spanned_lint(
+        STATIC_MUT_REF,
+        hir_id,
+        span,
+        errors::RefOfMutStatic { shared, why_note: (), label, sugg },
+    );
+}
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index f60d6950670d9..ac0c715c6b308 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -66,6 +66,7 @@ mod check;
 mod compare_impl_item;
 pub mod dropck;
 mod entry;
+mod errs;
 pub mod intrinsic;
 pub mod intrinsicck;
 mod region;
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 9124d50211056..4f22da4ba3b98 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1410,3 +1410,94 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> {
     pub mut_key: &'a str,
     pub ptr_ty: Ty<'a>,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_static_mut_ref, code = "E0796")]
+#[note]
+pub struct StaticMutRef {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sugg: StaticMutRefSugg,
+}
+
+#[derive(Subdiagnostic)]
+pub enum StaticMutRefSugg {
+    #[suggestion(
+        hir_analysis_suggestion,
+        style = "verbose",
+        code = "addr_of!({var})",
+        applicability = "maybe-incorrect"
+    )]
+    Shared {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+    #[suggestion(
+        hir_analysis_suggestion_mut,
+        style = "verbose",
+        code = "addr_of_mut!({var})",
+        applicability = "maybe-incorrect"
+    )]
+    Mut {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+}
+
+// STATIC_MUT_REF lint
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_static_mut_ref_lint)]
+#[note]
+pub struct RefOfMutStatic<'a> {
+    pub shared: &'a str,
+    #[note(hir_analysis_why_note)]
+    pub why_note: (),
+    #[subdiagnostic]
+    pub label: RefOfMutStaticLabel,
+    #[subdiagnostic]
+    pub sugg: RefOfMutStaticSugg,
+}
+
+#[derive(Subdiagnostic)]
+pub enum RefOfMutStaticLabel {
+    #[label(hir_analysis_label)]
+    Shared {
+        #[primary_span]
+        span: Span,
+    },
+    #[label(hir_analysis_label_mut)]
+    Mut {
+        #[primary_span]
+        span: Span,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub enum RefOfMutStaticSugg {
+    #[suggestion(
+        hir_analysis_suggestion,
+        style = "verbose",
+        code = "addr_of!({var})",
+        applicability = "maybe-incorrect"
+    )]
+    Shared {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+    #[suggestion(
+        hir_analysis_suggestion_mut,
+        style = "verbose",
+        code = "addr_of_mut!({var})",
+        applicability = "maybe-incorrect"
+    )]
+    Mut {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+}
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 19da51b7dcfa3..cfba6780a66fa 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -89,6 +89,7 @@ declare_lint_pass! {
         SINGLE_USE_LIFETIMES,
         SOFT_UNSTABLE,
         STABLE_FEATURES,
+        STATIC_MUT_REF,
         SUSPICIOUS_AUTO_TRAIT_IMPLS,
         TEST_UNSTABLE_LINT,
         TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
@@ -1767,6 +1768,57 @@ declare_lint! {
     };
 }
 
+declare_lint! {
+    /// The `static_mut_ref` lint checks for shared or mutable references
+    /// of mutable static inside `unsafe` blocks and `unsafe` functions.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2021
+    /// fn main() {
+    ///     static mut X: i32 = 23;
+    ///     static mut Y: i32 = 24;
+    ///
+    ///     unsafe {
+    ///         let y = &X;
+    ///         let ref x = X;
+    ///         let (x, y) = (&X, &Y);
+    ///         foo(&X);
+    ///     }
+    /// }
+    ///
+    /// unsafe fn _foo() {
+    ///     static mut X: i32 = 23;
+    ///     static mut Y: i32 = 24;
+    ///
+    ///     let y = &X;
+    ///     let ref x = X;
+    ///     let (x, y) = (&X, &Y);
+    ///     foo(&X);
+    /// }
+    ///
+    /// fn foo<'a>(_x: &'a i32) {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Shared or mutable references of mutable static are almost always a mistake and
+    /// can lead to undefined behavior and various other problems in your code.
+    ///
+    /// This lint is "warn" by default on editions up to 2021, from 2024 there is
+    /// a hard error instead.
+    pub STATIC_MUT_REF,
+    Warn,
+    "shared references or mutable references of mutable static is discouraged",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
+        reference: "issue #114447 <https://github.com/rust-lang/rust/issues/114447>",
+        explain_reason: false,
+    };
+}
+
 declare_lint! {
     /// The `absolute_paths_not_starting_with_crate` lint detects fully
     /// qualified paths that start with a module name instead of `crate`,

From 70ba4d14b3d1f81d27ad7b7bab81aac19e412c20 Mon Sep 17 00:00:00 2001
From: Obei Sideg <obei.sideg@gmail.com>
Date: Fri, 22 Dec 2023 15:00:25 +0300
Subject: [PATCH 03/17] Disallow reference to `static mut` for statements

---
 compiler/rustc_hir_analysis/src/check/errs.rs | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs
index 5862643c42f80..27bb2c57a5cb7 100644
--- a/compiler/rustc_hir_analysis/src/check/errs.rs
+++ b/compiler/rustc_hir_analysis/src/check/errs.rs
@@ -26,6 +26,25 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
     }
 }
 
+/// Check for shared or mutable references of `static mut` inside statement
+pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
+    if let hir::StmtKind::Local(loc) = stmt.kind
+        && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
+        && matches!(ba.0, rustc_ast::ByRef::Yes)
+        && let Some(init) = loc.init
+        && let Some(var) = is_path_static_mut(*init)
+    {
+        handle_static_mut_ref(
+            tcx,
+            init.span,
+            var,
+            loc.span.edition().at_least_rust_2024(),
+            matches!(ba.1, Mutability::Mut),
+            stmt.hir_id,
+        );
+    }
+}
+
 fn is_path_static_mut(expr: hir::Expr<'_>) -> Option<String> {
     if let hir::ExprKind::Path(qpath) = expr.kind
         && let hir::QPath::Resolved(_, path) = qpath

From a82fd2bc7cf15b8aa9ffbd7752999a3bc9cb8cea Mon Sep 17 00:00:00 2001
From: Obei Sideg <obei.sideg@gmail.com>
Date: Fri, 22 Dec 2023 15:03:45 +0300
Subject: [PATCH 04/17] Call `maybe_expr_static_mut` inside `resolve_expr`

---
 compiler/rustc_hir_analysis/src/check/region.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 542e69a6c34de..1abdc7b21df9d 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -18,6 +18,8 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::source_map;
 use rustc_span::Span;
 
+use super::errs::maybe_expr_static_mut;
+
 use std::mem;
 
 #[derive(Debug, Copy, Clone)]
@@ -242,6 +244,8 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
 fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
     debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr);
 
+    maybe_expr_static_mut(visitor.tcx, *expr);
+
     let prev_cx = visitor.cx;
     visitor.enter_node_scope_with_dtor(expr.hir_id.local_id);
 

From e36f7b7a5cacd9bfac9e7679ce6de26c03eb5795 Mon Sep 17 00:00:00 2001
From: Obei Sideg <obei.sideg@gmail.com>
Date: Fri, 22 Dec 2023 15:09:10 +0300
Subject: [PATCH 05/17] Call `maybe_stmt_static_mut` inside `resolve_stmt`

---
 compiler/rustc_hir_analysis/src/check/region.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 1abdc7b21df9d..5d5a4789734a7 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -18,7 +18,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::source_map;
 use rustc_span::Span;
 
-use super::errs::maybe_expr_static_mut;
+use super::errs::{maybe_expr_static_mut, maybe_stmt_static_mut};
 
 use std::mem;
 
@@ -226,6 +226,8 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
     let stmt_id = stmt.hir_id.local_id;
     debug!("resolve_stmt(stmt.id={:?})", stmt_id);
 
+    maybe_stmt_static_mut(visitor.tcx, *stmt);
+
     // Every statement will clean up the temporaries created during
     // execution of that statement. Therefore each statement has an
     // associated destruction scope that represents the scope of the

From 18edf9a64e8d84e72a6be67df3822e57dea8d34a Mon Sep 17 00:00:00 2001
From: Obei Sideg <obei.sideg@gmail.com>
Date: Fri, 22 Dec 2023 15:10:47 +0300
Subject: [PATCH 06/17] Add test for `E0796` and `static_mut_ref` lint

---
 .../reference-of-mut-static-safe.e2021.stderr | 26 ++++++
 .../reference-of-mut-static-safe.e2024.stderr | 15 +++
 .../ui/static/reference-of-mut-static-safe.rs | 13 +++
 .../reference-of-mut-static-unsafe-fn.rs      | 23 +++++
 .../reference-of-mut-static-unsafe-fn.stderr  | 63 +++++++++++++
 .../reference-of-mut-static.e2021.stderr      | 91 +++++++++++++++++++
 .../reference-of-mut-static.e2024.stderr      | 75 +++++++++++++++
 tests/ui/static/reference-of-mut-static.rs    | 50 ++++++++++
 8 files changed, 356 insertions(+)
 create mode 100644 tests/ui/static/reference-of-mut-static-safe.e2021.stderr
 create mode 100644 tests/ui/static/reference-of-mut-static-safe.e2024.stderr
 create mode 100644 tests/ui/static/reference-of-mut-static-safe.rs
 create mode 100644 tests/ui/static/reference-of-mut-static-unsafe-fn.rs
 create mode 100644 tests/ui/static/reference-of-mut-static-unsafe-fn.stderr
 create mode 100644 tests/ui/static/reference-of-mut-static.e2021.stderr
 create mode 100644 tests/ui/static/reference-of-mut-static.e2024.stderr
 create mode 100644 tests/ui/static/reference-of-mut-static.rs

diff --git a/tests/ui/static/reference-of-mut-static-safe.e2021.stderr b/tests/ui/static/reference-of-mut-static-safe.e2021.stderr
new file mode 100644
index 0000000000000..c38fe8790637e
--- /dev/null
+++ b/tests/ui/static/reference-of-mut-static-safe.e2021.stderr
@@ -0,0 +1,26 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/reference-of-mut-static-safe.rs:9:14
+   |
+LL |     let _x = &X;
+   |              ^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     let _x = addr_of!(X);
+   |              ~~~~~~~~~~~
+
+error[E0133]: use of mutable static is unsafe and requires unsafe function or block
+  --> $DIR/reference-of-mut-static-safe.rs:9:14
+   |
+LL |     let _x = &X;
+   |              ^^ use of mutable static
+   |
+   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
+
+error: aborting due to 1 previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/static/reference-of-mut-static-safe.e2024.stderr b/tests/ui/static/reference-of-mut-static-safe.e2024.stderr
new file mode 100644
index 0000000000000..53f81179de55a
--- /dev/null
+++ b/tests/ui/static/reference-of-mut-static-safe.e2024.stderr
@@ -0,0 +1,15 @@
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static-safe.rs:9:14
+   |
+LL |     let _x = &X;
+   |              ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     let _x = addr_of!(X);
+   |              ~~~~~~~~~~~
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0796`.
diff --git a/tests/ui/static/reference-of-mut-static-safe.rs b/tests/ui/static/reference-of-mut-static-safe.rs
new file mode 100644
index 0000000000000..5cb1a03bef512
--- /dev/null
+++ b/tests/ui/static/reference-of-mut-static-safe.rs
@@ -0,0 +1,13 @@
+// revisions: e2021 e2024
+
+// [e2021] edition:2021
+// [e2024] compile-flags: --edition 2024 -Z unstable-options
+
+fn main() {
+    static mut X: i32 = 1;
+
+    let _x = &X;
+    //[e2024]~^ reference of mutable static is disallowed [E0796]
+    //[e2021]~^^ use of mutable static is unsafe and requires unsafe function or block [E0133]
+    //[e2021]~^^^ shared reference of mutable static is discouraged [static_mut_ref]
+}
diff --git a/tests/ui/static/reference-of-mut-static-unsafe-fn.rs b/tests/ui/static/reference-of-mut-static-unsafe-fn.rs
new file mode 100644
index 0000000000000..6b1e77850e50d
--- /dev/null
+++ b/tests/ui/static/reference-of-mut-static-unsafe-fn.rs
@@ -0,0 +1,23 @@
+// compile-flags: --edition 2024 -Z unstable-options
+
+fn main() {}
+
+unsafe fn _foo() {
+    static mut X: i32 = 1;
+    static mut Y: i32 = 1;
+
+    let _y = &X;
+    //~^ ERROR reference of mutable static is disallowed
+
+    let ref _a = X;
+    //~^ ERROR reference of mutable static is disallowed
+
+    let (_b, _c) = (&X, &Y);
+    //~^ ERROR reference of mutable static is disallowed
+    //~^^ ERROR reference of mutable static is disallowed
+
+    foo(&X);
+    //~^ ERROR reference of mutable static is disallowed
+}
+
+fn foo<'a>(_x: &'a i32) {}
diff --git a/tests/ui/static/reference-of-mut-static-unsafe-fn.stderr b/tests/ui/static/reference-of-mut-static-unsafe-fn.stderr
new file mode 100644
index 0000000000000..5c6fdedfa96f7
--- /dev/null
+++ b/tests/ui/static/reference-of-mut-static-unsafe-fn.stderr
@@ -0,0 +1,63 @@
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static-unsafe-fn.rs:9:14
+   |
+LL |     let _y = &X;
+   |              ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     let _y = addr_of!(X);
+   |              ~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static-unsafe-fn.rs:12:18
+   |
+LL |     let ref _a = X;
+   |                  ^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     let ref _a = addr_of!(X);
+   |                  ~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static-unsafe-fn.rs:15:21
+   |
+LL |     let (_b, _c) = (&X, &Y);
+   |                     ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     let (_b, _c) = (addr_of!(X), &Y);
+   |                     ~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static-unsafe-fn.rs:15:25
+   |
+LL |     let (_b, _c) = (&X, &Y);
+   |                         ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     let (_b, _c) = (&X, addr_of!(Y));
+   |                         ~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static-unsafe-fn.rs:19:9
+   |
+LL |     foo(&X);
+   |         ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     foo(addr_of!(X));
+   |         ~~~~~~~~~~~
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0796`.
diff --git a/tests/ui/static/reference-of-mut-static.e2021.stderr b/tests/ui/static/reference-of-mut-static.e2021.stderr
new file mode 100644
index 0000000000000..77a6b3d304bdb
--- /dev/null
+++ b/tests/ui/static/reference-of-mut-static.e2021.stderr
@@ -0,0 +1,91 @@
+error: shared reference of mutable static is discouraged
+  --> $DIR/reference-of-mut-static.rs:16:18
+   |
+LL |         let _y = &X;
+   |                  ^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+note: the lint level is defined here
+  --> $DIR/reference-of-mut-static.rs:6:9
+   |
+LL | #![deny(static_mut_ref)]
+   |         ^^^^^^^^^^^^^^
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         let _y = addr_of!(X);
+   |                  ~~~~~~~~~~~
+
+error: mutable reference of mutable static is discouraged
+  --> $DIR/reference-of-mut-static.rs:20:18
+   |
+LL |         let _y = &mut X;
+   |                  ^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |         let _y = addr_of_mut!(X);
+   |                  ~~~~~~~~~~~~~~~
+
+error: shared reference of mutable static is discouraged
+  --> $DIR/reference-of-mut-static.rs:28:22
+   |
+LL |         let ref _a = X;
+   |                      ^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         let ref _a = addr_of!(X);
+   |                      ~~~~~~~~~~~
+
+error: shared reference of mutable static is discouraged
+  --> $DIR/reference-of-mut-static.rs:32:25
+   |
+LL |         let (_b, _c) = (&X, &Y);
+   |                         ^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         let (_b, _c) = (addr_of!(X), &Y);
+   |                         ~~~~~~~~~~~
+
+error: shared reference of mutable static is discouraged
+  --> $DIR/reference-of-mut-static.rs:32:29
+   |
+LL |         let (_b, _c) = (&X, &Y);
+   |                             ^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         let (_b, _c) = (&X, addr_of!(Y));
+   |                             ~~~~~~~~~~~
+
+error: shared reference of mutable static is discouraged
+  --> $DIR/reference-of-mut-static.rs:38:13
+   |
+LL |         foo(&X);
+   |             ^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         foo(addr_of!(X));
+   |             ~~~~~~~~~~~
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/static/reference-of-mut-static.e2024.stderr b/tests/ui/static/reference-of-mut-static.e2024.stderr
new file mode 100644
index 0000000000000..f445ec65a5d24
--- /dev/null
+++ b/tests/ui/static/reference-of-mut-static.e2024.stderr
@@ -0,0 +1,75 @@
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static.rs:16:18
+   |
+LL |         let _y = &X;
+   |                  ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         let _y = addr_of!(X);
+   |                  ~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static.rs:20:18
+   |
+LL |         let _y = &mut X;
+   |                  ^^^^^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |         let _y = addr_of_mut!(X);
+   |                  ~~~~~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static.rs:28:22
+   |
+LL |         let ref _a = X;
+   |                      ^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         let ref _a = addr_of!(X);
+   |                      ~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static.rs:32:25
+   |
+LL |         let (_b, _c) = (&X, &Y);
+   |                         ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         let (_b, _c) = (addr_of!(X), &Y);
+   |                         ~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static.rs:32:29
+   |
+LL |         let (_b, _c) = (&X, &Y);
+   |                             ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         let (_b, _c) = (&X, addr_of!(Y));
+   |                             ~~~~~~~~~~~
+
+error[E0796]: reference of mutable static is disallowed
+  --> $DIR/reference-of-mut-static.rs:38:13
+   |
+LL |         foo(&X);
+   |             ^^ reference of mutable static
+   |
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |         foo(addr_of!(X));
+   |             ~~~~~~~~~~~
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0796`.
diff --git a/tests/ui/static/reference-of-mut-static.rs b/tests/ui/static/reference-of-mut-static.rs
new file mode 100644
index 0000000000000..01a3b1fbd9b51
--- /dev/null
+++ b/tests/ui/static/reference-of-mut-static.rs
@@ -0,0 +1,50 @@
+// revisions: e2021 e2024
+
+// [e2021] edition:2021
+// [e2024] compile-flags: --edition 2024 -Z unstable-options
+
+#![deny(static_mut_ref)]
+
+use std::ptr::{addr_of, addr_of_mut};
+
+fn main() {
+    static mut X: i32 = 1;
+
+    static mut Y: i32 = 1;
+
+    unsafe {
+        let _y = &X;
+        //[e2024]~^ ERROR reference of mutable static is disallowed
+        //[e2021]~^^ ERROR shared reference of mutable static is discouraged [static_mut_ref]
+
+        let _y = &mut X;
+        //[e2024]~^ ERROR reference of mutable static is disallowed
+        //[e2021]~^^ ERROR mutable reference of mutable static is discouraged [static_mut_ref]
+
+        let _z = addr_of_mut!(X);
+
+        let _p = addr_of!(X);
+
+        let ref _a = X;
+        //[e2024]~^ ERROR reference of mutable static is disallowed
+        //[e2021]~^^ ERROR shared reference of mutable static is discouraged [static_mut_ref]
+
+        let (_b, _c) = (&X, &Y);
+        //[e2024]~^ ERROR reference of mutable static is disallowed
+        //[e2021]~^^ ERROR shared reference of mutable static is discouraged [static_mut_ref]
+        //[e2024]~^^^ ERROR reference of mutable static is disallowed
+        //[e2021]~^^^^ ERROR shared reference of mutable static is discouraged [static_mut_ref]
+
+        foo(&X);
+        //[e2024]~^ ERROR reference of mutable static is disallowed
+        //[e2021]~^^ ERROR shared reference of mutable static is discouraged [static_mut_ref]
+
+        static mut Z: &[i32; 3] = &[0, 1, 2];
+
+        let _ = Z.len();
+        let _ = Z[0];
+        let _ = format!("{:?}", Z);
+    }
+}
+
+fn foo<'a>(_x: &'a i32) {}

From 56173611d65bae5eacef80d15799ba89161cd38b Mon Sep 17 00:00:00 2001
From: klensy <klensy@users.noreply.github.com>
Date: Tue, 2 Jan 2024 22:36:01 +0300
Subject: [PATCH 07/17] don't reexport atomic::ordering via
 rustc_data_structures, use std import

---
 compiler/rustc_data_structures/src/sync.rs         |  7 ++-----
 compiler/rustc_driver_impl/src/lib.rs              |  3 +--
 compiler/rustc_query_impl/src/plumbing.rs          |  2 +-
 compiler/rustc_query_system/src/dep_graph/graph.rs | 14 +++++++-------
 compiler/rustc_session/src/session.rs              |  6 ++----
 5 files changed, 13 insertions(+), 19 deletions(-)

diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 43221d70e21cf..48edfba8da082 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -56,9 +56,6 @@ mod parallel;
 pub use parallel::scope;
 pub use parallel::{join, par_for_each_in, par_map, parallel_guard, try_par_for_each_in};
 
-pub use std::sync::atomic::Ordering;
-pub use std::sync::atomic::Ordering::SeqCst;
-
 pub use vec::{AppendOnlyIndexVec, AppendOnlyVec};
 
 mod vec;
@@ -67,8 +64,7 @@ mod freeze;
 pub use freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard};
 
 mod mode {
-    use super::Ordering;
-    use std::sync::atomic::AtomicU8;
+    use std::sync::atomic::{AtomicU8, Ordering};
 
     const UNINITIALIZED: u8 = 0;
     const DYN_NOT_THREAD_SAFE: u8 = 1;
@@ -113,6 +109,7 @@ cfg_match! {
     cfg(not(parallel_compiler)) => {
         use std::ops::Add;
         use std::cell::Cell;
+        use std::sync::atomic::Ordering;
 
         pub unsafe auto trait Send {}
         pub unsafe auto trait Sync {}
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index fd925b62702af..3add7e3c643ee 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -25,7 +25,6 @@ use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
 use rustc_data_structures::profiling::{
     get_resident_set_size, print_time_passes_entry, TimePassesFormat,
 };
-use rustc_data_structures::sync::SeqCst;
 use rustc_errors::registry::{InvalidErrorCode, Registry};
 use rustc_errors::{markdown, ColorConfig};
 use rustc_errors::{DiagCtxt, ErrorGuaranteed, PResult};
@@ -476,7 +475,7 @@ fn run_compiler(
             eprintln!(
                 "Fuel used by {}: {}",
                 sess.opts.unstable_opts.print_fuel.as_ref().unwrap(),
-                sess.print_fuel.load(SeqCst)
+                sess.print_fuel.load(Ordering::SeqCst)
             );
         }
 
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index f131a0f759320..fd7f1669ba456 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -69,7 +69,7 @@ impl QueryContext for QueryCtxt<'_> {
     fn next_job_id(self) -> QueryJobId {
         QueryJobId(
             NonZeroU64::new(
-                self.query_system.jobs.fetch_add(1, rustc_data_structures::sync::Ordering::Relaxed),
+                self.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
             )
             .unwrap(),
         )
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 9b06823dfbafb..1f09de0ed70c7 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -4,7 +4,7 @@ use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerR
 use rustc_data_structures::sharded::{self, Sharded};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::steal::Steal;
-use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
+use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc};
 use rustc_data_structures::unord::UnordMap;
 use rustc_index::IndexVec;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
@@ -13,7 +13,7 @@ use std::collections::hash_map::Entry;
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::marker::PhantomData;
-use std::sync::atomic::Ordering::Relaxed;
+use std::sync::atomic::Ordering;
 
 use super::query::DepGraphQuery;
 use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex};
@@ -476,7 +476,7 @@ impl<D: Deps> DepGraph<D> {
                 let task_deps = &mut *task_deps;
 
                 if cfg!(debug_assertions) {
-                    data.current.total_read_count.fetch_add(1, Relaxed);
+                    data.current.total_read_count.fetch_add(1, Ordering::Relaxed);
                 }
 
                 // As long as we only have a low number of reads we can avoid doing a hash
@@ -506,7 +506,7 @@ impl<D: Deps> DepGraph<D> {
                         }
                     }
                 } else if cfg!(debug_assertions) {
-                    data.current.total_duplicate_read_count.fetch_add(1, Relaxed);
+                    data.current.total_duplicate_read_count.fetch_add(1, Ordering::Relaxed);
                 }
             })
         }
@@ -976,8 +976,8 @@ impl<D: Deps> DepGraph<D> {
     pub fn print_incremental_info(&self) {
         if let Some(data) = &self.data {
             data.current.encoder.borrow().print_incremental_info(
-                data.current.total_read_count.load(Relaxed),
-                data.current.total_duplicate_read_count.load(Relaxed),
+                data.current.total_read_count.load(Ordering::Relaxed),
+                data.current.total_duplicate_read_count.load(Ordering::Relaxed),
             )
         }
     }
@@ -992,7 +992,7 @@ impl<D: Deps> DepGraph<D> {
 
     pub(crate) fn next_virtual_depnode_index(&self) -> DepNodeIndex {
         debug_assert!(self.data.is_none());
-        let index = self.virtual_dep_node_index.fetch_add(1, Relaxed);
+        let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed);
         DepNodeIndex::from_u32(index)
     }
 }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index ce166ae352fd7..0ada48986349e 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -14,9 +14,7 @@ use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_data_structures::jobserver::{self, Client};
 use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef};
-use rustc_data_structures::sync::{
-    AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread, Ordering::SeqCst,
-};
+use rustc_data_structures::sync::{AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread};
 use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
 use rustc_errors::emitter::{DynEmitter, HumanEmitter, HumanReadableErrorType};
 use rustc_errors::json::JsonEmitter;
@@ -44,7 +42,7 @@ use std::fmt;
 use std::ops::{Div, Mul};
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
-use std::sync::{atomic::AtomicBool, Arc};
+use std::sync::{atomic::AtomicBool, atomic::Ordering::SeqCst, Arc};
 
 struct OptimizationFuel {
     /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`.

From a8aa6878f63e53a9b0cfee542e9765407e1ca0d6 Mon Sep 17 00:00:00 2001
From: Obei Sideg <obei.sideg@gmail.com>
Date: Fri, 22 Dec 2023 15:12:01 +0300
Subject: [PATCH 08/17] Update test for `E0796` and `static_mut_ref` lint

---
 .../example/mini_core_hello_world.rs          |  3 +
 .../example/mini_core_hello_world.rs          |  3 +
 library/panic_unwind/src/seh.rs               |  4 +
 library/std/src/panicking.rs                  |  2 +
 .../src/sys/common/thread_local/fast_local.rs |  2 +
 .../sys/common/thread_local/static_local.rs   |  2 +
 library/std/src/thread/local.rs               |  2 +
 src/tools/lint-docs/src/groups.rs             |  1 +
 .../miri/tests/fail/tls/tls_static_dealloc.rs |  2 +
 src/tools/miri/tests/pass/static_mut.rs       |  3 +
 src/tools/miri/tests/pass/tls/tls_static.rs   |  2 +
 tests/ui/abi/statics/static-mut-foreign.rs    |  2 +
 .../ui/abi/statics/static-mut-foreign.stderr  | 31 +++++++
 .../borrowck/borrowck-access-permissions.rs   | 36 +++++---
 .../borrowck-access-permissions.stderr        | 33 +++++--
 .../borrowck-unsafe-static-mutable-borrows.rs |  9 +-
 ...rowck-unsafe-static-mutable-borrows.stderr | 17 ++++
 tests/ui/borrowck/issue-20801.rs              |  1 +
 tests/ui/borrowck/issue-20801.stderr          | 25 ++++--
 ...ue-55492-borrowck-migrate-scans-parents.rs | 41 ++++++---
 ...5492-borrowck-migrate-scans-parents.stderr | 75 +++++++++++++---
 tests/ui/consts/const_let_assign2.rs          |  1 +
 tests/ui/consts/const_let_assign2.stderr      | 17 ++++
 .../ui/consts/issue-17718-const-bad-values.rs |  3 +-
 .../issue-17718-const-bad-values.stderr       | 17 +++-
 ..._refers_to_static_cross_crate.32bit.stderr | 77 +++++++++-------
 ..._refers_to_static_cross_crate.64bit.stderr | 77 +++++++++-------
 .../const_refers_to_static_cross_crate.rs     | 22 +++--
 .../consts/static_mut_containing_mut_ref.rs   |  1 +
 .../static_mut_containing_mut_ref.stderr      | 17 ++++
 ...ic_mut_containing_mut_ref2.mut_refs.stderr | 23 ++++-
 .../consts/static_mut_containing_mut_ref2.rs  | 10 ++-
 ...tatic_mut_containing_mut_ref2.stock.stderr | 23 ++++-
 .../issue-23338-ensure-param-drop-order.rs    | 90 ++++++++++---------
 ...issue-23338-ensure-param-drop-order.stderr | 17 ++++
 tests/ui/error-codes/E0017.rs                 | 12 ++-
 tests/ui/error-codes/E0017.stderr             | 29 ++++--
 .../bare_type.rs}                             |  9 +-
 .../issues/issue-23611-enum-swap-in-drop.rs   | 44 ++++-----
 .../issue-23611-enum-swap-in-drop.stderr      | 17 ++++
 ...ead-local-static-mut-borrow-outlives-fn.rs |  5 +-
 ...local-static-mut-borrow-outlives-fn.stderr | 17 ++++
 .../reference-of-mut-static-safe.e2021.stderr |  4 +-
 tests/ui/static/safe-extern-statics-mut.rs    |  2 +
 .../ui/static/safe-extern-statics-mut.stderr  | 35 +++++++-
 tests/ui/statics/issue-15261.rs               |  3 +-
 tests/ui/statics/issue-15261.stderr           | 17 ++++
 tests/ui/statics/static-mut-xc.rs             |  3 +-
 tests/ui/statics/static-mut-xc.stderr         | 31 +++++++
 tests/ui/statics/static-recursive.rs          | 27 +++---
 tests/ui/statics/static-recursive.stderr      | 17 ++++
 tests/ui/thread-local/thread-local-static.rs  |  3 +-
 .../thread-local/thread-local-static.stderr   | 17 +++-
 .../thread-local-static.thir.stderr           | 59 ++++++++++++
 54 files changed, 805 insertions(+), 237 deletions(-)
 create mode 100644 tests/ui/abi/statics/static-mut-foreign.stderr
 create mode 100644 tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr
 create mode 100644 tests/ui/consts/const_let_assign2.stderr
 create mode 100644 tests/ui/consts/static_mut_containing_mut_ref.stderr
 create mode 100644 tests/ui/drop/issue-23338-ensure-param-drop-order.stderr
 rename tests/ui/{issues/issue-20616.rs => impl-header-lifetime-elision/bare_type.rs} (82%)
 create mode 100644 tests/ui/issues/issue-23611-enum-swap-in-drop.stderr
 create mode 100644 tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr
 create mode 100644 tests/ui/statics/issue-15261.stderr
 create mode 100644 tests/ui/statics/static-mut-xc.stderr
 create mode 100644 tests/ui/statics/static-recursive.stderr
 create mode 100644 tests/ui/thread-local/thread-local-static.thir.stderr

diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
index a1cdf31c68a00..2a7b1107ffcaa 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
@@ -111,6 +111,9 @@ fn start<T: Termination + 'static>(
 }
 
 static mut NUM: u8 = 6 * 7;
+
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[allow(static_mut_ref)]
 static NUM_REF: &'static u8 = unsafe { &NUM };
 
 unsafe fn zeroed<T>() -> T {
diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
index 40a1ad22c0e13..9827e299f2a31 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
@@ -98,6 +98,9 @@ fn start<T: Termination + 'static>(
 }
 
 static mut NUM: u8 = 6 * 7;
+
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[allow(static_mut_ref)]
 static NUM_REF: &'static u8 = unsafe { &NUM };
 
 macro_rules! assert {
diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs
index 99db00e549066..ccae406cc286c 100644
--- a/library/panic_unwind/src/seh.rs
+++ b/library/panic_unwind/src/seh.rs
@@ -261,6 +261,8 @@ cfg_if::cfg_if! {
    }
 }
 
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[cfg_attr(not(bootstrap), allow(static_mut_ref))]
 pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
     use core::intrinsics::atomic_store_seqcst;
 
@@ -322,6 +324,8 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
     _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
 }
 
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[cfg_attr(not(bootstrap), allow(static_mut_ref))]
 pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
     // A null payload here means that we got here from the catch (...) of
     // __rust_try. This happens when a non-Rust foreign exception is caught.
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 66b4ec37c8ec5..c80f15d8a611b 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -337,6 +337,8 @@ pub mod panic_count {
 #[doc(hidden)]
 #[cfg(not(feature = "panic_immediate_abort"))]
 #[unstable(feature = "update_panic_count", issue = "none")]
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[cfg_attr(not(bootstrap), allow(static_mut_ref))]
 pub mod panic_count {
     use crate::cell::Cell;
     use crate::sync::atomic::{AtomicUsize, Ordering};
diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs
index c0a9619bf7bfa..9206588be064e 100644
--- a/library/std/src/sys/common/thread_local/fast_local.rs
+++ b/library/std/src/sys/common/thread_local/fast_local.rs
@@ -13,6 +13,8 @@ pub macro thread_local_inner {
     (@key $t:ty, const $init:expr) => {{
         #[inline]
         #[deny(unsafe_op_in_unsafe_fn)]
+        // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+        #[cfg_attr(not(bootstrap), allow(static_mut_ref))]
         unsafe fn __getit(
             _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
         ) -> $crate::option::Option<&'static $t> {
diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs
index 5cb6c541a0ec5..51cba66fad76d 100644
--- a/library/std/src/sys/common/thread_local/static_local.rs
+++ b/library/std/src/sys/common/thread_local/static_local.rs
@@ -11,6 +11,8 @@ pub macro thread_local_inner {
     (@key $t:ty, const $init:expr) => {{
         #[inline] // see comments below
         #[deny(unsafe_op_in_unsafe_fn)]
+        // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+        #[cfg_attr(not(bootstrap), allow(static_mut_ref))]
         unsafe fn __getit(
             _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
         ) -> $crate::option::Option<&'static $t> {
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index 9cf37b0e6347c..338567777f753 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -180,6 +180,8 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")]
 #[allow_internal_unstable(thread_local_internals)]
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[cfg_attr(not(bootstrap), allow(static_mut_ref))]
 macro_rules! thread_local {
     // empty (base case for the recursion)
     () => {};
diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs
index 5be8ef7996bb2..c5cd30ccc3437 100644
--- a/src/tools/lint-docs/src/groups.rs
+++ b/src/tools/lint-docs/src/groups.rs
@@ -15,6 +15,7 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
     ("future-incompatible", "Lints that detect code that has future-compatibility problems"),
     ("rust-2018-compatibility", "Lints used to transition code from the 2015 edition to 2018"),
     ("rust-2021-compatibility", "Lints used to transition code from the 2018 edition to 2021"),
+    ("rust-2024-compatibility", "Lints used to transition code from the 2021 edition to 2024"),
 ];
 
 type LintGroups = BTreeMap<String, BTreeSet<String>>;
diff --git a/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs b/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs
index d5e6d37226ab3..762a8d85314f2 100644
--- a/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs
+++ b/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs
@@ -1,6 +1,8 @@
 //! Ensure that thread-local statics get deallocated when the thread dies.
 
 #![feature(thread_local)]
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#![allow(static_mut_ref)]
 
 #[thread_local]
 static mut TLS: u8 = 0;
diff --git a/src/tools/miri/tests/pass/static_mut.rs b/src/tools/miri/tests/pass/static_mut.rs
index 218b02525bd55..c1e58b70adb0b 100644
--- a/src/tools/miri/tests/pass/static_mut.rs
+++ b/src/tools/miri/tests/pass/static_mut.rs
@@ -1,4 +1,7 @@
 static mut FOO: i32 = 42;
+
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[allow(static_mut_ref)]
 static BAR: Foo = Foo(unsafe { &FOO as *const _ });
 
 #[allow(dead_code)]
diff --git a/src/tools/miri/tests/pass/tls/tls_static.rs b/src/tools/miri/tests/pass/tls/tls_static.rs
index fc4c8a283ddb2..9be00af47aa35 100644
--- a/src/tools/miri/tests/pass/tls/tls_static.rs
+++ b/src/tools/miri/tests/pass/tls/tls_static.rs
@@ -8,6 +8,8 @@
 //! test, we also check that thread-locals act as per-thread statics.
 
 #![feature(thread_local)]
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#![allow(static_mut_ref)]
 
 use std::thread;
 
diff --git a/tests/ui/abi/statics/static-mut-foreign.rs b/tests/ui/abi/statics/static-mut-foreign.rs
index ecd8ee94a01e3..eb732e7c2c31f 100644
--- a/tests/ui/abi/statics/static-mut-foreign.rs
+++ b/tests/ui/abi/statics/static-mut-foreign.rs
@@ -33,7 +33,9 @@ unsafe fn run() {
     rust_dbg_static_mut = -3;
     assert_eq!(rust_dbg_static_mut, -3);
     static_bound(&rust_dbg_static_mut);
+    //~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
     static_bound_set(&mut rust_dbg_static_mut);
+    //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
 }
 
 pub fn main() {
diff --git a/tests/ui/abi/statics/static-mut-foreign.stderr b/tests/ui/abi/statics/static-mut-foreign.stderr
new file mode 100644
index 0000000000000..144ac056f87e4
--- /dev/null
+++ b/tests/ui/abi/statics/static-mut-foreign.stderr
@@ -0,0 +1,31 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/static-mut-foreign.rs:35:18
+   |
+LL |     static_bound(&rust_dbg_static_mut);
+   |                  ^^^^^^^^^^^^^^^^^^^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     static_bound(addr_of!(rust_dbg_static_mut));
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/static-mut-foreign.rs:37:22
+   |
+LL |     static_bound_set(&mut rust_dbg_static_mut);
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |     static_bound_set(addr_of_mut!(rust_dbg_static_mut));
+   |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/borrowck/borrowck-access-permissions.rs b/tests/ui/borrowck/borrowck-access-permissions.rs
index 469ad508b0e77..1638644103ba4 100644
--- a/tests/ui/borrowck/borrowck-access-permissions.rs
+++ b/tests/ui/borrowck/borrowck-access-permissions.rs
@@ -1,21 +1,27 @@
-static static_x : i32 = 1;
-static mut static_x_mut : i32 = 1;
+static static_x: i32 = 1;
+static mut static_x_mut: i32 = 1;
 
 fn main() {
     let x = 1;
     let mut x_mut = 1;
 
-    { // borrow of local
+    {
+        // borrow of local
         let _y1 = &mut x; //~ ERROR [E0596]
         let _y2 = &mut x_mut; // No error
     }
 
-    { // borrow of static
+    {
+        // borrow of static
         let _y1 = &mut static_x; //~ ERROR [E0596]
-        unsafe { let _y2 = &mut static_x_mut; } // No error
+        unsafe {
+            let _y2 = &mut static_x_mut;
+            //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+        }
     }
 
-    { // borrow of deref to box
+    {
+        // borrow of deref to box
         let box_x = Box::new(1);
         let mut box_x_mut = Box::new(1);
 
@@ -23,7 +29,8 @@ fn main() {
         let _y2 = &mut *box_x_mut; // No error
     }
 
-    { // borrow of deref to reference
+    {
+        // borrow of deref to reference
         let ref_x = &x;
         let ref_x_mut = &mut x_mut;
 
@@ -31,9 +38,10 @@ fn main() {
         let _y2 = &mut *ref_x_mut; // No error
     }
 
-    { // borrow of deref to pointer
-        let ptr_x : *const _ = &x;
-        let ptr_mut_x : *mut _ = &mut x_mut;
+    {
+        // borrow of deref to pointer
+        let ptr_x: *const _ = &x;
+        let ptr_mut_x: *mut _ = &mut x_mut;
 
         unsafe {
             let _y1 = &mut *ptr_x; //~ ERROR [E0596]
@@ -41,8 +49,12 @@ fn main() {
         }
     }
 
-    { // borrowing mutably through an immutable reference
-        struct Foo<'a> { f: &'a mut i32, g: &'a i32 };
+    {
+        // borrowing mutably through an immutable reference
+        struct Foo<'a> {
+            f: &'a mut i32,
+            g: &'a i32,
+        };
         let mut foo = Foo { f: &mut x_mut, g: &x };
         let foo_ref = &foo;
         let _y = &mut *foo_ref.f; //~ ERROR [E0596]
diff --git a/tests/ui/borrowck/borrowck-access-permissions.stderr b/tests/ui/borrowck/borrowck-access-permissions.stderr
index c161e2d95b43a..93d92295dd9b6 100644
--- a/tests/ui/borrowck/borrowck-access-permissions.stderr
+++ b/tests/ui/borrowck/borrowck-access-permissions.stderr
@@ -1,5 +1,20 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/borrowck-access-permissions.rs:18:23
+   |
+LL |             let _y2 = &mut static_x_mut;
+   |                       ^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |             let _y2 = addr_of_mut!(static_x_mut);
+   |                       ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
-  --> $DIR/borrowck-access-permissions.rs:9:19
+  --> $DIR/borrowck-access-permissions.rs:10:19
    |
 LL |         let _y1 = &mut x;
    |                   ^^^^^^ cannot borrow as mutable
@@ -10,13 +25,13 @@ LL |     let mut x = 1;
    |         +++
 
 error[E0596]: cannot borrow immutable static item `static_x` as mutable
-  --> $DIR/borrowck-access-permissions.rs:14:19
+  --> $DIR/borrowck-access-permissions.rs:16:19
    |
 LL |         let _y1 = &mut static_x;
    |                   ^^^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `*box_x` as mutable, as `box_x` is not declared as mutable
-  --> $DIR/borrowck-access-permissions.rs:22:19
+  --> $DIR/borrowck-access-permissions.rs:28:19
    |
 LL |         let _y1 = &mut *box_x;
    |                   ^^^^^^^^^^^ cannot borrow as mutable
@@ -27,7 +42,7 @@ LL |         let mut box_x = Box::new(1);
    |             +++
 
 error[E0596]: cannot borrow `*ref_x` as mutable, as it is behind a `&` reference
-  --> $DIR/borrowck-access-permissions.rs:30:19
+  --> $DIR/borrowck-access-permissions.rs:37:19
    |
 LL |         let _y1 = &mut *ref_x;
    |                   ^^^^^^^^^^^ `ref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
@@ -38,18 +53,18 @@ LL |         let ref_x = &mut x;
    |                      +++
 
 error[E0596]: cannot borrow `*ptr_x` as mutable, as it is behind a `*const` pointer
-  --> $DIR/borrowck-access-permissions.rs:39:23
+  --> $DIR/borrowck-access-permissions.rs:47:23
    |
 LL |             let _y1 = &mut *ptr_x;
    |                       ^^^^^^^^^^^ `ptr_x` is a `*const` pointer, so the data it refers to cannot be borrowed as mutable
    |
 help: consider changing this to be a mutable pointer
    |
-LL |         let ptr_x : *const _ = &mut x;
-   |                                 +++
+LL |         let ptr_x: *const _ = &mut x;
+   |                                +++
 
 error[E0596]: cannot borrow `*foo_ref.f` as mutable, as it is behind a `&` reference
-  --> $DIR/borrowck-access-permissions.rs:48:18
+  --> $DIR/borrowck-access-permissions.rs:60:18
    |
 LL |         let _y = &mut *foo_ref.f;
    |                  ^^^^^^^^^^^^^^^ `foo_ref` is a `&` reference, so the data it refers to cannot be borrowed as mutable
@@ -59,6 +74,6 @@ help: consider changing this to be a mutable reference
 LL |         let foo_ref = &mut foo;
    |                        +++
 
-error: aborting due to 6 previous errors
+error: aborting due to 6 previous errors; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs
index adc7dfd541f48..1bf079e24cae4 100644
--- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs
+++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs
@@ -2,17 +2,22 @@
 
 // Test file taken from issue 45129 (https://github.com/rust-lang/rust/issues/45129)
 
-struct Foo { x: [usize; 2] }
+struct Foo {
+    x: [usize; 2],
+}
 
 static mut SFOO: Foo = Foo { x: [23, 32] };
 
 impl Foo {
-    fn x(&mut self) -> &mut usize { &mut self.x[0] }
+    fn x(&mut self) -> &mut usize {
+        &mut self.x[0]
+    }
 }
 
 fn main() {
     unsafe {
         let sfoo: *mut Foo = &mut SFOO;
+        //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
         let x = (*sfoo).x();
         (*sfoo).x[1] += 1;
         *x += 1;
diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr
new file mode 100644
index 0000000000000..7a3824f79a4c2
--- /dev/null
+++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr
@@ -0,0 +1,17 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/borrowck-unsafe-static-mutable-borrows.rs:19:30
+   |
+LL |         let sfoo: *mut Foo = &mut SFOO;
+   |                              ^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |         let sfoo: *mut Foo = addr_of_mut!(SFOO);
+   |                              ~~~~~~~~~~~~~~~~~~
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/borrowck/issue-20801.rs b/tests/ui/borrowck/issue-20801.rs
index c3f136f2876b6..ec83af5d5dfc6 100644
--- a/tests/ui/borrowck/issue-20801.rs
+++ b/tests/ui/borrowck/issue-20801.rs
@@ -12,6 +12,7 @@ fn imm_ref() -> &'static T {
 
 fn mut_ref() -> &'static mut T {
     unsafe { &mut GLOBAL_MUT_T }
+    //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
 }
 
 fn mut_ptr() -> *mut T {
diff --git a/tests/ui/borrowck/issue-20801.stderr b/tests/ui/borrowck/issue-20801.stderr
index 215bf01006369..b2bee2d880394 100644
--- a/tests/ui/borrowck/issue-20801.stderr
+++ b/tests/ui/borrowck/issue-20801.stderr
@@ -1,5 +1,20 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/issue-20801.rs:14:14
+   |
+LL |     unsafe { &mut GLOBAL_MUT_T }
+   |              ^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |     unsafe { addr_of_mut!(GLOBAL_MUT_T) }
+   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 error[E0507]: cannot move out of a mutable reference
-  --> $DIR/issue-20801.rs:26:22
+  --> $DIR/issue-20801.rs:27:22
    |
 LL |     let a = unsafe { *mut_ref() };
    |                      ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait
@@ -11,7 +26,7 @@ LL +     let a = unsafe { mut_ref() };
    |
 
 error[E0507]: cannot move out of a shared reference
-  --> $DIR/issue-20801.rs:29:22
+  --> $DIR/issue-20801.rs:30:22
    |
 LL |     let b = unsafe { *imm_ref() };
    |                      ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait
@@ -23,7 +38,7 @@ LL +     let b = unsafe { imm_ref() };
    |
 
 error[E0507]: cannot move out of a raw pointer
-  --> $DIR/issue-20801.rs:32:22
+  --> $DIR/issue-20801.rs:33:22
    |
 LL |     let c = unsafe { *mut_ptr() };
    |                      ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait
@@ -35,7 +50,7 @@ LL +     let c = unsafe { mut_ptr() };
    |
 
 error[E0507]: cannot move out of a raw pointer
-  --> $DIR/issue-20801.rs:35:22
+  --> $DIR/issue-20801.rs:36:22
    |
 LL |     let d = unsafe { *const_ptr() };
    |                      ^^^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait
@@ -46,6 +61,6 @@ LL -     let d = unsafe { *const_ptr() };
 LL +     let d = unsafe { const_ptr() };
    |
 
-error: aborting due to 4 previous errors
+error: aborting due to 4 previous errors; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs
index b3cce1b3a0611..9b172b4131911 100644
--- a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs
+++ b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs
@@ -8,7 +8,10 @@ mod borrowck_closures_unique {
         static mut Y: isize = 3;
         let mut c1 = |y: &'static mut isize| x = y;
         //~^ ERROR is not declared as mutable
-        unsafe { c1(&mut Y); }
+        unsafe {
+            c1(&mut Y);
+            //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+        }
     }
 }
 
@@ -17,36 +20,50 @@ mod borrowck_closures_unique_grandparent {
         static mut Z: isize = 3;
         let mut c1 = |z: &'static mut isize| {
             let mut c2 = |y: &'static mut isize| x = y;
-        //~^ ERROR is not declared as mutable
+            //~^ ERROR is not declared as mutable
             c2(z);
         };
-        unsafe { c1(&mut Z); }
+        unsafe {
+            c1(&mut Z);
+            //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+        }
     }
 }
 
 // adapted from mutability_errors.rs
 mod mutability_errors {
     pub fn capture_assign_whole(x: (i32,)) {
-        || { x = (1,); };
-        //~^ ERROR is not declared as mutable
+        || {
+            x = (1,);
+            //~^ ERROR is not declared as mutable
+        };
     }
     pub fn capture_assign_part(x: (i32,)) {
-        || { x.0 = 1; };
-        //~^ ERROR is not declared as mutable
+        || {
+            x.0 = 1;
+            //~^ ERROR is not declared as mutable
+        };
     }
     pub fn capture_reborrow_whole(x: (i32,)) {
-        || { &mut x; };
-        //~^ ERROR is not declared as mutable
+        || {
+            &mut x;
+            //~^ ERROR is not declared as mutable
+        };
     }
     pub fn capture_reborrow_part(x: (i32,)) {
-        || { &mut x.0; };
-        //~^ ERROR is not declared as mutable
+        || {
+            &mut x.0;
+            //~^ ERROR is not declared as mutable
+        };
     }
 }
 
 fn main() {
     static mut X: isize = 2;
-    unsafe { borrowck_closures_unique::e(&mut X); }
+    unsafe {
+        borrowck_closures_unique::e(&mut X);
+        //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+    }
 
     mutability_errors::capture_assign_whole((1000,));
     mutability_errors::capture_assign_part((2000,));
diff --git a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr
index 4c299cdc455ac..e4e4947fce1c1 100644
--- a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr
+++ b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr
@@ -1,3 +1,46 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:12:16
+   |
+LL |             c1(&mut Y);
+   |                ^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |             c1(addr_of_mut!(Y));
+   |                ~~~~~~~~~~~~~~~
+
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:27:16
+   |
+LL |             c1(&mut Z);
+   |                ^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |             c1(addr_of_mut!(Z));
+   |                ~~~~~~~~~~~~~~~
+
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:64:37
+   |
+LL |         borrowck_closures_unique::e(&mut X);
+   |                                     ^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |         borrowck_closures_unique::e(addr_of_mut!(X));
+   |                                     ~~~~~~~~~~~~~~~
+
 error[E0594]: cannot assign to `x`, as it is not declared as mutable
   --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:9:46
    |
@@ -8,7 +51,7 @@ LL |         let mut c1 = |y: &'static mut isize| x = y;
    |                                              ^^^^^ cannot assign
 
 error[E0594]: cannot assign to `x`, as it is not declared as mutable
-  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:19:50
+  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:22:50
    |
 LL |     pub fn ee(x: &'static mut isize) {
    |               - help: consider changing this to be mutable: `mut x`
@@ -17,38 +60,42 @@ LL |             let mut c2 = |y: &'static mut isize| x = y;
    |                                                  ^^^^^ cannot assign
 
 error[E0594]: cannot assign to `x`, as it is not declared as mutable
-  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:30:14
+  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:37:13
    |
 LL |     pub fn capture_assign_whole(x: (i32,)) {
    |                                 - help: consider changing this to be mutable: `mut x`
-LL |         || { x = (1,); };
-   |              ^^^^^^^^ cannot assign
+LL |         || {
+LL |             x = (1,);
+   |             ^^^^^^^^ cannot assign
 
 error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
-  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:34:14
+  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:43:13
    |
 LL |     pub fn capture_assign_part(x: (i32,)) {
    |                                - help: consider changing this to be mutable: `mut x`
-LL |         || { x.0 = 1; };
-   |              ^^^^^^^ cannot assign
+LL |         || {
+LL |             x.0 = 1;
+   |             ^^^^^^^ cannot assign
 
 error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
-  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:38:14
+  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:49:13
    |
 LL |     pub fn capture_reborrow_whole(x: (i32,)) {
    |                                   - help: consider changing this to be mutable: `mut x`
-LL |         || { &mut x; };
-   |              ^^^^^^ cannot borrow as mutable
+LL |         || {
+LL |             &mut x;
+   |             ^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `x.0` as mutable, as `x` is not declared as mutable
-  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:42:14
+  --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:55:13
    |
 LL |     pub fn capture_reborrow_part(x: (i32,)) {
    |                                  - help: consider changing this to be mutable: `mut x`
-LL |         || { &mut x.0; };
-   |              ^^^^^^^^ cannot borrow as mutable
+LL |         || {
+LL |             &mut x.0;
+   |             ^^^^^^^^ cannot borrow as mutable
 
-error: aborting due to 6 previous errors
+error: aborting due to 6 previous errors; 3 warnings emitted
 
 Some errors have detailed explanations: E0594, E0596.
 For more information about an error, try `rustc --explain E0594`.
diff --git a/tests/ui/consts/const_let_assign2.rs b/tests/ui/consts/const_let_assign2.rs
index 28265c85dd1f1..1c7afe0e3d6cb 100644
--- a/tests/ui/consts/const_let_assign2.rs
+++ b/tests/ui/consts/const_let_assign2.rs
@@ -16,6 +16,7 @@ static mut BB: AA = AA::new();
 
 fn main() {
     let ptr = unsafe { &mut BB };
+    //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
     for a in ptr.data.iter() {
         println!("{}", a);
     }
diff --git a/tests/ui/consts/const_let_assign2.stderr b/tests/ui/consts/const_let_assign2.stderr
new file mode 100644
index 0000000000000..2764153a8a590
--- /dev/null
+++ b/tests/ui/consts/const_let_assign2.stderr
@@ -0,0 +1,17 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/const_let_assign2.rs:18:24
+   |
+LL |     let ptr = unsafe { &mut BB };
+   |                        ^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |     let ptr = unsafe { addr_of_mut!(BB) };
+   |                        ~~~~~~~~~~~~~~~~
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/consts/issue-17718-const-bad-values.rs b/tests/ui/consts/issue-17718-const-bad-values.rs
index 62bbb3b569c37..4fedc48452bec 100644
--- a/tests/ui/consts/issue-17718-const-bad-values.rs
+++ b/tests/ui/consts/issue-17718-const-bad-values.rs
@@ -3,7 +3,8 @@ const C1: &'static mut [usize] = &mut [];
 
 static mut S: usize = 3;
 const C2: &'static mut usize = unsafe { &mut S };
-//~^ ERROR: constants cannot refer to statics
+//~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+//~^^ ERROR: constants cannot refer to statics
 //~| ERROR: constants cannot refer to statics
 
 fn main() {}
diff --git a/tests/ui/consts/issue-17718-const-bad-values.stderr b/tests/ui/consts/issue-17718-const-bad-values.stderr
index 405c2195dec4f..2dc91f52669e7 100644
--- a/tests/ui/consts/issue-17718-const-bad-values.stderr
+++ b/tests/ui/consts/issue-17718-const-bad-values.stderr
@@ -1,3 +1,18 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/issue-17718-const-bad-values.rs:5:41
+   |
+LL | const C2: &'static mut usize = unsafe { &mut S };
+   |                                         ^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL | const C2: &'static mut usize = unsafe { addr_of_mut!(S) };
+   |                                         ~~~~~~~~~~~~~~~
+
 error[E0764]: mutable references are not allowed in the final value of constants
   --> $DIR/issue-17718-const-bad-values.rs:1:34
    |
@@ -21,7 +36,7 @@ LL | const C2: &'static mut usize = unsafe { &mut S };
    = help: consider extracting the value of the `static` to a `const`, and referring to that
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error: aborting due to 3 previous errors
+error: aborting due to 3 previous errors; 1 warning emitted
 
 Some errors have detailed explanations: E0013, E0764.
 For more information about an error, try `rustc --explain E0013`.
diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr
index 7960648ce3a19..ed9db67542641 100644
--- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr
+++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr
@@ -1,3 +1,18 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/const_refers_to_static_cross_crate.rs:13:14
+   |
+LL |     unsafe { &static_cross_crate::ZERO }
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     unsafe { addr_of!(static_cross_crate::ZERO) }
+   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/const_refers_to_static_cross_crate.rs:10:1
    |
@@ -10,13 +25,13 @@ LL | const SLICE_MUT: &[u8; 1] = {
            }
 
 error: could not evaluate constant pattern
-  --> $DIR/const_refers_to_static_cross_crate.rs:34:9
+  --> $DIR/const_refers_to_static_cross_crate.rs:42:9
    |
 LL |         SLICE_MUT => true,
    |         ^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/const_refers_to_static_cross_crate.rs:15:1
+  --> $DIR/const_refers_to_static_cross_crate.rs:17:1
    |
 LL | const U8_MUT: &u8 = {
    | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant
@@ -27,31 +42,31 @@ LL | const U8_MUT: &u8 = {
            }
 
 error: could not evaluate constant pattern
-  --> $DIR/const_refers_to_static_cross_crate.rs:42:9
+  --> $DIR/const_refers_to_static_cross_crate.rs:50:9
    |
 LL |         U8_MUT => true,
    |         ^^^^^^
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/const_refers_to_static_cross_crate.rs:22:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:25:15
    |
 LL |     unsafe { &(*static_cross_crate::ZERO_REF)[0] }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
 
 error: could not evaluate constant pattern
-  --> $DIR/const_refers_to_static_cross_crate.rs:52:9
+  --> $DIR/const_refers_to_static_cross_crate.rs:60:9
    |
 LL |         U8_MUT2 => true,
    |         ^^^^^^^
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
 
 error: could not evaluate constant pattern
-  --> $DIR/const_refers_to_static_cross_crate.rs:59:9
+  --> $DIR/const_refers_to_static_cross_crate.rs:67:9
    |
 LL |         U8_MUT3 => true,
    |         ^^^^^^^
@@ -59,61 +74,61 @@ LL |         U8_MUT3 => true,
 warning: skipping const checks
    |
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:12:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:13:15
    |
 LL |     unsafe { &static_cross_crate::ZERO }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:12:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:13:15
    |
 LL |     unsafe { &static_cross_crate::ZERO }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:17:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:20:15
    |
 LL |     unsafe { &static_cross_crate::ZERO[0] }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:17:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:20:15
    |
 LL |     unsafe { &static_cross_crate::ZERO[0] }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:17:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:20:15
    |
 LL |     unsafe { &static_cross_crate::ZERO[0] }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:22:17
+  --> $DIR/const_refers_to_static_cross_crate.rs:25:17
    |
 LL |     unsafe { &(*static_cross_crate::ZERO_REF)[0] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 8 previous errors; 1 warning emitted
+error: aborting due to 8 previous errors; 2 warnings emitted
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr
index 6ae0b2d1bfef7..275323bc286c0 100644
--- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr
+++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr
@@ -1,3 +1,18 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/const_refers_to_static_cross_crate.rs:13:14
+   |
+LL |     unsafe { &static_cross_crate::ZERO }
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     unsafe { addr_of!(static_cross_crate::ZERO) }
+   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/const_refers_to_static_cross_crate.rs:10:1
    |
@@ -10,13 +25,13 @@ LL | const SLICE_MUT: &[u8; 1] = {
            }
 
 error: could not evaluate constant pattern
-  --> $DIR/const_refers_to_static_cross_crate.rs:34:9
+  --> $DIR/const_refers_to_static_cross_crate.rs:42:9
    |
 LL |         SLICE_MUT => true,
    |         ^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/const_refers_to_static_cross_crate.rs:15:1
+  --> $DIR/const_refers_to_static_cross_crate.rs:17:1
    |
 LL | const U8_MUT: &u8 = {
    | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant
@@ -27,31 +42,31 @@ LL | const U8_MUT: &u8 = {
            }
 
 error: could not evaluate constant pattern
-  --> $DIR/const_refers_to_static_cross_crate.rs:42:9
+  --> $DIR/const_refers_to_static_cross_crate.rs:50:9
    |
 LL |         U8_MUT => true,
    |         ^^^^^^
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/const_refers_to_static_cross_crate.rs:22:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:25:15
    |
 LL |     unsafe { &(*static_cross_crate::ZERO_REF)[0] }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
 
 error: could not evaluate constant pattern
-  --> $DIR/const_refers_to_static_cross_crate.rs:52:9
+  --> $DIR/const_refers_to_static_cross_crate.rs:60:9
    |
 LL |         U8_MUT2 => true,
    |         ^^^^^^^
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static
 
 error: could not evaluate constant pattern
-  --> $DIR/const_refers_to_static_cross_crate.rs:59:9
+  --> $DIR/const_refers_to_static_cross_crate.rs:67:9
    |
 LL |         U8_MUT3 => true,
    |         ^^^^^^^
@@ -59,61 +74,61 @@ LL |         U8_MUT3 => true,
 warning: skipping const checks
    |
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:12:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:13:15
    |
 LL |     unsafe { &static_cross_crate::ZERO }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:12:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:13:15
    |
 LL |     unsafe { &static_cross_crate::ZERO }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:17:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:20:15
    |
 LL |     unsafe { &static_cross_crate::ZERO[0] }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:17:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:20:15
    |
 LL |     unsafe { &static_cross_crate::ZERO[0] }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:17:15
+  --> $DIR/const_refers_to_static_cross_crate.rs:20:15
    |
 LL |     unsafe { &static_cross_crate::ZERO[0] }
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:22:17
+  --> $DIR/const_refers_to_static_cross_crate.rs:25:17
    |
 LL |     unsafe { &(*static_cross_crate::ZERO_REF)[0] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check that does not even have a feature gate
-  --> $DIR/const_refers_to_static_cross_crate.rs:27:20
+  --> $DIR/const_refers_to_static_cross_crate.rs:31:15
    |
-LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         match static_cross_crate::OPT_ZERO {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 8 previous errors; 1 warning emitted
+error: aborting due to 8 previous errors; 2 warnings emitted
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs
index bbaa32ddfd1bd..3eafa58d9f9fd 100644
--- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs
+++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs
@@ -7,13 +7,16 @@ extern crate static_cross_crate;
 
 // Sneaky: reference to a mutable static.
 // Allowing this would be a disaster for pattern matching, we could violate exhaustiveness checking!
-const SLICE_MUT: &[u8; 1] = { //~ ERROR undefined behavior to use this value
-//~| encountered a reference pointing to a static variable
+const SLICE_MUT: &[u8; 1] = {
+    //~^ ERROR undefined behavior to use this value
+    //~| encountered a reference pointing to a static variable
     unsafe { &static_cross_crate::ZERO }
+    //~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
 };
 
-const U8_MUT: &u8 = { //~ ERROR undefined behavior to use this value
-//~| encountered a reference pointing to a static variable
+const U8_MUT: &u8 = {
+    //~^ ERROR undefined behavior to use this value
+    //~| encountered a reference pointing to a static variable
     unsafe { &static_cross_crate::ZERO[0] }
 };
 
@@ -24,9 +27,14 @@ const U8_MUT2: &u8 = {
     //~| constant accesses static
 };
 const U8_MUT3: &u8 = {
-    unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
-    //~^ ERROR evaluation of constant value failed
-    //~| constant accesses static
+    unsafe {
+        match static_cross_crate::OPT_ZERO {
+            //~^ ERROR evaluation of constant value failed
+            //~| constant accesses static
+            Some(ref u) => u,
+            None => panic!(),
+        }
+    }
 };
 
 pub fn test(x: &[u8; 1]) -> bool {
diff --git a/tests/ui/consts/static_mut_containing_mut_ref.rs b/tests/ui/consts/static_mut_containing_mut_ref.rs
index df09c76c5584d..874aa59df0bb6 100644
--- a/tests/ui/consts/static_mut_containing_mut_ref.rs
+++ b/tests/ui/consts/static_mut_containing_mut_ref.rs
@@ -3,5 +3,6 @@
 static mut STDERR_BUFFER_SPACE: [u8; 42] = [0u8; 42];
 
 pub static mut STDERR_BUFFER: *mut [u8] = unsafe { &mut STDERR_BUFFER_SPACE };
+//~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
 
 fn main() {}
diff --git a/tests/ui/consts/static_mut_containing_mut_ref.stderr b/tests/ui/consts/static_mut_containing_mut_ref.stderr
new file mode 100644
index 0000000000000..56ceba41cf88c
--- /dev/null
+++ b/tests/ui/consts/static_mut_containing_mut_ref.stderr
@@ -0,0 +1,17 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/static_mut_containing_mut_ref.rs:5:52
+   |
+LL | pub static mut STDERR_BUFFER: *mut [u8] = unsafe { &mut STDERR_BUFFER_SPACE };
+   |                                                    ^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL | pub static mut STDERR_BUFFER: *mut [u8] = unsafe { addr_of_mut!(STDERR_BUFFER_SPACE) };
+   |                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr b/tests/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr
index 3d0de233569c9..bc32ecc2c35ff 100644
--- a/tests/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr
+++ b/tests/ui/consts/static_mut_containing_mut_ref2.mut_refs.stderr
@@ -1,9 +1,24 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/static_mut_containing_mut_ref2.rs:8:6
+   |
+LL |     *(&mut STDERR_BUFFER_SPACE) = 42;
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |     *addr_of_mut!(STDERR_BUFFER_SPACE) = 42;
+   |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 error[E0080]: could not evaluate static initializer
-  --> $DIR/static_mut_containing_mut_ref2.rs:7:45
+  --> $DIR/static_mut_containing_mut_ref2.rs:8:5
    |
-LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; };
-   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ modifying a static's initial value from another static's initializer
+LL |     *(&mut STDERR_BUFFER_SPACE) = 42;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ modifying a static's initial value from another static's initializer
 
-error: aborting due to 1 previous error
+error: aborting due to 1 previous error; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/static_mut_containing_mut_ref2.rs b/tests/ui/consts/static_mut_containing_mut_ref2.rs
index 61368546083db..fa79a78eab424 100644
--- a/tests/ui/consts/static_mut_containing_mut_ref2.rs
+++ b/tests/ui/consts/static_mut_containing_mut_ref2.rs
@@ -4,8 +4,12 @@
 
 static mut STDERR_BUFFER_SPACE: u8 = 0;
 
-pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; };
-//[mut_refs]~^ ERROR could not evaluate static initializer
-//[stock]~^^ ERROR mutable references are not allowed in statics
+pub static mut STDERR_BUFFER: () = unsafe {
+    *(&mut STDERR_BUFFER_SPACE) = 42;
+    //[mut_refs]~^ ERROR could not evaluate static initializer
+    //[stock]~^^ ERROR mutable references are not allowed in statics
+    //[mut_refs]~^^^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+    //[stock]~^^^^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+};
 
 fn main() {}
diff --git a/tests/ui/consts/static_mut_containing_mut_ref2.stock.stderr b/tests/ui/consts/static_mut_containing_mut_ref2.stock.stderr
index 3d5b012d42f37..c6e5b07e3b70a 100644
--- a/tests/ui/consts/static_mut_containing_mut_ref2.stock.stderr
+++ b/tests/ui/consts/static_mut_containing_mut_ref2.stock.stderr
@@ -1,12 +1,27 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/static_mut_containing_mut_ref2.rs:8:6
+   |
+LL |     *(&mut STDERR_BUFFER_SPACE) = 42;
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |     *addr_of_mut!(STDERR_BUFFER_SPACE) = 42;
+   |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 error[E0658]: mutable references are not allowed in statics
-  --> $DIR/static_mut_containing_mut_ref2.rs:7:46
+  --> $DIR/static_mut_containing_mut_ref2.rs:8:6
    |
-LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; };
-   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     *(&mut STDERR_BUFFER_SPACE) = 42;
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
 
-error: aborting due to 1 previous error
+error: aborting due to 1 previous error; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/drop/issue-23338-ensure-param-drop-order.rs b/tests/ui/drop/issue-23338-ensure-param-drop-order.rs
index a99f260dde3b2..52603744c45fc 100644
--- a/tests/ui/drop/issue-23338-ensure-param-drop-order.rs
+++ b/tests/ui/drop/issue-23338-ensure-param-drop-order.rs
@@ -13,38 +13,39 @@ pub fn main() {
     d::println("created empty log");
     test(&log);
 
-    assert_eq!(&log.borrow()[..],
-               [
-                   //                                    created empty log
-                   //    +-- Make D(da_0, 0)
-                   //    | +-- Make D(de_1, 1)
-                   //    | |                             calling foo
-                   //    | |                             entered foo
-                   //    | | +-- Make D(de_2, 2)
-                   //    | | | +-- Make D(da_1, 3)
-                   //    | | | | +-- Make D(de_3, 4)
-                   //    | | | | | +-- Make D(de_4, 5)
-                   3, // | | | +-- Drop D(da_1, 3)
-                   //    | | |   | |
-                   4, // | | |   +-- Drop D(de_3, 4)
-                   //    | | |     |
-                   //    | | |     |                     eval tail of foo
-                   //    | | | +-- Make D(de_5, 6)
-                   //    | | | | +-- Make D(de_6, 7)
-                   5, // | | | | | +-- Drop D(de_4, 5)
-                   //    | | | | |
-                   2, // | | +-- Drop D(de_2, 2)
-                   //    | |   | |
-                   6, // | |   +-- Drop D(de_5, 6)
-                   //    | |     |
-                   1, // | +-- Drop D(de_1, 1)
-                   //    |       |
-                   0, // +-- Drop D(da_0, 0)
-                   //            |
-                   //            |                       result D(de_6, 7)
-                   7 //          +-- Drop D(de_6, 7)
-
-                       ]);
+    assert_eq!(
+        &log.borrow()[..],
+        [
+            //                                    created empty log
+            //    +-- Make D(da_0, 0)
+            //    | +-- Make D(de_1, 1)
+            //    | |                             calling foo
+            //    | |                             entered foo
+            //    | | +-- Make D(de_2, 2)
+            //    | | | +-- Make D(da_1, 3)
+            //    | | | | +-- Make D(de_3, 4)
+            //    | | | | | +-- Make D(de_4, 5)
+            3, // | | | +-- Drop D(da_1, 3)
+            //    | | |   | |
+            4, // | | |   +-- Drop D(de_3, 4)
+            //    | | |     |
+            //    | | |     |                     eval tail of foo
+            //    | | | +-- Make D(de_5, 6)
+            //    | | | | +-- Make D(de_6, 7)
+            5, // | | | | | +-- Drop D(de_4, 5)
+            //    | | | | |
+            2, // | | +-- Drop D(de_2, 2)
+            //    | |   | |
+            6, // | |   +-- Drop D(de_5, 6)
+            //    | |     |
+            1, // | +-- Drop D(de_1, 1)
+            //    |       |
+            0, // +-- Drop D(da_0, 0)
+            //            |
+            //            |                       result D(de_6, 7)
+            7 //          +-- Drop D(de_6, 7)
+        ]
+    );
 }
 
 fn test<'a>(log: d::Log<'a>) {
@@ -57,13 +58,13 @@ fn test<'a>(log: d::Log<'a>) {
 
 fn foo<'a>(da0: D<'a>, de1: D<'a>) -> D<'a> {
     d::println("entered foo");
-    let de2 = de1.incr();      // creates D(de_2, 2)
+    let de2 = de1.incr(); // creates D(de_2, 2)
     let de4 = {
         let _da1 = da0.incr(); // creates D(da_1, 3)
-        de2.incr().incr()      // creates D(de_3, 4) and D(de_4, 5)
+        de2.incr().incr() // creates D(de_3, 4) and D(de_4, 5)
     };
     d::println("eval tail of foo");
-    de4.incr().incr()          // creates D(de_5, 6) and D(de_6, 7)
+    de4.incr().incr() // creates D(de_5, 6) and D(de_6, 7)
 }
 
 // This module provides simultaneous printouts of the dynamic extents
@@ -74,9 +75,9 @@ const PREF_INDENT: u32 = 16;
 
 pub mod d {
     #![allow(unused_parens)]
+    use std::cell::RefCell;
     use std::fmt;
     use std::mem;
-    use std::cell::RefCell;
 
     static mut counter: u32 = 0;
     static mut trails: u64 = 0;
@@ -89,7 +90,8 @@ pub mod d {
 
     pub fn max_width() -> u32 {
         unsafe {
-            (mem::size_of_val(&trails)*8) as u32
+            (mem::size_of_val(&trails) * 8) as u32
+            //~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
         }
     }
 
@@ -123,7 +125,11 @@ pub mod d {
     }
 
     pub struct D<'a> {
-        name: &'static str, i: u32, uid: u32, trail: u32, log: Log<'a>
+        name: &'static str,
+        i: u32,
+        uid: u32,
+        trail: u32,
+        log: Log<'a>,
     }
 
     impl<'a> fmt::Display for D<'a> {
@@ -139,9 +145,7 @@ pub mod d {
                 let ctr = counter;
                 counter += 1;
                 trails |= (1 << trail);
-                let ret = D {
-                    name: name, i: i, log: log, uid: ctr, trail: trail
-                };
+                let ret = D { name: name, i: i, log: log, uid: ctr, trail: trail };
                 indent_println(trail, &format!("+-- Make {}", ret));
                 ret
             }
@@ -153,7 +157,9 @@ pub mod d {
 
     impl<'a> Drop for D<'a> {
         fn drop(&mut self) {
-            unsafe { trails &= !(1 << self.trail); };
+            unsafe {
+                trails &= !(1 << self.trail);
+            };
             self.log.borrow_mut().push(self.uid);
             indent_println(self.trail, &format!("+-- Drop {}", self));
             indent_println(::PREF_INDENT, "");
diff --git a/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr b/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr
new file mode 100644
index 0000000000000..fd36ccbcbee47
--- /dev/null
+++ b/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr
@@ -0,0 +1,17 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/issue-23338-ensure-param-drop-order.rs:93:31
+   |
+LL |             (mem::size_of_val(&trails) * 8) as u32
+   |                               ^^^^^^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |             (mem::size_of_val(addr_of!(trails)) * 8) as u32
+   |                               ~~~~~~~~~~~~~~~~
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/error-codes/E0017.rs b/tests/ui/error-codes/E0017.rs
index c211ad1a2f8f6..9d3433fa543fd 100644
--- a/tests/ui/error-codes/E0017.rs
+++ b/tests/ui/error-codes/E0017.rs
@@ -3,12 +3,16 @@ const C: i32 = 2;
 static mut M: i32 = 3;
 
 const CR: &'static mut i32 = &mut C; //~ ERROR mutable references are not allowed
-                                     //~| WARN taking a mutable
+//~| WARN taking a mutable
+
 static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0658
-                                              //~| ERROR cannot borrow
-                                              //~| ERROR mutable references are not allowed
+//~| ERROR cannot borrow
+//~| ERROR mutable references are not allowed
 
 static CONST_REF: &'static mut i32 = &mut C; //~ ERROR mutable references are not allowed
-                                              //~| WARN taking a mutable
+//~| WARN taking a mutable
+
 static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR mutable references are not
+//~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+
 fn main() {}
diff --git a/tests/ui/error-codes/E0017.stderr b/tests/ui/error-codes/E0017.stderr
index 6e48f9582f1c0..ea6055da1c1fe 100644
--- a/tests/ui/error-codes/E0017.stderr
+++ b/tests/ui/error-codes/E0017.stderr
@@ -1,3 +1,18 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/E0017.rs:15:52
+   |
+LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M };
+   |                                                    ^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { addr_of_mut!(M) };
+   |                                                    ~~~~~~~~~~~~~~~
+
 warning: taking a mutable reference to a `const` item
   --> $DIR/E0017.rs:5:30
    |
@@ -20,7 +35,7 @@ LL | const CR: &'static mut i32 = &mut C;
    |                              ^^^^^^
 
 error[E0658]: mutation through a reference is not allowed in statics
-  --> $DIR/E0017.rs:7:39
+  --> $DIR/E0017.rs:8:39
    |
 LL | static STATIC_REF: &'static mut i32 = &mut X;
    |                                       ^^^^^^
@@ -29,19 +44,19 @@ LL | static STATIC_REF: &'static mut i32 = &mut X;
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
 
 error[E0764]: mutable references are not allowed in the final value of statics
-  --> $DIR/E0017.rs:7:39
+  --> $DIR/E0017.rs:8:39
    |
 LL | static STATIC_REF: &'static mut i32 = &mut X;
    |                                       ^^^^^^
 
 error[E0596]: cannot borrow immutable static item `X` as mutable
-  --> $DIR/E0017.rs:7:39
+  --> $DIR/E0017.rs:8:39
    |
 LL | static STATIC_REF: &'static mut i32 = &mut X;
    |                                       ^^^^^^ cannot borrow as mutable
 
 warning: taking a mutable reference to a `const` item
-  --> $DIR/E0017.rs:11:38
+  --> $DIR/E0017.rs:12:38
    |
 LL | static CONST_REF: &'static mut i32 = &mut C;
    |                                      ^^^^^^
@@ -55,18 +70,18 @@ LL | const C: i32 = 2;
    | ^^^^^^^^^^^^
 
 error[E0764]: mutable references are not allowed in the final value of statics
-  --> $DIR/E0017.rs:11:38
+  --> $DIR/E0017.rs:12:38
    |
 LL | static CONST_REF: &'static mut i32 = &mut C;
    |                                      ^^^^^^
 
 error[E0764]: mutable references are not allowed in the final value of statics
-  --> $DIR/E0017.rs:13:52
+  --> $DIR/E0017.rs:15:52
    |
 LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M };
    |                                                    ^^^^^^
 
-error: aborting due to 6 previous errors; 2 warnings emitted
+error: aborting due to 6 previous errors; 3 warnings emitted
 
 Some errors have detailed explanations: E0596, E0658, E0764.
 For more information about an error, try `rustc --explain E0596`.
diff --git a/tests/ui/issues/issue-20616.rs b/tests/ui/impl-header-lifetime-elision/bare_type.rs
similarity index 82%
rename from tests/ui/issues/issue-20616.rs
rename to tests/ui/impl-header-lifetime-elision/bare_type.rs
index 6c24d437272e4..9af98f870d2d5 100644
--- a/tests/ui/issues/issue-20616.rs
+++ b/tests/ui/impl-header-lifetime-elision/bare_type.rs
@@ -33,12 +33,11 @@ type TypeI<T,> = T;
 static STATIC: () = ();
 
 fn main() {
-
     // ensure token `>=` works fine
-    let _: TypeA<'static>= &STATIC;
-    let _: TypeA<'static,>= &STATIC;
+    let _: TypeA<'static> = &STATIC;
+    let _: TypeA<'static,> = &STATIC;
 
     // ensure token `>>=` works fine
-    let _: Box<TypeA<'static>>= Box::new(&STATIC);
-    let _: Box<TypeA<'static,>>= Box::new(&STATIC);
+    let _: Box<TypeA<'static>> = Box::new(&STATIC);
+    let _: Box<TypeA<'static,>> = Box::new(&STATIC);
 }
diff --git a/tests/ui/issues/issue-23611-enum-swap-in-drop.rs b/tests/ui/issues/issue-23611-enum-swap-in-drop.rs
index cdb130d600c5e..b967e6aecdd43 100644
--- a/tests/ui/issues/issue-23611-enum-swap-in-drop.rs
+++ b/tests/ui/issues/issue-23611-enum-swap-in-drop.rs
@@ -37,11 +37,9 @@ pub fn main() {
             // | | | +-- Make D(g_b_5, 50000005)
             // | | | |                                 in g_B(b4b2) from GaspB::drop
             // | | | +-- Drop D(g_b_5, 50000005)
-            50000005,
-            // | | |
+            50000005, // | | |
             // | | +-- Drop D(GaspB::drop_3, 30000004)
-            30000004,
-            // | |
+            30000004, // | |
             // +-- Drop D(test_1, 10000000)
             10000000,
             //   |
@@ -49,15 +47,13 @@ pub fn main() {
             // | | +-- Make D(f_a_4, 40000007)
             // | | |                                   in f_A(a3a0) from GaspA::drop
             // | | +-- Drop D(f_a_4, 40000007)
-            40000007,
-            // | |
+            40000007, // | |
             // +-- Drop D(GaspA::drop_2, 20000006)
-            20000006,
-            //   |
+            20000006, //   |
             //   +-- Drop D(drop_6, 60000002)
-            60000002
-            //
-                ]);
+            60000002 //
+        ]
+    );
 
     // For reference purposes, the old (incorrect) behavior would produce the following
     // output, which you can compare to the above:
@@ -106,8 +102,8 @@ fn test<'a>(log: d::Log<'a>) {
     let _e = E::B(GaspB(g_b, 0xB4B0, log, D::new("test", 1, log)), true);
 }
 
-struct GaspA<'a>(for <'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>);
-struct GaspB<'a>(for <'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>);
+struct GaspA<'a>(for<'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>);
+struct GaspB<'a>(for<'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>);
 
 impl<'a> Drop for GaspA<'a> {
     fn drop(&mut self) {
@@ -124,7 +120,8 @@ impl<'a> Drop for GaspB<'a> {
 }
 
 enum E<'a> {
-    A(GaspA<'a>, bool), B(GaspB<'a>, bool),
+    A(GaspA<'a>, bool),
+    B(GaspB<'a>, bool),
 }
 
 fn f_a(x: u32, ctxt: &str, log: d::Log) {
@@ -174,9 +171,9 @@ const PREF_INDENT: u32 = 20;
 
 pub mod d {
     #![allow(unused_parens)]
+    use std::cell::RefCell;
     use std::fmt;
     use std::mem;
-    use std::cell::RefCell;
 
     static mut counter: u16 = 0;
     static mut trails: u64 = 0;
@@ -189,7 +186,8 @@ pub mod d {
 
     pub fn max_width() -> u32 {
         unsafe {
-            (mem::size_of_val(&trails)*8) as u32
+            (mem::size_of_val(&trails) * 8) as u32
+            //~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
         }
     }
 
@@ -223,7 +221,11 @@ pub mod d {
     }
 
     pub struct D<'a> {
-        name: &'static str, i: u8, uid: u32, trail: u32, log: Log<'a>
+        name: &'static str,
+        i: u8,
+        uid: u32,
+        trail: u32,
+        log: Log<'a>,
     }
 
     impl<'a> fmt::Display for D<'a> {
@@ -239,9 +241,7 @@ pub mod d {
                 let ctr = ((i as u32) * 10_000_000) + (counter as u32);
                 counter += 1;
                 trails |= (1 << trail);
-                let ret = D {
-                    name: name, i: i, log: log, uid: ctr, trail: trail
-                };
+                let ret = D { name: name, i: i, log: log, uid: ctr, trail: trail };
                 indent_println(trail, &format!("+-- Make {}", ret));
                 ret
             }
@@ -250,7 +250,9 @@ pub mod d {
 
     impl<'a> Drop for D<'a> {
         fn drop(&mut self) {
-            unsafe { trails &= !(1 << self.trail); };
+            unsafe {
+                trails &= !(1 << self.trail);
+            };
             self.log.borrow_mut().push(self.uid);
             indent_println(self.trail, &format!("+-- Drop {}", self));
             indent_println(::PREF_INDENT, "");
diff --git a/tests/ui/issues/issue-23611-enum-swap-in-drop.stderr b/tests/ui/issues/issue-23611-enum-swap-in-drop.stderr
new file mode 100644
index 0000000000000..14a986a333264
--- /dev/null
+++ b/tests/ui/issues/issue-23611-enum-swap-in-drop.stderr
@@ -0,0 +1,17 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/issue-23611-enum-swap-in-drop.rs:189:31
+   |
+LL |             (mem::size_of_val(&trails) * 8) as u32
+   |                               ^^^^^^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |             (mem::size_of_val(addr_of!(trails)) * 8) as u32
+   |                               ~~~~~~~~~~~~~~~~
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs
index 7d3b00dfc7163..8eb544e8ab9d2 100644
--- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs
+++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs
@@ -14,9 +14,8 @@ struct S1 {
 
 impl S1 {
     fn new(_x: u64) -> S1 {
-        S1 {
-            a: unsafe { &mut X1 },
-        }
+        S1 { a: unsafe { &mut X1 } }
+        //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
     }
 }
 
diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr
new file mode 100644
index 0000000000000..17217cd5859d0
--- /dev/null
+++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr
@@ -0,0 +1,17 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/borrowck-thread-local-static-mut-borrow-outlives-fn.rs:17:26
+   |
+LL |         S1 { a: unsafe { &mut X1 } }
+   |                          ^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |         S1 { a: unsafe { addr_of_mut!(X1) } }
+   |                          ~~~~~~~~~~~~~~~~
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/static/reference-of-mut-static-safe.e2021.stderr b/tests/ui/static/reference-of-mut-static-safe.e2021.stderr
index c38fe8790637e..16f47ace3a930 100644
--- a/tests/ui/static/reference-of-mut-static-safe.e2021.stderr
+++ b/tests/ui/static/reference-of-mut-static-safe.e2021.stderr
@@ -14,10 +14,10 @@ LL |     let _x = addr_of!(X);
    |              ~~~~~~~~~~~
 
 error[E0133]: use of mutable static is unsafe and requires unsafe function or block
-  --> $DIR/reference-of-mut-static-safe.rs:9:14
+  --> $DIR/reference-of-mut-static-safe.rs:9:15
    |
 LL |     let _x = &X;
-   |              ^^ use of mutable static
+   |               ^ use of mutable static
    |
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
diff --git a/tests/ui/static/safe-extern-statics-mut.rs b/tests/ui/static/safe-extern-statics-mut.rs
index 324fa443aa50e..1c0662e0a6cec 100644
--- a/tests/ui/static/safe-extern-statics-mut.rs
+++ b/tests/ui/static/safe-extern-statics-mut.rs
@@ -10,6 +10,8 @@ extern "C" {
 fn main() {
     let b = B; //~ ERROR use of mutable static is unsafe
     let rb = &B; //~ ERROR use of mutable static is unsafe
+    //~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
     let xb = XB; //~ ERROR use of mutable static is unsafe
     let xrb = &XB; //~ ERROR use of mutable static is unsafe
+    //~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
 }
diff --git a/tests/ui/static/safe-extern-statics-mut.stderr b/tests/ui/static/safe-extern-statics-mut.stderr
index e390625f20a98..eda353ce6736f 100644
--- a/tests/ui/static/safe-extern-statics-mut.stderr
+++ b/tests/ui/static/safe-extern-statics-mut.stderr
@@ -1,3 +1,32 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/safe-extern-statics-mut.rs:12:14
+   |
+LL |     let rb = &B;
+   |              ^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     let rb = addr_of!(B);
+   |              ~~~~~~~~~~~
+
+warning: shared reference of mutable static is discouraged
+  --> $DIR/safe-extern-statics-mut.rs:15:15
+   |
+LL |     let xrb = &XB;
+   |               ^^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     let xrb = addr_of!(XB);
+   |               ~~~~~~~~~~~~
+
 error[E0133]: use of mutable static is unsafe and requires unsafe function or block
   --> $DIR/safe-extern-statics-mut.rs:11:13
    |
@@ -15,7 +44,7 @@ LL |     let rb = &B;
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
 error[E0133]: use of mutable static is unsafe and requires unsafe function or block
-  --> $DIR/safe-extern-statics-mut.rs:13:14
+  --> $DIR/safe-extern-statics-mut.rs:14:14
    |
 LL |     let xb = XB;
    |              ^^ use of mutable static
@@ -23,13 +52,13 @@ LL |     let xb = XB;
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
 error[E0133]: use of mutable static is unsafe and requires unsafe function or block
-  --> $DIR/safe-extern-statics-mut.rs:14:16
+  --> $DIR/safe-extern-statics-mut.rs:15:16
    |
 LL |     let xrb = &XB;
    |                ^^ use of mutable static
    |
    = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
 
-error: aborting due to 4 previous errors
+error: aborting due to 4 previous errors; 2 warnings emitted
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/statics/issue-15261.rs b/tests/ui/statics/issue-15261.rs
index ec413f6d1d2be..14422329b7dc8 100644
--- a/tests/ui/statics/issue-15261.rs
+++ b/tests/ui/statics/issue-15261.rs
@@ -6,6 +6,7 @@
 
 static mut n_mut: usize = 0;
 
-static n: &'static usize = unsafe{ &n_mut };
+static n: &'static usize = unsafe { &n_mut };
+//~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
 
 fn main() {}
diff --git a/tests/ui/statics/issue-15261.stderr b/tests/ui/statics/issue-15261.stderr
new file mode 100644
index 0000000000000..72d88ce1b3832
--- /dev/null
+++ b/tests/ui/statics/issue-15261.stderr
@@ -0,0 +1,17 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/issue-15261.rs:9:37
+   |
+LL | static n: &'static usize = unsafe { &n_mut };
+   |                                     ^^^^^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL | static n: &'static usize = unsafe { addr_of!(n_mut) };
+   |                                     ~~~~~~~~~~~~~~~
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/statics/static-mut-xc.rs b/tests/ui/statics/static-mut-xc.rs
index 1d172d26a5949..2fc265e02eaa3 100644
--- a/tests/ui/statics/static-mut-xc.rs
+++ b/tests/ui/statics/static-mut-xc.rs
@@ -7,7 +7,6 @@
 
 // aux-build:static_mut_xc.rs
 
-
 extern crate static_mut_xc;
 
 unsafe fn static_bound(_: &'static isize) {}
@@ -27,7 +26,9 @@ unsafe fn run() {
     static_mut_xc::a = -3;
     assert_eq!(static_mut_xc::a, -3);
     static_bound(&static_mut_xc::a);
+    //~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
     static_bound_set(&mut static_mut_xc::a);
+    //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
 }
 
 pub fn main() {
diff --git a/tests/ui/statics/static-mut-xc.stderr b/tests/ui/statics/static-mut-xc.stderr
new file mode 100644
index 0000000000000..37aa336bc50f7
--- /dev/null
+++ b/tests/ui/statics/static-mut-xc.stderr
@@ -0,0 +1,31 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/static-mut-xc.rs:28:18
+   |
+LL |     static_bound(&static_mut_xc::a);
+   |                  ^^^^^^^^^^^^^^^^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL |     static_bound(addr_of!(static_mut_xc::a));
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/static-mut-xc.rs:30:22
+   |
+LL |     static_bound_set(&mut static_mut_xc::a);
+   |                      ^^^^^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |     static_bound_set(addr_of_mut!(static_mut_xc::a));
+   |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/statics/static-recursive.rs b/tests/ui/statics/static-recursive.rs
index 95dadc81f811d..216beb0206d9c 100644
--- a/tests/ui/statics/static-recursive.rs
+++ b/tests/ui/statics/static-recursive.rs
@@ -1,36 +1,43 @@
 // run-pass
+
 static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 };
+//~^ WARN shared reference of mutable static is discouraged [static_mut_ref]
 
 struct StaticDoubleLinked {
     prev: &'static StaticDoubleLinked,
     next: &'static StaticDoubleLinked,
     data: i32,
-    head: bool
+    head: bool,
 }
 
-static L1: StaticDoubleLinked = StaticDoubleLinked{prev: &L3, next: &L2, data: 1, head: true};
-static L2: StaticDoubleLinked = StaticDoubleLinked{prev: &L1, next: &L3, data: 2, head: false};
-static L3: StaticDoubleLinked = StaticDoubleLinked{prev: &L2, next: &L1, data: 3, head: false};
-
+static L1: StaticDoubleLinked = StaticDoubleLinked { prev: &L3, next: &L2, data: 1, head: true };
+static L2: StaticDoubleLinked = StaticDoubleLinked { prev: &L1, next: &L3, data: 2, head: false };
+static L3: StaticDoubleLinked = StaticDoubleLinked { prev: &L2, next: &L1, data: 3, head: false };
 
 pub fn main() {
-    unsafe { assert_eq!(S, *(S as *const *const u8)); }
+    unsafe {
+        assert_eq!(S, *(S as *const *const u8));
+    }
 
     let mut test_vec = Vec::new();
     let mut cur = &L1;
     loop {
         test_vec.push(cur.data);
         cur = cur.next;
-        if cur.head { break }
+        if cur.head {
+            break;
+        }
     }
-    assert_eq!(&test_vec, &[1,2,3]);
+    assert_eq!(&test_vec, &[1, 2, 3]);
 
     let mut test_vec = Vec::new();
     let mut cur = &L1;
     loop {
         cur = cur.prev;
         test_vec.push(cur.data);
-        if cur.head { break }
+        if cur.head {
+            break;
+        }
     }
-    assert_eq!(&test_vec, &[3,2,1]);
+    assert_eq!(&test_vec, &[3, 2, 1]);
 }
diff --git a/tests/ui/statics/static-recursive.stderr b/tests/ui/statics/static-recursive.stderr
new file mode 100644
index 0000000000000..15888e5c68d84
--- /dev/null
+++ b/tests/ui/statics/static-recursive.stderr
@@ -0,0 +1,17 @@
+warning: shared reference of mutable static is discouraged
+  --> $DIR/static-recursive.rs:3:36
+   |
+LL | static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 };
+   |                                    ^^ shared reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+   |
+LL | static mut S: *const u8 = unsafe { addr_of!(S) as *const *const u8 as *const u8 };
+   |                                    ~~~~~~~~~~~
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/thread-local/thread-local-static.rs b/tests/ui/thread-local/thread-local-static.rs
index e6cd4839dda7e..f5fb098489746 100644
--- a/tests/ui/thread-local/thread-local-static.rs
+++ b/tests/ui/thread-local/thread-local-static.rs
@@ -8,7 +8,8 @@ static mut STATIC_VAR_2: [u32; 8] = [4; 8];
 const fn g(x: &mut [u32; 8]) {
     //~^ ERROR mutable references are not allowed
     std::mem::swap(x, &mut STATIC_VAR_2)
-    //~^ ERROR thread-local statics cannot be accessed
+    //~^ WARN mutable reference of mutable static is discouraged [static_mut_ref]
+    //~^^ ERROR thread-local statics cannot be accessed
     //~| ERROR mutable references are not allowed
     //~| ERROR use of mutable static is unsafe
     //~| constant functions cannot refer to statics
diff --git a/tests/ui/thread-local/thread-local-static.stderr b/tests/ui/thread-local/thread-local-static.stderr
index c1777dd60db68..b03f4580c2cf1 100644
--- a/tests/ui/thread-local/thread-local-static.stderr
+++ b/tests/ui/thread-local/thread-local-static.stderr
@@ -1,3 +1,18 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/thread-local-static.rs:10:23
+   |
+LL |     std::mem::swap(x, &mut STATIC_VAR_2)
+   |                       ^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |     std::mem::swap(x, addr_of_mut!(STATIC_VAR_2))
+   |                       ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 error[E0133]: use of mutable static is unsafe and requires unsafe function or block
   --> $DIR/thread-local-static.rs:10:28
    |
@@ -38,7 +53,7 @@ LL |     std::mem::swap(x, &mut STATIC_VAR_2)
    = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
 
-error: aborting due to 5 previous errors
+error: aborting due to 5 previous errors; 1 warning emitted
 
 Some errors have detailed explanations: E0013, E0133, E0625, E0658.
 For more information about an error, try `rustc --explain E0013`.
diff --git a/tests/ui/thread-local/thread-local-static.thir.stderr b/tests/ui/thread-local/thread-local-static.thir.stderr
new file mode 100644
index 0000000000000..2043b268c090c
--- /dev/null
+++ b/tests/ui/thread-local/thread-local-static.thir.stderr
@@ -0,0 +1,59 @@
+warning: mutable reference of mutable static is discouraged
+  --> $DIR/thread-local-static.rs:12:23
+   |
+LL |     std::mem::swap(x, &mut STATIC_VAR_2)
+   |                       ^^^^^^^^^^^^^^^^^ mutable reference of mutable static
+   |
+   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
+   = note: reference of mutable static is a hard error from 2024 edition
+   = note: mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+   = note: `#[warn(static_mut_ref)]` on by default
+help: mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+   |
+LL |     std::mem::swap(x, addr_of_mut!(STATIC_VAR_2))
+   |                       ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0658]: mutable references are not allowed in constant functions
+  --> $DIR/thread-local-static.rs:10:12
+   |
+LL | const fn g(x: &mut [u32; 8]) {
+   |            ^
+   |
+   = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+   = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
+
+error[E0625]: thread-local statics cannot be accessed at compile-time
+  --> $DIR/thread-local-static.rs:12:28
+   |
+LL |     std::mem::swap(x, &mut STATIC_VAR_2)
+   |                            ^^^^^^^^^^^^
+
+error[E0013]: constant functions cannot refer to statics
+  --> $DIR/thread-local-static.rs:12:28
+   |
+LL |     std::mem::swap(x, &mut STATIC_VAR_2)
+   |                            ^^^^^^^^^^^^
+   |
+   = help: consider extracting the value of the `static` to a `const`, and referring to that
+
+error[E0658]: mutable references are not allowed in constant functions
+  --> $DIR/thread-local-static.rs:12:23
+   |
+LL |     std::mem::swap(x, &mut STATIC_VAR_2)
+   |                       ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+   = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
+
+error[E0133]: use of mutable static is unsafe and requires unsafe function or block
+  --> $DIR/thread-local-static.rs:12:23
+   |
+LL |     std::mem::swap(x, &mut STATIC_VAR_2)
+   |                       ^^^^^^^^^^^^^^^^^ use of mutable static
+   |
+   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
+
+error: aborting due to 5 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0013, E0133, E0625, E0658.
+For more information about an error, try `rustc --explain E0013`.

From 4071572cb416a1740e5530130b0454f9ce2db869 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Sun, 7 Jan 2024 14:59:59 +0000
Subject: [PATCH 09/17] Merge dead bb pruning and unreachable bb deduplication.

---
 compiler/rustc_middle/src/mir/mod.rs          |  1 +
 .../rustc_mir_transform/src/const_goto.rs     |  2 +-
 .../src/deduplicate_blocks.rs                 |  2 +-
 .../src/early_otherwise_branch.rs             |  2 +-
 compiler/rustc_mir_transform/src/inline.rs    |  5 +-
 .../rustc_mir_transform/src/match_branches.rs |  2 +-
 .../src/remove_unneeded_drops.rs              |  2 +-
 .../src/separate_const_switch.rs              |  4 +-
 compiler/rustc_mir_transform/src/simplify.rs  | 92 ++++++++-----------
 ...await.b-{closure#0}.coroutine_resume.0.mir |  6 +-
 ...ng.identity.JumpThreading.panic-abort.diff | 10 +-
 ...g.identity.JumpThreading.panic-unwind.diff | 10 +-
 tests/mir-opt/jump_threading.rs               |  8 +-
 ...d_constant.main.GVN.32bit.panic-abort.diff | 24 ++---
 ..._constant.main.GVN.32bit.panic-unwind.diff | 18 ++--
 ...d_constant.main.GVN.64bit.panic-abort.diff | 24 ++---
 ..._constant.main.GVN.64bit.panic-unwind.diff | 18 ++--
 ...t_switch.identity.SeparateConstSwitch.diff | 22 ++---
 ...witch.too_complex.SeparateConstSwitch.diff | 28 +++---
 19 files changed, 124 insertions(+), 156 deletions(-)

diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index d426f6d8969ac..36f5ba161d5f1 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1316,6 +1316,7 @@ impl<'tcx> BasicBlockData<'tcx> {
     }
 
     /// Does the block have no statements and an unreachable terminator?
+    #[inline]
     pub fn is_empty_unreachable(&self) -> bool {
         self.statements.is_empty() && matches!(self.terminator().kind, TerminatorKind::Unreachable)
     }
diff --git a/compiler/rustc_mir_transform/src/const_goto.rs b/compiler/rustc_mir_transform/src/const_goto.rs
index 3884346076eea..cb5b66b314d69 100644
--- a/compiler/rustc_mir_transform/src/const_goto.rs
+++ b/compiler/rustc_mir_transform/src/const_goto.rs
@@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for ConstGoto {
         // if we applied optimizations, we potentially have some cfg to cleanup to
         // make it easier for further passes
         if should_simplify {
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
             simplify_locals(body, tcx);
         }
     }
diff --git a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
index b40b2ec8bfdc4..824974970bb3f 100644
--- a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
+++ b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
@@ -25,7 +25,7 @@ impl<'tcx> MirPass<'tcx> for DeduplicateBlocks {
         if has_opts_to_apply {
             let mut opt_applier = OptApplier { tcx, duplicates };
             opt_applier.visit_body(body);
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index 6eb6cb069fec0..0d600f0f937de 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -212,7 +212,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
         // Since this optimization adds new basic blocks and invalidates others,
         // clean up the cfg to make it nicer for other passes
         if should_cleanup {
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 8ad804bf3e7aa..ebefc3b47cc4f 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -15,7 +15,7 @@ use rustc_target::abi::FieldIdx;
 use rustc_target::spec::abi::Abi;
 
 use crate::cost_checker::CostChecker;
-use crate::simplify::{remove_dead_blocks, CfgSimplifier};
+use crate::simplify::simplify_cfg;
 use crate::util;
 use std::iter;
 use std::ops::{Range, RangeFrom};
@@ -56,8 +56,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
         let _guard = span.enter();
         if inline(tcx, body) {
             debug!("running simplify cfg on {:?}", body.source);
-            CfgSimplifier::new(body).simplify();
-            remove_dead_blocks(body);
+            simplify_cfg(body);
             deref_finder(tcx, body);
         }
     }
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 1c4aa37d57ff5..6d4332793af31 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -174,7 +174,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
         }
 
         if should_cleanup {
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index 5d528bed35647..2778d91e17b99 100644
--- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -38,7 +38,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
         // if we applied optimizations, we potentially have some cfg to cleanup to
         // make it easier for further passes
         if should_simplify {
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs
index 6e22690d8dad5..7120ef7214271 100644
--- a/compiler/rustc_mir_transform/src/separate_const_switch.rs
+++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs
@@ -50,11 +50,11 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
         sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
     }
 
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // If execution did something, applying a simplification layer
         // helps later passes optimize the copy away.
         if separate_const_switch(body) > 0 {
-            super::simplify::simplify_cfg(tcx, body);
+            super::simplify::simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index 856a0f227714d..8c8818bd68e6f 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -27,7 +27,6 @@
 //! naively generate still contains the `_a = ()` write in the unreachable block "after" the
 //! return.
 
-use rustc_data_structures::fx::FxIndexSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
@@ -62,9 +61,8 @@ impl SimplifyCfg {
     }
 }
 
-pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+pub(crate) fn simplify_cfg(body: &mut Body<'_>) {
     CfgSimplifier::new(body).simplify();
-    remove_duplicate_unreachable_blocks(tcx, body);
     remove_dead_blocks(body);
 
     // FIXME: Should probably be moved into some kind of pass manager
@@ -76,9 +74,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
         self.name()
     }
 
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source);
-        simplify_cfg(tcx, body);
+        simplify_cfg(body);
     }
 }
 
@@ -289,55 +287,25 @@ pub fn simplify_duplicate_switch_targets(terminator: &mut Terminator<'_>) {
     }
 }
 
-pub fn remove_duplicate_unreachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-    struct OptApplier<'tcx> {
-        tcx: TyCtxt<'tcx>,
-        duplicates: FxIndexSet<BasicBlock>,
-    }
-
-    impl<'tcx> MutVisitor<'tcx> for OptApplier<'tcx> {
-        fn tcx(&self) -> TyCtxt<'tcx> {
-            self.tcx
-        }
-
-        fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
-            for target in terminator.successors_mut() {
-                // We don't have to check whether `target` is a cleanup block, because have
-                // entirely excluded cleanup blocks in building the set of duplicates.
-                if self.duplicates.contains(target) {
-                    *target = self.duplicates[0];
-                }
-            }
-
-            simplify_duplicate_switch_targets(terminator);
-
-            self.super_terminator(terminator, location);
-        }
-    }
+pub(crate) fn remove_dead_blocks(body: &mut Body<'_>) {
+    let should_deduplicate_unreachable = |bbdata: &BasicBlockData<'_>| {
+        // CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
+        // terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
+        // before then so we need to handle missing terminators.
+        // We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
+        // don't emit empty unreachable cleanup blocks, so this simple check suffices.
+        bbdata.terminator.is_some() && bbdata.is_empty_unreachable() && !bbdata.is_cleanup
+    };
 
-    let unreachable_blocks = body
+    let reachable = traversal::reachable_as_bitset(body);
+    let empty_unreachable_blocks = body
         .basic_blocks
         .iter_enumerated()
-        .filter(|(_, bb)| {
-            // CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
-            // terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
-            // before then so we need to handle missing terminators.
-            // We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
-            // don't emit empty unreachable cleanup blocks, so this simple check suffices.
-            bb.terminator.is_some() && bb.is_empty_unreachable() && !bb.is_cleanup
-        })
-        .map(|(block, _)| block)
-        .collect::<FxIndexSet<_>>();
-
-    if unreachable_blocks.len() > 1 {
-        OptApplier { tcx, duplicates: unreachable_blocks }.visit_body(body);
-    }
-}
+        .filter(|(bb, bbdata)| should_deduplicate_unreachable(bbdata) && reachable.contains(*bb))
+        .count();
 
-pub fn remove_dead_blocks(body: &mut Body<'_>) {
-    let reachable = traversal::reachable_as_bitset(body);
     let num_blocks = body.basic_blocks.len();
-    if num_blocks == reachable.count() {
+    if num_blocks == reachable.count() && empty_unreachable_blocks <= 1 {
         return;
     }
 
@@ -346,14 +314,28 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
     let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
     let mut orig_index = 0;
     let mut used_index = 0;
-    basic_blocks.raw.retain(|_| {
-        let keep = reachable.contains(BasicBlock::new(orig_index));
-        if keep {
-            replacements[orig_index] = BasicBlock::new(used_index);
-            used_index += 1;
+    let mut kept_unreachable = None;
+    basic_blocks.raw.retain(|bbdata| {
+        let orig_bb = BasicBlock::new(orig_index);
+        if !reachable.contains(orig_bb) {
+            orig_index += 1;
+            return false;
+        }
+
+        let used_bb = BasicBlock::new(used_index);
+        if should_deduplicate_unreachable(bbdata) {
+            let kept_unreachable = *kept_unreachable.get_or_insert(used_bb);
+            if kept_unreachable != used_bb {
+                replacements[orig_index] = kept_unreachable;
+                orig_index += 1;
+                return false;
+            }
         }
+
+        replacements[orig_index] = used_bb;
+        used_index += 1;
         orig_index += 1;
-        keep
+        true
     });
 
     for block in basic_blocks {
diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir
index 3a9c80caa1e64..3c0d4008c9018 100644
--- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir
+++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir
@@ -108,7 +108,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>,
 
     bb0: {
         _39 = discriminant((*(_1.0: &mut {async fn body@$DIR/async_await.rs:15:18: 18:2})));
-        switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb30];
+        switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb9];
     }
 
     bb1: {
@@ -345,8 +345,4 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body@$DIR/async_await.rs:15:18: 18:2}>,
     bb29: {
         assert(const false, "`async fn` resumed after completion") -> [success: bb29, unwind unreachable];
     }
-
-    bb30: {
-        unreachable;
-    }
 }
diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff
index f50603a66a56f..f04ca72dd6d97 100644
--- a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff
+++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff
@@ -56,7 +56,7 @@
           StorageLive(_11);
           StorageLive(_12);
           _10 = discriminant(_4);
-          switchInt(move _10) -> [0: bb8, 1: bb6, otherwise: bb7];
+          switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb2];
       }
   
       bb1: {
@@ -114,20 +114,16 @@
           _3 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _13);
           StorageDead(_13);
 -         goto -> bb5;
-+         goto -> bb9;
++         goto -> bb8;
       }
   
       bb7: {
-          unreachable;
-      }
-  
-      bb8: {
           _11 = move ((_4 as Ok).0: i32);
           _3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _11);
           goto -> bb5;
 +     }
 + 
-+     bb9: {
++     bb8: {
 +         StorageDead(_12);
 +         StorageDead(_11);
 +         StorageDead(_10);
diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff
index f50603a66a56f..f04ca72dd6d97 100644
--- a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff
+++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff
@@ -56,7 +56,7 @@
           StorageLive(_11);
           StorageLive(_12);
           _10 = discriminant(_4);
-          switchInt(move _10) -> [0: bb8, 1: bb6, otherwise: bb7];
+          switchInt(move _10) -> [0: bb7, 1: bb6, otherwise: bb2];
       }
   
       bb1: {
@@ -114,20 +114,16 @@
           _3 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _13);
           StorageDead(_13);
 -         goto -> bb5;
-+         goto -> bb9;
++         goto -> bb8;
       }
   
       bb7: {
-          unreachable;
-      }
-  
-      bb8: {
           _11 = move ((_4 as Ok).0: i32);
           _3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _11);
           goto -> bb5;
 +     }
 + 
-+     bb9: {
++     bb8: {
 +         StorageDead(_12);
 +         StorageDead(_11);
 +         StorageDead(_10);
diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs
index 66e5c5d3c11cb..0cbdaa085bc97 100644
--- a/tests/mir-opt/jump_threading.rs
+++ b/tests/mir-opt/jump_threading.rs
@@ -50,7 +50,7 @@ fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
     // CHECK-LABEL: fn identity(
     // CHECK: bb0: {
     // CHECK:     [[x:_.*]] = _1;
-    // CHECK:     switchInt(move {{_.*}}) -> [0: bb8, 1: bb6, otherwise: bb7];
+    // CHECK:     switchInt(move {{_.*}}) -> [0: bb7, 1: bb6, otherwise: bb2];
     // CHECK: bb1: {
     // CHECK:     {{_.*}} = (([[controlflow:_.*]] as Continue).0: i32);
     // CHECK:     _0 = Result::<i32, i32>::Ok(
@@ -68,14 +68,12 @@ fn identity(x: Result<i32, i32>) -> Result<i32, i32> {
     // CHECK: bb6: {
     // CHECK:     {{_.*}} = move (([[x]] as Err).0: i32);
     // CHECK:     [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Break(
-    // CHECK:     goto -> bb9;
+    // CHECK:     goto -> bb8;
     // CHECK: bb7: {
-    // CHECK:     unreachable;
-    // CHECK: bb8: {
     // CHECK:     {{_.*}} = move (([[x]] as Ok).0: i32);
     // CHECK:     [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Continue(
     // CHECK:     goto -> bb5;
-    // CHECK: bb9: {
+    // CHECK: bb8: {
     // CHECK:     goto -> bb3;
     Ok(x?)
 }
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
index 5cb528c0d5f9d..47e0d402347d3 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
@@ -56,9 +56,9 @@
 +         _2 = const Option::<Layout>::None;
           StorageLive(_10);
 -         _10 = discriminant(_2);
--         switchInt(move _10) -> [0: bb1, 1: bb3, otherwise: bb2];
+-         switchInt(move _10) -> [0: bb1, 1: bb2, otherwise: bb6];
 +         _10 = const 0_isize;
-+         switchInt(const 0_isize) -> [0: bb1, 1: bb3, otherwise: bb2];
++         switchInt(const 0_isize) -> [0: bb1, 1: bb2, otherwise: bb6];
       }
   
       bb1: {
@@ -66,10 +66,6 @@
       }
   
       bb2: {
-          unreachable;
-      }
-  
-      bb3: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
 +         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }};
           StorageDead(_10);
@@ -79,18 +75,18 @@
           StorageLive(_5);
           StorageLive(_6);
           _9 = const _;
--         _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind unreachable];
-+         _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb4, unwind unreachable];
+-         _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb3, unwind unreachable];
++         _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb3, unwind unreachable];
       }
   
-      bb4: {
+      bb3: {
           StorageLive(_12);
           StorageLive(_15);
           _12 = discriminant(_6);
-          switchInt(move _12) -> [0: bb6, 1: bb5, otherwise: bb2];
+          switchInt(move _12) -> [0: bb5, 1: bb4, otherwise: bb6];
       }
   
-      bb5: {
+      bb4: {
           _15 = const "called `Result::unwrap()` on an `Err` value";
           StorageLive(_16);
           StorageLive(_17);
@@ -100,7 +96,7 @@
           _14 = result::unwrap_failed(move _15, move _16) -> unwind unreachable;
       }
   
-      bb6: {
+      bb5: {
           _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>);
           StorageDead(_15);
           StorageDead(_12);
@@ -115,6 +111,10 @@
           StorageDead(_3);
           return;
       }
+  
+      bb6: {
+          unreachable;
+      }
   }
 + 
 + ALLOC0 (size: 8, align: 4) {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
index 1e1585f20aefa..dee57ce6c27bb 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
@@ -41,9 +41,9 @@
 +         _2 = const Option::<Layout>::None;
           StorageLive(_10);
 -         _10 = discriminant(_2);
--         switchInt(move _10) -> [0: bb2, 1: bb4, otherwise: bb3];
+-         switchInt(move _10) -> [0: bb2, 1: bb3, otherwise: bb5];
 +         _10 = const 0_isize;
-+         switchInt(const 0_isize) -> [0: bb2, 1: bb4, otherwise: bb3];
++         switchInt(const 0_isize) -> [0: bb2, 1: bb3, otherwise: bb5];
       }
   
       bb1: {
@@ -64,10 +64,6 @@
       }
   
       bb3: {
-          unreachable;
-      }
-  
-      bb4: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
 +         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }};
           StorageDead(_10);
@@ -77,13 +73,17 @@
           StorageLive(_5);
           StorageLive(_6);
           _9 = const _;
--         _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb5, unwind continue];
-+         _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb5, unwind continue];
+-         _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind continue];
++         _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum32) }}, const false) -> [return: bb4, unwind continue];
       }
   
-      bb5: {
+      bb4: {
           _5 = Result::<NonNull<[u8]>, std::alloc::AllocError>::unwrap(move _6) -> [return: bb1, unwind continue];
       }
+  
+      bb5: {
+          unreachable;
+      }
   }
 + 
 + ALLOC0 (size: 8, align: 4) {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
index e655af559a18a..a255b15920cba 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
@@ -56,9 +56,9 @@
 +         _2 = const Option::<Layout>::None;
           StorageLive(_10);
 -         _10 = discriminant(_2);
--         switchInt(move _10) -> [0: bb1, 1: bb3, otherwise: bb2];
+-         switchInt(move _10) -> [0: bb1, 1: bb2, otherwise: bb6];
 +         _10 = const 0_isize;
-+         switchInt(const 0_isize) -> [0: bb1, 1: bb3, otherwise: bb2];
++         switchInt(const 0_isize) -> [0: bb1, 1: bb2, otherwise: bb6];
       }
   
       bb1: {
@@ -66,10 +66,6 @@
       }
   
       bb2: {
-          unreachable;
-      }
-  
-      bb3: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
 +         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }};
           StorageDead(_10);
@@ -79,18 +75,18 @@
           StorageLive(_5);
           StorageLive(_6);
           _9 = const _;
--         _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind unreachable];
-+         _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}, const false) -> [return: bb4, unwind unreachable];
+-         _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb3, unwind unreachable];
++         _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}, const false) -> [return: bb3, unwind unreachable];
       }
   
-      bb4: {
+      bb3: {
           StorageLive(_12);
           StorageLive(_15);
           _12 = discriminant(_6);
-          switchInt(move _12) -> [0: bb6, 1: bb5, otherwise: bb2];
+          switchInt(move _12) -> [0: bb5, 1: bb4, otherwise: bb6];
       }
   
-      bb5: {
+      bb4: {
           _15 = const "called `Result::unwrap()` on an `Err` value";
           StorageLive(_16);
           StorageLive(_17);
@@ -100,7 +96,7 @@
           _14 = result::unwrap_failed(move _15, move _16) -> unwind unreachable;
       }
   
-      bb6: {
+      bb5: {
           _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>);
           StorageDead(_15);
           StorageDead(_12);
@@ -115,6 +111,10 @@
           StorageDead(_3);
           return;
       }
+  
+      bb6: {
+          unreachable;
+      }
   }
 + 
 + ALLOC0 (size: 16, align: 8) {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
index a6658713a026b..192ffea259157 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
@@ -41,9 +41,9 @@
 +         _2 = const Option::<Layout>::None;
           StorageLive(_10);
 -         _10 = discriminant(_2);
--         switchInt(move _10) -> [0: bb2, 1: bb4, otherwise: bb3];
+-         switchInt(move _10) -> [0: bb2, 1: bb3, otherwise: bb5];
 +         _10 = const 0_isize;
-+         switchInt(const 0_isize) -> [0: bb2, 1: bb4, otherwise: bb3];
++         switchInt(const 0_isize) -> [0: bb2, 1: bb3, otherwise: bb5];
       }
   
       bb1: {
@@ -64,10 +64,6 @@
       }
   
       bb3: {
-          unreachable;
-      }
-  
-      bb4: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
 +         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }};
           StorageDead(_10);
@@ -77,13 +73,17 @@
           StorageLive(_5);
           StorageLive(_6);
           _9 = const _;
--         _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb5, unwind continue];
-+         _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}, const false) -> [return: bb5, unwind continue];
+-         _6 = std::alloc::Global::alloc_impl(_9, _1, const false) -> [return: bb4, unwind continue];
++         _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum64) }}, const false) -> [return: bb4, unwind continue];
       }
   
-      bb5: {
+      bb4: {
           _5 = Result::<NonNull<[u8]>, std::alloc::AllocError>::unwrap(move _6) -> [return: bb1, unwind continue];
       }
+  
+      bb5: {
+          unreachable;
+      }
   }
 + 
 + ALLOC0 (size: 16, align: 8) {
diff --git a/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff
index d287b20c4ead0..a12db0a730c57 100644
--- a/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff
+++ b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff
@@ -52,7 +52,7 @@
           StorageLive(_10);
           StorageLive(_11);
           _9 = discriminant(_1);
-          switchInt(move _9) -> [0: bb6, 1: bb5, otherwise: bb2];
+          switchInt(move _9) -> [0: bb5, 1: bb4, otherwise: bb6];
       }
   
       bb1: {
@@ -63,10 +63,6 @@
       }
   
       bb2: {
-          unreachable;
-      }
-  
-      bb3: {
           _6 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>);
           _13 = ((_6 as Err).0: i32);
           _0 = Result::<i32, i32>::Err(move _13);
@@ -74,27 +70,31 @@
           return;
       }
   
-      bb4: {
+      bb3: {
           StorageDead(_11);
           StorageDead(_10);
           StorageDead(_9);
           _5 = discriminant(_3);
-          switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2];
+          switchInt(move _5) -> [0: bb1, 1: bb2, otherwise: bb6];
       }
   
-      bb5: {
+      bb4: {
           _11 = ((_1 as Err).0: i32);
           StorageLive(_12);
           _12 = Result::<Infallible, i32>::Err(move _11);
           _3 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _12);
           StorageDead(_12);
-          goto -> bb4;
+          goto -> bb3;
       }
   
-      bb6: {
+      bb5: {
           _10 = ((_1 as Ok).0: i32);
           _3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _10);
-          goto -> bb4;
+          goto -> bb3;
+      }
+  
+      bb6: {
+          unreachable;
       }
   }
   
diff --git a/tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff b/tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff
index e2bf33f7fbcc0..80f40b86919eb 100644
--- a/tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff
+++ b/tests/mir-opt/separate_const_switch.too_complex.SeparateConstSwitch.diff
@@ -30,47 +30,47 @@
       bb0: {
           StorageLive(_2);
           _3 = discriminant(_1);
-          switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2];
+          switchInt(move _3) -> [0: bb2, 1: bb1, otherwise: bb7];
       }
   
       bb1: {
           _6 = ((_1 as Err).0: usize);
           _2 = ControlFlow::<usize, i32>::Break(_6);
-          goto -> bb4;
+          goto -> bb3;
       }
   
       bb2: {
-          unreachable;
-      }
-  
-      bb3: {
           _4 = ((_1 as Ok).0: i32);
           _2 = ControlFlow::<usize, i32>::Continue(_4);
-          goto -> bb4;
+          goto -> bb3;
       }
   
-      bb4: {
+      bb3: {
           _8 = discriminant(_2);
-          switchInt(move _8) -> [0: bb6, 1: bb5, otherwise: bb2];
+          switchInt(move _8) -> [0: bb5, 1: bb4, otherwise: bb7];
       }
   
-      bb5: {
+      bb4: {
           StorageLive(_11);
           _11 = ((_2 as Break).0: usize);
           _0 = Option::<i32>::None;
           StorageDead(_11);
-          goto -> bb7;
+          goto -> bb6;
       }
   
-      bb6: {
+      bb5: {
           _9 = ((_2 as Continue).0: i32);
           _0 = Option::<i32>::Some(_9);
-          goto -> bb7;
+          goto -> bb6;
       }
   
-      bb7: {
+      bb6: {
           StorageDead(_2);
           return;
       }
+  
+      bb7: {
+          unreachable;
+      }
   }
   

From e61be1bfae982b991828d3edd64a0eaea8bc2d55 Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Mon, 8 Jan 2024 12:00:40 -0700
Subject: [PATCH 10/17] rustdoc-search: reuse empty map/array in function
 signatures

Map is implemented as a pointer to a mutable object.
Rustdoc never mutates function signatures after constructing them,
but the JS engine doesn't know that.

To save a bunch of memory, use a single immutable map
for every decoded type object with no bindings or generics.
---
 src/librustdoc/html/static/js/search.js | 26 ++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index a5e2bc1c7afd0..5bd375a86a787 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -2717,9 +2717,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
      * @return {Array<FunctionSearchType>}
      */
     function buildItemSearchTypeAll(types, lowercasePaths) {
-        return types.map(type => buildItemSearchType(type, lowercasePaths));
+        return types.length > 0 ?
+            types.map(type => buildItemSearchType(type, lowercasePaths)) :
+            EMPTY_GENERICS_ARRAY;
     }
 
+    /**
+     * Empty, immutable map used in item search types with no bindings.
+     *
+     * @type {Map<integer, Array<Functiontype>>}
+     */
+    const EMPTY_BINDINGS_MAP = new Map();
+
+    /**
+     * Empty, immutable map used in item search types with no bindings.
+     *
+     * @type {Array<Functiontype>}
+     */
+    const EMPTY_GENERICS_ARRAY = [];
+
     /**
      * Converts a single type.
      *
@@ -2732,15 +2748,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         let pathIndex, generics, bindings;
         if (typeof type === "number") {
             pathIndex = type;
-            generics = [];
-            bindings = new Map();
+            generics = EMPTY_GENERICS_ARRAY;
+            bindings = EMPTY_BINDINGS_MAP;
         } else {
             pathIndex = type[PATH_INDEX_DATA];
             generics = buildItemSearchTypeAll(
                 type[GENERICS_DATA],
                 lowercasePaths
             );
-            if (type.length > BINDINGS_DATA) {
+            if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) {
                 bindings = new Map(type[BINDINGS_DATA].map(binding => {
                     const [assocType, constraints] = binding;
                     // Associated type constructors are represented sloppily in rustdoc's
@@ -2759,7 +2775,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                     ];
                 }));
             } else {
-                bindings = new Map();
+                bindings = EMPTY_BINDINGS_MAP;
             }
         }
         if (pathIndex < 0) {

From 835680286207b633f4727310829a709b585f7b56 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Sat, 20 May 2023 08:21:56 +0000
Subject: [PATCH 11/17] Move promote_consts back to rustc_mir_transform.

---
 .../rustc_const_eval/src/transform/mod.rs     |  1 -
 compiler/rustc_mir_transform/src/lib.rs       |  2 +-
 .../src}/promote_consts.rs                    | 19 ++++++-------------
 3 files changed, 7 insertions(+), 15 deletions(-)
 rename compiler/{rustc_const_eval/src/transform => rustc_mir_transform/src}/promote_consts.rs (99%)

diff --git a/compiler/rustc_const_eval/src/transform/mod.rs b/compiler/rustc_const_eval/src/transform/mod.rs
index a2928bdf51b83..e3582c7d31746 100644
--- a/compiler/rustc_const_eval/src/transform/mod.rs
+++ b/compiler/rustc_const_eval/src/transform/mod.rs
@@ -1,3 +1,2 @@
 pub mod check_consts;
-pub mod promote_consts;
 pub mod validate;
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index f5f51c0ec8ad1..dfc4ff3b7a353 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -93,6 +93,7 @@ mod multiple_return_terminators;
 mod normalize_array_len;
 mod nrvo;
 mod prettify;
+mod promote_consts;
 mod ref_prop;
 mod remove_noop_landing_pads;
 mod remove_storage_markers;
@@ -114,7 +115,6 @@ mod uninhabited_enum_branching;
 mod unreachable_prop;
 
 use rustc_const_eval::transform::check_consts::{self, ConstCx};
-use rustc_const_eval::transform::promote_consts;
 use rustc_const_eval::transform::validate;
 use rustc_mir_dataflow::rustc_peek;
 
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
similarity index 99%
rename from compiler/rustc_const_eval/src/transform/promote_consts.rs
rename to compiler/rustc_mir_transform/src/promote_consts.rs
index 155cf4ff9e2c7..9a60e83d322f6 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -25,7 +25,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
 use std::cell::Cell;
 use std::{cmp, iter, mem};
 
-use crate::transform::check_consts::{qualifs, ConstCx};
+use rustc_const_eval::transform::check_consts::{qualifs, ConstCx};
 
 /// A `MirPass` for promotion.
 ///
@@ -64,7 +64,7 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
 
 /// State of a temporary during collection and promotion.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum TempState {
+enum TempState {
     /// No references to this temp.
     Undefined,
     /// One direct assignment and any number of direct uses.
@@ -78,18 +78,11 @@ pub enum TempState {
     PromotedOut,
 }
 
-impl TempState {
-    pub fn is_promotable(&self) -> bool {
-        debug!("is_promotable: self={:?}", self);
-        matches!(self, TempState::Defined { .. })
-    }
-}
-
 /// A "root candidate" for promotion, which will become the
 /// returned value in a promoted MIR, unless it's a subset
 /// of a larger candidate.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Candidate {
+struct Candidate {
     location: Location,
 }
 
@@ -162,7 +155,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
     }
 }
 
-pub fn collect_temps_and_candidates<'tcx>(
+fn collect_temps_and_candidates<'tcx>(
     ccx: &ConstCx<'_, 'tcx>,
 ) -> (IndexVec<Local, TempState>, Vec<Candidate>) {
     let mut collector = Collector {
@@ -676,7 +669,7 @@ impl<'tcx> Validator<'_, 'tcx> {
 }
 
 // FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
-pub fn validate_candidates(
+fn validate_candidates(
     ccx: &ConstCx<'_, '_>,
     temps: &mut IndexSlice<Local, TempState>,
     candidates: &[Candidate],
@@ -930,7 +923,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
     }
 }
 
-pub fn promote_candidates<'tcx>(
+fn promote_candidates<'tcx>(
     body: &mut Body<'tcx>,
     tcx: TyCtxt<'tcx>,
     mut temps: IndexVec<Local, TempState>,

From cae0dc28332065dfa3fa168fdc1f2818bcbdf0c3 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Sat, 20 May 2023 08:54:16 +0000
Subject: [PATCH 12/17] Simplify code flow.

---
 compiler/rustc_mir_transform/src/lib.rs       |   1 +
 .../rustc_mir_transform/src/promote_consts.rs | 378 ++++++++----------
 2 files changed, 157 insertions(+), 222 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index dfc4ff3b7a353..915ddcb2775dc 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -1,5 +1,6 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
+#![feature(assert_matches)]
 #![feature(box_patterns)]
 #![feature(cow_is_borrowed)]
 #![feature(decl_macro)]
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 9a60e83d322f6..9b9c9aae88bfd 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -12,6 +12,7 @@
 //! initialization and can otherwise silence errors, if
 //! move analysis runs after promotion on broken MIR.
 
+use either::{Left, Right};
 use rustc_hir as hir;
 use rustc_middle::mir;
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
@@ -22,6 +23,7 @@ use rustc_span::Span;
 
 use rustc_index::{Idx, IndexSlice, IndexVec};
 
+use std::assert_matches::assert_matches;
 use std::cell::Cell;
 use std::{cmp, iter, mem};
 
@@ -116,41 +118,38 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
 
         let temp = &mut self.temps[index];
         debug!("visit_local: temp={:?}", temp);
-        if *temp == TempState::Undefined {
-            match context {
+        *temp = match *temp {
+            TempState::Undefined => match context {
                 PlaceContext::MutatingUse(MutatingUseContext::Store)
                 | PlaceContext::MutatingUse(MutatingUseContext::Call) => {
-                    *temp = TempState::Defined { location, uses: 0, valid: Err(()) };
+                    TempState::Defined { location, uses: 0, valid: Err(()) }
+                }
+                _ => TempState::Unpromotable,
+            },
+            TempState::Defined { ref mut uses, .. } => {
+                // We always allow borrows, even mutable ones, as we need
+                // to promote mutable borrows of some ZSTs e.g., `&mut []`.
+                let allowed_use = match context {
+                    PlaceContext::MutatingUse(MutatingUseContext::Borrow)
+                    | PlaceContext::NonMutatingUse(_) => true,
+                    PlaceContext::MutatingUse(_) | PlaceContext::NonUse(_) => false,
+                };
+                debug!("visit_local: allowed_use={:?}", allowed_use);
+                if allowed_use {
+                    *uses += 1;
                     return;
                 }
-                _ => { /* mark as unpromotable below */ }
+                TempState::Unpromotable
             }
-        } else if let TempState::Defined { uses, .. } = temp {
-            // We always allow borrows, even mutable ones, as we need
-            // to promote mutable borrows of some ZSTs e.g., `&mut []`.
-            let allowed_use = match context {
-                PlaceContext::MutatingUse(MutatingUseContext::Borrow)
-                | PlaceContext::NonMutatingUse(_) => true,
-                PlaceContext::MutatingUse(_) | PlaceContext::NonUse(_) => false,
-            };
-            debug!("visit_local: allowed_use={:?}", allowed_use);
-            if allowed_use {
-                *uses += 1;
-                return;
-            }
-            /* mark as unpromotable below */
-        }
-        *temp = TempState::Unpromotable;
+            _ => TempState::Unpromotable,
+        };
     }
 
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         self.super_rvalue(rvalue, location);
 
-        match *rvalue {
-            Rvalue::Ref(..) => {
-                self.candidates.push(Candidate { location });
-            }
-            _ => {}
+        if let Rvalue::Ref(..) = *rvalue {
+            self.candidates.push(Candidate { location });
         }
     }
 }
@@ -189,230 +188,165 @@ struct Unpromotable;
 
 impl<'tcx> Validator<'_, 'tcx> {
     fn validate_candidate(&mut self, candidate: Candidate) -> Result<(), Unpromotable> {
-        let loc = candidate.location;
-        let statement = &self.body[loc.block].statements[loc.statement_index];
-        match &statement.kind {
-            StatementKind::Assign(box (_, Rvalue::Ref(_, kind, place))) => {
-                // We can only promote interior borrows of promotable temps (non-temps
-                // don't get promoted anyway).
-                self.validate_local(place.local)?;
-
-                // The reference operation itself must be promotable.
-                // (Needs to come after `validate_local` to avoid ICEs.)
-                self.validate_ref(*kind, place)?;
+        let Left(statement) = self.body.stmt_at(candidate.location) else { bug!() };
+        let Some((_, Rvalue::Ref(_, kind, place))) = statement.kind.as_assign() else { bug!() };
 
-                // We do not check all the projections (they do not get promoted anyway),
-                // but we do stay away from promoting anything involving a dereference.
-                if place.projection.contains(&ProjectionElem::Deref) {
-                    return Err(Unpromotable);
-                }
+        // We can only promote interior borrows of promotable temps (non-temps
+        // don't get promoted anyway).
+        self.validate_local(place.local)?;
 
-                Ok(())
-            }
-            _ => bug!(),
+        // The reference operation itself must be promotable.
+        // (Needs to come after `validate_local` to avoid ICEs.)
+        self.validate_ref(*kind, place)?;
+
+        // We do not check all the projections (they do not get promoted anyway),
+        // but we do stay away from promoting anything involving a dereference.
+        if place.projection.contains(&ProjectionElem::Deref) {
+            return Err(Unpromotable);
         }
+
+        Ok(())
     }
 
     // FIXME(eddyb) maybe cache this?
     fn qualif_local<Q: qualifs::Qualif>(&mut self, local: Local) -> bool {
-        if let TempState::Defined { location: loc, .. } = self.temps[local] {
-            let num_stmts = self.body[loc.block].statements.len();
-
-            if loc.statement_index < num_stmts {
-                let statement = &self.body[loc.block].statements[loc.statement_index];
-                match &statement.kind {
-                    StatementKind::Assign(box (_, rhs)) => qualifs::in_rvalue::<Q, _>(
-                        self.ccx,
-                        &mut |l| self.qualif_local::<Q>(l),
-                        rhs,
-                    ),
-                    _ => {
+        let TempState::Defined { location: loc, .. } = self.temps[local] else {
+            return false;
+        };
+
+        let stmt_or_term = self.body.stmt_at(loc);
+        match stmt_or_term {
+            Left(statement) => {
+                let Some((_, rhs)) = statement.kind.as_assign() else {
+                    span_bug!(statement.source_info.span, "{:?} is not an assignment", statement)
+                };
+                qualifs::in_rvalue::<Q, _>(self.ccx, &mut |l| self.qualif_local::<Q>(l), rhs)
+            }
+            Right(terminator) => {
+                assert_matches!(terminator.kind, TerminatorKind::Call { .. });
+                let return_ty = self.body.local_decls[local].ty;
+                Q::in_any_value_of_ty(self.ccx, return_ty)
+            }
+        }
+    }
+
+    fn validate_local(&mut self, local: Local) -> Result<(), Unpromotable> {
+        let TempState::Defined { location: loc, uses, valid } = self.temps[local] else {
+            return Err(Unpromotable);
+        };
+
+        // We cannot promote things that need dropping, since the promoted value would not get
+        // dropped.
+        if self.qualif_local::<qualifs::NeedsDrop>(local) {
+            return Err(Unpromotable);
+        }
+
+        if valid.is_ok() {
+            return Ok(());
+        }
+
+        let ok = {
+            let stmt_or_term = self.body.stmt_at(loc);
+            match stmt_or_term {
+                Left(statement) => {
+                    let Some((_, rhs)) = statement.kind.as_assign() else {
                         span_bug!(
                             statement.source_info.span,
                             "{:?} is not an assignment",
                             statement
-                        );
-                    }
+                        )
+                    };
+                    self.validate_rvalue(rhs)
                 }
-            } else {
-                let terminator = self.body[loc.block].terminator();
-                match &terminator.kind {
-                    TerminatorKind::Call { .. } => {
-                        let return_ty = self.body.local_decls[local].ty;
-                        Q::in_any_value_of_ty(self.ccx, return_ty)
-                    }
+                Right(terminator) => match &terminator.kind {
+                    TerminatorKind::Call { func, args, .. } => self.validate_call(func, args),
+                    TerminatorKind::Yield { .. } => Err(Unpromotable),
                     kind => {
                         span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                     }
-                }
+                },
             }
-        } else {
-            false
-        }
-    }
+        };
 
-    fn validate_local(&mut self, local: Local) -> Result<(), Unpromotable> {
-        if let TempState::Defined { location: loc, uses, valid } = self.temps[local] {
-            // We cannot promote things that need dropping, since the promoted value
-            // would not get dropped.
-            if self.qualif_local::<qualifs::NeedsDrop>(local) {
-                return Err(Unpromotable);
-            }
-            valid.or_else(|_| {
-                let ok = {
-                    let block = &self.body[loc.block];
-                    let num_stmts = block.statements.len();
-
-                    if loc.statement_index < num_stmts {
-                        let statement = &block.statements[loc.statement_index];
-                        match &statement.kind {
-                            StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs),
-                            _ => {
-                                span_bug!(
-                                    statement.source_info.span,
-                                    "{:?} is not an assignment",
-                                    statement
-                                );
-                            }
-                        }
-                    } else {
-                        let terminator = block.terminator();
-                        match &terminator.kind {
-                            TerminatorKind::Call { func, args, .. } => {
-                                self.validate_call(func, args)
-                            }
-                            TerminatorKind::Yield { .. } => Err(Unpromotable),
-                            kind => {
-                                span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
-                            }
-                        }
-                    }
-                };
-                self.temps[local] = match ok {
-                    Ok(()) => TempState::Defined { location: loc, uses, valid: Ok(()) },
-                    Err(_) => TempState::Unpromotable,
-                };
-                ok
-            })
-        } else {
-            Err(Unpromotable)
-        }
+        self.temps[local] = match ok {
+            Ok(()) => TempState::Defined { location: loc, uses, valid: Ok(()) },
+            Err(_) => TempState::Unpromotable,
+        };
+
+        ok
     }
 
     fn validate_place(&mut self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> {
-        match place.last_projection() {
-            None => self.validate_local(place.local),
-            Some((place_base, elem)) => {
-                // Validate topmost projection, then recurse.
-                match elem {
-                    ProjectionElem::Deref => {
-                        let mut promotable = false;
-                        // When a static is used by-value, that gets desugared to `*STATIC_ADDR`,
-                        // and we need to be able to promote this. So check if this deref matches
-                        // that specific pattern.
-
-                        // We need to make sure this is a `Deref` of a local with no further projections.
-                        // Discussion can be found at
-                        // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
-                        if let Some(local) = place_base.as_local() {
-                            if let TempState::Defined { location, .. } = self.temps[local] {
-                                let def_stmt = self.body[location.block]
-                                    .statements
-                                    .get(location.statement_index);
-                                if let Some(Statement {
-                                    kind:
-                                        StatementKind::Assign(box (
-                                            _,
-                                            Rvalue::Use(Operand::Constant(c)),
-                                        )),
-                                    ..
-                                }) = def_stmt
-                                {
-                                    if let Some(did) = c.check_static_ptr(self.tcx) {
-                                        // Evaluating a promoted may not read statics except if it got
-                                        // promoted from a static (this is a CTFE check). So we
-                                        // can only promote static accesses inside statics.
-                                        if let Some(hir::ConstContext::Static(..)) = self.const_kind
-                                        {
-                                            if !self.tcx.is_thread_local_static(did) {
-                                                promotable = true;
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        if !promotable {
-                            return Err(Unpromotable);
-                        }
-                    }
-                    ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
-                        return Err(Unpromotable);
-                    }
+        let Some((place_base, elem)) = place.last_projection() else {
+            return self.validate_local(place.local);
+        };
 
-                    ProjectionElem::ConstantIndex { .. }
-                    | ProjectionElem::Subtype(_)
-                    | ProjectionElem::Subslice { .. } => {}
-
-                    ProjectionElem::Index(local) => {
-                        let mut promotable = false;
-                        // Only accept if we can predict the index and are indexing an array.
-                        let val = if let TempState::Defined { location: loc, .. } =
-                            self.temps[local]
-                        {
-                            let block = &self.body[loc.block];
-                            if loc.statement_index < block.statements.len() {
-                                let statement = &block.statements[loc.statement_index];
-                                match &statement.kind {
-                                    StatementKind::Assign(box (
-                                        _,
-                                        Rvalue::Use(Operand::Constant(c)),
-                                    )) => c.const_.try_eval_target_usize(self.tcx, self.param_env),
-                                    _ => None,
-                                }
-                            } else {
-                                None
-                            }
-                        } else {
-                            None
-                        };
-                        if let Some(idx) = val {
-                            // Determine the type of the thing we are indexing.
-                            let ty = place_base.ty(self.body, self.tcx).ty;
-                            match ty.kind() {
-                                ty::Array(_, len) => {
-                                    // It's an array; determine its length.
-                                    if let Some(len) =
-                                        len.try_eval_target_usize(self.tcx, self.param_env)
-                                    {
-                                        // If the index is in-bounds, go ahead.
-                                        if idx < len {
-                                            promotable = true;
-                                        }
-                                    }
-                                }
-                                _ => {}
-                            }
-                        }
-                        if !promotable {
-                            return Err(Unpromotable);
-                        }
+        // Validate topmost projection, then recurse.
+        match elem {
+            // Recurse directly.
+            ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Subtype(_)
+            | ProjectionElem::Subslice { .. } => {}
 
-                        self.validate_local(local)?;
-                    }
+            // Never recurse.
+            ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
+                return Err(Unpromotable);
+            }
 
-                    ProjectionElem::Field(..) => {
-                        let base_ty = place_base.ty(self.body, self.tcx).ty;
-                        if base_ty.is_union() {
-                            // No promotion of union field accesses.
-                            return Err(Unpromotable);
-                        }
-                    }
+            ProjectionElem::Deref => {
+                // When a static is used by-value, that gets desugared to `*STATIC_ADDR`,
+                // and we need to be able to promote this. So check if this deref matches
+                // that specific pattern.
+
+                // We need to make sure this is a `Deref` of a local with no further projections.
+                // Discussion can be found at
+                // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
+                if let Some(local) = place_base.as_local()
+                    && let TempState::Defined { location, .. } = self.temps[local]
+                    && let Left(def_stmt) = self.body.stmt_at(location)
+                    && let Some((_, Rvalue::Use(Operand::Constant(c)))) = def_stmt.kind.as_assign()
+                    && let Some(did) = c.check_static_ptr(self.tcx)
+                    // Evaluating a promoted may not read statics except if it got
+                    // promoted from a static (this is a CTFE check). So we
+                    // can only promote static accesses inside statics.
+                    && let Some(hir::ConstContext::Static(..)) = self.const_kind
+                    && !self.tcx.is_thread_local_static(did)
+                {
+                    // Recurse.
+                } else {
+                    return Err(Unpromotable);
                 }
+            }
+            ProjectionElem::Index(local) => {
+                // Only accept if we can predict the index and are indexing an array.
+                if let TempState::Defined { location: loc, .. } = self.temps[local]
+                    && let Left(statement) =  self.body.stmt_at(loc)
+                    && let Some((_, Rvalue::Use(Operand::Constant(c)))) = statement.kind.as_assign()
+                    && let Some(idx) = c.const_.try_eval_target_usize(self.tcx, self.param_env)
+                    // Determine the type of the thing we are indexing.
+                    && let ty::Array(_, len) = place_base.ty(self.body, self.tcx).ty.kind()
+                    // It's an array; determine its length.
+                    && let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env)
+                    // If the index is in-bounds, go ahead.
+                    && idx < len
+                {
+                    self.validate_local(local)?;
+                    // Recurse.
+                } else {
+                    return Err(Unpromotable);
+                }
+            }
 
-                self.validate_place(place_base)
+            ProjectionElem::Field(..) => {
+                let base_ty = place_base.ty(self.body, self.tcx).ty;
+                if base_ty.is_union() {
+                    // No promotion of union field accesses.
+                    return Err(Unpromotable);
+                }
             }
         }
+
+        self.validate_place(place_base)
     }
 
     fn validate_operand(&mut self, operand: &Operand<'tcx>) -> Result<(), Unpromotable> {

From 5d6463c26c0f20f855da4ca54caa583c2258fb2f Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Mon, 8 Jan 2024 22:40:32 +0000
Subject: [PATCH 13/17] Make match exhaustive.

---
 compiler/rustc_mir_transform/src/promote_consts.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 9b9c9aae88bfd..841b86fed4548 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -141,7 +141,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
                 }
                 TempState::Unpromotable
             }
-            _ => TempState::Unpromotable,
+            TempState::Unpromotable | TempState::PromotedOut => TempState::Unpromotable,
         };
     }
 

From a2b765fc37e2de38502912aea051d3fdde42357c Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Mon, 8 Jan 2024 09:43:58 +1100
Subject: [PATCH 14/17] Remove `-Zdont-buffer-diagnostics`.

It was added in #54232. It seems like it was aimed at NLL development,
which is well in the past. Also, it looks like `-Ztreat-err-as-bug` can
be used to achieve the same effect. So it doesn't seem necessary.
---
 compiler/rustc_errors/src/diagnostic_builder.rs | 3 +--
 compiler/rustc_errors/src/lib.rs                | 3 ---
 compiler/rustc_interface/src/tests.rs           | 1 -
 compiler/rustc_session/src/config.rs            | 1 -
 compiler/rustc_session/src/options.rs           | 3 ---
 5 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index e72791d89a292..3789cdaf354aa 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -266,8 +266,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// Converts the builder to a `Diagnostic` for later emission,
     /// unless dcx has disabled such buffering.
     pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> {
-        let flags = self.dcx.inner.lock().flags;
-        if flags.dont_buffer_diagnostics || flags.treat_err_as_bug.is_some() {
+        if self.dcx.inner.lock().flags.treat_err_as_bug.is_some() {
             self.emit();
             return None;
         }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index b97ec02675a60..76b7e0d79a98c 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -524,9 +524,6 @@ pub struct DiagCtxtFlags {
     /// If Some, the Nth error-level diagnostic is upgraded to bug-level.
     /// (rustc: see `-Z treat-err-as-bug`)
     pub treat_err_as_bug: Option<NonZeroUsize>,
-    /// If true, immediately emit diagnostics that would otherwise be buffered.
-    /// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`)
-    pub dont_buffer_diagnostics: bool,
     /// Show macro backtraces.
     /// (rustc: see `-Z macro-backtrace`)
     pub macro_backtrace: bool,
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index f3f59b05682db..444e3700cf772 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -659,7 +659,6 @@ fn test_unstable_options_tracking_hash() {
     // tidy-alphabetical-start
     untracked!(assert_incr_state, Some(String::from("loaded")));
     untracked!(deduplicate_diagnostics, false);
-    untracked!(dont_buffer_diagnostics, true);
     untracked!(dump_dep_graph, true);
     untracked!(dump_mir, Some(String::from("abc")));
     untracked!(dump_mir_dataflow, true);
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 12d8293ecd2f6..61796d7a6cafc 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1146,7 +1146,6 @@ impl UnstableOptions {
         DiagCtxtFlags {
             can_emit_warnings,
             treat_err_as_bug: self.treat_err_as_bug,
-            dont_buffer_diagnostics: self.dont_buffer_diagnostics,
             macro_backtrace: self.macro_backtrace,
             deduplicate_diagnostics: self.deduplicate_diagnostics,
             track_diagnostics: self.track_diagnostics,
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 842bf1d60f66d..6d96ec4d6f67d 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1553,9 +1553,6 @@ options! {
     dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED],
         "in dep-info output, omit targets for tracking dependencies of the dep-info files \
         themselves (default: no)"),
-    dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
-        "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \
-        (default: no)"),
     dual_proc_macros: bool = (false, parse_bool, [TRACKED],
         "load proc macros for both target and host, but only link to the target (default: no)"),
     dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],

From c3cd657210403b9b8b0e8557bc1604d1890e6821 Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Mon, 8 Jan 2024 14:29:55 -0700
Subject: [PATCH 15/17] rustdoc-search: intern function search types

This takes advantage of more reuse opportunities.
Along with the empty object commit, they
bringing memory usage down about 20% over the original.
---
 src/librustdoc/html/static/js/search.js | 89 ++++++++++++++++++++-----
 1 file changed, 72 insertions(+), 17 deletions(-)

diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 5bd375a86a787..7995a33f09f9b 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -2725,17 +2725,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
     /**
      * Empty, immutable map used in item search types with no bindings.
      *
-     * @type {Map<integer, Array<Functiontype>>}
+     * @type {Map<number, Array<FunctionType>>}
      */
     const EMPTY_BINDINGS_MAP = new Map();
 
     /**
      * Empty, immutable map used in item search types with no bindings.
      *
-     * @type {Array<Functiontype>}
+     * @type {Array<FunctionType>}
      */
     const EMPTY_GENERICS_ARRAY = [];
 
+    /**
+     * Object pool for function types with no bindings or generics.
+     * This is reset after loading the index.
+     *
+     * @type {Map<number|null, FunctionType>}
+     */
+    let TYPES_POOL = new Map();
+
     /**
      * Converts a single type.
      *
@@ -2778,35 +2786,80 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 bindings = EMPTY_BINDINGS_MAP;
             }
         }
+        /**
+         * @type {FunctionType}
+         */
+        let result;
         if (pathIndex < 0) {
             // types less than 0 are generic parameters
             // the actual names of generic parameters aren't stored, since they aren't API
-            return {
+            result = {
                 id: pathIndex,
                 ty: TY_GENERIC,
                 path: null,
                 generics,
                 bindings,
             };
-        }
-        if (pathIndex === 0) {
+        } else if (pathIndex === 0) {
             // `0` is used as a sentinel because it's fewer bytes than `null`
-            return {
+            result = {
                 id: null,
                 ty: null,
                 path: null,
                 generics,
                 bindings,
             };
+        } else {
+            const item = lowercasePaths[pathIndex - 1];
+            result = {
+                id: buildTypeMapIndex(item.name, isAssocType),
+                ty: item.ty,
+                path: item.path,
+                generics,
+                bindings,
+            };
         }
-        const item = lowercasePaths[pathIndex - 1];
-        return {
-            id: buildTypeMapIndex(item.name, isAssocType),
-            ty: item.ty,
-            path: item.path,
-            generics,
-            bindings,
-        };
+        const cr = TYPES_POOL.get(result.id);
+        if (cr) {
+            // Shallow equality check. Since this function is used
+            // to construct every type object, this should be mostly
+            // equivalent to a deep equality check, except if there's
+            // a conflict, we don't keep the old one around, so it's
+            // not a fully precise implementation of hashcons.
+            if (cr.generics.length === result.generics.length &&
+                cr.generics !== result.generics &&
+                cr.generics.every((x, i) => result.generics[i] === x)
+            ) {
+                result.generics = cr.generics;
+            }
+            if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) {
+                let ok = true;
+                for (const [k, v] of cr.bindings.entries()) {
+                    const v2 = result.bindings.get(v);
+                    if (!v2) {
+                        ok = false;
+                        break;
+                    }
+                    if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) {
+                        result.bindings.set(k, v);
+                    } else if (v !== v2) {
+                        ok = false;
+                        break;
+                    }
+                }
+                if (ok) {
+                    result.bindings = cr.bindings;
+                }
+            }
+            if (cr.ty === result.ty && cr.path === result.path
+                && cr.bindings === result.bindings && cr.generics === result.generics
+                && cr.ty === result.ty
+            ) {
+                return cr;
+            }
+        }
+        TYPES_POOL.set(result.id, result);
+        return result;
     }
 
     /**
@@ -2817,7 +2870,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
      * object-based encoding so that the actual search code is more readable and easier to debug.
      *
      * The raw function search type format is generated using serde in
-     * librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType
+     * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string
      *
      * @param {{
      *  string: string,
@@ -2986,8 +3039,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         const fb = {
             id: null,
             ty: 0,
-            generics: [],
-            bindings: new Map(),
+            generics: EMPTY_GENERICS_ARRAY,
+            bindings: EMPTY_BINDINGS_MAP,
         };
         for (const [k, v] of type.bindings.entries()) {
             fb.id = k;
@@ -3215,6 +3268,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             }
             currentIndex += itemTypes.length;
         }
+        // Drop the (rather large) hash table used for reusing function items
+        TYPES_POOL = new Map();
     }
 
     /**

From c8ded5260176495ee8012e8f162766897dfc611b Mon Sep 17 00:00:00 2001
From: Erik Desjardins <erikdesjardins@users.noreply.github.com>
Date: Mon, 8 Jan 2024 21:36:02 -0500
Subject: [PATCH 16/17] GNU/Hurd: unconditionally use inline stack probes

LLVM 11 has been unsupported since 45591408b18e7f93fcf8c09210c9a5a102d84b37,
so this doesn't need to be conditional on the LLVM version.
---
 compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs
index 9102673ef7791..6884e078c27e5 100644
--- a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs
@@ -5,7 +5,7 @@ pub fn target() -> Target {
     base.cpu = "pentiumpro".into();
     base.max_atomic_width = Some(64);
     base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]);
-    base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
+    base.stack_probes = StackProbeType::Inline;
 
     Target {
         llvm_target: "i686-unknown-hurd-gnu".into(),

From 36110147229f49024187df6b17175dd5810a26bc Mon Sep 17 00:00:00 2001
From: Urgau <urgau@numericable.fr>
Date: Fri, 22 Dec 2023 19:25:15 +0100
Subject: [PATCH 17/17] Rework and improve unstable documentation of check-cfg

---
 .../src/compiler-flags/check-cfg.md           | 224 ++++++++++--------
 1 file changed, 123 insertions(+), 101 deletions(-)

diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md
index 8ab6e83d99e79..e8fc2fe986e22 100644
--- a/src/doc/unstable-book/src/compiler-flags/check-cfg.md
+++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md
@@ -4,18 +4,16 @@ The tracking issue for this feature is: [#82450](https://github.com/rust-lang/ru
 
 ------------------------
 
-This feature allows you to enable complete or partial checking of configuration.
+This feature enables checking of conditional configuration.
 
 `rustc` accepts the `--check-cfg` option, which specifies whether to check conditions and how to
-check them. The `--check-cfg` option takes a value, called the _check cfg specification_. The
-check cfg specification is parsed using the Rust metadata syntax, just as the `--cfg` option is.
+check them. The `--check-cfg` option takes a value, called the _check cfg specification_.
+This specification has one form:
 
-`--check-cfg` option take one form:
+1. `--check-cfg cfg(...)` mark a configuration and it's expected values as expected.
 
-1. `--check-cfg cfg(...)` enables checking the values within list-valued conditions.
-
-NOTE: No implicit expectation is added when using `--cfg` for both forms. Users are expected to
-pass all expected names and values using `cfg(...)`.
+*No implicit expectation is added when using `--cfg`. Users are expected to
+pass all expected names and values using the _check cfg specification_.*
 
 ## The `cfg(...)` form
 
@@ -23,7 +21,7 @@ The `cfg(...)` form enables checking the values within list-valued conditions. I
 basic form:
 
 ```bash
-rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))'
+rustc --check-cfg 'cfg(name, values("value1", "value2", ... "valueN"))'
 ```
 
 where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
@@ -31,162 +29,186 @@ string. `name` specifies the name of the condition, such as `feature` or `my_cfg
 
 When the `cfg(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
 attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
-and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
-list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
-lint diagnostic. The default diagnostic level for this lint is `Warn`.
+attribute and `cfg!(name = "value")` macro call. It will check that the `"value"` specified is
+present in the list of expected values. If `"value"` is not in it, then `rustc` will report an
+`unexpected_cfgs` lint diagnostic. The default diagnostic level for this lint is `Warn`.
 
-The command line `--cfg` arguments are currently *NOT* checked but may very well be checked in
-the future.
+*The command line `--cfg` arguments are currently *NOT* checked but may very well be checked in
+the future.*
 
-To enable checking of values, but to provide an empty set of expected values, use these forms:
+To enable checking of values, but to provide an *none*/empty set of expected values
+(ie. expect `#[cfg(name)]`), use these forms:
 
 ```bash
-rustc --check-cfg 'cfg(name1, ..., nameN)'
-rustc --check-cfg 'cfg(name1, ..., nameN, values())'
+rustc --check-cfg 'cfg(name)'
+rustc --check-cfg 'cfg(name, values())'
 ```
 
 To enable checking of name but not values (i.e. unknown expected values), use this form:
 
 ```bash
-rustc --check-cfg 'cfg(name1, ..., nameN, values(any()))'
+rustc --check-cfg 'cfg(name, values(any()))'
+```
+
+To avoid repeating the same set of values, use this form:
+
+```bash
+rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))'
 ```
 
 The `--check-cfg cfg(...)` option can be repeated, both for the same condition name and for
 different names. If it is repeated for the same condition name, then the sets of values for that
-condition are merged together (presedence is given to `any()`).
+condition are merged together (precedence is given to `values(any())`).
 
 ## Well known names and values
 
 `rustc` has a internal list of well known names and their corresponding values.
 Those well known names and values follows the same stability as what they refer to.
 
-Well known values checking is always enabled as long as a `--check-cfg` argument is present.
+Well known names and values checking is always enabled as long as at least one
+`--check-cfg` argument is present.
+
+As of `2024-01-09T`, the list of known names is as follows:
+
+<!--- See CheckCfg::fill_well_known in compiler/rustc_session/src/config.rs -->
+
+ - `debug_assertions`
+ - `doc`
+ - `doctest`
+ - `miri`
+ - `overflow_checks`
+ - `panic`
+ - `proc_macro`
+ - `relocation_model`
+ - `sanitize`
+ - `sanitizer_cfi_generalize_pointers`
+ - `sanitizer_cfi_normalize_integers`
+ - `target_abi`
+ - `target_arch`
+ - `target_endian`
+ - `target_env`
+ - `target_family`
+ - `target_feature`
+ - `target_has_atomic`
+ - `target_has_atomic_equal_alignment`
+ - `target_has_atomic_load_store`
+ - `target_os`
+ - `target_pointer_width`
+ - `target_thread_local`
+ - `target_vendor`
+ - `test`
+ - `unix`
+ - `windows`
+
+Like with `values(any())`, well known names checking can be disabled by passing `cfg(any())`
+as argument to `--check-cfg`.
 
-Well known names checking is always enable as long as a `--check-cfg` argument is present
-**unless** any `cfg(any())` argument is passed.
+## Examples
 
-To disable checking of well known names, use this form:
+### Equivalence table
 
-```bash
-rustc --check-cfg 'cfg(any())'
-```
+This table describe the equivalence of a `--cfg` argument to a `--check-cfg` argument.
 
-NOTE: If one want to enable values and names checking without having any cfg to declare, one
-can use an empty `cfg()` argument.
+| `--cfg`                       | `--check-cfg`                                              |
+|-----------------------------|----------------------------------------------------------|
+| *nothing*                     | *nothing* or `--check-cfg=cfg()` (to enable the checking)  |
+| `--cfg foo`                   | `--check-cfg=cfg(foo) or --check-cfg=cfg(foo, values())`   |
+| `--cfg foo=""`                | `--check-cfg=cfg(foo, values(""))`                         |
+| `--cfg foo="bar"`             | `--check-cfg=cfg(foo, values("bar"))`                      |
+| `--cfg foo="1" --cfg foo="2"` | `--check-cfg=cfg(foo, values("1", "2"))`                   |
+| `--cfg foo="1" --cfg bar="2"` | `--check-cfg=cfg(foo, values("1")) --check-cfg=cfg(bar, values("2"))` |
+| `--cfg foo --cfg foo="bar"`   | `--check-cfg=cfg(foo) --check-cfg=cfg(foo, values("bar"))` |
 
-## Examples
+NOTE: There is (currently) no way to express that a condition name is expected but no (!= none)
+values are expected. Passing an empty `values()` means *(none)* in the sense of `#[cfg(foo)]`
+with no value. Users are expected to NOT pass a `--check-cfg` with that condition name.
+
+### Example: Cargo-like `feature` example
 
 Consider this command line:
 
 ```bash
 rustc --check-cfg 'cfg(feature, values("lion", "zebra"))' \
-      --cfg 'feature="lion"' -Z unstable-options \
-      example.rs
+      --cfg 'feature="lion"' -Z unstable-options example.rs
 ```
 
 This command line indicates that this crate has two features: `lion` and `zebra`. The `lion`
-feature is enabled, while the `zebra` feature is disabled. Exhaustive checking of names and
-values are enabled by default. Consider compiling this code:
+feature is enabled, while the `zebra` feature is disabled.
+Given the `--check-cfg` arguments, exhaustive checking of names and
+values are enabled.
 
+`example.rs`:
 ```rust
-// This is expected, and tame_lion() will be compiled
-#[cfg(feature = "lion")]
+#[cfg(feature = "lion")]     // This condition is expected, as "lion" is an expected value of `feature`
 fn tame_lion(lion: Lion) {}
 
-// This is expected, and ride_zebra() will NOT be compiled.
-#[cfg(feature = "zebra")]
-fn ride_zebra(zebra: Zebra) {}
+#[cfg(feature = "zebra")]    // This condition is expected, as "zebra" is an expected value of `feature`
+                             // but the condition will still evaluate to false
+                             // since only --cfg feature="lion" was passed
+fn ride_zebra(z: Zebra) {}
 
-// This is UNEXPECTED, and will cause a compiler warning (by default).
-#[cfg(feature = "platypus")]
+#[cfg(feature = "platypus")] // This condition is UNEXPECTED, as "platypus" is NOT an expected value of
+                             // `feature` and will cause a compiler warning (by default).
 fn poke_platypus() {}
 
-// This is UNEXPECTED, because 'feechure' is not a known condition name,
-// and will cause a compiler warning (by default).
-#[cfg(feechure = "lion")]
+#[cfg(feechure = "lion")]    // This condition is UNEXPECTED, as 'feechure' is NOT a expected condition
+                             // name, no `cfg(feechure, ...)` was passed in `--check-cfg`
 fn tame_lion() {}
 
-// This is UNEXPECTED, because 'windows' is a well known condition name,
-// and because 'windows' doesn't take any values,
-// and will cause a compiler warning (by default).
-#[cfg(windows = "unix")]
+#[cfg(windows = "unix")]     // This condition is UNEXPECTED, as while 'windows' is a well known
+                             // condition name, it doens't expect any values
 fn tame_windows() {}
 ```
 
-### Example: Checking condition names, but not values
+### Example: Multiple names and values
 
 ```bash
-# This turns on checking for condition names, but not values, such as 'feature' values.
-rustc --check-cfg 'cfg(is_embedded, has_feathers, values(any()))' \
-      --cfg has_feathers -Z unstable-options
+rustc --check-cfg 'cfg(is_embedded, has_feathers)' \
+      --check-cfg 'cfg(feature, values("zapping", "lasers"))' \
+      --cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
 ```
 
 ```rust
-#[cfg(is_embedded)]         // This is expected as "is_embedded" was provided in cfg()
-fn do_embedded() {}         // and because names exhaustiveness was not disabled
-
-#[cfg(has_feathers)]        // This is expected as "has_feathers" was provided in cfg()
-fn do_features() {}         // and because names exhaustiveness was not disabled
+#[cfg(is_embedded)]         // This condition is expected, as 'is_embedded' was provided in --check-cfg
+fn do_embedded() {}         // and doesn't take any value
 
-#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in cfg()
-                                 // and because no value checking was enable for "has_feathers"
-                                 // no warning is emitted for the value "zapping"
-fn do_zapping() {}
+#[cfg(has_feathers)]        // This condition is expected, as 'has_feathers' was provided in --check-cfg
+fn do_features() {}         // and doesn't take any value
 
-#[cfg(has_mumble_frotz)]    // This is UNEXPECTED because names checking is enable and
-                            // "has_mumble_frotz" was not provided in cfg()
+#[cfg(has_mumble_frotz)]    // This condition is UNEXPECTED, as 'has_mumble_frotz' was NEVER provided
+                            // in any --check-cfg arguments
 fn do_mumble_frotz() {}
-```
-
-### Example: Checking feature values, but not condition names
 
-```bash
-# This turns on checking for feature values, but not for condition names.
-rustc --check-cfg 'cfg(feature, values("zapping", "lasers"))' \
-      --check-cfg 'cfg(any())' \
-      --cfg 'feature="zapping"' -Z unstable-options
-```
-
-```rust
-#[cfg(is_embedded)]         // This is doesn't raise a warning, because names checking was
-                            // disabled by 'cfg(any())'
-fn do_embedded() {}
-
-#[cfg(has_feathers)]        // Same as above, 'cfg(any())' was provided so no name
-                            // checking is performed
-fn do_features() {}
-
-#[cfg(feature = "lasers")]  // This is expected, "lasers" is in the cfg(feature) list
+#[cfg(feature = "lasers")]  // This condition is expected, as "lasers" is an expected value of `feature`
 fn shoot_lasers() {}
 
-#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the
-                            // cfg(feature) list
+#[cfg(feature = "monkeys")] // This condition is UNEXPECTED, as "monkeys" is NOT an expected value of
+                            // `feature`
 fn write_shakespeare() {}
 ```
 
-### Example: Checking both condition names and feature values
+### Example: Condition names without values
 
 ```bash
-# This turns on checking for feature values and for condition names.
-rustc --check-cfg 'cfg(is_embedded, has_feathers)' \
-      --check-cfg 'cfg(feature, values("zapping", "lasers"))' \
-      --cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
+rustc --check-cfg 'cfg(is_embedded, has_feathers, values(any()))' \
+      --cfg has_feathers -Z unstable-options
 ```
 
 ```rust
-#[cfg(is_embedded)]         // This is expected because "is_embedded" was provided in cfg()
-fn do_embedded() {}         // and doesn't take any value
-
-#[cfg(has_feathers)]        // This is expected because "has_feathers" was provided in cfg()
-fn do_features() {}         // and deosn't take any value
+#[cfg(is_embedded)]      // This condition is expected, as 'is_embedded' was provided in --check-cfg
+                         // as condition name
+fn do_embedded() {}
 
-#[cfg(has_mumble_frotz)]    // This is UNEXPECTED, because "has_mumble_frotz" was never provided
-fn do_mumble_frotz() {}
+#[cfg(has_feathers)]     // This condition is expected, as "has_feathers" was provided in --check-cfg
+                         // as condition name
+fn do_features() {}
 
-#[cfg(feature = "lasers")]  // This is expected, "lasers" is in the cfg(feature) list
-fn shoot_lasers() {}
+#[cfg(has_feathers = "zapping")] // This condition is expected, as "has_feathers" was provided in
+                                 // and because *any* values is expected for 'has_feathers' no
+                                 // warning is emitted for the value "zapping"
+fn do_zapping() {}
 
-#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in
-                            // the cfg(feature) list
-fn write_shakespeare() {}
+#[cfg(has_mumble_frotz)] // This condition is UNEXPECTED, as 'has_mumble_frotz' was not provided
+                         // in any --check-cfg arguments
+fn do_mumble_frotz() {}
 ```