about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-20 02:58:08 +0000
committerbors <bors@rust-lang.org>2024-01-20 02:58:08 +0000
commit128148d4cf742c3056e9bfc65e7cc9531cb0f815 (patch)
tree0eedeffb6a188f3dd18f99d070ba66e814779213
parent0547c41f906760ce117a55ca690820b44d8e7eef (diff)
parentee126973b0075060d9d95e401533181c5e8c5864 (diff)
Auto merge of #120136 - matthiaskrgr:rollup-3zzb0z9, r=matthiaskrgr
Rollup of 9 pull requests

Successful merges:

 - #117561 (Stabilize `slice_first_last_chunk`)
 - #117662 ([rustdoc] Allows links in headings)
 - #119815 (Format sources into the error message when loading codegen backends)
 - #119835 (Exhaustiveness: simplify empty pattern logic)
 - #119984 (Change return type of unstable `Waker::noop()` from `Waker` to `&Waker`.)
 - #120009 (never_patterns: typecheck never patterns)
 - #120122 (Don't add needs-triage to A-diagnostics)
 - #120126 (Suggest `.swap()` when encountering conflicting borrows from `mem::swap` on a slice)
 - #120134 (Restrict access to the private field of newtype indexes)

Failed merges:

 - #119968 (Remove unused/unnecessary features)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock16
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs97
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock6
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml2
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs3
-rw-r--r--compiler/rustc_index_macros/src/newtype.rs10
-rw-r--r--compiler/rustc_interface/Cargo.toml2
-rw-r--r--compiler/rustc_interface/src/lib.rs3
-rw-r--r--compiler/rustc_interface/src/util.rs12
-rw-r--r--compiler/rustc_metadata/Cargo.toml2
-rw-r--r--compiler/rustc_mir_build/messages.ftl5
-rw-r--r--compiler/rustc_mir_build/src/errors.rs10
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs19
-rw-r--r--compiler/rustc_pattern_analysis/src/constructor.rs19
-rw-r--r--compiler/rustc_pattern_analysis/src/lints.rs2
-rw-r--r--compiler/rustc_pattern_analysis/src/usefulness.rs57
-rw-r--r--compiler/rustc_serialize/src/lib.rs1
-rw-r--r--compiler/rustc_type_ir/src/lib.rs6
-rw-r--r--library/core/src/array/mod.rs8
-rw-r--r--library/core/src/slice/mod.rs290
-rw-r--r--library/core/src/task/wake.rs17
-rw-r--r--library/core/tests/async_iter/mod.rs3
-rw-r--r--library/core/tests/slice.rs52
-rw-r--r--src/librustdoc/html/markdown.rs9
-rw-r--r--src/librustdoc/html/markdown/tests.rs60
-rw-r--r--src/librustdoc/html/render/mod.rs20
-rw-r--r--src/librustdoc/html/render/print_item.rs112
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css21
-rw-r--r--src/tools/miri/tests/pass/async-fn.rs3
-rw-r--r--src/tools/miri/tests/pass/dyn-star.rs3
-rw-r--r--src/tools/miri/tests/pass/future-self-referential.rs6
-rw-r--r--src/tools/miri/tests/pass/issues/issue-miri-2068.rs3
-rw-r--r--src/tools/miri/tests/pass/move-data-across-await-point.rs3
-rw-r--r--tests/coverage/async.coverage3
-rw-r--r--tests/coverage/async.rs3
-rw-r--r--tests/coverage/async2.coverage3
-rw-r--r--tests/coverage/async2.rs3
-rw-r--r--tests/coverage/async_block.coverage3
-rw-r--r--tests/coverage/async_block.rs3
-rw-r--r--tests/coverage/closure_macro_async.coverage3
-rw-r--r--tests/coverage/closure_macro_async.rs3
-rw-r--r--tests/rustdoc-gui/docblock-details.goml2
-rw-r--r--tests/rustdoc-gui/headers-color.goml8
-rw-r--r--tests/rustdoc-gui/headings-anchor.goml32
-rw-r--r--tests/rustdoc/disambiguate-anchors-header-29449.rs15
-rw-r--r--tests/rustdoc/links-in-headings.rs14
-rw-r--r--tests/rustdoc/remove-url-from-headings.rs11
-rw-r--r--tests/rustdoc/short-docblock.rs6
-rw-r--r--tests/ui/async-await/for-await-passthrough.rs3
-rw-r--r--tests/ui/async-await/for-await.rs3
-rw-r--r--tests/ui/async-await/in-trait/async-default-fn-overridden.rs3
-rw-r--r--tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs3
-rw-r--r--tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr2
-rw-r--r--tests/ui/coroutine/async-gen-yield-ty-is-unit.rs3
-rw-r--r--tests/ui/coroutine/async_gen_fn_iter.rs3
-rw-r--r--tests/ui/dyn-star/dispatch-on-pin-mut.rs5
-rw-r--r--tests/ui/pattern/never_patterns.rs73
-rw-r--r--tests/ui/pattern/never_patterns.stderr17
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr66
-rw-r--r--tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs125
-rw-r--r--tests/ui/suggestions/suggest-slice-swap.fixed9
-rw-r--r--tests/ui/suggestions/suggest-slice-swap.rs9
-rw-r--r--tests/ui/suggestions/suggest-slice-swap.stderr17
-rw-r--r--triagebot.toml3
64 files changed, 745 insertions, 593 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f5ce875ccd4..182ac07c7e8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2189,16 +2189,6 @@ dependencies = [
 
 [[package]]
 name = "libloading"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
-dependencies = [
- "cfg-if",
- "winapi",
-]
-
-[[package]]
-name = "libloading"
 version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
@@ -2479,7 +2469,7 @@ dependencies = [
  "lazy_static",
  "libc",
  "libffi",
- "libloading 0.8.1",
+ "libloading",
  "log",
  "measureme",
  "rand",
@@ -4005,7 +3995,7 @@ dependencies = [
 name = "rustc_interface"
 version = "0.0.0"
 dependencies = [
- "libloading 0.7.4",
+ "libloading",
  "rustc-rayon",
  "rustc-rayon-core",
  "rustc_ast",
@@ -4135,7 +4125,7 @@ name = "rustc_metadata"
 version = "0.0.0"
 dependencies = [
  "bitflags 2.4.1",
- "libloading 0.7.4",
+ "libloading",
  "odht",
  "rustc_ast",
  "rustc_attr",
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 8909f47af2d..da67862a48d 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -26,6 +26,7 @@ use rustc_span::hygiene::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
 use rustc_trait_selection::traits::ObligationCtxt;
 use std::iter;
 
@@ -1304,14 +1305,96 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         place: Place<'tcx>,
         borrowed_place: Place<'tcx>,
     ) {
-        if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
-            (&place.projection[..], &borrowed_place.projection[..])
+        let tcx = self.infcx.tcx;
+        let hir = tcx.hir();
+
+        if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)])
+        | (
+            [ProjectionElem::Deref, ProjectionElem::Index(index1)],
+            [ProjectionElem::Deref, ProjectionElem::Index(index2)],
+        ) = (&place.projection[..], &borrowed_place.projection[..])
         {
-            err.help(
-                "consider using `.split_at_mut(position)` or similar method to obtain \
-                     two mutable non-overlapping sub-slices",
-            )
-            .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
+            let mut note_default_suggestion = || {
+                err.help(
+                    "consider using `.split_at_mut(position)` or similar method to obtain \
+                         two mutable non-overlapping sub-slices",
+                )
+                .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
+            };
+
+            let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else {
+                note_default_suggestion();
+                return;
+            };
+
+            let mut expr_finder =
+                FindExprBySpan::new(self.body.local_decls[*index1].source_info.span);
+            expr_finder.visit_expr(hir.body(body_id).value);
+            let Some(index1) = expr_finder.result else {
+                note_default_suggestion();
+                return;
+            };
+
+            expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span);
+            expr_finder.visit_expr(hir.body(body_id).value);
+            let Some(index2) = expr_finder.result else {
+                note_default_suggestion();
+                return;
+            };
+
+            let sm = tcx.sess.source_map();
+
+            let Ok(index1_str) = sm.span_to_snippet(index1.span) else {
+                note_default_suggestion();
+                return;
+            };
+
+            let Ok(index2_str) = sm.span_to_snippet(index2.span) else {
+                note_default_suggestion();
+                return;
+            };
+
+            let Some(object) = hir.parent_id_iter(index1.hir_id).find_map(|id| {
+                if let hir::Node::Expr(expr) = tcx.hir_node(id)
+                    && let hir::ExprKind::Index(obj, ..) = expr.kind
+                {
+                    Some(obj)
+                } else {
+                    None
+                }
+            }) else {
+                note_default_suggestion();
+                return;
+            };
+
+            let Ok(obj_str) = sm.span_to_snippet(object.span) else {
+                note_default_suggestion();
+                return;
+            };
+
+            let Some(swap_call) = hir.parent_id_iter(object.hir_id).find_map(|id| {
+                if let hir::Node::Expr(call) = tcx.hir_node(id)
+                    && let hir::ExprKind::Call(callee, ..) = call.kind
+                    && let hir::ExprKind::Path(qpath) = callee.kind
+                    && let hir::QPath::Resolved(None, res) = qpath
+                    && let hir::def::Res::Def(_, did) = res.res
+                    && tcx.is_diagnostic_item(sym::mem_swap, did)
+                {
+                    Some(call)
+                } else {
+                    None
+                }
+            }) else {
+                note_default_suggestion();
+                return;
+            };
+
+            err.span_suggestion(
+                swap_call.span,
+                "use `.swap()` to swap elements at the specified indices instead",
+                format!("{obj_str}.swap({index1_str}, {index2_str})"),
+                Applicability::MachineApplicable,
+            );
         }
     }
 
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index 74e7afee7bc..6d6a1200f50 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -246,12 +246,12 @@ checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 
 [[package]]
 name = "libloading"
-version = "0.7.4"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
 dependencies = [
  "cfg-if",
- "winapi",
+ "windows-sys",
 ]
 
 [[package]]
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index fdac789423c..c57e964168f 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.toml
+++ b/compiler/rustc_codegen_cranelift/Cargo.toml
@@ -19,7 +19,7 @@ gimli = { version = "0.28", default-features = false, features = ["write"]}
 object = { version = "0.32", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
 
 indexmap = "2.0.0"
-libloading = { version = "0.7.3", optional = true }
+libloading = { version = "0.8.0", optional = true }
 smallvec = "1.8.1"
 
 [patch.crates-io]
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index fe8c36dbe06..2060e01e1d6 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -178,8 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let ty = match pat.kind {
             PatKind::Wild | PatKind::Err(_) => expected,
-            // FIXME(never_patterns): check the type is uninhabited. If that is not possible within
-            // typeck, do that in a later phase.
+            // We allow any type here; we ensure that the type is uninhabited during match checking.
             PatKind::Never => expected,
             PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
             PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),
diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs
index ede8416125d..0b25628b9e1 100644
--- a/compiler/rustc_index_macros/src/newtype.rs
+++ b/compiler/rustc_index_macros/src/newtype.rs
@@ -104,7 +104,7 @@ impl Parse for Newtype {
                 #gate_rustc_only
                 impl<E: ::rustc_serialize::Encoder> ::rustc_serialize::Encodable<E> for #name {
                     fn encode(&self, e: &mut E) {
-                        e.emit_u32(self.private);
+                        e.emit_u32(self.as_u32());
                     }
                 }
             }
@@ -164,7 +164,7 @@ impl Parse for Newtype {
                         #[inline]
                         fn eq(l: &Option<Self>, r: &Option<Self>) -> bool {
                             if #max_val < u32::MAX {
-                                l.map(|i| i.private).unwrap_or(#max_val+1) == r.map(|i| i.private).unwrap_or(#max_val+1)
+                                l.map(|i| i.as_u32()).unwrap_or(#max_val+1) == r.map(|i| i.as_u32()).unwrap_or(#max_val+1)
                             } else {
                                 match (l, r) {
                                     (Some(l), Some(r)) => r == l,
@@ -188,7 +188,7 @@ impl Parse for Newtype {
             #[cfg_attr(#gate_rustc_only_cfg, rustc_layout_scalar_valid_range_end(#max))]
             #[cfg_attr(#gate_rustc_only_cfg, rustc_pass_by_value)]
             #vis struct #name {
-                private: u32,
+                private_use_as_methods_instead: u32,
             }
 
             #(#consts)*
@@ -238,7 +238,7 @@ impl Parse for Newtype {
                 /// Prefer using `from_u32`.
                 #[inline]
                 #vis const unsafe fn from_u32_unchecked(value: u32) -> Self {
-                    Self { private: value }
+                    Self { private_use_as_methods_instead: value }
                 }
 
                 /// Extracts the value of this index as a `usize`.
@@ -250,7 +250,7 @@ impl Parse for Newtype {
                 /// Extracts the value of this index as a `u32`.
                 #[inline]
                 #vis const fn as_u32(self) -> u32 {
-                    self.private
+                    self.private_use_as_methods_instead
                 }
 
                 /// Extracts the value of this index as a `usize`.
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index 319e8175809..a238eacda44 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-libloading = "0.7.1"
+libloading = "0.8.0"
 rustc-rayon = { version = "0.5.0", optional = true }
 rustc-rayon-core = { version = "0.5.0", optional = true }
 rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index cfa46447845..764306ce6ec 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -1,9 +1,10 @@
 #![feature(box_patterns)]
 #![feature(decl_macro)]
+#![feature(error_iter)]
 #![feature(internal_output_capture)]
-#![feature(thread_spawn_unchecked)]
 #![feature(lazy_cell)]
 #![feature(let_chains)]
+#![feature(thread_spawn_unchecked)]
 #![feature(try_blocks)]
 #![recursion_limit = "256"]
 #![deny(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 9fd44e46b31..76b9e8de75f 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -162,15 +162,21 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
 }
 
 fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn {
+    fn format_err(e: &(dyn std::error::Error + 'static)) -> String {
+        e.sources().map(|e| format!(": {e}")).collect()
+    }
     let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
-        let err = format!("couldn't load codegen backend {path:?}: {err}");
+        let err = format!("couldn't load codegen backend {path:?}{}", format_err(&err));
         early_dcx.early_fatal(err);
     });
 
     let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
         .unwrap_or_else(|e| {
-            let err = format!("couldn't load codegen backend: {e}");
-            early_dcx.early_fatal(err);
+            let e = format!(
+                "`__rustc_codegen_backend` symbol lookup in the codegen backend failed{}",
+                format_err(&e)
+            );
+            early_dcx.early_fatal(e);
         });
 
     // Intentionally leak the dynamic library. We can't ever unload it
diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml
index 08cc8173eb0..79d3482472a 100644
--- a/compiler/rustc_metadata/Cargo.toml
+++ b/compiler/rustc_metadata/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
-libloading = "0.7.1"
+libloading = "0.8.0"
 odht = { version = "0.3.1", features = ["nightly"] }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 615b553434f..2f11cb123ee 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -234,6 +234,11 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
 
 mir_build_non_const_path = runtime values cannot be referenced in patterns
 
+mir_build_non_empty_never_pattern =
+    mismatched types
+    .label = a never pattern must be used on an uninhabited type
+    .note = the matched value is of type `{$ty}`
+
 mir_build_non_exhaustive_match_all_arms_guarded =
     match arms with guards don't count towards exhaustivity
 
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 61ad99acf38..e3cc21cef11 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -788,6 +788,16 @@ pub struct FloatPattern;
 #[diag(mir_build_pointer_pattern)]
 pub struct PointerPattern;
 
+#[derive(Diagnostic)]
+#[diag(mir_build_non_empty_never_pattern)]
+#[note]
+pub struct NonEmptyNeverPattern<'tcx> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub ty: Ty<'tcx>,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(mir_build_indirect_structural_match)]
 #[note(mir_build_type_not_structural_tip)]
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index f6c5e4a5cd6..e8ba83a5527 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -276,10 +276,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
         } else {
             // Check the pattern for some things unrelated to exhaustiveness.
             let refutable = if cx.refutable { Refutable } else { Irrefutable };
+            let mut err = Ok(());
             pat.walk_always(|pat| {
                 check_borrow_conflicts_in_at_patterns(self, pat);
                 check_for_bindings_named_same_as_variants(self, pat, refutable);
+                err = err.and(check_never_pattern(cx, pat));
             });
+            err?;
             Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
         }
     }
@@ -289,7 +292,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
     fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
         use ExprKind::*;
         match &scrutinee.kind {
-            // Both pointers and references can validly point to a place with invalid data.
+            // Pointers can validly point to a place with invalid data. It is undecided whether
+            // references can too, so we conservatively assume they can.
             Deref { .. } => false,
             // Inherit validity of the parent place, unless the parent is an union.
             Field { lhs, .. } => {
@@ -811,6 +815,19 @@ fn check_for_bindings_named_same_as_variants(
     }
 }
 
+/// Check that never patterns are only used on inhabited types.
+fn check_never_pattern<'tcx>(
+    cx: &MatchCheckCtxt<'_, 'tcx>,
+    pat: &Pat<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+    if let PatKind::Never = pat.kind {
+        if !cx.is_uninhabited(pat.ty) {
+            return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
+        }
+    }
+    Ok(())
+}
+
 fn report_irrefutable_let_patterns(
     tcx: TyCtxt<'_>,
     id: HirId,
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index 76098505b79..eba71a23435 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -861,12 +861,14 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
     /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges
     /// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation
     /// and its invariants.
-    #[instrument(level = "debug", skip(self, pcx, ctors), ret)]
+    #[instrument(level = "debug", skip(self, ctors), ret)]
     pub(crate) fn split<'a>(
         &self,
-        pcx: &PlaceCtxt<'a, Cx>,
         ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
-    ) -> SplitConstructorSet<Cx> {
+    ) -> SplitConstructorSet<Cx>
+    where
+        Cx: 'a,
+    {
         let mut present: SmallVec<[_; 1]> = SmallVec::new();
         // Empty constructors found missing.
         let mut missing_empty = Vec::new();
@@ -1006,17 +1008,6 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
             }
         }
 
-        // We have now grouped all the constructors into 3 buckets: present, missing, missing_empty.
-        // In the absence of the `exhaustive_patterns` feature however, we don't count nested empty
-        // types as empty. Only non-nested `!` or `enum Foo {}` are considered empty.
-        if !pcx.mcx.tycx.is_exhaustive_patterns_feature_on()
-            && !(pcx.is_scrutinee && matches!(self, Self::NoConstructors))
-        {
-            // Treat all missing constructors as nonempty.
-            // This clears `missing_empty`.
-            missing.append(&mut missing_empty);
-        }
-
         SplitConstructorSet { present, missing, missing_empty }
     }
 }
diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs
index 4266e2a405e..d9dbd8250ef 100644
--- a/compiler/rustc_pattern_analysis/src/lints.rs
+++ b/compiler/rustc_pattern_analysis/src/lints.rs
@@ -56,7 +56,7 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
     ) -> Result<SplitConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
         let column_ctors = self.patterns.iter().map(|p| p.ctor());
         let ctors_for_ty = &pcx.ctors_for_ty()?;
-        Ok(ctors_for_ty.split(pcx, column_ctors))
+        Ok(ctors_for_ty.split(column_ctors))
     }
 
     /// Does specialization: given a constructor, this takes the patterns from the column that match
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index c7894994213..dac354a1c52 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -737,15 +737,13 @@ pub(crate) struct PlaceCtxt<'a, Cx: TypeCx> {
     pub(crate) mcx: MatchCtxt<'a, Cx>,
     /// Type of the place under investigation.
     pub(crate) ty: Cx::Ty,
-    /// Whether the place is the original scrutinee place, as opposed to a subplace of it.
-    pub(crate) is_scrutinee: bool,
 }
 
 impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> {
     /// A `PlaceCtxt` when code other than `is_useful` needs one.
     #[cfg_attr(not(feature = "rustc"), allow(dead_code))]
     pub(crate) fn new_dummy(mcx: MatchCtxt<'a, Cx>, ty: Cx::Ty) -> Self {
-        PlaceCtxt { mcx, ty, is_scrutinee: false }
+        PlaceCtxt { mcx, ty }
     }
 
     pub(crate) fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize {
@@ -768,9 +766,6 @@ impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> {
 pub enum ValidityConstraint {
     ValidOnly,
     MaybeInvalid,
-    /// Option for backwards compatibility: the place is not known to be valid but we allow omitting
-    /// `useful && !reachable` arms anyway.
-    MaybeInvalidButAllowOmittingArms,
 }
 
 impl ValidityConstraint {
@@ -778,20 +773,9 @@ impl ValidityConstraint {
         if is_valid_only { ValidOnly } else { MaybeInvalid }
     }
 
-    fn allow_omitting_side_effecting_arms(self) -> Self {
-        match self {
-            MaybeInvalid | MaybeInvalidButAllowOmittingArms => MaybeInvalidButAllowOmittingArms,
-            // There are no side-effecting empty arms here, nothing to do.
-            ValidOnly => ValidOnly,
-        }
-    }
-
     fn is_known_valid(self) -> bool {
         matches!(self, ValidOnly)
     }
-    fn allows_omitting_empty_arms(self) -> bool {
-        matches!(self, ValidOnly | MaybeInvalidButAllowOmittingArms)
-    }
 
     /// If the place has validity given by `self` and we read that the value at the place has
     /// constructor `ctor`, this computes what we can assume about the validity of the constructor
@@ -814,7 +798,7 @@ impl fmt::Display for ValidityConstraint {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let s = match self {
             ValidOnly => "✓",
-            MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?",
+            MaybeInvalid => "?",
         };
         write!(f, "{s}")
     }
@@ -1447,41 +1431,44 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
     };
 
     debug!("ty: {ty:?}");
-    let pcx = &PlaceCtxt { mcx, ty, is_scrutinee: is_top_level };
+    let pcx = &PlaceCtxt { mcx, ty };
+    let ctors_for_ty = pcx.ctors_for_ty()?;
 
     // Whether the place/column we are inspecting is known to contain valid data.
     let place_validity = matrix.place_validity[0];
-    // For backwards compability we allow omitting some empty arms that we ideally shouldn't.
-    let place_validity = place_validity.allow_omitting_side_effecting_arms();
+    // We treat match scrutinees of type `!` or `EmptyEnum` differently.
+    let is_toplevel_exception =
+        is_top_level && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
+    // Whether empty patterns can be omitted for exhaustiveness.
+    let can_omit_empty_arms = is_toplevel_exception || mcx.tycx.is_exhaustive_patterns_feature_on();
+    // Whether empty patterns are counted as useful or not.
+    let empty_arms_are_unreachable = place_validity.is_known_valid() && can_omit_empty_arms;
 
     // Analyze the constructors present in this column.
     let ctors = matrix.heads().map(|p| p.ctor());
-    let ctors_for_ty = pcx.ctors_for_ty()?;
-    let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics.
-    let split_set = ctors_for_ty.split(pcx, ctors);
+    let mut split_set = ctors_for_ty.split(ctors);
     let all_missing = split_set.present.is_empty();
-
     // Build the set of constructors we will specialize with. It must cover the whole type.
+    // We need to iterate over a full set of constructors, so we add `Missing` to represent the
+    // missing ones. This is explained under "Constructor Splitting" at the top of this file.
     let mut split_ctors = split_set.present;
-    if !split_set.missing.is_empty() {
-        // We need to iterate over a full set of constructors, so we add `Missing` to represent the
-        // missing ones. This is explained under "Constructor Splitting" at the top of this file.
-        split_ctors.push(Constructor::Missing);
-    } else if !split_set.missing_empty.is_empty() && !place_validity.is_known_valid() {
-        // The missing empty constructors are reachable if the place can contain invalid data.
+    if !(split_set.missing.is_empty()
+        && (split_set.missing_empty.is_empty() || empty_arms_are_unreachable))
+    {
         split_ctors.push(Constructor::Missing);
     }
 
     // Decide what constructors to report.
+    let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. });
     let always_report_all = is_top_level && !is_integers;
     // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing".
     let report_individual_missing_ctors = always_report_all || !all_missing;
     // Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() =>
-    // split_ctors.contains(Missing)`. The converse usually holds except in the
-    // `MaybeInvalidButAllowOmittingArms` backwards-compatibility case.
+    // split_ctors.contains(Missing)`. The converse usually holds except when
+    // `!place_validity.is_known_valid()`.
     let mut missing_ctors = split_set.missing;
-    if !place_validity.allows_omitting_empty_arms() {
-        missing_ctors.extend(split_set.missing_empty);
+    if !can_omit_empty_arms {
+        missing_ctors.append(&mut split_set.missing_empty);
     }
 
     let mut ret = WitnessMatrix::empty();
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index 35275821453..48bee4cd2f0 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -16,7 +16,6 @@
 #![feature(min_specialization)]
 #![feature(never_type)]
 #![feature(ptr_sub_ptr)]
-#![feature(slice_first_last_chunk)]
 #![cfg_attr(test, feature(test))]
 #![allow(rustc::internal)]
 #![deny(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index bff93859645..83efbfb855f 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -327,21 +327,21 @@ impl UniverseIndex {
     /// name the region `'a`, but that region was not nameable from
     /// `U` because it was not in scope there.
     pub fn next_universe(self) -> UniverseIndex {
-        UniverseIndex::from_u32(self.private.checked_add(1).unwrap())
+        UniverseIndex::from_u32(self.as_u32().checked_add(1).unwrap())
     }
 
     /// Returns `true` if `self` can name a name from `other` -- in other words,
     /// if the set of names in `self` is a superset of those in
     /// `other` (`self >= other`).
     pub fn can_name(self, other: UniverseIndex) -> bool {
-        self.private >= other.private
+        self >= other
     }
 
     /// Returns `true` if `self` cannot name some names from `other` -- in other
     /// words, if the set of names in `self` is a strict subset of
     /// those in `other` (`self < other`).
     pub fn cannot_name(self, other: UniverseIndex) -> bool {
-        self.private < other.private
+        self < other
     }
 }
 
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index 34213637a32..9d95b32409c 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -647,7 +647,7 @@ impl<T, const N: usize> [T; N] {
     )]
     #[inline]
     pub fn split_array_ref<const M: usize>(&self) -> (&[T; M], &[T]) {
-        (&self[..]).split_array_ref::<M>()
+        (&self[..]).split_first_chunk::<M>().unwrap()
     }
 
     /// Divides one mutable array reference into two at an index.
@@ -680,7 +680,7 @@ impl<T, const N: usize> [T; N] {
     )]
     #[inline]
     pub fn split_array_mut<const M: usize>(&mut self) -> (&mut [T; M], &mut [T]) {
-        (&mut self[..]).split_array_mut::<M>()
+        (&mut self[..]).split_first_chunk_mut::<M>().unwrap()
     }
 
     /// Divides one array reference into two at an index from the end.
@@ -725,7 +725,7 @@ impl<T, const N: usize> [T; N] {
     )]
     #[inline]
     pub fn rsplit_array_ref<const M: usize>(&self) -> (&[T], &[T; M]) {
-        (&self[..]).rsplit_array_ref::<M>()
+        (&self[..]).split_last_chunk::<M>().unwrap()
     }
 
     /// Divides one mutable array reference into two at an index from the end.
@@ -758,7 +758,7 @@ impl<T, const N: usize> [T; N] {
     )]
     #[inline]
     pub fn rsplit_array_mut<const M: usize>(&mut self) -> (&mut [T], &mut [T; M]) {
-        (&mut self[..]).rsplit_array_mut::<M>()
+        (&mut self[..]).split_last_chunk_mut::<M>().unwrap()
     }
 }
 
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 5edc89e4cb5..ee36d93576b 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -296,7 +296,7 @@ impl<T> [T] {
         if let [.., last] = self { Some(last) } else { None }
     }
 
-    /// Returns a mutable pointer to the last item in the slice.
+    /// Returns a mutable reference to the last item in the slice.
     ///
     /// # Examples
     ///
@@ -316,13 +316,13 @@ impl<T> [T] {
         if let [.., last] = self { Some(last) } else { None }
     }
 
-    /// Returns the first `N` elements of the slice, or `None` if it has fewer than `N` elements.
+    /// Return an array reference to the first `N` items in the slice.
+    ///
+    /// If the slice is not at least `N` in length, this will return `None`.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_first_last_chunk)]
-    ///
     /// let u = [10, 40, 30];
     /// assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());
     ///
@@ -332,27 +332,26 @@ impl<T> [T] {
     /// let w: &[i32] = &[];
     /// assert_eq!(Some(&[]), w.first_chunk::<0>());
     /// ```
-    #[unstable(feature = "slice_first_last_chunk", issue = "111774")]
-    #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
     #[inline]
+    #[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
     pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]> {
         if self.len() < N {
             None
         } else {
             // SAFETY: We explicitly check for the correct number of elements,
             //   and do not let the reference outlive the slice.
-            Some(unsafe { &*(self.as_ptr() as *const [T; N]) })
+            Some(unsafe { &*(self.as_ptr().cast::<[T; N]>()) })
         }
     }
 
-    /// Returns a mutable reference to the first `N` elements of the slice,
-    /// or `None` if it has fewer than `N` elements.
+    /// Return a mutable array reference to the first `N` items in the slice.
+    ///
+    /// If the slice is not at least `N` in length, this will return `None`.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_first_last_chunk)]
-    ///
     /// let x = &mut [0, 1, 2];
     ///
     /// if let Some(first) = x.first_chunk_mut::<2>() {
@@ -360,10 +359,12 @@ impl<T> [T] {
     ///     first[1] = 4;
     /// }
     /// assert_eq!(x, &[5, 4, 2]);
+    ///
+    /// assert_eq!(None, x.first_chunk_mut::<4>());
     /// ```
-    #[unstable(feature = "slice_first_last_chunk", issue = "111774")]
-    #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
     #[inline]
+    #[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
     pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
         if self.len() < N {
             None
@@ -371,28 +372,29 @@ impl<T> [T] {
             // SAFETY: We explicitly check for the correct number of elements,
             //   do not let the reference outlive the slice,
             //   and require exclusive access to the entire slice to mutate the chunk.
-            Some(unsafe { &mut *(self.as_mut_ptr() as *mut [T; N]) })
+            Some(unsafe { &mut *(self.as_mut_ptr().cast::<[T; N]>()) })
         }
     }
 
-    /// Returns the first `N` elements of the slice and the remainder,
-    /// or `None` if it has fewer than `N` elements.
+    /// Return an array reference to the first `N` items in the slice and the remaining slice.
+    ///
+    /// If the slice is not at least `N` in length, this will return `None`.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_first_last_chunk)]
-    ///
     /// let x = &[0, 1, 2];
     ///
     /// if let Some((first, elements)) = x.split_first_chunk::<2>() {
     ///     assert_eq!(first, &[0, 1]);
     ///     assert_eq!(elements, &[2]);
     /// }
+    ///
+    /// assert_eq!(None, x.split_first_chunk::<4>());
     /// ```
-    #[unstable(feature = "slice_first_last_chunk", issue = "111774")]
-    #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
     #[inline]
+    #[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
     pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
         if self.len() < N {
             None
@@ -402,18 +404,18 @@ impl<T> [T] {
 
             // SAFETY: We explicitly check for the correct number of elements,
             //   and do not let the references outlive the slice.
-            Some((unsafe { &*(first.as_ptr() as *const [T; N]) }, tail))
+            Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail))
         }
     }
 
-    /// Returns a mutable reference to the first `N` elements of the slice and the remainder,
-    /// or `None` if it has fewer than `N` elements.
+    /// Return a mutable array reference to the first `N` items in the slice and the remaining
+    /// slice.
+    ///
+    /// If the slice is not at least `N` in length, this will return `None`.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_first_last_chunk)]
-    ///
     /// let x = &mut [0, 1, 2];
     ///
     /// if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
@@ -422,10 +424,12 @@ impl<T> [T] {
     ///     elements[0] = 5;
     /// }
     /// assert_eq!(x, &[3, 4, 5]);
+    ///
+    /// assert_eq!(None, x.split_first_chunk_mut::<4>());
     /// ```
-    #[unstable(feature = "slice_first_last_chunk", issue = "111774")]
-    #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
     #[inline]
+    #[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
     pub const fn split_first_chunk_mut<const N: usize>(
         &mut self,
     ) -> Option<(&mut [T; N], &mut [T])> {
@@ -438,29 +442,30 @@ impl<T> [T] {
             // SAFETY: We explicitly check for the correct number of elements,
             //   do not let the reference outlive the slice,
             //   and enforce exclusive mutability of the chunk by the split.
-            Some((unsafe { &mut *(first.as_mut_ptr() as *mut [T; N]) }, tail))
+            Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail))
         }
     }
 
-    /// Returns the last `N` elements of the slice and the remainder,
-    /// or `None` if it has fewer than `N` elements.
+    /// Return an array reference to the last `N` items in the slice and the remaining slice.
+    ///
+    /// If the slice is not at least `N` in length, this will return `None`.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_first_last_chunk)]
-    ///
     /// let x = &[0, 1, 2];
     ///
-    /// if let Some((last, elements)) = x.split_last_chunk::<2>() {
-    ///     assert_eq!(last, &[1, 2]);
+    /// if let Some((elements, last)) = x.split_last_chunk::<2>() {
     ///     assert_eq!(elements, &[0]);
+    ///     assert_eq!(last, &[1, 2]);
     /// }
+    ///
+    /// assert_eq!(None, x.split_last_chunk::<4>());
     /// ```
-    #[unstable(feature = "slice_first_last_chunk", issue = "111774")]
-    #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
     #[inline]
-    pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
+    #[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])> {
         if self.len() < N {
             None
         } else {
@@ -469,32 +474,35 @@ impl<T> [T] {
 
             // SAFETY: We explicitly check for the correct number of elements,
             //   and do not let the references outlive the slice.
-            Some((unsafe { &*(last.as_ptr() as *const [T; N]) }, init))
+            Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) }))
         }
     }
 
-    /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty.
+    /// Return a mutable array reference to the last `N` items in the slice and the remaining
+    /// slice.
+    ///
+    /// If the slice is not at least `N` in length, this will return `None`.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_first_last_chunk)]
-    ///
     /// let x = &mut [0, 1, 2];
     ///
-    /// if let Some((last, elements)) = x.split_last_chunk_mut::<2>() {
+    /// if let Some((elements, last)) = x.split_last_chunk_mut::<2>() {
     ///     last[0] = 3;
     ///     last[1] = 4;
     ///     elements[0] = 5;
     /// }
     /// assert_eq!(x, &[5, 3, 4]);
+    ///
+    /// assert_eq!(None, x.split_last_chunk_mut::<4>());
     /// ```
-    #[unstable(feature = "slice_first_last_chunk", issue = "111774")]
-    #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
     #[inline]
+    #[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
     pub const fn split_last_chunk_mut<const N: usize>(
         &mut self,
-    ) -> Option<(&mut [T; N], &mut [T])> {
+    ) -> Option<(&mut [T], &mut [T; N])> {
         if self.len() < N {
             None
         } else {
@@ -504,17 +512,17 @@ impl<T> [T] {
             // SAFETY: We explicitly check for the correct number of elements,
             //   do not let the reference outlive the slice,
             //   and enforce exclusive mutability of the chunk by the split.
-            Some((unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }, init))
+            Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }))
         }
     }
 
-    /// Returns the last element of the slice, or `None` if it is empty.
+    /// Return an array reference to the last `N` items in the slice.
+    ///
+    /// If the slice is not at least `N` in length, this will return `None`.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_first_last_chunk)]
-    ///
     /// let u = [10, 40, 30];
     /// assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());
     ///
@@ -524,9 +532,9 @@ impl<T> [T] {
     /// let w: &[i32] = &[];
     /// assert_eq!(Some(&[]), w.last_chunk::<0>());
     /// ```
-    #[unstable(feature = "slice_first_last_chunk", issue = "111774")]
-    #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
     #[inline]
+    #[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
     pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> {
         if self.len() < N {
             None
@@ -537,17 +545,17 @@ impl<T> [T] {
 
             // SAFETY: We explicitly check for the correct number of elements,
             //   and do not let the references outlive the slice.
-            Some(unsafe { &*(last.as_ptr() as *const [T; N]) })
+            Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) })
         }
     }
 
-    /// Returns a mutable pointer to the last item in the slice.
+    /// Return a mutable array reference to the last `N` items in the slice.
+    ///
+    /// If the slice is not at least `N` in length, this will return `None`.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(slice_first_last_chunk)]
-    ///
     /// let x = &mut [0, 1, 2];
     ///
     /// if let Some(last) = x.last_chunk_mut::<2>() {
@@ -555,10 +563,12 @@ impl<T> [T] {
     ///     last[1] = 20;
     /// }
     /// assert_eq!(x, &[0, 10, 20]);
+    ///
+    /// assert_eq!(None, x.last_chunk_mut::<4>());
     /// ```
-    #[unstable(feature = "slice_first_last_chunk", issue = "111774")]
-    #[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
     #[inline]
+    #[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
     pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
         if self.len() < N {
             None
@@ -570,7 +580,7 @@ impl<T> [T] {
             // SAFETY: We explicitly check for the correct number of elements,
             //   do not let the reference outlive the slice,
             //   and require exclusive access to the entire slice to mutate the chunk.
-            Some(unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) })
+            Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })
         }
     }
 
@@ -1859,7 +1869,6 @@ impl<T> [T] {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "const_slice_split_at_not_mut", since = "1.71.0")]
-    #[rustc_allow_const_fn_unstable(slice_split_at_unchecked)]
     #[inline]
     #[track_caller]
     #[must_use]
@@ -1946,7 +1955,10 @@ impl<T> [T] {
     /// }
     /// ```
     #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")]
-    #[rustc_const_unstable(feature = "slice_split_at_unchecked", issue = "76014")]
+    #[rustc_const_stable(
+        feature = "const_slice_split_at_unchecked",
+        since = "CURRENT_RUSTC_VERSION"
+    )]
     #[inline]
     #[must_use]
     pub const unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T]) {
@@ -2019,164 +2031,6 @@ impl<T> [T] {
         unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) }
     }
 
-    /// Divides one slice into an array and a remainder slice at an index.
-    ///
-    /// The array will contain all indices from `[0, N)` (excluding
-    /// the index `N` itself) and the slice will contain all
-    /// indices from `[N, len)` (excluding the index `len` itself).
-    ///
-    /// # Panics
-    ///
-    /// Panics if `N > len`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(split_array)]
-    ///
-    /// let v = &[1, 2, 3, 4, 5, 6][..];
-    ///
-    /// {
-    ///    let (left, right) = v.split_array_ref::<0>();
-    ///    assert_eq!(left, &[]);
-    ///    assert_eq!(right, [1, 2, 3, 4, 5, 6]);
-    /// }
-    ///
-    /// {
-    ///     let (left, right) = v.split_array_ref::<2>();
-    ///     assert_eq!(left, &[1, 2]);
-    ///     assert_eq!(right, [3, 4, 5, 6]);
-    /// }
-    ///
-    /// {
-    ///     let (left, right) = v.split_array_ref::<6>();
-    ///     assert_eq!(left, &[1, 2, 3, 4, 5, 6]);
-    ///     assert_eq!(right, []);
-    /// }
-    /// ```
-    #[unstable(feature = "split_array", reason = "new API", issue = "90091")]
-    #[inline]
-    #[track_caller]
-    #[must_use]
-    pub fn split_array_ref<const N: usize>(&self) -> (&[T; N], &[T]) {
-        let (a, b) = self.split_at(N);
-        // SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at)
-        unsafe { (&*(a.as_ptr() as *const [T; N]), b) }
-    }
-
-    /// Divides one mutable slice into an array and a remainder slice at an index.
-    ///
-    /// The array will contain all indices from `[0, N)` (excluding
-    /// the index `N` itself) and the slice will contain all
-    /// indices from `[N, len)` (excluding the index `len` itself).
-    ///
-    /// # Panics
-    ///
-    /// Panics if `N > len`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(split_array)]
-    ///
-    /// let mut v = &mut [1, 0, 3, 0, 5, 6][..];
-    /// let (left, right) = v.split_array_mut::<2>();
-    /// assert_eq!(left, &mut [1, 0]);
-    /// assert_eq!(right, [3, 0, 5, 6]);
-    /// left[1] = 2;
-    /// right[1] = 4;
-    /// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
-    /// ```
-    #[unstable(feature = "split_array", reason = "new API", issue = "90091")]
-    #[inline]
-    #[track_caller]
-    #[must_use]
-    pub fn split_array_mut<const N: usize>(&mut self) -> (&mut [T; N], &mut [T]) {
-        let (a, b) = self.split_at_mut(N);
-        // SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at_mut)
-        unsafe { (&mut *(a.as_mut_ptr() as *mut [T; N]), b) }
-    }
-
-    /// Divides one slice into an array and a remainder slice at an index from
-    /// the end.
-    ///
-    /// The slice will contain all indices from `[0, len - N)` (excluding
-    /// the index `len - N` itself) and the array will contain all
-    /// indices from `[len - N, len)` (excluding the index `len` itself).
-    ///
-    /// # Panics
-    ///
-    /// Panics if `N > len`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(split_array)]
-    ///
-    /// let v = &[1, 2, 3, 4, 5, 6][..];
-    ///
-    /// {
-    ///    let (left, right) = v.rsplit_array_ref::<0>();
-    ///    assert_eq!(left, [1, 2, 3, 4, 5, 6]);
-    ///    assert_eq!(right, &[]);
-    /// }
-    ///
-    /// {
-    ///     let (left, right) = v.rsplit_array_ref::<2>();
-    ///     assert_eq!(left, [1, 2, 3, 4]);
-    ///     assert_eq!(right, &[5, 6]);
-    /// }
-    ///
-    /// {
-    ///     let (left, right) = v.rsplit_array_ref::<6>();
-    ///     assert_eq!(left, []);
-    ///     assert_eq!(right, &[1, 2, 3, 4, 5, 6]);
-    /// }
-    /// ```
-    #[unstable(feature = "split_array", reason = "new API", issue = "90091")]
-    #[inline]
-    #[must_use]
-    pub fn rsplit_array_ref<const N: usize>(&self) -> (&[T], &[T; N]) {
-        assert!(N <= self.len());
-        let (a, b) = self.split_at(self.len() - N);
-        // SAFETY: b points to [T; N]? Yes it's [T] of length N (checked by split_at)
-        unsafe { (a, &*(b.as_ptr() as *const [T; N])) }
-    }
-
-    /// Divides one mutable slice into an array and a remainder slice at an
-    /// index from the end.
-    ///
-    /// The slice will contain all indices from `[0, len - N)` (excluding
-    /// the index `N` itself) and the array will contain all
-    /// indices from `[len - N, len)` (excluding the index `len` itself).
-    ///
-    /// # Panics
-    ///
-    /// Panics if `N > len`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(split_array)]
-    ///
-    /// let mut v = &mut [1, 0, 3, 0, 5, 6][..];
-    /// let (left, right) = v.rsplit_array_mut::<4>();
-    /// assert_eq!(left, [1, 0]);
-    /// assert_eq!(right, &mut [3, 0, 5, 6]);
-    /// left[1] = 2;
-    /// right[1] = 4;
-    /// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
-    /// ```
-    #[unstable(feature = "split_array", reason = "new API", issue = "90091")]
-    #[inline]
-    #[must_use]
-    pub fn rsplit_array_mut<const N: usize>(&mut self) -> (&mut [T], &mut [T; N]) {
-        assert!(N <= self.len());
-        let (a, b) = self.split_at_mut(self.len() - N);
-        // SAFETY: b points to [T; N]? Yes it's [T] of length N (checked by split_at_mut)
-        unsafe { (a, &mut *(b.as_mut_ptr() as *mut [T; N])) }
-    }
-
     /// Returns an iterator over subslices separated by elements that match
     /// `pred`. The matched element is not contained in the subslices.
     ///
diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs
index 9c41b8b4f46..077852b0120 100644
--- a/library/core/src/task/wake.rs
+++ b/library/core/src/task/wake.rs
@@ -329,12 +329,14 @@ impl Waker {
         Waker { waker }
     }
 
-    /// Creates a new `Waker` that does nothing when `wake` is called.
+    /// Returns a reference to a `Waker` that does nothing when used.
     ///
     /// This is mostly useful for writing tests that need a [`Context`] to poll
     /// some futures, but are not expecting those futures to wake the waker or
     /// do not need to do anything specific if it happens.
     ///
+    /// If an owned `Waker` is needed, `clone()` this one.
+    ///
     /// # Examples
     ///
     /// ```
@@ -343,8 +345,7 @@ impl Waker {
     /// use std::future::Future;
     /// use std::task;
     ///
-    /// let waker = task::Waker::noop();
-    /// let mut cx = task::Context::from_waker(&waker);
+    /// let mut cx = task::Context::from_waker(task::Waker::noop());
     ///
     /// let mut future = Box::pin(async { 10 });
     /// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10));
@@ -352,7 +353,12 @@ impl Waker {
     #[inline]
     #[must_use]
     #[unstable(feature = "noop_waker", issue = "98286")]
-    pub const fn noop() -> Waker {
+    pub const fn noop() -> &'static Waker {
+        // Ideally all this data would be explicitly `static` because it is used by reference and
+        // only ever needs one copy. But `const fn`s (and `const` items) cannot refer to statics,
+        // even though their values can be promoted to static. (That might change; see #119618.)
+        // An alternative would be a `pub static NOOP: &Waker`, but associated static items are not
+        // currently allowed either, and making it non-associated would be unergonomic.
         const VTABLE: RawWakerVTable = RawWakerVTable::new(
             // Cloning just returns a new no-op raw waker
             |_| RAW,
@@ -364,8 +370,9 @@ impl Waker {
             |_| {},
         );
         const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE);
+        const WAKER_REF: &Waker = &Waker { waker: RAW };
 
-        Waker { waker: RAW }
+        WAKER_REF
     }
 
     /// Get a reference to the underlying [`RawWaker`].
diff --git a/library/core/tests/async_iter/mod.rs b/library/core/tests/async_iter/mod.rs
index 0c30bd1dfea..4f425d7286d 100644
--- a/library/core/tests/async_iter/mod.rs
+++ b/library/core/tests/async_iter/mod.rs
@@ -7,8 +7,7 @@ fn into_async_iter() {
     let async_iter = async_iter::from_iter(0..3);
     let mut async_iter = pin!(async_iter.into_async_iter());
 
-    let waker = core::task::Waker::noop();
-    let mut cx = &mut core::task::Context::from_waker(&waker);
+    let mut cx = &mut core::task::Context::from_waker(core::task::Waker::noop());
 
     assert_eq!(async_iter.as_mut().poll_next(&mut cx), Poll::Ready(Some(0)));
     assert_eq!(async_iter.as_mut().poll_next(&mut cx), Poll::Ready(Some(1)));
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
index cc1fc7e4d7e..c7a5feb04ae 100644
--- a/library/core/tests/slice.rs
+++ b/library/core/tests/slice.rs
@@ -2398,37 +2398,45 @@ mod swap_panics {
 }
 
 #[test]
-fn slice_split_array_mut() {
+fn slice_split_first_chunk_mut() {
     let v = &mut [1, 2, 3, 4, 5, 6][..];
 
     {
-        let (left, right) = v.split_array_mut::<0>();
+        let (left, right) = v.split_first_chunk_mut::<0>().unwrap();
         assert_eq!(left, &mut []);
         assert_eq!(right, [1, 2, 3, 4, 5, 6]);
     }
 
     {
-        let (left, right) = v.split_array_mut::<6>();
+        let (left, right) = v.split_first_chunk_mut::<6>().unwrap();
         assert_eq!(left, &mut [1, 2, 3, 4, 5, 6]);
         assert_eq!(right, []);
     }
+
+    {
+        assert!(v.split_first_chunk_mut::<7>().is_none());
+    }
 }
 
 #[test]
-fn slice_rsplit_array_mut() {
+fn slice_split_last_chunk_mut() {
     let v = &mut [1, 2, 3, 4, 5, 6][..];
 
     {
-        let (left, right) = v.rsplit_array_mut::<0>();
+        let (left, right) = v.split_last_chunk_mut::<0>().unwrap();
         assert_eq!(left, [1, 2, 3, 4, 5, 6]);
         assert_eq!(right, &mut []);
     }
 
     {
-        let (left, right) = v.rsplit_array_mut::<6>();
+        let (left, right) = v.split_last_chunk_mut::<6>().unwrap();
         assert_eq!(left, []);
         assert_eq!(right, &mut [1, 2, 3, 4, 5, 6]);
     }
+
+    {
+        assert!(v.split_last_chunk_mut::<7>().is_none());
+    }
 }
 
 #[test]
@@ -2443,38 +2451,6 @@ fn split_as_slice() {
     assert_eq!(split.as_slice(), &[]);
 }
 
-#[should_panic]
-#[test]
-fn slice_split_array_ref_out_of_bounds() {
-    let v = &[1, 2, 3, 4, 5, 6][..];
-
-    let _ = v.split_array_ref::<7>();
-}
-
-#[should_panic]
-#[test]
-fn slice_split_array_mut_out_of_bounds() {
-    let v = &mut [1, 2, 3, 4, 5, 6][..];
-
-    let _ = v.split_array_mut::<7>();
-}
-
-#[should_panic]
-#[test]
-fn slice_rsplit_array_ref_out_of_bounds() {
-    let v = &[1, 2, 3, 4, 5, 6][..];
-
-    let _ = v.rsplit_array_ref::<7>();
-}
-
-#[should_panic]
-#[test]
-fn slice_rsplit_array_mut_out_of_bounds() {
-    let v = &mut [1, 2, 3, 4, 5, 6][..];
-
-    let _ = v.rsplit_array_mut::<7>();
-}
-
 #[test]
 fn slice_split_once() {
     let v = &[1, 2, 3, 2, 4][..];
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 642265f5f6b..bad1511dfd2 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -530,7 +530,6 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
             for event in &mut self.inner {
                 match &event.0 {
                     Event::End(Tag::Heading(..)) => break,
-                    Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {}
                     Event::Text(text) | Event::Code(text) => {
                         id.extend(text.chars().filter_map(slugify));
                         self.buf.push_back(event);
@@ -549,12 +548,10 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
 
             let level =
                 std::cmp::min(level as u32 + (self.heading_offset as u32), MAX_HEADER_LEVEL);
-            self.buf.push_back((Event::Html(format!("</a></h{level}>").into()), 0..0));
+            self.buf.push_back((Event::Html(format!("</h{level}>").into()), 0..0));
 
-            let start_tags = format!(
-                "<h{level} id=\"{id}\">\
-                    <a href=\"#{id}\">",
-            );
+            let start_tags =
+                format!("<h{level} id=\"{id}\"><a class=\"doc-anchor\" href=\"#{id}\">§</a>");
             return Some((Event::Html(start_tags.into()), 0..0));
         }
         event
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index 5eba1d0609f..4dd176b3a69 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -311,26 +311,38 @@ fn test_header() {
         assert_eq!(output, expect, "original: {}", input);
     }
 
-    t("# Foo bar", "<h2 id=\"foo-bar\"><a href=\"#foo-bar\">Foo bar</a></h2>");
+    t(
+        "# Foo bar",
+        "<h2 id=\"foo-bar\"><a class=\"doc-anchor\" href=\"#foo-bar\">§</a>Foo bar</h2>",
+    );
     t(
         "## Foo-bar_baz qux",
         "<h3 id=\"foo-bar_baz-qux\">\
-         <a href=\"#foo-bar_baz-qux\">Foo-bar_baz qux</a></h3>",
+             <a class=\"doc-anchor\" href=\"#foo-bar_baz-qux\">§</a>\
+             Foo-bar_baz qux\
+         </h3>",
     );
     t(
         "### **Foo** *bar* baz!?!& -_qux_-%",
         "<h4 id=\"foo-bar-baz--qux-\">\
-            <a href=\"#foo-bar-baz--qux-\"><strong>Foo</strong> \
-            <em>bar</em> baz!?!&amp; -<em>qux</em>-%</a>\
+            <a class=\"doc-anchor\" href=\"#foo-bar-baz--qux-\">§</a>\
+            <strong>Foo</strong> <em>bar</em> baz!?!&amp; -<em>qux</em>-%\
          </h4>",
     );
     t(
         "#### **Foo?** & \\*bar?!*  _`baz`_ ❤ #qux",
         "<h5 id=\"foo--bar--baz--qux\">\
-             <a href=\"#foo--bar--baz--qux\"><strong>Foo?</strong> &amp; *bar?!*  \
-             <em><code>baz</code></em> ❤ #qux</a>\
+             <a class=\"doc-anchor\" href=\"#foo--bar--baz--qux\">§</a>\
+             <strong>Foo?</strong> &amp; *bar?!*  <em><code>baz</code></em> ❤ #qux\
          </h5>",
     );
+    t(
+        "# Foo [bar](https://hello.yo)",
+        "<h2 id=\"foo-bar\">\
+             <a class=\"doc-anchor\" href=\"#foo-bar\">§</a>\
+             Foo <a href=\"https://hello.yo\">bar</a>\
+         </h2>",
+    );
 }
 
 #[test]
@@ -351,12 +363,36 @@ fn test_header_ids_multiple_blocks() {
         assert_eq!(output, expect, "original: {}", input);
     }
 
-    t(&mut map, "# Example", "<h2 id=\"example\"><a href=\"#example\">Example</a></h2>");
-    t(&mut map, "# Panics", "<h2 id=\"panics\"><a href=\"#panics\">Panics</a></h2>");
-    t(&mut map, "# Example", "<h2 id=\"example-1\"><a href=\"#example-1\">Example</a></h2>");
-    t(&mut map, "# Search", "<h2 id=\"search-1\"><a href=\"#search-1\">Search</a></h2>");
-    t(&mut map, "# Example", "<h2 id=\"example-2\"><a href=\"#example-2\">Example</a></h2>");
-    t(&mut map, "# Panics", "<h2 id=\"panics-1\"><a href=\"#panics-1\">Panics</a></h2>");
+    t(
+        &mut map,
+        "# Example",
+        "<h2 id=\"example\"><a class=\"doc-anchor\" href=\"#example\">§</a>Example</h2>",
+    );
+    t(
+        &mut map,
+        "# Panics",
+        "<h2 id=\"panics\"><a class=\"doc-anchor\" href=\"#panics\">§</a>Panics</h2>",
+    );
+    t(
+        &mut map,
+        "# Example",
+        "<h2 id=\"example-1\"><a class=\"doc-anchor\" href=\"#example-1\">§</a>Example</h2>",
+    );
+    t(
+        &mut map,
+        "# Search",
+        "<h2 id=\"search-1\"><a class=\"doc-anchor\" href=\"#search-1\">§</a>Search</h2>",
+    );
+    t(
+        &mut map,
+        "# Example",
+        "<h2 id=\"example-2\"><a class=\"doc-anchor\" href=\"#example-2\">§</a>Example</h2>",
+    );
+    t(
+        &mut map,
+        "# Panics",
+        "<h2 id=\"panics-1\"><a class=\"doc-anchor\" href=\"#panics-1\">§</a>Panics</h2>",
+    );
 }
 
 #[test]
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index bea5ccd7c86..ac7ae291d29 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1207,17 +1207,31 @@ impl<'a> AssocItemLink<'a> {
     }
 }
 
-fn write_impl_section_heading(mut w: impl fmt::Write, title: &str, id: &str) {
+pub fn write_section_heading(
+    w: &mut impl fmt::Write,
+    title: &str,
+    id: &str,
+    extra_class: Option<&str>,
+    extra: impl fmt::Display,
+) {
+    let (extra_class, whitespace) = match extra_class {
+        Some(extra) => (extra, " "),
+        None => ("", ""),
+    };
     write!(
         w,
-        "<h2 id=\"{id}\" class=\"section-header\">\
+        "<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
             {title}\
             <a href=\"#{id}\" class=\"anchor\">§</a>\
-         </h2>"
+         </h2>{extra}",
     )
     .unwrap();
 }
 
+fn write_impl_section_heading(w: &mut impl fmt::Write, title: &str, id: &str) {
+    write_section_heading(w, title, id, None, "")
+}
+
 pub(crate) fn render_all_impls(
     mut w: impl Write,
     cx: &mut Context<'_>,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 3b91fbdcb29..71186319e07 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -19,8 +19,8 @@ use super::{
     item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
     render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
     render_impl, render_rightside, render_stability_since_raw,
-    render_stability_since_raw_with_extra, AssocItemLink, AssocItemRender, Context,
-    ImplRenderingParameters, RenderMode,
+    render_stability_since_raw_with_extra, write_section_heading, AssocItemLink, AssocItemRender,
+    Context, ImplRenderingParameters, RenderMode,
 };
 use crate::clean;
 use crate::config::ModuleSorting;
@@ -425,13 +425,12 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
                 w.write_str(ITEM_TABLE_CLOSE);
             }
             last_section = Some(my_section);
-            write!(
+            write_section_heading(
                 w,
-                "<h2 id=\"{id}\" class=\"section-header\">\
-                    <a href=\"#{id}\">{name}</a>\
-                 </h2>{ITEM_TABLE_OPEN}",
-                id = cx.derive_id(my_section.id()),
-                name = my_section.name(),
+                my_section.name(),
+                &cx.derive_id(my_section.id()),
+                None,
+                ITEM_TABLE_OPEN,
             );
         }
 
@@ -814,16 +813,6 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
     // Trait documentation
     write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
-    fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
-        write!(
-            w,
-            "<h2 id=\"{0}\" class=\"section-header\">\
-                {1}<a href=\"#{0}\" class=\"anchor\">§</a>\
-             </h2>{2}",
-            id, title, extra_content
-        )
-    }
-
     fn trait_item(w: &mut Buffer, cx: &mut Context<'_>, m: &clean::Item, t: &clean::Item) {
         let name = m.name.unwrap();
         info!("Documenting {name} on {ty_name:?}", ty_name = t.name);
@@ -857,10 +846,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
     }
 
     if !required_types.is_empty() {
-        write_small_section_header(
+        write_section_heading(
             w,
-            "required-associated-types",
             "Required Associated Types",
+            "required-associated-types",
+            None,
             "<div class=\"methods\">",
         );
         for t in required_types {
@@ -869,10 +859,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
         w.write_str("</div>");
     }
     if !provided_types.is_empty() {
-        write_small_section_header(
+        write_section_heading(
             w,
-            "provided-associated-types",
             "Provided Associated Types",
+            "provided-associated-types",
+            None,
             "<div class=\"methods\">",
         );
         for t in provided_types {
@@ -882,10 +873,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
     }
 
     if !required_consts.is_empty() {
-        write_small_section_header(
+        write_section_heading(
             w,
-            "required-associated-consts",
             "Required Associated Constants",
+            "required-associated-consts",
+            None,
             "<div class=\"methods\">",
         );
         for t in required_consts {
@@ -894,10 +886,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
         w.write_str("</div>");
     }
     if !provided_consts.is_empty() {
-        write_small_section_header(
+        write_section_heading(
             w,
-            "provided-associated-consts",
             "Provided Associated Constants",
+            "provided-associated-consts",
+            None,
             "<div class=\"methods\">",
         );
         for t in provided_consts {
@@ -908,10 +901,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
 
     // Output the documentation for each function individually
     if !required_methods.is_empty() || must_implement_one_of_functions.is_some() {
-        write_small_section_header(
+        write_section_heading(
             w,
-            "required-methods",
             "Required Methods",
+            "required-methods",
+            None,
             "<div class=\"methods\">",
         );
 
@@ -929,10 +923,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
         w.write_str("</div>");
     }
     if !provided_methods.is_empty() {
-        write_small_section_header(
+        write_section_heading(
             w,
-            "provided-methods",
             "Provided Methods",
+            "provided-methods",
+            None,
             "<div class=\"methods\">",
         );
         for m in provided_methods {
@@ -949,10 +944,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
     let mut extern_crates = FxHashSet::default();
 
     if !t.is_object_safe(cx.tcx()) {
-        write_small_section_header(
+        write_section_heading(
             w,
-            "object-safety",
             "Object Safety",
+            "object-safety",
+            None,
             &format!(
                 "<div class=\"object-safety-info\">This trait is <b>not</b> \
                 <a href=\"{base}/reference/items/traits.html#object-safety\">\
@@ -996,7 +992,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
         foreign.sort_by_cached_key(|i| ImplString::new(i, cx));
 
         if !foreign.is_empty() {
-            write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
+            write_section_heading(w, "Implementations on Foreign Types", "foreign-impls", None, "");
 
             for implementor in foreign {
                 let provided_methods = implementor.inner_impl().provided_trait_methods(tcx);
@@ -1021,10 +1017,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
             }
         }
 
-        write_small_section_header(
+        write_section_heading(
             w,
-            "implementors",
             "Implementors",
+            "implementors",
+            None,
             "<div id=\"implementors-list\">",
         );
         for implementor in concrete {
@@ -1033,10 +1030,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
         w.write_str("</div>");
 
         if t.is_auto(tcx) {
-            write_small_section_header(
+            write_section_heading(
                 w,
-                "synthetic-implementors",
                 "Auto implementors",
+                "synthetic-implementors",
+                None,
                 "<div id=\"synthetic-implementors-list\">",
             );
             for implementor in synthetic {
@@ -1054,18 +1052,20 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
     } else {
         // even without any implementations to write in, we still want the heading and list, so the
         // implementors javascript file pulled in below has somewhere to write the impls into
-        write_small_section_header(
+        write_section_heading(
             w,
-            "implementors",
             "Implementors",
+            "implementors",
+            None,
             "<div id=\"implementors-list\"></div>",
         );
 
         if t.is_auto(tcx) {
-            write_small_section_header(
+            write_section_heading(
                 w,
-                "synthetic-implementors",
                 "Auto implementors",
+                "synthetic-implementors",
+                None,
                 "<div id=\"synthetic-implementors-list\"></div>",
             );
         }
@@ -1248,11 +1248,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
     write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
     if let Some(inner_type) = &t.inner_type {
-        write!(
-            w,
-            "<h2 id=\"aliased-type\" class=\"section-header\">\
-                Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
-        );
+        write_section_heading(w, "Aliased Type", "aliased-type", None, "");
 
         match inner_type {
             clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
@@ -1673,16 +1669,14 @@ fn item_variants(
     enum_def_id: DefId,
 ) {
     let tcx = cx.tcx();
-    write!(
+    write_section_heading(
         w,
-        "<h2 id=\"variants\" class=\"variants section-header\">\
-            Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
-        </h2>\
-        {}\
-        <div class=\"variants\">",
-        document_non_exhaustive_header(it),
-        document_non_exhaustive(it)
+        &format!("Variants{}", document_non_exhaustive_header(it)),
+        "variants",
+        Some("variants"),
+        format!("{}<div class=\"variants\">", document_non_exhaustive(it)),
     );
+
     let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
     for (index, variant) in variants.iter_enumerated() {
         if variant.is_stripped() {
@@ -1930,16 +1924,12 @@ fn item_fields(
         .peekable();
     if let None | Some(CtorKind::Fn) = ctor_kind {
         if fields.peek().is_some() {
-            write!(
-                w,
-                "<h2 id=\"fields\" class=\"fields section-header\">\
-                     {}{}<a href=\"#fields\" class=\"anchor\">§</a>\
-                 </h2>\
-                 {}",
+            let title = format!(
+                "{}{}",
                 if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
                 document_non_exhaustive_header(it),
-                document_non_exhaustive(it)
             );
+            write_section_heading(w, &title, "fields", Some("fields"), document_non_exhaustive(it));
             for (index, (field, ty)) in fields.enumerate() {
                 let field_name =
                     field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index cd53fcb8b7c..9c593aa85d9 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -849,11 +849,30 @@ nav.sub {
 h2.section-header > .anchor {
 	padding-right: 6px;
 }
+a.doc-anchor {
+	color: var(--main-color);
+	display: none;
+	position: absolute;
+	left: -17px;
+	/* We add this padding so that when the cursor moves from the heading's text to the anchor,
+	   the anchor doesn't disappear. */
+	padding-right: 5px;
+	/* And this padding is used to make the anchor larger and easier to click on. */
+	padding-left: 3px;
+}
+*:hover > .doc-anchor {
+	display: block;
+}
+/* If the first element of the top doc block is a heading, we don't want to ever display its anchor
+because of the `[-]` element which would overlap with it. */
+.top-doc > .docblock > *:first-child > .doc-anchor {
+	display: none !important;
+}
 
 .main-heading a:hover,
 .example-wrap .rust a:hover,
 .all-items a:hover,
-.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,
+.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),
 .docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,
 .item-info a {
 	text-decoration: underline;
diff --git a/src/tools/miri/tests/pass/async-fn.rs b/src/tools/miri/tests/pass/async-fn.rs
index 6c92735df0a..13400c88c71 100644
--- a/src/tools/miri/tests/pass/async-fn.rs
+++ b/src/tools/miri/tests/pass/async-fn.rs
@@ -76,8 +76,7 @@ async fn uninhabited_variant() {
 fn run_fut<T>(fut: impl Future<Output = T>) -> T {
     use std::task::{Context, Poll, Waker};
 
-    let waker = Waker::noop();
-    let mut context = Context::from_waker(&waker);
+    let mut context = Context::from_waker(Waker::noop());
 
     let mut pinned = Box::pin(fut);
     loop {
diff --git a/src/tools/miri/tests/pass/dyn-star.rs b/src/tools/miri/tests/pass/dyn-star.rs
index 8e26c4850fa..dab589b4651 100644
--- a/src/tools/miri/tests/pass/dyn-star.rs
+++ b/src/tools/miri/tests/pass/dyn-star.rs
@@ -93,8 +93,7 @@ fn dispatch_on_pin_mut() {
     let mut fut = async_main();
 
     // Poll loop, just to test the future...
-    let waker = Waker::noop();
-    let ctx = &mut Context::from_waker(&waker);
+    let ctx = &mut Context::from_waker(Waker::noop());
 
     loop {
         match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } {
diff --git a/src/tools/miri/tests/pass/future-self-referential.rs b/src/tools/miri/tests/pass/future-self-referential.rs
index 38cb700fd58..8aeb26a7a95 100644
--- a/src/tools/miri/tests/pass/future-self-referential.rs
+++ b/src/tools/miri/tests/pass/future-self-referential.rs
@@ -77,8 +77,7 @@ impl Future for DoStuff {
 }
 
 fn run_fut<T>(fut: impl Future<Output = T>) -> T {
-    let waker = Waker::noop();
-    let mut context = Context::from_waker(&waker);
+    let mut context = Context::from_waker(Waker::noop());
 
     let mut pinned = pin!(fut);
     loop {
@@ -90,8 +89,7 @@ fn run_fut<T>(fut: impl Future<Output = T>) -> T {
 }
 
 fn self_referential_box() {
-    let waker = Waker::noop();
-    let cx = &mut Context::from_waker(&waker);
+    let cx = &mut Context::from_waker(Waker::noop());
 
     async fn my_fut() -> i32 {
         let val = 10;
diff --git a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs
index f18c4a3a065..ccee2221e29 100644
--- a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs
+++ b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs
@@ -6,8 +6,7 @@ use std::task::{Context, Poll, Waker};
 
 pub fn fuzzing_block_on<O, F: Future<Output = O>>(fut: F) -> O {
     let mut fut = std::pin::pin!(fut);
-    let waker = Waker::noop();
-    let mut context = Context::from_waker(&waker);
+    let mut context = Context::from_waker(Waker::noop());
     loop {
         match fut.as_mut().poll(&mut context) {
             Poll::Ready(v) => return v,
diff --git a/src/tools/miri/tests/pass/move-data-across-await-point.rs b/src/tools/miri/tests/pass/move-data-across-await-point.rs
index 9bea6ea5742..1a93a6bf664 100644
--- a/src/tools/miri/tests/pass/move-data-across-await-point.rs
+++ b/src/tools/miri/tests/pass/move-data-across-await-point.rs
@@ -56,8 +56,7 @@ fn data_moved() {
 fn run_fut<T>(fut: impl Future<Output = T>) -> T {
     use std::task::{Context, Poll, Waker};
 
-    let waker = Waker::noop();
-    let mut context = Context::from_waker(&waker);
+    let mut context = Context::from_waker(Waker::noop());
 
     let mut pinned = Box::pin(fut);
     loop {
diff --git a/tests/coverage/async.coverage b/tests/coverage/async.coverage
index 015e03d5165..23161458850 100644
--- a/tests/coverage/async.coverage
+++ b/tests/coverage/async.coverage
@@ -117,8 +117,7 @@
    LL|       |    #[coverage(off)]
    LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
    LL|       |        let mut future = pin!(future);
-   LL|       |        let waker = Waker::noop();
-   LL|       |        let mut context = Context::from_waker(&waker);
+   LL|       |        let mut context = Context::from_waker(Waker::noop());
    LL|       |
    LL|       |        loop {
    LL|       |            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
diff --git a/tests/coverage/async.rs b/tests/coverage/async.rs
index abc9e5f7f64..df29682968d 100644
--- a/tests/coverage/async.rs
+++ b/tests/coverage/async.rs
@@ -110,8 +110,7 @@ mod executor {
     #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
         let mut future = pin!(future);
-        let waker = Waker::noop();
-        let mut context = Context::from_waker(&waker);
+        let mut context = Context::from_waker(Waker::noop());
 
         loop {
             if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
diff --git a/tests/coverage/async2.coverage b/tests/coverage/async2.coverage
index acd83de9493..a69eefe72cb 100644
--- a/tests/coverage/async2.coverage
+++ b/tests/coverage/async2.coverage
@@ -41,8 +41,7 @@
    LL|       |    #[coverage(off)]
    LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
    LL|       |        let mut future = pin!(future);
-   LL|       |        let waker = Waker::noop();
-   LL|       |        let mut context = Context::from_waker(&waker);
+   LL|       |        let mut context = Context::from_waker(Waker::noop());
    LL|       |
    LL|       |        loop {
    LL|       |            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
diff --git a/tests/coverage/async2.rs b/tests/coverage/async2.rs
index 393573f7dc9..ae83f0103e6 100644
--- a/tests/coverage/async2.rs
+++ b/tests/coverage/async2.rs
@@ -39,8 +39,7 @@ mod executor {
     #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
         let mut future = pin!(future);
-        let waker = Waker::noop();
-        let mut context = Context::from_waker(&waker);
+        let mut context = Context::from_waker(Waker::noop());
 
         loop {
             if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
diff --git a/tests/coverage/async_block.coverage b/tests/coverage/async_block.coverage
index 297397ca26c..0e24b80124f 100644
--- a/tests/coverage/async_block.coverage
+++ b/tests/coverage/async_block.coverage
@@ -24,8 +24,7 @@
    LL|       |    #[coverage(off)]
    LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
    LL|       |        let mut future = pin!(future);
-   LL|       |        let waker = Waker::noop();
-   LL|       |        let mut context = Context::from_waker(&waker);
+   LL|       |        let mut context = Context::from_waker(Waker::noop());
    LL|       |
    LL|       |        loop {
    LL|       |            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
diff --git a/tests/coverage/async_block.rs b/tests/coverage/async_block.rs
index 9d8647bf1f2..f94bcfe3193 100644
--- a/tests/coverage/async_block.rs
+++ b/tests/coverage/async_block.rs
@@ -23,8 +23,7 @@ mod executor {
     #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
         let mut future = pin!(future);
-        let waker = Waker::noop();
-        let mut context = Context::from_waker(&waker);
+        let mut context = Context::from_waker(Waker::noop());
 
         loop {
             if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
diff --git a/tests/coverage/closure_macro_async.coverage b/tests/coverage/closure_macro_async.coverage
index 2c9bd4ac97a..2336d4d0528 100644
--- a/tests/coverage/closure_macro_async.coverage
+++ b/tests/coverage/closure_macro_async.coverage
@@ -54,8 +54,7 @@
    LL|       |    #[coverage(off)]
    LL|       |    pub fn block_on<F: Future>(mut future: F) -> F::Output {
    LL|       |        let mut future = pin!(future);
-   LL|       |        let waker = Waker::noop();
-   LL|       |        let mut context = Context::from_waker(&waker);
+   LL|       |        let mut context = Context::from_waker(Waker::noop());
    LL|       |
    LL|       |        loop {
    LL|       |            if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
diff --git a/tests/coverage/closure_macro_async.rs b/tests/coverage/closure_macro_async.rs
index a7f0cabb4c2..9f5721b5e26 100644
--- a/tests/coverage/closure_macro_async.rs
+++ b/tests/coverage/closure_macro_async.rs
@@ -53,8 +53,7 @@ mod executor {
     #[coverage(off)]
     pub fn block_on<F: Future>(mut future: F) -> F::Output {
         let mut future = pin!(future);
-        let waker = Waker::noop();
-        let mut context = Context::from_waker(&waker);
+        let mut context = Context::from_waker(Waker::noop());
 
         loop {
             if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
diff --git a/tests/rustdoc-gui/docblock-details.goml b/tests/rustdoc-gui/docblock-details.goml
index 8e6d2ba824f..4b8f5b54fac 100644
--- a/tests/rustdoc-gui/docblock-details.goml
+++ b/tests/rustdoc-gui/docblock-details.goml
@@ -6,7 +6,7 @@ reload:
 
 // We first check that the headers in the `.top-doc` doc block still have their
 // bottom border.
-assert-text: (".top-doc .docblock > h3", "Hello")
+assert-text: (".top-doc .docblock > h3", "§Hello")
 assert-css: (
     ".top-doc .docblock > h3",
     {"border-bottom": "1px solid #d2d2d2"},
diff --git a/tests/rustdoc-gui/headers-color.goml b/tests/rustdoc-gui/headers-color.goml
index 19185818f40..80d11c9c849 100644
--- a/tests/rustdoc-gui/headers-color.goml
+++ b/tests/rustdoc-gui/headers-color.goml
@@ -1,4 +1,4 @@
-// This test check for headers text and background colors for the different themes.
+// This test check for headings text and background colors for the different themes.
 
 define-function: (
     "check-colors",
@@ -45,7 +45,7 @@ call-function: (
         "color": "#c5c5c5",
         "code_header_color": "#e6e1cf",
         "focus_background_color": "rgba(255, 236, 164, 0.06)",
-        "headings_color": "#39afd7",
+        "headings_color": "#c5c5c5",
     },
 )
 call-function: (
@@ -55,7 +55,7 @@ call-function: (
         "color": "#ddd",
         "code_header_color": "#ddd",
         "focus_background_color": "#494a3d",
-        "headings_color": "#d2991d",
+        "headings_color": "#ddd",
     },
 )
 call-function: (
@@ -65,6 +65,6 @@ call-function: (
         "color": "black",
         "code_header_color": "black",
         "focus_background_color": "#fdffd3",
-        "headings_color": "#3873ad",
+        "headings_color": "black",
     },
 )
diff --git a/tests/rustdoc-gui/headings-anchor.goml b/tests/rustdoc-gui/headings-anchor.goml
new file mode 100644
index 00000000000..f568caa3b07
--- /dev/null
+++ b/tests/rustdoc-gui/headings-anchor.goml
@@ -0,0 +1,32 @@
+// Test to ensure that the headings anchor behave as expected.
+go-to: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
+show-text: true
+
+define-function: (
+    "check-heading-anchor",
+    (heading_id),
+    block {
+        // The anchor should not be displayed by default.
+        assert-css: ("#" + |heading_id| + " .doc-anchor", { "display": "none" })
+        // We ensure that hovering the heading makes the anchor visible.
+        move-cursor-to: "#" + |heading_id|
+        assert-css: ("#" + |heading_id| + ":hover .doc-anchor", { "display": "block" })
+        // We then ensure that moving from the heading to the anchor doesn't make the anchor
+        // disappear.
+        move-cursor-to: "#" + |heading_id| + " .doc-anchor"
+        assert-css: ("#" + |heading_id| + " .doc-anchor:hover", {
+            "display": "block",
+            // We also ensure that there is no underline decoration.
+            "text-decoration-line": "none",
+        })
+    }
+)
+
+move-cursor-to: "#top-doc-prose-title"
+// If the top documentation block first element is a heading, we should never display its anchor
+// to prevent it from overlapping with the `[-]` element.
+assert-css: ("#top-doc-prose-title:hover .doc-anchor", { "display": "none" })
+
+call-function: ("check-heading-anchor", ("top-doc-prose-sub-heading"))
+call-function: ("check-heading-anchor", ("top-doc-prose-sub-sub-heading"))
+call-function: ("check-heading-anchor", ("you-know-the-drill"))
diff --git a/tests/rustdoc/disambiguate-anchors-header-29449.rs b/tests/rustdoc/disambiguate-anchors-header-29449.rs
index 38a4954fc13..1388af7df4b 100644
--- a/tests/rustdoc/disambiguate-anchors-header-29449.rs
+++ b/tests/rustdoc/disambiguate-anchors-header-29449.rs
@@ -5,18 +5,23 @@
 pub struct Foo;
 
 impl Foo {
-    // @has - '//*[@id="examples"]//a' 'Examples'
-    // @has - '//*[@id="panics"]//a' 'Panics'
+    // @has - '//*[@id="examples"]' 'Examples'
+    // @has - '//*[@id="examples"]/a[@href="#examples"]' '§'
+    // @has - '//*[@id="panics"]' 'Panics'
+    // @has - '//*[@id="panics"]/a[@href="#panics"]' '§'
     /// # Examples
     /// # Panics
     pub fn bar() {}
 
-    // @has - '//*[@id="examples-1"]//a' 'Examples'
+    // @has - '//*[@id="examples-1"]' 'Examples'
+    // @has - '//*[@id="examples-1"]/a[@href="#examples-1"]' '§'
     /// # Examples
     pub fn bar_1() {}
 
-    // @has - '//*[@id="examples-2"]//a' 'Examples'
-    // @has - '//*[@id="panics-1"]//a' 'Panics'
+    // @has - '//*[@id="examples-2"]' 'Examples'
+    // @has - '//*[@id="examples-2"]/a[@href="#examples-2"]' '§'
+    // @has - '//*[@id="panics-1"]' 'Panics'
+    // @has - '//*[@id="panics-1"]/a[@href="#panics-1"]' '§'
     /// # Examples
     /// # Panics
     pub fn bar_2() {}
diff --git a/tests/rustdoc/links-in-headings.rs b/tests/rustdoc/links-in-headings.rs
new file mode 100644
index 00000000000..c5bee1a7975
--- /dev/null
+++ b/tests/rustdoc/links-in-headings.rs
@@ -0,0 +1,14 @@
+#![crate_name = "foo"]
+
+//! # Heading with [a link](https://a.com) inside
+//!
+//! And even with
+//!
+//! ## [multiple](https://b.com) [links](https://c.com)
+//!
+//! !
+
+// @has 'foo/index.html'
+// @has - '//h2/a[@href="https://a.com"]' 'a link'
+// @has - '//h3/a[@href="https://b.com"]' 'multiple'
+// @has - '//h3/a[@href="https://c.com"]' 'links'
diff --git a/tests/rustdoc/remove-url-from-headings.rs b/tests/rustdoc/remove-url-from-headings.rs
index 599c429a6e1..8f477028619 100644
--- a/tests/rustdoc/remove-url-from-headings.rs
+++ b/tests/rustdoc/remove-url-from-headings.rs
@@ -1,9 +1,12 @@
+// It actually checks that the link is kept in the headings as expected now.
+
 #![crate_name = "foo"]
 
 // @has foo/fn.foo.html
-// @!has - '//a[@href="http://a.a"]' ''
-// @has - '//a[@href="#implementing-stuff-somewhere"]' 'Implementing stuff somewhere'
-// @has - '//a[@href="#another-one-urg"]' 'Another one urg'
+// @has - '//a[@href="http://a.a"]' 'stuff'
+// @has - '//*[@id="implementing-stuff-somewhere"]' 'Implementing stuff somewhere'
+// @has - '//a[@href="http://b.b"]' 'one'
+// @has - '//*[@id="another-one-urg"]' 'Another one urg'
 
 /// fooo
 ///
@@ -13,5 +16,5 @@
 ///
 /// # Another [one][two] urg
 ///
-/// [two]: http://a.a
+/// [two]: http://b.b
 pub fn foo() {}
diff --git a/tests/rustdoc/short-docblock.rs b/tests/rustdoc/short-docblock.rs
index 791d3547c9f..151a42a9c9e 100644
--- a/tests/rustdoc/short-docblock.rs
+++ b/tests/rustdoc/short-docblock.rs
@@ -2,8 +2,9 @@
 
 // @has foo/index.html '//*[@class="desc docblock-short"]' 'fooo'
 // @!has foo/index.html '//*[@class="desc docblock-short"]/h1' 'fooo'
-// @has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' 'fooo'
 
+// @has foo/fn.foo.html '//h2[@id="fooo"]' 'fooo'
+// @has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' '§'
 /// # fooo
 ///
 /// foo
@@ -11,8 +12,9 @@ pub fn foo() {}
 
 // @has foo/index.html '//*[@class="desc docblock-short"]' 'mooood'
 // @!has foo/index.html '//*[@class="desc docblock-short"]/h2' 'mooood'
-// @has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' 'mooood'
 
+// @has foo/foo/index.html '//h3[@id="mooood"]' 'mooood'
+// @has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' '§'
 /// ## mooood
 ///
 /// foo mod
diff --git a/tests/ui/async-await/for-await-passthrough.rs b/tests/ui/async-await/for-await-passthrough.rs
index 7fa133aaedc..b1a382958a1 100644
--- a/tests/ui/async-await/for-await-passthrough.rs
+++ b/tests/ui/async-await/for-await-passthrough.rs
@@ -25,8 +25,7 @@ async fn real_main() {
 
 fn main() {
     let future = real_main();
-    let waker = std::task::Waker::noop();
-    let mut cx = &mut core::task::Context::from_waker(&waker);
+    let mut cx = &mut core::task::Context::from_waker(std::task::Waker::noop());
     let mut future = core::pin::pin!(future);
     while let core::task::Poll::Pending = future.as_mut().poll(&mut cx) {}
 }
diff --git a/tests/ui/async-await/for-await.rs b/tests/ui/async-await/for-await.rs
index 6345ceb0c27..00dbdfb2389 100644
--- a/tests/ui/async-await/for-await.rs
+++ b/tests/ui/async-await/for-await.rs
@@ -17,8 +17,7 @@ async fn real_main() {
 
 fn main() {
     let future = real_main();
-    let waker = std::task::Waker::noop();
-    let mut cx = &mut core::task::Context::from_waker(&waker);
+    let mut cx = &mut core::task::Context::from_waker(std::task::Waker::noop());
     let mut future = core::pin::pin!(future);
     while let core::task::Poll::Pending = future.as_mut().poll(&mut cx) {}
 }
diff --git a/tests/ui/async-await/in-trait/async-default-fn-overridden.rs b/tests/ui/async-await/in-trait/async-default-fn-overridden.rs
index 491dfcc6ae0..8c01f1bddef 100644
--- a/tests/ui/async-await/in-trait/async-default-fn-overridden.rs
+++ b/tests/ui/async-await/in-trait/async-default-fn-overridden.rs
@@ -40,8 +40,7 @@ fn main() {
     let mut fut = pin!(async_main());
 
     // Poll loop, just to test the future...
-    let waker = Waker::noop();
-    let ctx = &mut Context::from_waker(&waker);
+    let ctx = &mut Context::from_waker(Waker::noop());
 
     loop {
         match fut.as_mut().poll(ctx) {
diff --git a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs
index f21abf012ba..e2fd9f9dfea 100644
--- a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs
+++ b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs
@@ -43,8 +43,7 @@ fn main() {
     let mut fut = pin!(async_main());
 
     // Poll loop, just to test the future...
-    let waker = Waker::noop();
-    let ctx = &mut Context::from_waker(&waker);
+    let ctx = &mut Context::from_waker(Waker::noop());
 
     loop {
         match fut.as_mut().poll(ctx) {
diff --git a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr
index 0560cd9c5fe..b7336485eb8 100644
--- a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr
+++ b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr
@@ -21,7 +21,7 @@ LL |     default async fn foo(_: T) -> &'static str {
    = note: specialization behaves in inconsistent and surprising ways with async functions in traits, and for now is disallowed
 
 error[E0599]: no method named `poll` found for struct `Pin<&mut impl Future<Output = ()>>` in the current scope
-  --> $DIR/dont-project-to-specializable-projection.rs:50:28
+  --> $DIR/dont-project-to-specializable-projection.rs:49:28
    |
 LL |         match fut.as_mut().poll(ctx) {
    |                            ^^^^ method not found in `Pin<&mut impl Future<Output = ()>>`
diff --git a/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs b/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
index aac74d3eacb..80c0b69a6f7 100644
--- a/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
+++ b/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
@@ -11,7 +11,6 @@ async gen fn gen_fn() -> &'static str {
 
 pub fn main() {
     let async_iterator = pin!(gen_fn());
-    let waker = Waker::noop();
-    let ctx = &mut Context::from_waker(&waker);
+    let ctx = &mut Context::from_waker(Waker::noop());
     async_iterator.poll_next(ctx);
 }
diff --git a/tests/ui/coroutine/async_gen_fn_iter.rs b/tests/ui/coroutine/async_gen_fn_iter.rs
index ec6464d0048..604156b4d37 100644
--- a/tests/ui/coroutine/async_gen_fn_iter.rs
+++ b/tests/ui/coroutine/async_gen_fn_iter.rs
@@ -74,8 +74,7 @@ fn main() {
     let mut fut = pin!(async_main());
 
     // Poll loop, just to test the future...
-    let waker = Waker::noop();
-    let ctx = &mut Context::from_waker(&waker);
+    let ctx = &mut Context::from_waker(Waker::noop());
 
     loop {
         match fut.as_mut().poll(ctx) {
diff --git a/tests/ui/dyn-star/dispatch-on-pin-mut.rs b/tests/ui/dyn-star/dispatch-on-pin-mut.rs
index c4ae279e6c1..151aa9092fb 100644
--- a/tests/ui/dyn-star/dispatch-on-pin-mut.rs
+++ b/tests/ui/dyn-star/dispatch-on-pin-mut.rs
@@ -19,15 +19,14 @@ async fn async_main() {
 // ------------------------------------------------------------------------- //
 // Implementation Details Below...
 
-use std::task::*;
 use std::pin::pin;
+use std::task::*;
 
 fn main() {
     let mut fut = pin!(async_main());
 
     // Poll loop, just to test the future...
-    let waker = Waker::noop();
-    let ctx = &mut Context::from_waker(&waker);
+    let ctx = &mut Context::from_waker(Waker::noop());
 
     loop {
         match fut.as_mut().poll(ctx) {
diff --git a/tests/ui/pattern/never_patterns.rs b/tests/ui/pattern/never_patterns.rs
deleted file mode 100644
index 8f44f8a6559..00000000000
--- a/tests/ui/pattern/never_patterns.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-#![feature(never_patterns)]
-#![allow(incomplete_features)]
-
-enum Void {}
-
-fn main() {}
-
-// The classic use for empty types.
-fn safe_unwrap_result<T>(res: Result<T, Void>) {
-    let Ok(_x) = res; //~ ERROR refutable pattern in local binding
-    let (Ok(_x) | Err(!)) = &res;
-    let (Ok(_x) | Err(&!)) = res.as_ref();
-}
-
-// Check we only accept `!` where we want to.
-fn never_pattern_location(void: Void) {
-    // FIXME(never_patterns): Don't accept on a non-empty type.
-    match Some(0) {
-        None => {}
-        Some(!),
-    }
-    // FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
-    match () {
-        () => {}
-        !,
-    }
-    // FIXME(never_patterns): Don't accept even on an empty branch.
-    match None::<Void> {
-        None => {}
-        !,
-    }
-    // FIXME(never_patterns): Let alone if the emptiness is behind a reference.
-    match None::<&Void> {
-        None => {}
-        !,
-    }
-    // Participate in match ergonomics.
-    match &void {
-        !
-    }
-    match &&void {
-        !
-    }
-    match &&void {
-        &!
-    }
-    match &None::<Void> {
-        None => {}
-        Some(!)
-    }
-    match None::<&Void> {
-        None => {}
-        Some(!),
-    }
-    // Accept on a composite empty type.
-    match None::<&(u32, Void)> {
-        None => {}
-        Some(&!),
-    }
-    // Accept on an simple empty type.
-    match None::<Void> {
-        None => {}
-        Some(!),
-    }
-    match None::<&Void> {
-        None => {}
-        Some(&!),
-    }
-    match None::<&(u32, Void)> {
-        None => {}
-        Some(&(_, !)),
-    }
-}
diff --git a/tests/ui/pattern/never_patterns.stderr b/tests/ui/pattern/never_patterns.stderr
deleted file mode 100644
index 20eeb01cf71..00000000000
--- a/tests/ui/pattern/never_patterns.stderr
+++ /dev/null
@@ -1,17 +0,0 @@
-error[E0005]: refutable pattern in local binding
-  --> $DIR/never_patterns.rs:10:9
-   |
-LL |     let Ok(_x) = res;
-   |         ^^^^^^ pattern `Err(_)` not covered
-   |
-   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
-   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
-   = note: the matched value is of type `Result<T, Void>`
-help: you might want to use `let else` to handle the variant that isn't matched
-   |
-LL |     let Ok(_x) = res else { todo!() };
-   |                      ++++++++++++++++
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr
new file mode 100644
index 00000000000..013a8b53a55
--- /dev/null
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr
@@ -0,0 +1,66 @@
+error: mismatched types
+  --> $DIR/typeck.rs:25:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `()`
+
+error: mismatched types
+  --> $DIR/typeck.rs:29:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `(i32, bool)`
+
+error: mismatched types
+  --> $DIR/typeck.rs:33:13
+   |
+LL |         (_, !),
+   |             ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `bool`
+
+error: mismatched types
+  --> $DIR/typeck.rs:38:14
+   |
+LL |         Some(!),
+   |              ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `i32`
+
+error: mismatched types
+  --> $DIR/typeck.rs:45:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `()`
+
+error: mismatched types
+  --> $DIR/typeck.rs:52:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `Option<Void>`
+
+error: mismatched types
+  --> $DIR/typeck.rs:57:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `[Void]`
+
+error: mismatched types
+  --> $DIR/typeck.rs:63:9
+   |
+LL |         !,
+   |         ^ a never pattern must be used on an uninhabited type
+   |
+   = note: the matched value is of type `Option<&Void>`
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
new file mode 100644
index 00000000000..31a23fa002c
--- /dev/null
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
@@ -0,0 +1,125 @@
+// revisions: pass fail
+//[pass] check-pass
+//[fail] check-fail
+#![feature(never_patterns)]
+#![feature(exhaustive_patterns)]
+#![allow(incomplete_features)]
+
+#[derive(Copy, Clone)]
+enum Void {}
+
+fn main() {}
+
+// The classic use for empty types.
+fn safe_unwrap_result<T: Copy>(res: Result<T, Void>) {
+    let Ok(_x) = res;
+    let (Ok(_x) | Err(!)) = &res;
+    let (Ok(_x) | Err(!)) = res.as_ref();
+}
+
+// Check we only accept `!` where we want to.
+#[cfg(fail)]
+fn never_pattern_typeck_fail(void: Void) {
+    // Don't accept on a non-empty type.
+    match () {
+        !,
+        //[fail]~^ ERROR: mismatched types
+    }
+    match (0, false) {
+        !,
+        //[fail]~^ ERROR: mismatched types
+    }
+    match (0, false) {
+        (_, !),
+        //[fail]~^ ERROR: mismatched types
+    }
+    match Some(0) {
+        None => {}
+        Some(!),
+        //[fail]~^ ERROR: mismatched types
+    }
+
+    // Don't accept on an arbitrary type, even if there are no more branches.
+    match () {
+        () => {}
+        !,
+        //[fail]~^ ERROR: mismatched types
+    }
+
+    // Don't accept even on an empty branch.
+    match None::<Void> {
+        None => {}
+        !,
+        //[fail]~^ ERROR: mismatched types
+    }
+    match (&[] as &[Void]) {
+        [] => {}
+        !,
+        //[fail]~^ ERROR: mismatched types
+    }
+    // Let alone if the emptiness is behind a reference.
+    match None::<&Void> {
+        None => {}
+        !,
+        //[fail]~^ ERROR: mismatched types
+    }
+}
+
+#[cfg(pass)]
+fn never_pattern_typeck_pass(void: Void) {
+    // Participate in match ergonomics.
+    match &void {
+        !,
+    }
+    match &&void {
+        !,
+    }
+    match &&void {
+        &!,
+    }
+    match &None::<Void> {
+        None => {}
+        Some(!),
+    }
+    match None::<&Void> {
+        None => {}
+        Some(!),
+    }
+
+    // Accept on a directly empty type.
+    match void {
+        !,
+    }
+    match &void {
+        &!,
+    }
+    match None::<Void> {
+        None => {}
+        Some(!),
+    }
+    match None::<&Void> {
+        None => {}
+        Some(&!),
+    }
+    match None::<&(u32, Void)> {
+        None => {}
+        Some(&(_, !)),
+    }
+    match (&[] as &[Void]) {
+        [] => {}
+        [!],
+    }
+    // Accept on a composite empty type.
+    match None::<&(u32, Void)> {
+        None => {}
+        Some(&!),
+    }
+    match None::<&(u32, Void)> {
+        None => {}
+        Some(!),
+    }
+    match None::<&Result<Void, Void>> {
+        None => {}
+        Some(!),
+    }
+}
diff --git a/tests/ui/suggestions/suggest-slice-swap.fixed b/tests/ui/suggestions/suggest-slice-swap.fixed
new file mode 100644
index 00000000000..05b7ec26379
--- /dev/null
+++ b/tests/ui/suggestions/suggest-slice-swap.fixed
@@ -0,0 +1,9 @@
+// run-rustfix
+#![allow(dead_code)]
+
+fn swap(arr: &mut [u32; 2]) {
+    arr.swap(1, 0);
+    //~^ ERROR cannot borrow `arr[_]` as mutable more than once at a time
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/suggest-slice-swap.rs b/tests/ui/suggestions/suggest-slice-swap.rs
new file mode 100644
index 00000000000..9f3659aac16
--- /dev/null
+++ b/tests/ui/suggestions/suggest-slice-swap.rs
@@ -0,0 +1,9 @@
+// run-rustfix
+#![allow(dead_code)]
+
+fn swap(arr: &mut [u32; 2]) {
+    std::mem::swap(&mut arr[0], &mut arr[1]);
+    //~^ ERROR cannot borrow `arr[_]` as mutable more than once at a time
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/suggest-slice-swap.stderr b/tests/ui/suggestions/suggest-slice-swap.stderr
new file mode 100644
index 00000000000..2840fc0a761
--- /dev/null
+++ b/tests/ui/suggestions/suggest-slice-swap.stderr
@@ -0,0 +1,17 @@
+error[E0499]: cannot borrow `arr[_]` as mutable more than once at a time
+  --> $DIR/suggest-slice-swap.rs:5:33
+   |
+LL |     std::mem::swap(&mut arr[0], &mut arr[1]);
+   |     -------------- -----------  ^^^^^^^^^^^ second mutable borrow occurs here
+   |     |              |
+   |     |              first mutable borrow occurs here
+   |     first borrow later used by call
+   |
+help: use `.swap()` to swap elements at the specified indices instead
+   |
+LL |     arr.swap(1, 0);
+   |     ~~~~~~~~~~~~~~
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0499`.
diff --git a/triagebot.toml b/triagebot.toml
index b3f3051e177..b684f3ca55c 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -364,7 +364,8 @@ new_pr = true
 [autolabel."needs-triage"]
 new_issue = true
 exclude_labels = [
-    "C-tracking-issue"
+    "C-tracking-issue",
+    "A-diagnostics",
 ]
 
 [autolabel."WG-trait-system-refactor"]