about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-09 14:50:14 +0000
committerbors <bors@rust-lang.org>2024-01-09 14:50:14 +0000
commit5876c8cdfd3df742c334d6447d44d760c77103b6 (patch)
treeb2437656721ba0a5f4068566975525a9bab87974 /compiler
parentbe00c5a9b89161b7f45ba80340f709e8e41122f9 (diff)
parentf41d7739880e72743a74722ae6fcc1be9f7b4e5c (diff)
downloadrust-5876c8cdfd3df742c334d6447d44d760c77103b6.tar.gz
rust-5876c8cdfd3df742c334d6447d44d760c77103b6.zip
Auto merge of #119767 - GuillaumeGomez:rollup-fbp26yb, r=GuillaumeGomez
Rollup of 9 pull requests

Successful merges:

 - #117556 (Disallow reference to `static mut` and adding `static_mut_ref` lint)
 - #118748 (std: getrandom simplification for freebsd.)
 - #119282 (Rework and improve the unstable documentation of check-cfg)
 - #119527 (don't reexport atomic::ordering via rustc_data_structures, use std import)
 - #119668 (Simplify implementation of MIR promotion)
 - #119699 (Merge dead bb pruning and unreachable bb deduplication.)
 - #119723 (Remove `-Zdont-buffer-diagnostics`.)
 - #119756 (rustdoc-search: reuse individual types in function signatures)
 - #119758 (GNU/Hurd: unconditionally use inline stack probes)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs3
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs3
-rw-r--r--compiler/rustc_const_eval/src/transform/mod.rs1
-rw-r--r--compiler/rustc_data_structures/src/sync.rs7
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs3
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs1
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0796.md22
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs3
-rw-r--r--compiler/rustc_errors/src/lib.rs3
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml1
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl14
-rw-r--r--compiler/rustc_hir_analysis/src/check/errs.rs97
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs91
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs52
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs1
-rw-r--r--compiler/rustc_mir_transform/src/const_goto.rs2
-rw-r--r--compiler/rustc_mir_transform/src/deduplicate_blocks.rs2
-rw-r--r--compiler/rustc_mir_transform/src/early_otherwise_branch.rs2
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs5
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs3
-rw-r--r--compiler/rustc_mir_transform/src/match_branches.rs2
-rw-r--r--compiler/rustc_mir_transform/src/promote_consts.rs (renamed from compiler/rustc_const_eval/src/transform/promote_consts.rs)397
-rw-r--r--compiler/rustc_mir_transform/src/remove_unneeded_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/separate_const_switch.rs4
-rw-r--r--compiler/rustc_mir_transform/src/simplify.rs92
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs2
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs14
-rw-r--r--compiler/rustc_session/src/config.rs1
-rw-r--r--compiler/rustc_session/src/options.rs3
-rw-r--r--compiler/rustc_session/src/session.rs6
-rw-r--r--compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs2
34 files changed, 517 insertions, 332 deletions
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 a1cdf31c68a..2a7b1107ffc 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 40a1ad22c0e..9827e299f2a 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/compiler/rustc_const_eval/src/transform/mod.rs b/compiler/rustc_const_eval/src/transform/mod.rs
index a2928bdf51b..e3582c7d317 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_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 43221d70e21..48edfba8da0 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 da3776ef96a..3047f5d47e4 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_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 1028d43f9c5..a1391cceb71 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 00000000000..cea18f8db85
--- /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_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index e72791d89a2..3789cdaf354 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 b97ec02675a..76b7e0d79a9 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_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index b671bebeb05..b5ebc1fab76 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 d8b6b9a1272..6a17668ad17 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 00000000000..27bb2c57a5c
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/errs.rs
@@ -0,0 +1,97 @@
+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,
+        );
+    }
+}
+
+/// 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
+        && 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 f60d6950670..ac0c715c6b3 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/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 542e69a6c34..5d5a4789734 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, maybe_stmt_static_mut};
+
 use std::mem;
 
 #[derive(Debug, Copy, Clone)]
@@ -224,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
@@ -242,6 +246,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);
 
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 9124d502110..4f22da4ba3b 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_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index f3f59b05682..444e3700cf7 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_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index c74d4bb651c..e917e7cb02b 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -88,6 +88,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`,
     /// `self`, or an extern crate name
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index d426f6d8969..36f5ba161d5 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 3884346076e..cb5b66b314d 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 b40b2ec8bfd..824974970bb 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 6eb6cb069fe..0d600f0f937 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 8ad804bf3e7..ebefc3b47cc 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/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 2c1602dadc1..43c5b3873b9 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)]
@@ -94,6 +95,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;
@@ -115,7 +117,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_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 1c4aa37d57f..6d4332793af 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_const_eval/src/transform/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 155cf4ff9e2..841b86fed45 100644
--- a/compiler/rustc_const_eval/src/transform/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,10 +23,11 @@ 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};
 
-use crate::transform::check_consts::{qualifs, ConstCx};
+use rustc_const_eval::transform::check_consts::{qualifs, ConstCx};
 
 /// A `MirPass` for promotion.
 ///
@@ -64,7 +66,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 +80,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,
 }
 
@@ -123,46 +118,43 @@ 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 | TempState::PromotedOut => 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 });
         }
     }
 }
 
-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 {
@@ -196,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> {
@@ -676,7 +603,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 +857,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>,
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index 5d528bed356..2778d91e17b 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 6e22690d8da..7120ef72142 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 856a0f22771..8c8818bd68e 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/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 7e0fbf3d76c..a827717d9bb 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 9b06823dfba..1f09de0ed70 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/config.rs b/compiler/rustc_session/src/config.rs
index 12d8293ecd2..61796d7a6ca 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 9c1c1c491b9..772c6df2e2d 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],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 8491d9b4abc..720599f6095 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`.
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 9102673ef77..6884e078c27 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(),