From a11fbeec1fc0ad1b8c8814dd2fa4704c4d5e8e0e Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Fri, 7 Mar 2025 17:09:12 +0100
Subject: [PATCH 01/27] Small code improvement in rustdoc hidden stripper

---
 src/librustdoc/passes/strip_hidden.rs | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index bcdca862862d4..692ce21d6cfbc 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -91,19 +91,21 @@ impl DocFolder for Stripper<'_, '_> {
 
         if let clean::ImportItem(clean::Import { source, .. }) = &i.kind
             && let Some(source_did) = source.did
-            && let Some(import_def_id) = i.def_id().and_then(|def_id| def_id.as_local())
         {
-            let reexports = reexport_chain(self.tcx, import_def_id, source_did);
+            if self.tcx.is_doc_hidden(source_did) {
+                return None;
+            } else if let Some(import_def_id) = i.def_id().and_then(|def_id| def_id.as_local()) {
+                let reexports = reexport_chain(self.tcx, import_def_id, source_did);
 
-            // Check if any reexport in the chain has a hidden source
-            let has_hidden_source = reexports
-                .iter()
-                .filter_map(|reexport| reexport.id())
-                .any(|reexport_did| self.tcx.is_doc_hidden(reexport_did))
-                || self.tcx.is_doc_hidden(source_did);
+                // Check if any reexport in the chain has a hidden source
+                let has_hidden_source = reexports
+                    .iter()
+                    .filter_map(|reexport| reexport.id())
+                    .any(|reexport_did| self.tcx.is_doc_hidden(reexport_did));
 
-            if has_hidden_source {
-                return None;
+                if has_hidden_source {
+                    return None;
+                }
             }
         }
 

From e89c84913690bad837b658ade502cb0af7df768b Mon Sep 17 00:00:00 2001
From: xizheyin <xizheyin@smail.nju.edu.cn>
Date: Mon, 17 Mar 2025 20:40:01 +0800
Subject: [PATCH 02/27] Clean up librustdoc::html::render to be better
 encapsulated

Signed-off-by: xizheyin <xizheyin@smail.nju.edu.cn>
---
 src/librustdoc/formats/cache.rs   | 14 ++---
 src/librustdoc/html/render/mod.rs | 93 ++++++++++++++++++++-----------
 2 files changed, 68 insertions(+), 39 deletions(-)

diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 2648641e53e75..2a612fa9b8cda 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -574,20 +574,20 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
     );
     let aliases = item.attrs.get_doc_aliases();
     let deprecation = item.deprecation(tcx);
-    let index_item = IndexItem {
-        ty: item.type_(),
-        defid: Some(defid),
+    let index_item = IndexItem::new(
+        item.type_(),
+        Some(defid),
         name,
         path,
         desc,
-        parent: parent_did,
-        parent_idx: None,
-        exact_path: None,
+        parent_did,
+        None,
+        None,
         impl_id,
         search_type,
         aliases,
         deprecation,
-    };
+    );
     cache.search_index.push(index_item);
 }
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 2237e0f987bc5..b9a2e65b09076 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -13,6 +13,9 @@
 //! is cloned per-thread and contains information about what is currently being
 //! rendered.
 //!
+//! The main entry point to the rendering system is the implementation of
+//! `FormatRenderer` on `Context`.
+//!
 //! In order to speed up rendering (mostly because of markdown rendering), the
 //! rendering process has been parallelized. This parallelization is only
 //! exposed through the `crate` method on the context, and then also from the
@@ -90,7 +93,7 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
 /// Specifies whether rendering directly implemented trait items or ones from a certain Deref
 /// impl.
 #[derive(Copy, Clone, Debug)]
-pub(crate) enum AssocItemRender<'a> {
+enum AssocItemRender<'a> {
     All,
     DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
 }
@@ -98,7 +101,7 @@ pub(crate) enum AssocItemRender<'a> {
 /// For different handling of associated items from the Deref target of a type rather than the type
 /// itself.
 #[derive(Copy, Clone, PartialEq)]
-pub(crate) enum RenderMode {
+enum RenderMode {
     Normal,
     ForDeref { mut_: bool },
 }
@@ -110,23 +113,55 @@ pub(crate) enum RenderMode {
 /// by hand to a large JS file at the end of cache-creation.
 #[derive(Debug)]
 pub(crate) struct IndexItem {
-    pub(crate) ty: ItemType,
-    pub(crate) defid: Option<DefId>,
-    pub(crate) name: Symbol,
-    pub(crate) path: String,
-    pub(crate) desc: String,
-    pub(crate) parent: Option<DefId>,
-    pub(crate) parent_idx: Option<isize>,
-    pub(crate) exact_path: Option<String>,
-    pub(crate) impl_id: Option<DefId>,
-    pub(crate) search_type: Option<IndexItemFunctionType>,
-    pub(crate) aliases: Box<[Symbol]>,
-    pub(crate) deprecation: Option<Deprecation>,
+    ty: ItemType,
+    defid: Option<DefId>,
+    name: Symbol,
+    path: String,
+    desc: String,
+    parent: Option<DefId>,
+    parent_idx: Option<isize>,
+    exact_path: Option<String>,
+    impl_id: Option<DefId>,
+    search_type: Option<IndexItemFunctionType>,
+    aliases: Box<[Symbol]>,
+    deprecation: Option<Deprecation>,
+}
+
+impl IndexItem {
+    pub fn new(
+        ty: ItemType,
+        defid: Option<DefId>,
+        name: Symbol,
+        path: String,
+        desc: String,
+        parent: Option<DefId>,
+        parent_idx: Option<isize>,
+        exact_path: Option<String>,
+        impl_id: Option<DefId>,
+        search_type: Option<IndexItemFunctionType>,
+        aliases: Box<[Symbol]>,
+        deprecation: Option<Deprecation>,
+    ) -> Self {
+        Self {
+            ty,
+            defid,
+            name,
+            path,
+            desc,
+            parent,
+            parent_idx,
+            exact_path,
+            impl_id,
+            search_type,
+            aliases,
+            deprecation,
+        }
+    }
 }
 
 /// A type used for the search index.
 #[derive(Debug, Eq, PartialEq)]
-pub(crate) struct RenderType {
+struct RenderType {
     id: Option<RenderTypeId>,
     generics: Option<Vec<RenderType>>,
     bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>,
@@ -137,7 +172,7 @@ impl RenderType {
     // The contents of the lists are always integers in self-terminating hex
     // form, handled by `RenderTypeId::write_to_string`, so no commas are
     // needed to separate the items.
-    pub fn write_to_string(&self, string: &mut String) {
+    fn write_to_string(&self, string: &mut String) {
         fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) {
             // 0 is a sentinel, everything else is one-indexed
             match id {
@@ -177,7 +212,7 @@ impl RenderType {
 }
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub(crate) enum RenderTypeId {
+enum RenderTypeId {
     DefId(DefId),
     Primitive(clean::PrimitiveType),
     AssociatedType(Symbol),
@@ -186,7 +221,7 @@ pub(crate) enum RenderTypeId {
 }
 
 impl RenderTypeId {
-    pub fn write_to_string(&self, string: &mut String) {
+    fn write_to_string(&self, string: &mut String) {
         let id: i32 = match &self {
             // 0 is a sentinel, everything else is one-indexed
             // concrete type
@@ -209,7 +244,7 @@ pub(crate) struct IndexItemFunctionType {
 }
 
 impl IndexItemFunctionType {
-    pub fn write_to_string<'a>(
+    fn write_to_string<'a>(
         &'a self,
         string: &mut String,
         backref_queue: &mut VecDeque<&'a IndexItemFunctionType>,
@@ -309,7 +344,7 @@ impl ItemEntry {
 }
 
 impl ItemEntry {
-    pub(crate) fn print(&self) -> impl fmt::Display {
+    fn print(&self) -> impl fmt::Display {
         fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name)))
     }
 }
@@ -760,7 +795,7 @@ fn short_item_info(
 
 // Render the list of items inside one of the sections "Trait Implementations",
 // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
-pub(crate) fn render_impls(
+fn render_impls(
     cx: &Context<'_>,
     mut w: impl Write,
     impls: &[&Impl],
@@ -1201,7 +1236,7 @@ impl<'a> AssocItemLink<'a> {
     }
 }
 
-pub fn write_section_heading(
+fn write_section_heading(
     title: &str,
     id: &str,
     extra_class: Option<&str>,
@@ -1226,7 +1261,7 @@ fn write_impl_section_heading(title: &str, id: &str) -> impl fmt::Display {
     write_section_heading(title, id, None, "")
 }
 
-pub(crate) fn render_all_impls(
+fn render_all_impls(
     mut w: impl Write,
     cx: &Context<'_>,
     containing_item: &clean::Item,
@@ -1473,10 +1508,7 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
     }
 }
 
-pub(crate) fn notable_traits_button(
-    ty: &clean::Type,
-    cx: &Context<'_>,
-) -> Option<impl fmt::Display> {
+fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> {
     if ty.is_unit() {
         // Very common fast path.
         return None;
@@ -1588,10 +1620,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
     (format!("{:#}", ty.print(cx)), out)
 }
 
-pub(crate) fn notable_traits_json<'a>(
-    tys: impl Iterator<Item = &'a clean::Type>,
-    cx: &Context<'_>,
-) -> String {
+fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String {
     let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
     mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
     struct NotableTraitsMap(Vec<(String, String)>);
@@ -2171,7 +2200,7 @@ fn render_rightside(
     })
 }
 
-pub(crate) fn render_impl_summary(
+fn render_impl_summary(
     cx: &Context<'_>,
     i: &Impl,
     parent: &clean::Item,

From abe6e88623976e36dab2f4c4fa9f01444d3b411a Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Wed, 19 Mar 2025 02:19:33 +0000
Subject: [PATCH 03/27] Allow drivers to supply a list of extra symbols to
 intern

---
 compiler/rustc_driver_impl/src/lib.rs         |  1 +
 compiler/rustc_hir/src/tests.rs               |  2 +-
 compiler/rustc_interface/src/interface.rs     |  5 +++
 compiler/rustc_interface/src/tests.rs         |  2 +-
 compiler/rustc_interface/src/util.rs          | 31 +++++++++++++------
 compiler/rustc_macros/src/symbols.rs          | 19 ++++++++----
 compiler/rustc_metadata/src/rmeta/decoder.rs  |  2 +-
 compiler/rustc_metadata/src/rmeta/encoder.rs  |  2 +-
 .../rustc_middle/src/query/on_disk_cache.rs   |  4 +--
 compiler/rustc_span/src/lib.rs                | 15 ++++++---
 compiler/rustc_span/src/symbol.rs             | 24 +++++++-------
 compiler/rustc_span/src/symbol/tests.rs       |  2 +-
 src/librustdoc/core.rs                        |  1 +
 src/librustdoc/doctest.rs                     |  1 +
 src/tools/clippy/Cargo.toml                   |  1 +
 .../src/attrs/deprecated_cfg_attr.rs          |  4 +--
 .../src/attrs/useless_attribute.rs            |  4 +--
 .../src/doc/needless_doctest_main.rs          |  2 +-
 src/tools/clippy/clippy_utils/src/lib.rs      |  4 ++-
 src/tools/clippy/clippy_utils/src/sym.rs      | 23 ++++++++++++++
 src/tools/clippy/src/driver.rs                |  1 +
 tests/ui-fulldeps/run-compiler-twice.rs       |  1 +
 22 files changed, 104 insertions(+), 47 deletions(-)
 create mode 100644 src/tools/clippy/clippy_utils/src/sym.rs

diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 8ede6e413368f..3aa9a75d89343 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -259,6 +259,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))
         hash_untracked_state: None,
         register_lints: None,
         override_queries: None,
+        extra_symbols: Vec::new(),
         make_codegen_backend: None,
         registry: diagnostics_registry(),
         using_internal_features: &USING_INTERNAL_FEATURES,
diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs
index 0837444ffdbe5..18c2bfdac8ce1 100644
--- a/compiler/rustc_hir/src/tests.rs
+++ b/compiler/rustc_hir/src/tests.rs
@@ -17,7 +17,7 @@ fn def_path_hash_depends_on_crate_id() {
     // the crate by changing the crate disambiguator (e.g. via bumping the
     // crate's version number).
 
-    create_session_globals_then(Edition::Edition2024, None, || {
+    create_session_globals_then(Edition::Edition2024, &[], None, || {
         let id0 = StableCrateId::new(Symbol::intern("foo"), false, vec!["1".to_string()], "");
         let id1 = StableCrateId::new(Symbol::intern("foo"), false, vec!["2".to_string()], "");
 
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 3f87b1a547be5..5717a2b8743aa 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -340,6 +340,10 @@ pub struct Config {
     /// the list of queries.
     pub override_queries: Option<fn(&Session, &mut Providers)>,
 
+    /// An extra set of symbols to add to the symbol interner, the symbol indices
+    /// will start at [`PREDEFINED_SYMBOLS_COUNT`](rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT)
+    pub extra_symbols: Vec<&'static str>,
+
     /// This is a callback from the driver that is called to create a codegen backend.
     ///
     /// Has no uses within this repository, but is used by bjorn3 for "the
@@ -401,6 +405,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
         &early_dcx,
         config.opts.edition,
         config.opts.unstable_opts.threads,
+        &config.extra_symbols,
         SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind },
         |current_gcx| {
             // The previous `early_dcx` can't be reused here because it doesn't
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index b44be1710edf7..903cda3cb5b55 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -53,7 +53,7 @@ where
         checksum_hash_kind,
     });
 
-    rustc_span::create_session_globals_then(DEFAULT_EDITION, sm_inputs, || {
+    rustc_span::create_session_globals_then(DEFAULT_EDITION, &[], sm_inputs, || {
         let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
         let io = CompilerIO {
             input: Input::Str { name: FileName::Custom(String::new()), input: String::new() },
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 5cccab893bb35..fceaa6577ab1b 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -117,6 +117,7 @@ fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
     thread_stack_size: usize,
     edition: Edition,
     sm_inputs: SourceMapInputs,
+    extra_symbols: &[&'static str],
     f: F,
 ) -> R {
     // The "thread pool" is a single spawned thread in the non-parallel
@@ -134,9 +135,12 @@ fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
         // name contains null bytes.
         let r = builder
             .spawn_scoped(s, move || {
-                rustc_span::create_session_globals_then(edition, Some(sm_inputs), || {
-                    f(CurrentGcx::new())
-                })
+                rustc_span::create_session_globals_then(
+                    edition,
+                    extra_symbols,
+                    Some(sm_inputs),
+                    || f(CurrentGcx::new()),
+                )
             })
             .unwrap()
             .join();
@@ -152,6 +156,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send,
     thread_builder_diag: &EarlyDiagCtxt,
     edition: Edition,
     threads: usize,
+    extra_symbols: &[&'static str],
     sm_inputs: SourceMapInputs,
     f: F,
 ) -> R {
@@ -168,12 +173,18 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send,
     let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap());
 
     if !sync::is_dyn_thread_safe() {
-        return run_in_thread_with_globals(thread_stack_size, edition, sm_inputs, |current_gcx| {
-            // Register the thread for use with the `WorkerLocal` type.
-            registry.register();
-
-            f(current_gcx)
-        });
+        return run_in_thread_with_globals(
+            thread_stack_size,
+            edition,
+            sm_inputs,
+            extra_symbols,
+            |current_gcx| {
+                // Register the thread for use with the `WorkerLocal` type.
+                registry.register();
+
+                f(current_gcx)
+            },
+        );
     }
 
     let current_gcx = FromDyn::from(CurrentGcx::new());
@@ -217,7 +228,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send,
     // pool. Upon creation, each worker thread created gets a copy of the
     // session globals in TLS. This is possible because `SessionGlobals` impls
     // `Send` in the parallel compiler.
-    rustc_span::create_session_globals_then(edition, Some(sm_inputs), || {
+    rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || {
         rustc_span::with_session_globals(|session_globals| {
             let session_globals = FromDyn::from(session_globals);
             builder
diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs
index 37200f62eb5a2..9fd62e898a425 100644
--- a/compiler/rustc_macros/src/symbols.rs
+++ b/compiler/rustc_macros/src/symbols.rs
@@ -295,10 +295,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
     }
 
     let symbol_digits_base = entries.map["0"].idx;
-    let preinterned_symbols_count = entries.len();
+    let predefined_symbols_count = entries.len();
     let output = quote! {
         const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base;
-        const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count;
+
+        /// The number of predefined symbols; this is the the first index for
+        /// extra pre-interned symbols in an Interner created via
+        /// [`Interner::with_extra_symbols`].
+        pub const PREDEFINED_SYMBOLS_COUNT: u32 = #predefined_symbols_count;
 
         #[doc(hidden)]
         #[allow(non_upper_case_globals)]
@@ -315,10 +319,13 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
         }
 
         impl Interner {
-            pub(crate) fn fresh() -> Self {
-                Interner::prefill(&[
-                    #prefill_stream
-                ])
+            /// Creates an `Interner` with the predefined symbols from the `symbols!` macro and
+            /// any extra symbols provided by external drivers such as Clippy
+            pub(crate) fn with_extra_symbols(extra_symbols: &[&'static str]) -> Self {
+                Interner::prefill(
+                    &[#prefill_stream],
+                    extra_symbols,
+                )
             }
         }
     };
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index f6cf218db9d0b..0c3a6096f955f 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -564,7 +564,7 @@ impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> {
             }
             SYMBOL_PREINTERNED => {
                 let symbol_index = self.read_u32();
-                Symbol::new_from_decoded(symbol_index)
+                Symbol::new(symbol_index)
             }
             _ => unreachable!(),
         }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 7ab3d432bdf81..308f3e933f8b9 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -202,7 +202,7 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> {
 
     fn encode_symbol(&mut self, symbol: Symbol) {
         // if symbol preinterned, emit tag and symbol index
-        if symbol.is_preinterned() {
+        if symbol.is_predefined() {
             self.opaque.emit_u8(SYMBOL_PREINTERNED);
             self.opaque.emit_u32(symbol.as_u32());
         } else {
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 14e3ce8bef6b2..9745a69a5a20b 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -675,7 +675,7 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> {
             }
             SYMBOL_PREINTERNED => {
                 let symbol_index = self.read_u32();
-                Symbol::new_from_decoded(symbol_index)
+                Symbol::new(symbol_index)
             }
             _ => unreachable!(),
         }
@@ -892,7 +892,7 @@ impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> {
     // copy&paste impl from rustc_metadata
     fn encode_symbol(&mut self, symbol: Symbol) {
         // if symbol preinterned, emit tag and symbol index
-        if symbol.is_preinterned() {
+        if symbol.is_predefined() {
             self.encoder.emit_u8(SYMBOL_PREINTERNED);
             self.encoder.emit_u32(symbol.as_u32());
         } else {
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index f19d4d9f3624e..432b1b86142b3 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -116,9 +116,13 @@ pub struct SessionGlobals {
 }
 
 impl SessionGlobals {
-    pub fn new(edition: Edition, sm_inputs: Option<SourceMapInputs>) -> SessionGlobals {
+    pub fn new(
+        edition: Edition,
+        extra_symbols: &[&'static str],
+        sm_inputs: Option<SourceMapInputs>,
+    ) -> SessionGlobals {
         SessionGlobals {
-            symbol_interner: symbol::Interner::fresh(),
+            symbol_interner: symbol::Interner::with_extra_symbols(extra_symbols),
             span_interner: Lock::new(span_encoding::SpanInterner::default()),
             metavar_spans: Default::default(),
             hygiene_data: Lock::new(hygiene::HygieneData::new(edition)),
@@ -129,6 +133,7 @@ impl SessionGlobals {
 
 pub fn create_session_globals_then<R>(
     edition: Edition,
+    extra_symbols: &[&'static str],
     sm_inputs: Option<SourceMapInputs>,
     f: impl FnOnce() -> R,
 ) -> R {
@@ -137,7 +142,7 @@ pub fn create_session_globals_then<R>(
         "SESSION_GLOBALS should never be overwritten! \
          Use another thread if you need another SessionGlobals"
     );
-    let session_globals = SessionGlobals::new(edition, sm_inputs);
+    let session_globals = SessionGlobals::new(edition, extra_symbols, sm_inputs);
     SESSION_GLOBALS.set(&session_globals, f)
 }
 
@@ -156,7 +161,7 @@ where
     F: FnOnce(&SessionGlobals) -> R,
 {
     if !SESSION_GLOBALS.is_set() {
-        let session_globals = SessionGlobals::new(edition, None);
+        let session_globals = SessionGlobals::new(edition, &[], None);
         SESSION_GLOBALS.set(&session_globals, || SESSION_GLOBALS.with(f))
     } else {
         SESSION_GLOBALS.with(f)
@@ -172,7 +177,7 @@ where
 
 /// Default edition, no source map.
 pub fn create_default_session_globals_then<R>(f: impl FnOnce() -> R) -> R {
-    create_session_globals_then(edition::DEFAULT_EDITION, None, f)
+    create_session_globals_then(edition::DEFAULT_EDITION, &[], None, f)
 }
 
 // If this ever becomes non thread-local, `decode_syntax_context`
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8a8bec35d8194..a17568c249c8b 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -2506,15 +2506,10 @@ rustc_index::newtype_index! {
 }
 
 impl Symbol {
-    const fn new(n: u32) -> Self {
+    pub const fn new(n: u32) -> Self {
         Symbol(SymbolIndex::from_u32(n))
     }
 
-    /// for use in Decoder only
-    pub fn new_from_decoded(n: u32) -> Self {
-        Self::new(n)
-    }
-
     /// Maps a string to its interned representation.
     #[rustc_diagnostic_item = "SymbolIntern"]
     pub fn intern(string: &str) -> Self {
@@ -2600,11 +2595,14 @@ struct InternerInner {
 }
 
 impl Interner {
-    fn prefill(init: &[&'static str]) -> Self {
-        Interner(Lock::new(InternerInner {
-            arena: Default::default(),
-            strings: init.iter().copied().collect(),
-        }))
+    fn prefill(init: &[&'static str], extra: &[&'static str]) -> Self {
+        let strings = FxIndexSet::from_iter(init.iter().copied().chain(extra.iter().copied()));
+        assert_eq!(
+            strings.len(),
+            init.len() + extra.len(),
+            "`init` or `extra` contain duplicate symbols",
+        );
+        Interner(Lock::new(InternerInner { arena: Default::default(), strings }))
     }
 
     #[inline]
@@ -2730,8 +2728,8 @@ impl Symbol {
     }
 
     /// Is this symbol was interned in compiler's `symbols!` macro
-    pub fn is_preinterned(self) -> bool {
-        self.as_u32() < PREINTERNED_SYMBOLS_COUNT
+    pub fn is_predefined(self) -> bool {
+        self.as_u32() < PREDEFINED_SYMBOLS_COUNT
     }
 }
 
diff --git a/compiler/rustc_span/src/symbol/tests.rs b/compiler/rustc_span/src/symbol/tests.rs
index c6aa7627b2b54..660d0d7179afa 100644
--- a/compiler/rustc_span/src/symbol/tests.rs
+++ b/compiler/rustc_span/src/symbol/tests.rs
@@ -3,7 +3,7 @@ use crate::create_default_session_globals_then;
 
 #[test]
 fn interner_tests() {
-    let i = Interner::prefill(&[]);
+    let i = Interner::prefill(&[], &[]);
     // first one is zero:
     assert_eq!(i.intern("dog"), Symbol::new(0));
     // re-use gets the same entry:
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index c47e42670c909..c4dea79370d1f 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -321,6 +321,7 @@ pub(crate) fn create_config(
                 (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id)
             };
         }),
+        extra_symbols: Vec::new(),
         make_codegen_backend: None,
         registry: rustc_driver::diagnostics_registry(),
         ice_file: None,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index a2808bddb3acc..88eaa52c6deba 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -191,6 +191,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
         hash_untracked_state: None,
         register_lints: Some(Box::new(crate::lint::register_lints)),
         override_queries: None,
+        extra_symbols: Vec::new(),
         make_codegen_backend: None,
         registry: rustc_driver::diagnostics_registry(),
         ice_file: None,
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index c4588002dc990..6c0b3af573dbe 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -25,6 +25,7 @@ path = "src/driver.rs"
 [dependencies]
 clippy_config = { path = "clippy_config" }
 clippy_lints = { path = "clippy_lints" }
+clippy_utils = { path = "clippy_utils" }
 rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
 tempfile = { version = "3.3", optional = true }
 termize = "0.1"
diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
index cd38aed26a3e0..7fab97d3ea146 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
@@ -1,10 +1,10 @@
 use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, MsrvStack};
+use clippy_utils::sym;
 use rustc_ast::AttrStyle;
 use rustc_errors::Applicability;
 use rustc_lint::EarlyContext;
-use rustc_span::sym;
 
 pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) {
     // check cfg_attr
@@ -18,7 +18,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) {
             && msrv.meets(msrvs::TOOL_ATTRIBUTES)
             // check for `rustfmt_skip` and `rustfmt::skip`
             && let Some(skip_item) = &items[1].meta_item()
-            && (skip_item.has_name(sym!(rustfmt_skip))
+            && (skip_item.has_name(sym::rustfmt_skip)
                 || skip_item
                     .path
                     .segments
diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
index e3e081ce08e9f..1cb43ab02a305 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
@@ -2,10 +2,10 @@ use super::USELESS_ATTRIBUTE;
 use super::utils::{is_lint_level, is_word, namespace_and_lint};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{SpanRangeExt, first_line_of_span};
+use clippy_utils::sym;
 use rustc_ast::{Attribute, Item, ItemKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, LintContext};
-use rustc_span::sym;
 
 pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
     let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use));
@@ -61,7 +61,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
                             if is_word(lint, sym::unused_imports) && skip_unused_imports {
                                 return;
                             }
-                            if is_word(lint, sym!(unused_extern_crates)) {
+                            if is_word(lint, sym::unused_extern_crates) {
                                 return;
                             }
                         },
diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
index 3008082c2329d..bf98b1337bc0e 100644
--- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
@@ -38,7 +38,7 @@ pub fn check(
     // of all `#[test]` attributes in not ignored code examples
     fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec<Range<usize>>) {
         rustc_driver::catch_fatal_errors(|| {
-            rustc_span::create_session_globals_then(edition, None, || {
+            rustc_span::create_session_globals_then(edition, &[], None, || {
                 let mut test_attr_spans = vec![];
                 let filename = FileName::anon_source_code(&code);
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index eb4e1a7722f3c..12f7d6416e3e2 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -3,6 +3,7 @@
 #![feature(f128)]
 #![feature(f16)]
 #![feature(if_let_guard)]
+#![feature(macro_metavar_expr)]
 #![feature(macro_metavar_expr_concat)]
 #![feature(let_chains)]
 #![feature(never_type)]
@@ -74,6 +75,7 @@ pub mod qualify_min_const_fn;
 pub mod source;
 pub mod str_utils;
 pub mod sugg;
+pub mod sym;
 pub mod ty;
 pub mod usage;
 pub mod visitors;
@@ -125,7 +127,7 @@ use rustc_middle::ty::{
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{Ident, Symbol, kw};
-use rustc_span::{InnerSpan, Span, sym};
+use rustc_span::{InnerSpan, Span};
 use visitors::{Visitable, for_each_unconsumed_temporary};
 
 use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs
new file mode 100644
index 0000000000000..1b098ea073a51
--- /dev/null
+++ b/src/tools/clippy/clippy_utils/src/sym.rs
@@ -0,0 +1,23 @@
+#![allow(non_upper_case_globals)]
+
+use rustc_span::symbol::{Symbol, PREDEFINED_SYMBOLS_COUNT};
+
+pub use rustc_span::sym::*;
+
+macro_rules! generate {
+    ($($sym:ident,)*) => {
+        /// To be supplied to [`rustc_interface::Config`]
+        pub const EXTRA_SYMBOLS: &[&str] = &[
+            $(stringify!($sym),)*
+        ];
+
+        $(
+            pub const $sym: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
+        )*
+    };
+}
+
+generate! {
+    rustfmt_skip,
+    unused_extern_crates,
+}
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index e4092bcd10564..df9c4e8e6ae9a 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -160,6 +160,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
             clippy_lints::register_lints(lint_store, conf);
             clippy_lints::register_pre_expansion_lints(lint_store, conf);
         }));
+        config.extra_symbols = clippy_utils::sym::EXTRA_SYMBOLS.into();
 
         // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
         // run on the unoptimized MIR. On the other hand this results in some false negatives. If
diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs
index ffc19b138a573..fa651baa7bc8f 100644
--- a/tests/ui-fulldeps/run-compiler-twice.rs
+++ b/tests/ui-fulldeps/run-compiler-twice.rs
@@ -70,6 +70,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path
         hash_untracked_state: None,
         register_lints: None,
         override_queries: None,
+        extra_symbols: Vec::new(),
         make_codegen_backend: None,
         registry: rustc_driver::diagnostics_registry(),
         using_internal_features: &rustc_driver::USING_INTERNAL_FEATURES,

From f9770e742ad3853d2a472e422b00cdbe8578faae Mon Sep 17 00:00:00 2001
From: Mads Marquart <mads@marquart.dk>
Date: Mon, 24 Mar 2025 21:55:14 +0100
Subject: [PATCH 04/27] Test linking and running no_std binaries

---
 tests/ui/no_std/simple-runs.rs | 41 ++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 tests/ui/no_std/simple-runs.rs

diff --git a/tests/ui/no_std/simple-runs.rs b/tests/ui/no_std/simple-runs.rs
new file mode 100644
index 0000000000000..8931ac7ed11be
--- /dev/null
+++ b/tests/ui/no_std/simple-runs.rs
@@ -0,0 +1,41 @@
+//! Check that `no_std` binaries can link and run without depending on `libstd`.
+
+//@ run-pass
+//@ compile-flags: -Cpanic=abort
+//@ ignore-wasm different `main` convention
+
+#![no_std]
+#![no_main]
+
+use core::ffi::{c_char, c_int};
+use core::panic::PanicInfo;
+
+// # Linux
+//
+// Linking `libc` is required by crt1.o, otherwise the linker fails with:
+// > /usr/bin/ld: in function `_start': undefined reference to `__libc_start_main'
+//
+// # Apple
+//
+// Linking `libSystem` is required, otherwise the linker fails with:
+// > ld: dynamic executables or dylibs must link with libSystem.dylib
+//
+// With the new linker introduced in Xcode 15, the error is instead:
+// > Undefined symbols: "dyld_stub_binder", referenced from: <initial-undefines>
+//
+// This _can_ be worked around by raising the deployment target with
+// MACOSX_DEPLOYMENT_TARGET=13.0, though it's a bit hard to test that while
+// still allowing the test suite to support running with older Xcode versions.
+#[cfg_attr(all(not(target_vendor = "apple"), unix), link(name = "c"))]
+#[cfg_attr(target_vendor = "apple", link(name = "System"))]
+extern "C" {}
+
+#[panic_handler]
+fn panic_handler(_info: &PanicInfo<'_>) -> ! {
+    loop {}
+}
+
+#[no_mangle]
+extern "C" fn main(_argc: c_int, _argv: *const *const c_char) -> c_int {
+    0
+}

From b8c4c163f0e2e3438eb474c6b0ae5fa545061aa9 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sat, 5 Apr 2025 20:37:56 +0000
Subject: [PATCH 05/27] Suppress missing field error when autoderef bottoms out
 in infer

---
 compiler/rustc_hir_typeck/src/expr.rs |  9 +++++++--
 tests/ui/typeck/issue-65611.rs        |  1 -
 tests/ui/typeck/issue-65611.stderr    | 11 ++---------
 3 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 45ab8e03db578..64465b609aded 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -2915,8 +2915,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
         // We failed to check the expression, report an error.
 
-        // Emits an error if we deref an infer variable, like calling `.field` on a base type of &_.
-        self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
+        // Emits an error if we deref an infer variable, like calling `.field` on a base type
+        // of `&_`. We can also use this to suppress unnecessary "missing field" errors that
+        // will follow ambiguity errors.
+        let final_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
+        if let ty::Error(_) = final_ty.kind() {
+            return final_ty;
+        }
 
         if let Some((adjustments, did)) = private_candidate {
             // (#90483) apply adjustments to avoid ExprUseVisitor from
diff --git a/tests/ui/typeck/issue-65611.rs b/tests/ui/typeck/issue-65611.rs
index 7645311496d8c..0dae75927a86d 100644
--- a/tests/ui/typeck/issue-65611.rs
+++ b/tests/ui/typeck/issue-65611.rs
@@ -58,6 +58,5 @@ fn main() {
     let mut buffer = ArrayVec::new();
     let x = buffer.last().unwrap().0.clone();
     //~^ ERROR type annotations needed
-    //~| ERROR no field `0` on type `&_`
     buffer.reverse();
 }
diff --git a/tests/ui/typeck/issue-65611.stderr b/tests/ui/typeck/issue-65611.stderr
index 2278450a6d8eb..52f0f0cffff9b 100644
--- a/tests/ui/typeck/issue-65611.stderr
+++ b/tests/ui/typeck/issue-65611.stderr
@@ -4,13 +4,6 @@ error[E0282]: type annotations needed
 LL |     let x = buffer.last().unwrap().0.clone();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T`
 
-error[E0609]: no field `0` on type `&_`
-  --> $DIR/issue-65611.rs:59:36
-   |
-LL |     let x = buffer.last().unwrap().0.clone();
-   |                                    ^ unknown field
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
-Some errors have detailed explanations: E0282, E0609.
-For more information about an error, try `rustc --explain E0282`.
+For more information about this error, try `rustc --explain E0282`.

From 34e97592f4dc8f4e44a309e3bf69cdcea22fc047 Mon Sep 17 00:00:00 2001
From: Zalathar <Zalathar@users.noreply.github.com>
Date: Tue, 8 Apr 2025 12:23:01 +1000
Subject: [PATCH 06/27] compiletest: Trim whitespace from environment variable
 names

---
 src/tools/compiletest/src/header.rs           | 21 ++++++++---------
 src/tools/compiletest/src/runtest.rs          |  8 +++----
 .../ui/compiletest-self-test/trim-env-name.rs | 23 +++++++++++++++++++
 3 files changed, 36 insertions(+), 16 deletions(-)
 create mode 100644 tests/ui/compiletest-self-test/trim-env-name.rs

diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index a0178f4bcc576..e3adbb66dd9e3 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -441,7 +441,7 @@ impl TestProps {
                         ln,
                         UNSET_EXEC_ENV,
                         &mut self.unset_exec_env,
-                        |r| r,
+                        |r| r.trim().to_owned(),
                     );
                     config.push_name_value_directive(
                         ln,
@@ -453,7 +453,7 @@ impl TestProps {
                         ln,
                         UNSET_RUSTC_ENV,
                         &mut self.unset_rustc_env,
-                        |r| r,
+                        |r| r.trim().to_owned(),
                     );
                     config.push_name_value_directive(
                         ln,
@@ -979,16 +979,13 @@ impl Config {
 
     fn parse_env(nv: String) -> (String, String) {
         // nv is either FOO or FOO=BAR
-        let mut strs: Vec<String> = nv.splitn(2, '=').map(str::to_owned).collect();
-
-        match strs.len() {
-            1 => (strs.pop().unwrap(), String::new()),
-            2 => {
-                let end = strs.pop().unwrap();
-                (strs.pop().unwrap(), end)
-            }
-            n => panic!("Expected 1 or 2 strings, not {}", n),
-        }
+        // FIXME(Zalathar): The form without `=` seems to be unused; should
+        // we drop support for it?
+        let (name, value) = nv.split_once('=').unwrap_or((&nv, ""));
+        // Trim whitespace from the name, so that `//@ exec-env: FOO=BAR`
+        // sees the name as `FOO` and not ` FOO`.
+        let name = name.trim();
+        (name.to_owned(), value.to_owned())
     }
 
     fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> {
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index c8a60b68da8b2..70d07b5f13232 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -971,16 +971,16 @@ impl<'test> TestCx<'test> {
         delete_after_success: bool,
     ) -> ProcRes {
         let prepare_env = |cmd: &mut Command| {
-            for key in &self.props.unset_exec_env {
-                cmd.env_remove(key);
-            }
-
             for (key, val) in &self.props.exec_env {
                 cmd.env(key, val);
             }
             for (key, val) in env_extra {
                 cmd.env(key, val);
             }
+
+            for key in &self.props.unset_exec_env {
+                cmd.env_remove(key);
+            }
         };
 
         let proc_res = match &*self.config.target {
diff --git a/tests/ui/compiletest-self-test/trim-env-name.rs b/tests/ui/compiletest-self-test/trim-env-name.rs
new file mode 100644
index 0000000000000..0cb6efe9f7658
--- /dev/null
+++ b/tests/ui/compiletest-self-test/trim-env-name.rs
@@ -0,0 +1,23 @@
+//@ edition: 2024
+//@ revisions: set unset
+//@ run-pass
+//@ ignore-cross-compile (assume that non-cross targets have working env vars)
+//@ rustc-env: MY_RUSTC_ENV =  my-rustc-value
+//@ exec-env:  MY_EXEC_ENV  =  my-exec-value
+//@[unset] unset-rustc-env:    MY_RUSTC_ENV
+//@[unset] unset-exec-env:     MY_EXEC_ENV
+
+// Check that compiletest trims whitespace from environment variable names
+// specified in `rustc-env` and `exec-env` directives, so that
+// `//@ exec-env: FOO=bar` sees the name as `FOO` and not ` FOO`.
+//
+// Values are currently not trimmed.
+//
+// Since this is a compiletest self-test, only run it on non-cross targets,
+// to avoid having to worry about weird targets that don't support env vars.
+
+fn main() {
+    let is_set = cfg!(set);
+    assert_eq!(option_env!("MY_RUSTC_ENV"), is_set.then_some("  my-rustc-value"));
+    assert_eq!(std::env::var("MY_EXEC_ENV").ok().as_deref(), is_set.then_some("  my-exec-value"));
+}

From a0d9c8726678fdc6a43d51e388e1a92e388d5fc2 Mon Sep 17 00:00:00 2001
From: Nadrieril <nadrieril+git@gmail.com>
Date: Sun, 6 Apr 2025 14:34:47 +0200
Subject: [PATCH 07/27] Inline `calc_default_binding_mode`

---
 compiler/rustc_hir_typeck/src/pat.rs | 158 +++++++++++----------------
 1 file changed, 66 insertions(+), 92 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 8641348bffbbe..e6e262279cd9f 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -163,9 +163,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 enum AdjustMode {
     /// Peel off all immediate reference types.
     Peel,
-    /// Reset binding mode to the initial mode.
-    /// Used for destructuring assignment, where we don't want any match ergonomics.
-    Reset,
     /// Pass on the input binding mode and expected type.
     Pass,
 }
@@ -322,6 +319,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self, pat_info))]
     fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>) {
         let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
+        #[cfg(debug_assertions)]
+        if binding_mode == ByRef::Yes(Mutability::Mut)
+            && max_ref_mutbl != MutblCap::Mut
+            && self.downgrade_mut_inside_shared()
+        {
+            span_bug!(pat.span, "Pattern mutability cap violated!");
+        }
 
         let path_res = match pat.kind {
             PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
@@ -330,8 +334,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => None,
         };
         let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
-        let (expected, binding_mode, max_ref_mutbl) =
-            self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl);
+        let (expected, binding_mode, max_ref_mutbl) = match adjust_mode {
+            // When we perform destructuring assignment, we disable default match bindings, which
+            // are unintuitive in this context.
+            _ if !pat.default_binding_modes => (expected, ByRef::No, MutblCap::Mut),
+            AdjustMode::Pass => (expected, binding_mode, max_ref_mutbl),
+            // Peel off as many immediately nested `& mut?` from the expected type as possible
+            // and return the new expected type and binding default binding mode.
+            // The adjustments vector, if non-empty is stored in a table.
+            AdjustMode::Peel => {
+                let mut binding_mode = binding_mode;
+                let mut max_ref_mutbl = max_ref_mutbl;
+                let mut expected = self.try_structurally_resolve_type(pat.span, expected);
+                // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
+                // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
+                // the `Some(5)` which is not of type Ref.
+                //
+                // For each ampersand peeled off, update the binding mode and push the original
+                // type into the adjustments vector.
+                //
+                // See the examples in `ui/match-defbm*.rs`.
+                let mut pat_adjustments = vec![];
+                while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
+                    debug!("inspecting {:?}", expected);
+
+                    debug!("current discriminant is Ref, inserting implicit deref");
+                    // Preserve the reference type. We'll need it later during THIR lowering.
+                    pat_adjustments.push(expected);
+
+                    expected = self.try_structurally_resolve_type(pat.span, inner_ty);
+                    binding_mode = ByRef::Yes(match binding_mode {
+                        // If default binding mode is by value, make it `ref` or `ref mut`
+                        // (depending on whether we observe `&` or `&mut`).
+                        ByRef::No |
+                        // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
+                        ByRef::Yes(Mutability::Mut) => inner_mutability,
+                        // Once a `ref`, always a `ref`.
+                        // This is because a `& &mut` cannot mutate the underlying value.
+                        ByRef::Yes(Mutability::Not) => Mutability::Not,
+                    });
+                }
+
+                if self.downgrade_mut_inside_shared() {
+                    binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
+                }
+                if binding_mode == ByRef::Yes(Mutability::Not) {
+                    max_ref_mutbl = MutblCap::Not;
+                }
+
+                if !pat_adjustments.is_empty() {
+                    debug!("default binding mode is now {:?}", binding_mode);
+                    self.typeck_results
+                        .borrow_mut()
+                        .pat_adjustments_mut()
+                        .insert(pat.hir_id, pat_adjustments);
+                }
+
+                (expected, binding_mode, max_ref_mutbl)
+            }
+        };
         let pat_info = PatInfo {
             binding_mode,
             max_ref_mutbl,
@@ -437,39 +498,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
     }
 
-    /// Compute the new expected type and default binding mode from the old ones
-    /// as well as the pattern form we are currently checking.
-    fn calc_default_binding_mode(
-        &self,
-        pat: &'tcx Pat<'tcx>,
-        expected: Ty<'tcx>,
-        def_br: ByRef,
-        adjust_mode: AdjustMode,
-        max_ref_mutbl: MutblCap,
-    ) -> (Ty<'tcx>, ByRef, MutblCap) {
-        #[cfg(debug_assertions)]
-        if def_br == ByRef::Yes(Mutability::Mut)
-            && max_ref_mutbl != MutblCap::Mut
-            && self.downgrade_mut_inside_shared()
-        {
-            span_bug!(pat.span, "Pattern mutability cap violated!");
-        }
-        match adjust_mode {
-            AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
-            AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut),
-            AdjustMode::Peel => self.peel_off_references(pat, expected, def_br, max_ref_mutbl),
-        }
-    }
-
     /// How should the binding mode and expected type be adjusted?
     ///
     /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
     fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
-        // When we perform destructuring assignment, we disable default match bindings, which are
-        // unintuitive in this context.
-        if !pat.default_binding_modes {
-            return AdjustMode::Reset;
-        }
         match &pat.kind {
             // Type checking these product-like types successfully always require
             // that the expected type be of those types and not reference types.
@@ -526,64 +558,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    /// Peel off as many immediately nested `& mut?` from the expected type as possible
-    /// and return the new expected type and binding default binding mode.
-    /// The adjustments vector, if non-empty is stored in a table.
-    fn peel_off_references(
-        &self,
-        pat: &'tcx Pat<'tcx>,
-        expected: Ty<'tcx>,
-        mut def_br: ByRef,
-        mut max_ref_mutbl: MutblCap,
-    ) -> (Ty<'tcx>, ByRef, MutblCap) {
-        let mut expected = self.try_structurally_resolve_type(pat.span, expected);
-        // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
-        // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
-        // the `Some(5)` which is not of type Ref.
-        //
-        // For each ampersand peeled off, update the binding mode and push the original
-        // type into the adjustments vector.
-        //
-        // See the examples in `ui/match-defbm*.rs`.
-        let mut pat_adjustments = vec![];
-        while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
-            debug!("inspecting {:?}", expected);
-
-            debug!("current discriminant is Ref, inserting implicit deref");
-            // Preserve the reference type. We'll need it later during THIR lowering.
-            pat_adjustments.push(expected);
-
-            expected = self.try_structurally_resolve_type(pat.span, inner_ty);
-            def_br = ByRef::Yes(match def_br {
-                // If default binding mode is by value, make it `ref` or `ref mut`
-                // (depending on whether we observe `&` or `&mut`).
-                ByRef::No |
-                // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
-                ByRef::Yes(Mutability::Mut) => inner_mutability,
-                // Once a `ref`, always a `ref`.
-                // This is because a `& &mut` cannot mutate the underlying value.
-                ByRef::Yes(Mutability::Not) => Mutability::Not,
-            });
-        }
-
-        if self.downgrade_mut_inside_shared() {
-            def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
-        }
-        if def_br == ByRef::Yes(Mutability::Not) {
-            max_ref_mutbl = MutblCap::Not;
-        }
-
-        if !pat_adjustments.is_empty() {
-            debug!("default binding mode is now {:?}", def_br);
-            self.typeck_results
-                .borrow_mut()
-                .pat_adjustments_mut()
-                .insert(pat.hir_id, pat_adjustments);
-        }
-
-        (expected, def_br, max_ref_mutbl)
-    }
-
     fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> {
         let ty = match &lt.kind {
             rustc_hir::PatExprKind::Lit { lit, negated } => {

From 9f57903e9cdafda3e32ce0e3472ad7fe2a9c1e3a Mon Sep 17 00:00:00 2001
From: Nadrieril <nadrieril+git@gmail.com>
Date: Sun, 6 Apr 2025 14:38:16 +0200
Subject: [PATCH 08/27] Insert adjustments incrementally

---
 compiler/rustc_hir_typeck/src/pat.rs | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index e6e262279cd9f..70eb21ca3560f 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -354,13 +354,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // type into the adjustments vector.
                 //
                 // See the examples in `ui/match-defbm*.rs`.
-                let mut pat_adjustments = vec![];
                 while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
                     debug!("inspecting {:?}", expected);
 
                     debug!("current discriminant is Ref, inserting implicit deref");
                     // Preserve the reference type. We'll need it later during THIR lowering.
-                    pat_adjustments.push(expected);
+                    self.typeck_results
+                        .borrow_mut()
+                        .pat_adjustments_mut()
+                        .entry(pat.hir_id)
+                        .or_default()
+                        .push(expected);
 
                     expected = self.try_structurally_resolve_type(pat.span, inner_ty);
                     binding_mode = ByRef::Yes(match binding_mode {
@@ -382,13 +386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     max_ref_mutbl = MutblCap::Not;
                 }
 
-                if !pat_adjustments.is_empty() {
-                    debug!("default binding mode is now {:?}", binding_mode);
-                    self.typeck_results
-                        .borrow_mut()
-                        .pat_adjustments_mut()
-                        .insert(pat.hir_id, pat_adjustments);
-                }
+                debug!("default binding mode is now {:?}", binding_mode);
 
                 (expected, binding_mode, max_ref_mutbl)
             }

From 19950b52c70a5989c4937c5a91e1184439bdad5c Mon Sep 17 00:00:00 2001
From: Nadrieril <nadrieril+git@gmail.com>
Date: Sun, 6 Apr 2025 14:52:52 +0200
Subject: [PATCH 09/27] Turn the peeling loop into a recursive call

---
 compiler/rustc_hir_typeck/src/pat.rs | 80 ++++++++++++++++------------
 1 file changed, 46 insertions(+), 34 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 70eb21ca3560f..ca51a28c4be85 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -318,6 +318,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Conversely, inside this module, `check_pat_top` should never be used.
     #[instrument(level = "debug", skip(self, pat_info))]
     fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>) {
+        let opt_path_res = match pat.kind {
+            PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
+                Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span))
+            }
+            _ => None,
+        };
+        let adjust_mode = self.calc_adjust_mode(pat, opt_path_res.map(|(res, ..)| res));
+        self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
+    }
+
+    // Helper to avoid resolving the same path pattern several times.
+    fn check_pat_inner(
+        &self,
+        pat: &'tcx Pat<'tcx>,
+        opt_path_res: Option<(Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>])>,
+        adjust_mode: AdjustMode,
+        expected: Ty<'tcx>,
+        pat_info: PatInfo<'tcx>,
+    ) {
         let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
         #[cfg(debug_assertions)]
         if binding_mode == ByRef::Yes(Mutability::Mut)
@@ -327,34 +346,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             span_bug!(pat.span, "Pattern mutability cap violated!");
         }
 
-        let path_res = match pat.kind {
-            PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
-                Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span))
-            }
-            _ => None,
-        };
-        let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
         let (expected, binding_mode, max_ref_mutbl) = match adjust_mode {
             // When we perform destructuring assignment, we disable default match bindings, which
             // are unintuitive in this context.
             _ if !pat.default_binding_modes => (expected, ByRef::No, MutblCap::Mut),
             AdjustMode::Pass => (expected, binding_mode, max_ref_mutbl),
-            // Peel off as many immediately nested `& mut?` from the expected type as possible
-            // and return the new expected type and binding default binding mode.
-            // The adjustments vector, if non-empty is stored in a table.
+            // Peel an immediately nested `& mut?` from the expected type if possible and return the
+            // new expected type and binding default binding mode.
             AdjustMode::Peel => {
-                let mut binding_mode = binding_mode;
-                let mut max_ref_mutbl = max_ref_mutbl;
-                let mut expected = self.try_structurally_resolve_type(pat.span, expected);
-                // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
-                // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
-                // the `Some(5)` which is not of type Ref.
-                //
-                // For each ampersand peeled off, update the binding mode and push the original
-                // type into the adjustments vector.
+                let expected = self.try_structurally_resolve_type(pat.span, expected);
+                // Peel off a `&` or `&mut` from the scrutinee type. For each ampersand peeled off,
+                // update the binding mode and push the original type into the adjustments vector.
                 //
-                // See the examples in `ui/match-defbm*.rs`.
-                while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
+                // See the examples in `tests/ui/rfcs/rfc-2005-default-binding-mode`.
+                if let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
                     debug!("inspecting {:?}", expected);
 
                     debug!("current discriminant is Ref, inserting implicit deref");
@@ -366,8 +371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .or_default()
                         .push(expected);
 
-                    expected = self.try_structurally_resolve_type(pat.span, inner_ty);
-                    binding_mode = ByRef::Yes(match binding_mode {
+                    let mut binding_mode = ByRef::Yes(match binding_mode {
                         // If default binding mode is by value, make it `ref` or `ref mut`
                         // (depending on whether we observe `&` or `&mut`).
                         ByRef::No |
@@ -377,18 +381,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // This is because a `& &mut` cannot mutate the underlying value.
                         ByRef::Yes(Mutability::Not) => Mutability::Not,
                     });
-                }
 
-                if self.downgrade_mut_inside_shared() {
-                    binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
-                }
-                if binding_mode == ByRef::Yes(Mutability::Not) {
-                    max_ref_mutbl = MutblCap::Not;
+                    if self.downgrade_mut_inside_shared() {
+                        binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
+                    }
+                    let mut max_ref_mutbl = max_ref_mutbl;
+                    if binding_mode == ByRef::Yes(Mutability::Not) {
+                        max_ref_mutbl = MutblCap::Not;
+                    }
+                    debug!("default binding mode is now {:?}", binding_mode);
+                    let pat_info = PatInfo { binding_mode, max_ref_mutbl, ..pat_info };
+                    return self.check_pat_inner(
+                        pat,
+                        opt_path_res,
+                        adjust_mode,
+                        inner_ty,
+                        pat_info,
+                    );
+                } else {
+                    (expected, binding_mode, max_ref_mutbl)
                 }
-
-                debug!("default binding mode is now {:?}", binding_mode);
-
-                (expected, binding_mode, max_ref_mutbl)
             }
         };
         let pat_info = PatInfo {
@@ -409,7 +421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     pat.hir_id,
                     *span,
                     qpath,
-                    path_res.unwrap(),
+                    opt_path_res.unwrap(),
                     expected,
                     &pat_info.top_info,
                 );

From 38adb9931cd934fefe2a2f65ca0071612c9ec583 Mon Sep 17 00:00:00 2001
From: Nadrieril <nadrieril+git@gmail.com>
Date: Sun, 6 Apr 2025 15:08:55 +0200
Subject: [PATCH 10/27] Reorganize `check_pat_inner`

---
 compiler/rustc_hir_typeck/src/pat.rs | 118 ++++++++++++++-------------
 1 file changed, 61 insertions(+), 57 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index ca51a28c4be85..8f01bb1c297c6 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -337,7 +337,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         pat_info: PatInfo<'tcx>,
     ) {
-        let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
+        let PatInfo { mut binding_mode, mut max_ref_mutbl, current_depth, .. } = pat_info;
         #[cfg(debug_assertions)]
         if binding_mode == ByRef::Yes(Mutability::Mut)
             && max_ref_mutbl != MutblCap::Mut
@@ -346,72 +346,76 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             span_bug!(pat.span, "Pattern mutability cap violated!");
         }
 
-        let (expected, binding_mode, max_ref_mutbl) = match adjust_mode {
+        if !pat.default_binding_modes {
             // When we perform destructuring assignment, we disable default match bindings, which
             // are unintuitive in this context.
-            _ if !pat.default_binding_modes => (expected, ByRef::No, MutblCap::Mut),
-            AdjustMode::Pass => (expected, binding_mode, max_ref_mutbl),
-            // Peel an immediately nested `& mut?` from the expected type if possible and return the
-            // new expected type and binding default binding mode.
-            AdjustMode::Peel => {
-                let expected = self.try_structurally_resolve_type(pat.span, expected);
-                // Peel off a `&` or `&mut` from the scrutinee type. For each ampersand peeled off,
-                // update the binding mode and push the original type into the adjustments vector.
-                //
-                // See the examples in `tests/ui/rfcs/rfc-2005-default-binding-mode`.
-                if let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
-                    debug!("inspecting {:?}", expected);
-
-                    debug!("current discriminant is Ref, inserting implicit deref");
-                    // Preserve the reference type. We'll need it later during THIR lowering.
-                    self.typeck_results
-                        .borrow_mut()
-                        .pat_adjustments_mut()
-                        .entry(pat.hir_id)
-                        .or_default()
-                        .push(expected);
-
-                    let mut binding_mode = ByRef::Yes(match binding_mode {
-                        // If default binding mode is by value, make it `ref` or `ref mut`
-                        // (depending on whether we observe `&` or `&mut`).
-                        ByRef::No |
-                        // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
-                        ByRef::Yes(Mutability::Mut) => inner_mutability,
-                        // Once a `ref`, always a `ref`.
-                        // This is because a `& &mut` cannot mutate the underlying value.
-                        ByRef::Yes(Mutability::Not) => Mutability::Not,
-                    });
-
-                    if self.downgrade_mut_inside_shared() {
-                        binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
-                    }
-                    let mut max_ref_mutbl = max_ref_mutbl;
-                    if binding_mode == ByRef::Yes(Mutability::Not) {
-                        max_ref_mutbl = MutblCap::Not;
-                    }
-                    debug!("default binding mode is now {:?}", binding_mode);
-                    let pat_info = PatInfo { binding_mode, max_ref_mutbl, ..pat_info };
-                    return self.check_pat_inner(
-                        pat,
-                        opt_path_res,
-                        adjust_mode,
-                        inner_ty,
-                        pat_info,
-                    );
-                } else {
-                    (expected, binding_mode, max_ref_mutbl)
-                }
-            }
+            binding_mode = ByRef::No;
+            max_ref_mutbl = MutblCap::Mut;
+        };
+        // Resolve type if needed.
+        let expected = if let AdjustMode::Peel = adjust_mode
+            && pat.default_binding_modes
+        {
+            self.try_structurally_resolve_type(pat.span, expected)
+        } else {
+            expected
         };
+        let old_pat_info = pat_info;
         let pat_info = PatInfo {
             binding_mode,
             max_ref_mutbl,
-            top_info: ti,
-            decl_origin: pat_info.decl_origin,
             current_depth: current_depth + 1,
+            top_info: old_pat_info.top_info,
+            decl_origin: old_pat_info.decl_origin,
         };
 
         let ty = match pat.kind {
+            // Peel off a `&` or `&mut` from the scrutinee type. See the examples in
+            // `tests/ui/rfcs/rfc-2005-default-binding-mode`.
+            _ if let AdjustMode::Peel = adjust_mode
+                && pat.default_binding_modes
+                && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() =>
+            {
+                debug!("inspecting {:?}", expected);
+
+                debug!("current discriminant is Ref, inserting implicit deref");
+                // Preserve the reference type. We'll need it later during THIR lowering.
+                self.typeck_results
+                    .borrow_mut()
+                    .pat_adjustments_mut()
+                    .entry(pat.hir_id)
+                    .or_default()
+                    .push(expected);
+
+                binding_mode = ByRef::Yes(match binding_mode {
+                    // If default binding mode is by value, make it `ref` or `ref mut`
+                    // (depending on whether we observe `&` or `&mut`).
+                    ByRef::No |
+                    // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
+                    ByRef::Yes(Mutability::Mut) => inner_mutability,
+                    // Once a `ref`, always a `ref`.
+                    // This is because a `& &mut` cannot mutate the underlying value.
+                    ByRef::Yes(Mutability::Not) => Mutability::Not,
+                });
+
+                if self.downgrade_mut_inside_shared() {
+                    binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
+                }
+                if binding_mode == ByRef::Yes(Mutability::Not) {
+                    max_ref_mutbl = MutblCap::Not;
+                }
+                debug!("default binding mode is now {:?}", binding_mode);
+
+                // Use the old pat info to keep `current_depth` to its old value.
+                let new_pat_info = PatInfo { binding_mode, max_ref_mutbl, ..old_pat_info };
+                return self.check_pat_inner(
+                    pat,
+                    opt_path_res,
+                    adjust_mode,
+                    inner_ty,
+                    new_pat_info,
+                );
+            }
             PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
             // We allow any type here; we ensure that the type is uninhabited during match checking.
             PatKind::Never => expected,

From 6588018fb413efe0476773982355ba772671bd85 Mon Sep 17 00:00:00 2001
From: Nadrieril <nadrieril+git@gmail.com>
Date: Sun, 6 Apr 2025 15:10:47 +0200
Subject: [PATCH 11/27] Return a type from `check_pat_inner`

---
 compiler/rustc_hir_typeck/src/pat.rs | 104 +++++++++++++--------------
 1 file changed, 49 insertions(+), 55 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 8f01bb1c297c6..c635c2c3f2d22 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -325,7 +325,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => None,
         };
         let adjust_mode = self.calc_adjust_mode(pat, opt_path_res.map(|(res, ..)| res));
-        self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
+        let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
+        self.write_ty(pat.hir_id, ty);
+
+        // (note_1): In most of the cases where (note_1) is referenced
+        // (literals and constants being the exception), we relate types
+        // using strict equality, even though subtyping would be sufficient.
+        // There are a few reasons for this, some of which are fairly subtle
+        // and which cost me (nmatsakis) an hour or two debugging to remember,
+        // so I thought I'd write them down this time.
+        //
+        // 1. There is no loss of expressiveness here, though it does
+        // cause some inconvenience. What we are saying is that the type
+        // of `x` becomes *exactly* what is expected. This can cause unnecessary
+        // errors in some cases, such as this one:
+        //
+        // ```
+        // fn foo<'x>(x: &'x i32) {
+        //    let a = 1;
+        //    let mut z = x;
+        //    z = &a;
+        // }
+        // ```
+        //
+        // The reason we might get an error is that `z` might be
+        // assigned a type like `&'x i32`, and then we would have
+        // a problem when we try to assign `&a` to `z`, because
+        // the lifetime of `&a` (i.e., the enclosing block) is
+        // shorter than `'x`.
+        //
+        // HOWEVER, this code works fine. The reason is that the
+        // expected type here is whatever type the user wrote, not
+        // the initializer's type. In this case the user wrote
+        // nothing, so we are going to create a type variable `Z`.
+        // Then we will assign the type of the initializer (`&'x i32`)
+        // as a subtype of `Z`: `&'x i32 <: Z`. And hence we
+        // will instantiate `Z` as a type `&'0 i32` where `'0` is
+        // a fresh region variable, with the constraint that `'x : '0`.
+        // So basically we're all set.
+        //
+        // Note that there are two tests to check that this remains true
+        // (`regions-reassign-{match,let}-bound-pointer.rs`).
+        //
+        // 2. An outdated issue related to the old HIR borrowck. See the test
+        // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
     }
 
     // Helper to avoid resolving the same path pattern several times.
@@ -336,7 +379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         adjust_mode: AdjustMode,
         expected: Ty<'tcx>,
         pat_info: PatInfo<'tcx>,
-    ) {
+    ) -> Ty<'tcx> {
         let PatInfo { mut binding_mode, mut max_ref_mutbl, current_depth, .. } = pat_info;
         #[cfg(debug_assertions)]
         if binding_mode == ByRef::Yes(Mutability::Mut)
@@ -369,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             decl_origin: old_pat_info.decl_origin,
         };
 
-        let ty = match pat.kind {
+        match pat.kind {
             // Peel off a `&` or `&mut` from the scrutinee type. See the examples in
             // `tests/ui/rfcs/rfc-2005-default-binding-mode`.
             _ if let AdjustMode::Peel = adjust_mode
@@ -408,13 +451,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // Use the old pat info to keep `current_depth` to its old value.
                 let new_pat_info = PatInfo { binding_mode, max_ref_mutbl, ..old_pat_info };
-                return self.check_pat_inner(
-                    pat,
-                    opt_path_res,
-                    adjust_mode,
-                    inner_ty,
-                    new_pat_info,
-                );
+                // Recurse with the new expected type.
+                self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
             }
             PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
             // We allow any type here; we ensure that the type is uninhabited during match checking.
@@ -465,51 +503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             PatKind::Slice(before, slice, after) => {
                 self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
             }
-        };
-
-        self.write_ty(pat.hir_id, ty);
-
-        // (note_1): In most of the cases where (note_1) is referenced
-        // (literals and constants being the exception), we relate types
-        // using strict equality, even though subtyping would be sufficient.
-        // There are a few reasons for this, some of which are fairly subtle
-        // and which cost me (nmatsakis) an hour or two debugging to remember,
-        // so I thought I'd write them down this time.
-        //
-        // 1. There is no loss of expressiveness here, though it does
-        // cause some inconvenience. What we are saying is that the type
-        // of `x` becomes *exactly* what is expected. This can cause unnecessary
-        // errors in some cases, such as this one:
-        //
-        // ```
-        // fn foo<'x>(x: &'x i32) {
-        //    let a = 1;
-        //    let mut z = x;
-        //    z = &a;
-        // }
-        // ```
-        //
-        // The reason we might get an error is that `z` might be
-        // assigned a type like `&'x i32`, and then we would have
-        // a problem when we try to assign `&a` to `z`, because
-        // the lifetime of `&a` (i.e., the enclosing block) is
-        // shorter than `'x`.
-        //
-        // HOWEVER, this code works fine. The reason is that the
-        // expected type here is whatever type the user wrote, not
-        // the initializer's type. In this case the user wrote
-        // nothing, so we are going to create a type variable `Z`.
-        // Then we will assign the type of the initializer (`&'x i32`)
-        // as a subtype of `Z`: `&'x i32 <: Z`. And hence we
-        // will instantiate `Z` as a type `&'0 i32` where `'0` is
-        // a fresh region variable, with the constraint that `'x : '0`.
-        // So basically we're all set.
-        //
-        // Note that there are two tests to check that this remains true
-        // (`regions-reassign-{match,let}-bound-pointer.rs`).
-        //
-        // 2. An outdated issue related to the old HIR borrowck. See the test
-        // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
+        }
     }
 
     /// How should the binding mode and expected type be adjusted?

From f458151b7aa773a46dfce8c58aa41de9ed697c5e Mon Sep 17 00:00:00 2001
From: Nadrieril <nadrieril+git@gmail.com>
Date: Sun, 6 Apr 2025 15:48:07 +0200
Subject: [PATCH 12/27] Remove redundant assignment

---
 compiler/rustc_hir_typeck/src/pat.rs | 22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index c635c2c3f2d22..fbc783c050904 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -380,21 +380,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         pat_info: PatInfo<'tcx>,
     ) -> Ty<'tcx> {
-        let PatInfo { mut binding_mode, mut max_ref_mutbl, current_depth, .. } = pat_info;
         #[cfg(debug_assertions)]
-        if binding_mode == ByRef::Yes(Mutability::Mut)
-            && max_ref_mutbl != MutblCap::Mut
+        if pat_info.binding_mode == ByRef::Yes(Mutability::Mut)
+            && pat_info.max_ref_mutbl != MutblCap::Mut
             && self.downgrade_mut_inside_shared()
         {
             span_bug!(pat.span, "Pattern mutability cap violated!");
         }
 
-        if !pat.default_binding_modes {
-            // When we perform destructuring assignment, we disable default match bindings, which
-            // are unintuitive in this context.
-            binding_mode = ByRef::No;
-            max_ref_mutbl = MutblCap::Mut;
-        };
         // Resolve type if needed.
         let expected = if let AdjustMode::Peel = adjust_mode
             && pat.default_binding_modes
@@ -404,13 +397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             expected
         };
         let old_pat_info = pat_info;
-        let pat_info = PatInfo {
-            binding_mode,
-            max_ref_mutbl,
-            current_depth: current_depth + 1,
-            top_info: old_pat_info.top_info,
-            decl_origin: old_pat_info.decl_origin,
-        };
+        let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info };
 
         match pat.kind {
             // Peel off a `&` or `&mut` from the scrutinee type. See the examples in
@@ -430,7 +417,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .or_default()
                     .push(expected);
 
-                binding_mode = ByRef::Yes(match binding_mode {
+                let mut binding_mode = ByRef::Yes(match pat_info.binding_mode {
                     // If default binding mode is by value, make it `ref` or `ref mut`
                     // (depending on whether we observe `&` or `&mut`).
                     ByRef::No |
@@ -441,6 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ByRef::Yes(Mutability::Not) => Mutability::Not,
                 });
 
+                let mut max_ref_mutbl = pat_info.max_ref_mutbl;
                 if self.downgrade_mut_inside_shared() {
                     binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
                 }

From 12311ef8ac93028f55fae69cb2e3caabab93ad49 Mon Sep 17 00:00:00 2001
From: Jacob Lifshay <programmerjake@gmail.com>
Date: Tue, 8 Apr 2025 16:21:52 -0700
Subject: [PATCH 13/27] fix title of offset_of_enum feature

---
 src/doc/unstable-book/src/language-features/offset-of-enum.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/unstable-book/src/language-features/offset-of-enum.md b/src/doc/unstable-book/src/language-features/offset-of-enum.md
index 1960d6299eb00..78c0d87f639ed 100644
--- a/src/doc/unstable-book/src/language-features/offset-of-enum.md
+++ b/src/doc/unstable-book/src/language-features/offset-of-enum.md
@@ -1,4 +1,4 @@
-# `offset_of_slice`
+# `offset_of_enum`
 
 The tracking issue for this feature is: [#120141]
 

From f151ceadfe19a4c12c1381468cc8c516f3be9f39 Mon Sep 17 00:00:00 2001
From: Manuel Drehwald <git@manuel.drehwald.info>
Date: Tue, 8 Apr 2025 21:54:34 -0400
Subject: [PATCH 14/27] emit a better error message for using the macro
 incorrectly

---
 compiler/rustc_builtin_macros/src/autodiff.rs | 2 +-
 tests/ui/autodiff/autodiff_illegal.rs         | 4 ++--
 tests/ui/autodiff/autodiff_illegal.stderr     | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs
index 351413dea493c..4161829480d38 100644
--- a/compiler/rustc_builtin_macros/src/autodiff.rs
+++ b/compiler/rustc_builtin_macros/src/autodiff.rs
@@ -234,7 +234,7 @@ mod llvm_enzyme {
         let meta_item_vec: ThinVec<MetaItemInner> = match meta_item.kind {
             ast::MetaItemKind::List(ref vec) => vec.clone(),
             _ => {
-                dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
+                dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() });
                 return vec![item];
             }
         };
diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs
index 2f2cd8d93532f..a916bd8b857b9 100644
--- a/tests/ui/autodiff/autodiff_illegal.rs
+++ b/tests/ui/autodiff/autodiff_illegal.rs
@@ -63,7 +63,7 @@ fn dummy() {
 // Malformed, where args?
 #[autodiff]
 pub fn f7(x: f64) {
-    //~^ ERROR autodiff must be applied to function
+    //~^ ERROR autodiff requires at least a name and mode
     unimplemented!()
 }
 
@@ -77,7 +77,7 @@ pub fn f8(x: f64) {
 // Invalid attribute syntax
 #[autodiff = ""]
 pub fn f9(x: f64) {
-    //~^ ERROR autodiff must be applied to function
+    //~^ ERROR autodiff requires at least a name and mode
     unimplemented!()
 }
 
diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr
index 3752b27e7dd1b..b119f61b8ae66 100644
--- a/tests/ui/autodiff/autodiff_illegal.stderr
+++ b/tests/ui/autodiff/autodiff_illegal.stderr
@@ -62,7 +62,7 @@ error: autodiff must be applied to function
 LL |     let add_one_v2 = |x: u32| -> u32 { x + 1 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: autodiff must be applied to function
+error: autodiff requires at least a name and mode
   --> $DIR/autodiff_illegal.rs:65:1
    |
 LL | / pub fn f7(x: f64) {
@@ -80,7 +80,7 @@ LL | |     unimplemented!()
 LL | | }
    | |_^
 
-error: autodiff must be applied to function
+error: autodiff requires at least a name and mode
   --> $DIR/autodiff_illegal.rs:79:1
    |
 LL | / pub fn f9(x: f64) {

From f419b18d16be992b5db21e0008aa7be16f92803f Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Wed, 9 Apr 2025 09:30:51 +1000
Subject: [PATCH 15/27] Return early on an error path in `parse_item_impl`.

Currently the code continues, using an empty path, but it doesn't need
to.
---
 compiler/rustc_parse/src/parser/item.rs | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 3647bf2c378ee..915a30e5469e6 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -602,21 +602,13 @@ impl<'a> Parser<'a> {
         let polarity = self.parse_polarity();
 
         // Parse both types and traits as a type, then reinterpret if necessary.
-        let err_path = |span| ast::Path::from_ident(Ident::new(kw::Empty, span));
         let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt)
         {
             let span = self.prev_token.span.between(self.token.span);
-            self.dcx().emit_err(errors::MissingTraitInTraitImpl {
+            return Err(self.dcx().create_err(errors::MissingTraitInTraitImpl {
                 span,
                 for_span: span.to(self.token.span),
-            });
-
-            P(Ty {
-                kind: TyKind::Path(None, err_path(span)),
-                span,
-                id: DUMMY_NODE_ID,
-                tokens: None,
-            })
+            }));
         } else {
             self.parse_ty_with_generics_recovery(&generics)?
         };
@@ -671,7 +663,7 @@ impl<'a> Parser<'a> {
                                 span: ty_first.span,
                             });
                         }
-                        err_path(ty_first.span)
+                        ast::Path::from_ident(Ident::new(kw::Empty, ty_first.span))
                     }
                 };
                 let trait_ref = TraitRef { path, ref_id: ty_first.id };

From 7ae5c7f32dfc85c0e57949dee1c54bd1c6586e37 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Wed, 9 Apr 2025 14:16:22 +1000
Subject: [PATCH 16/27] Avoid an empty trait name in impl blocks.

`resolve_ident_in_lexical_scope` checks for an empty name. Why is this
necessary? Because `parse_item_impl` can produce an `impl` block with an
empty trait name in some cases. This is pretty gross and very
non-obvious.

This commit avoids the use of the empty trait name. In one case the
trait name is instead pulled from `TyKind::ImplTrait`, which prevents
the output for `tests/ui/impl-trait/extra-impl-in-trait-impl.rs` from
changing. In the other case we just fail the parse and don't try to
recover. I think losing error recovery in this obscure case is worth
the code cleanup.

This change affects `tests/ui/parser/impl-parsing.rs`, which is split in
two, and the obsolete `..` syntax cases are removed (they are tested
elsewhere).
---
 compiler/rustc_parse/src/parser/item.rs |  9 +++---
 compiler/rustc_resolve/src/ident.rs     |  3 --
 tests/ui/parser/impl-parsing-2.rs       |  4 +++
 tests/ui/parser/impl-parsing-2.stderr   | 18 ++++++++++++
 tests/ui/parser/impl-parsing.rs         |  5 ----
 tests/ui/parser/impl-parsing.stderr     | 37 ++-----------------------
 6 files changed, 29 insertions(+), 47 deletions(-)
 create mode 100644 tests/ui/parser/impl-parsing-2.rs
 create mode 100644 tests/ui/parser/impl-parsing-2.stderr

diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 915a30e5469e6..0650181634075 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -649,6 +649,7 @@ impl<'a> Parser<'a> {
                     other => {
                         if let TyKind::ImplTrait(_, bounds) = other
                             && let [bound] = bounds.as_slice()
+                            && let GenericBound::Trait(poly_trait_ref) = bound
                         {
                             // Suggest removing extra `impl` keyword:
                             // `impl<T: Default> impl Default for Wrapper<T>`
@@ -658,12 +659,12 @@ impl<'a> Parser<'a> {
                                 extra_impl_kw,
                                 impl_trait_span: ty_first.span,
                             });
+                            poly_trait_ref.trait_ref.path.clone()
                         } else {
-                            self.dcx().emit_err(errors::ExpectedTraitInTraitImplFoundType {
-                                span: ty_first.span,
-                            });
+                            return Err(self.dcx().create_err(
+                                errors::ExpectedTraitInTraitImplFoundType { span: ty_first.span },
+                            ));
                         }
-                        ast::Path::from_ident(Ident::new(kw::Empty, ty_first.span))
                     }
                 };
                 let trait_ref = TraitRef { path, ref_id: ty_first.id };
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 5f0a2a597e9b4..180d6af219d11 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -296,9 +296,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
     ) -> Option<LexicalScopeBinding<'ra>> {
         assert!(ns == TypeNS || ns == ValueNS);
         let orig_ident = ident;
-        if ident.name == kw::Empty {
-            return Some(LexicalScopeBinding::Res(Res::Err));
-        }
         let (general_span, normalized_span) = if ident.name == kw::SelfUpper {
             // FIXME(jseyfried) improve `Self` hygiene
             let empty_span = ident.span.with_ctxt(SyntaxContext::root());
diff --git a/tests/ui/parser/impl-parsing-2.rs b/tests/ui/parser/impl-parsing-2.rs
new file mode 100644
index 0000000000000..7a71217b21c59
--- /dev/null
+++ b/tests/ui/parser/impl-parsing-2.rs
@@ -0,0 +1,4 @@
+impl ! {} // OK
+
+default unsafe FAIL //~ ERROR expected item, found keyword `unsafe`
+//~^ ERROR `default` is not followed by an item
diff --git a/tests/ui/parser/impl-parsing-2.stderr b/tests/ui/parser/impl-parsing-2.stderr
new file mode 100644
index 0000000000000..45e2c428242d6
--- /dev/null
+++ b/tests/ui/parser/impl-parsing-2.stderr
@@ -0,0 +1,18 @@
+error: `default` is not followed by an item
+  --> $DIR/impl-parsing-2.rs:3:1
+   |
+LL | default unsafe FAIL
+   | ^^^^^^^ the `default` qualifier
+   |
+   = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`
+
+error: expected item, found keyword `unsafe`
+  --> $DIR/impl-parsing-2.rs:3:9
+   |
+LL | default unsafe FAIL
+   |         ^^^^^^ expected item
+   |
+   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/parser/impl-parsing.rs b/tests/ui/parser/impl-parsing.rs
index 80ce888557078..7692a81dd42cd 100644
--- a/tests/ui/parser/impl-parsing.rs
+++ b/tests/ui/parser/impl-parsing.rs
@@ -2,9 +2,4 @@ impl ! {} // OK
 impl ! where u8: Copy {} // OK
 
 impl Trait Type {} //~ ERROR missing `for` in a trait impl
-impl Trait .. {} //~ ERROR missing `for` in a trait impl
 impl ?Sized for Type {} //~ ERROR expected a trait, found type
-impl ?Sized for .. {} //~ ERROR expected a trait, found type
-
-default unsafe FAIL //~ ERROR expected item, found keyword `unsafe`
-//~^ ERROR `default` is not followed by an item
diff --git a/tests/ui/parser/impl-parsing.stderr b/tests/ui/parser/impl-parsing.stderr
index 6a24a9453e631..b2512120dc87c 100644
--- a/tests/ui/parser/impl-parsing.stderr
+++ b/tests/ui/parser/impl-parsing.stderr
@@ -9,44 +9,11 @@ help: add `for` here
 LL | impl Trait for Type {}
    |            +++
 
-error: missing `for` in a trait impl
-  --> $DIR/impl-parsing.rs:5:11
-   |
-LL | impl Trait .. {}
-   |           ^
-   |
-help: add `for` here
-   |
-LL | impl Trait for .. {}
-   |            +++
-
 error: expected a trait, found type
-  --> $DIR/impl-parsing.rs:6:6
+  --> $DIR/impl-parsing.rs:5:6
    |
 LL | impl ?Sized for Type {}
    |      ^^^^^^
 
-error: expected a trait, found type
-  --> $DIR/impl-parsing.rs:7:6
-   |
-LL | impl ?Sized for .. {}
-   |      ^^^^^^
-
-error: `default` is not followed by an item
-  --> $DIR/impl-parsing.rs:9:1
-   |
-LL | default unsafe FAIL
-   | ^^^^^^^ the `default` qualifier
-   |
-   = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`
-
-error: expected item, found keyword `unsafe`
-  --> $DIR/impl-parsing.rs:9:9
-   |
-LL | default unsafe FAIL
-   |         ^^^^^^ expected item
-   |
-   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
-
-error: aborting due to 6 previous errors
+error: aborting due to 2 previous errors
 

From b2aa9d0620bbfa09f67c7bbfae2dd553d39bc90a Mon Sep 17 00:00:00 2001
From: Oli Scherer <github333195615777966@oli-obk.de>
Date: Tue, 8 Apr 2025 10:52:16 +0000
Subject: [PATCH 17/27] Remove some dead or leftover code related to
 rustc-intrinsic abi removal

---
 .../src/error_codes/E0622.md                  |  4 ++-
 compiler/rustc_error_codes/src/lib.rs         |  2 +-
 .../rustc_hir_analysis/src/check/check.rs     | 11 -------
 .../rustc_hir_analysis/src/check/intrinsic.rs | 33 +++++--------------
 tests/ui/error-codes/E0622.rs                 | 14 --------
 tests/ui/error-codes/E0622.stderr             |  9 -----
 6 files changed, 13 insertions(+), 60 deletions(-)
 delete mode 100644 tests/ui/error-codes/E0622.rs
 delete mode 100644 tests/ui/error-codes/E0622.stderr

diff --git a/compiler/rustc_error_codes/src/error_codes/E0622.md b/compiler/rustc_error_codes/src/error_codes/E0622.md
index e6ff949d3e9f9..9b8131a061e39 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0622.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0622.md
@@ -1,8 +1,10 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 An intrinsic was declared without being a function.
 
 Erroneous code example:
 
-```compile_fail,E0622
+```no_run
 #![feature(intrinsics)]
 #![allow(internal_features)]
 
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index dfeef5a957d69..2488d870899ce 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -397,7 +397,7 @@ E0618: 0618,
 E0619: 0619,
 E0620: 0620,
 E0621: 0621,
-E0622: 0622,
+E0622: 0622, // REMOVED: rustc-intrinsic ABI was removed
 E0623: 0623,
 E0624: 0624,
 E0625: 0625,
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index e3ed20e1b318d..2e0e57ba88791 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -719,7 +719,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
                     def_id,
                     tcx.def_ident_span(def_id).unwrap(),
                     i.name,
-                    ExternAbi::Rust,
                 )
             }
         }
@@ -787,16 +786,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             for item in items {
                 let def_id = item.id.owner_id.def_id;
 
-                if tcx.has_attr(def_id, sym::rustc_intrinsic) {
-                    intrinsic::check_intrinsic_type(
-                        tcx,
-                        item.id.owner_id.def_id,
-                        item.span,
-                        item.ident.name,
-                        abi,
-                    );
-                }
-
                 let generics = tcx.generics_of(def_id);
                 let own_counts = generics.own_counts();
                 if generics.own_params.len() - own_counts.lifetimes != 0 {
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 0bf9e127989f0..d62ca7e198773 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -1,9 +1,8 @@
 //! Type-checking for the `#[rustc_intrinsic]` intrinsics that the compiler exposes.
 
 use rustc_abi::ExternAbi;
-use rustc_errors::codes::*;
-use rustc_errors::{DiagMessage, struct_span_code_err};
-use rustc_hir::{self as hir, Safety};
+use rustc_errors::DiagMessage;
+use rustc_hir::{self as hir};
 use rustc_middle::bug;
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -26,17 +25,10 @@ fn equate_intrinsic_type<'tcx>(
     sig: ty::PolyFnSig<'tcx>,
 ) {
     let (generics, span) = match tcx.hir_node_by_def_id(def_id) {
-        hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. })
-        | hir::Node::ForeignItem(hir::ForeignItem {
-            kind: hir::ForeignItemKind::Fn(_, _, generics),
-            ..
-        }) => (tcx.generics_of(def_id), generics.span),
-        _ => {
-            struct_span_code_err!(tcx.dcx(), span, E0622, "intrinsic must be a function")
-                .with_span_label(span, "expected a function")
-                .emit();
-            return;
+        hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. }) => {
+            (tcx.generics_of(def_id), generics.span)
         }
+        _ => tcx.dcx().span_bug(span, "intrinsic must be a function"),
     };
     let own_counts = generics.own_counts();
 
@@ -70,13 +62,7 @@ fn equate_intrinsic_type<'tcx>(
 }
 
 /// Returns the unsafety of the given intrinsic.
-pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hir::Safety {
-    let has_safe_attr = if tcx.has_attr(intrinsic_id, sym::rustc_intrinsic) {
-        tcx.fn_sig(intrinsic_id).skip_binder().safety()
-    } else {
-        // Old-style intrinsics are never safe
-        Safety::Unsafe
-    };
+fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hir::Safety {
     let is_in_list = match tcx.item_name(intrinsic_id.into()) {
         // When adding a new intrinsic to this list,
         // it's usually worth updating that intrinsic's documentation
@@ -148,7 +134,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
         _ => hir::Safety::Unsafe,
     };
 
-    if has_safe_attr != is_in_list {
+    if tcx.fn_sig(intrinsic_id).skip_binder().safety() != is_in_list {
         tcx.dcx().struct_span_err(
             tcx.def_span(intrinsic_id),
             DiagMessage::from(format!(
@@ -163,12 +149,11 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
 
 /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
 /// and in `library/core/src/intrinsics.rs`.
-pub fn check_intrinsic_type(
+pub(crate) fn check_intrinsic_type(
     tcx: TyCtxt<'_>,
     intrinsic_id: LocalDefId,
     span: Span,
     intrinsic_name: Symbol,
-    abi: ExternAbi,
 ) {
     let generics = tcx.generics_of(intrinsic_id);
     let param = |n| {
@@ -706,7 +691,7 @@ pub fn check_intrinsic_type(
         };
         (n_tps, 0, n_cts, inputs, output, safety)
     };
-    let sig = tcx.mk_fn_sig(inputs, output, false, safety, abi);
+    let sig = tcx.mk_fn_sig(inputs, output, false, safety, ExternAbi::Rust);
     let sig = ty::Binder::bind_with_vars(sig, bound_vars);
     equate_intrinsic_type(tcx, span, intrinsic_id, n_tps, n_lts, n_cts, sig)
 }
diff --git a/tests/ui/error-codes/E0622.rs b/tests/ui/error-codes/E0622.rs
deleted file mode 100644
index 0c2a4f226d80c..0000000000000
--- a/tests/ui/error-codes/E0622.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-#![feature(intrinsics)]
-
-extern "C" {
-
-    #[rustc_intrinsic]
-    pub static atomic_singlethreadfence_seqcst: unsafe extern "C" fn();
-    //~^ ERROR intrinsic must be a function [E0622]
-}
-
-fn main() {
-    unsafe {
-        atomic_singlethreadfence_seqcst();
-    }
-}
diff --git a/tests/ui/error-codes/E0622.stderr b/tests/ui/error-codes/E0622.stderr
deleted file mode 100644
index c0aea542af04e..0000000000000
--- a/tests/ui/error-codes/E0622.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0622]: intrinsic must be a function
-  --> $DIR/E0622.rs:6:5
-   |
-LL |     pub static atomic_singlethreadfence_seqcst: unsafe extern "C" fn();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a function
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0622`.

From cf685653131bf76a88457eb8a88ee91c0d9fa821 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Wed, 9 Apr 2025 13:48:39 +0200
Subject: [PATCH 18/27] Temporarily leave the review rotation

---
 triagebot.toml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/triagebot.toml b/triagebot.toml
index 756536dc2e7b9..64c85bbb38110 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1123,6 +1123,7 @@ cc = ["@ZuseZ4"]
 warn_non_default_branch.enable = true
 contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
 users_on_vacation = [
+    "fmease",
     "jyn514",
     "saethlin",
     "Noratrieb",

From 32c8f7dbcf17e8a97a3a99e112e86a604d12f0a2 Mon Sep 17 00:00:00 2001
From: xizheyin <xizheyin@smail.nju.edu.cn>
Date: Wed, 9 Apr 2025 20:51:52 +0800
Subject: [PATCH 19/27] librustdoc: remove IndexItem::new, use previous fields
 constructor

Signed-off-by: xizheyin <xizheyin@smail.nju.edu.cn>
---
 src/librustdoc/formats/cache.rs   | 14 ++++----
 src/librustdoc/html/render/mod.rs | 56 +++++++------------------------
 2 files changed, 19 insertions(+), 51 deletions(-)

diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 2a612fa9b8cda..2648641e53e75 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -574,20 +574,20 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
     );
     let aliases = item.attrs.get_doc_aliases();
     let deprecation = item.deprecation(tcx);
-    let index_item = IndexItem::new(
-        item.type_(),
-        Some(defid),
+    let index_item = IndexItem {
+        ty: item.type_(),
+        defid: Some(defid),
         name,
         path,
         desc,
-        parent_did,
-        None,
-        None,
+        parent: parent_did,
+        parent_idx: None,
+        exact_path: None,
         impl_id,
         search_type,
         aliases,
         deprecation,
-    );
+    };
     cache.search_index.push(index_item);
 }
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index b9a2e65b09076..ad6fa110ae883 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -113,50 +113,18 @@ enum RenderMode {
 /// by hand to a large JS file at the end of cache-creation.
 #[derive(Debug)]
 pub(crate) struct IndexItem {
-    ty: ItemType,
-    defid: Option<DefId>,
-    name: Symbol,
-    path: String,
-    desc: String,
-    parent: Option<DefId>,
-    parent_idx: Option<isize>,
-    exact_path: Option<String>,
-    impl_id: Option<DefId>,
-    search_type: Option<IndexItemFunctionType>,
-    aliases: Box<[Symbol]>,
-    deprecation: Option<Deprecation>,
-}
-
-impl IndexItem {
-    pub fn new(
-        ty: ItemType,
-        defid: Option<DefId>,
-        name: Symbol,
-        path: String,
-        desc: String,
-        parent: Option<DefId>,
-        parent_idx: Option<isize>,
-        exact_path: Option<String>,
-        impl_id: Option<DefId>,
-        search_type: Option<IndexItemFunctionType>,
-        aliases: Box<[Symbol]>,
-        deprecation: Option<Deprecation>,
-    ) -> Self {
-        Self {
-            ty,
-            defid,
-            name,
-            path,
-            desc,
-            parent,
-            parent_idx,
-            exact_path,
-            impl_id,
-            search_type,
-            aliases,
-            deprecation,
-        }
-    }
+    pub(crate) ty: ItemType,
+    pub(crate) defid: Option<DefId>,
+    pub(crate) name: Symbol,
+    pub(crate) path: String,
+    pub(crate) desc: String,
+    pub(crate) parent: Option<DefId>,
+    pub(crate) parent_idx: Option<isize>,
+    pub(crate) exact_path: Option<String>,
+    pub(crate) impl_id: Option<DefId>,
+    pub(crate) search_type: Option<IndexItemFunctionType>,
+    pub(crate) aliases: Box<[Symbol]>,
+    pub(crate) deprecation: Option<Deprecation>,
 }
 
 /// A type used for the search index.

From 67ff33666f3f59578c2ca7e337fd55129439edb2 Mon Sep 17 00:00:00 2001
From: Ben Kimock <kimockb@gmail.com>
Date: Wed, 9 Apr 2025 12:03:28 -0400
Subject: [PATCH 20/27] saethlin is back from vacation

---
 triagebot.toml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/triagebot.toml b/triagebot.toml
index 756536dc2e7b9..8573e62a567e2 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1124,7 +1124,6 @@ warn_non_default_branch.enable = true
 contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
 users_on_vacation = [
     "jyn514",
-    "saethlin",
     "Noratrieb",
 ]
 

From 50d0ce1b42d67ce98b2ccac55d22a2ff8abe3273 Mon Sep 17 00:00:00 2001
From: Scott McMurray <scottmcm@users.noreply.github.com>
Date: Fri, 21 Feb 2025 21:00:43 -0800
Subject: [PATCH 21/27] Ensure `swap_nonoverlapping` is really always untyped

---
 library/core/src/ptr/mod.rs                   | 136 +++++++++++-------
 library/coretests/tests/ptr.rs                |  36 +++++
 tests/assembly/x86_64-typed-swap.rs           |  28 ++++
 tests/codegen/simd/swap-simd-types.rs         |   8 +-
 tests/codegen/swap-large-types.rs             |  76 ++++++----
 tests/codegen/swap-small-types.rs             |  78 +++++++---
 .../consts/missing_span_in_backtrace.stderr   |   6 +-
 7 files changed, 272 insertions(+), 96 deletions(-)

diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index ea53da78d3bd2..2357ba23aa0d2 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -398,6 +398,7 @@ use crate::cmp::Ordering;
 use crate::intrinsics::const_eval_select;
 use crate::marker::FnPtr;
 use crate::mem::{self, MaybeUninit, SizedTypeProperties};
+use crate::num::NonZero;
 use crate::{fmt, hash, intrinsics, ub_checks};
 
 mod alignment;
@@ -1094,51 +1095,25 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
             // are pointers inside `T` we will copy them in one go rather than trying to copy a part
             // of a pointer (which would not work).
             // SAFETY: Same preconditions as this function
-            unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }
+            unsafe { swap_nonoverlapping_const(x, y, count) }
         } else {
-            macro_rules! attempt_swap_as_chunks {
-                ($ChunkTy:ty) => {
-                    if align_of::<T>() >= align_of::<$ChunkTy>()
-                        && size_of::<T>() % size_of::<$ChunkTy>() == 0
-                    {
-                        let x: *mut $ChunkTy = x.cast();
-                        let y: *mut $ChunkTy = y.cast();
-                        let count = count * (size_of::<T>() / size_of::<$ChunkTy>());
-                        // SAFETY: these are the same bytes that the caller promised were
-                        // ok, just typed as `MaybeUninit<ChunkTy>`s instead of as `T`s.
-                        // The `if` condition above ensures that we're not violating
-                        // alignment requirements, and that the division is exact so
-                        // that we don't lose any bytes off the end.
-                        return unsafe { swap_nonoverlapping_simple_untyped(x, y, count) };
-                    }
-                };
+            // Going though a slice here helps codegen know the size fits in `isize`
+            let slice = slice_from_raw_parts_mut(x, count);
+            // SAFETY: This is all readable from the pointer, meaning it's one
+            // allocated object, and thus cannot be more than isize::MAX bytes.
+            let bytes = unsafe { mem::size_of_val_raw::<[T]>(slice) };
+            if let Some(bytes) = NonZero::new(bytes) {
+                // SAFETY: These are the same ranges, just expressed in a different
+                // type, so they're still non-overlapping.
+                unsafe { swap_nonoverlapping_bytes(x.cast(), y.cast(), bytes) };
             }
-
-            // Split up the slice into small power-of-two-sized chunks that LLVM is able
-            // to vectorize (unless it's a special type with more-than-pointer alignment,
-            // because we don't want to pessimize things like slices of SIMD vectors.)
-            if align_of::<T>() <= size_of::<usize>()
-            && (!size_of::<T>().is_power_of_two()
-                || size_of::<T>() > size_of::<usize>() * 2)
-            {
-                attempt_swap_as_chunks!(usize);
-                attempt_swap_as_chunks!(u8);
-            }
-
-            // SAFETY: Same preconditions as this function
-            unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }
         }
     )
 }
 
 /// Same behavior and safety conditions as [`swap_nonoverlapping`]
-///
-/// LLVM can vectorize this (at least it can for the power-of-two-sized types
-/// `swap_nonoverlapping` tries to use) so no need to manually SIMD it.
 #[inline]
-const unsafe fn swap_nonoverlapping_simple_untyped<T>(x: *mut T, y: *mut T, count: usize) {
-    let x = x.cast::<MaybeUninit<T>>();
-    let y = y.cast::<MaybeUninit<T>>();
+const unsafe fn swap_nonoverlapping_const<T>(x: *mut T, y: *mut T, count: usize) {
     let mut i = 0;
     while i < count {
         // SAFETY: By precondition, `i` is in-bounds because it's below `n`
@@ -1147,26 +1122,91 @@ const unsafe fn swap_nonoverlapping_simple_untyped<T>(x: *mut T, y: *mut T, coun
         // and it's distinct from `x` since the ranges are non-overlapping
         let y = unsafe { y.add(i) };
 
-        // If we end up here, it's because we're using a simple type -- like
-        // a small power-of-two-sized thing -- or a special type with particularly
-        // large alignment, particularly SIMD types.
-        // Thus, we're fine just reading-and-writing it, as either it's small
-        // and that works well anyway or it's special and the type's author
-        // presumably wanted things to be done in the larger chunk.
-
         // SAFETY: we're only ever given pointers that are valid to read/write,
         // including being aligned, and nothing here panics so it's drop-safe.
         unsafe {
-            let a: MaybeUninit<T> = read(x);
-            let b: MaybeUninit<T> = read(y);
-            write(x, b);
-            write(y, a);
+            // Note that it's critical that these use `copy_nonoverlapping`,
+            // rather than `read`/`write`, to avoid #134713 if T has padding.
+            let mut temp = MaybeUninit::<T>::uninit();
+            copy_nonoverlapping(x, temp.as_mut_ptr(), 1);
+            copy_nonoverlapping(y, x, 1);
+            copy_nonoverlapping(temp.as_ptr(), y, 1);
         }
 
         i += 1;
     }
 }
 
+// Don't let MIR inline this, because we really want it to keep its noalias metadata
+#[rustc_no_mir_inline]
+#[inline]
+fn swap_chunk<const N: usize>(x: &mut MaybeUninit<[u8; N]>, y: &mut MaybeUninit<[u8; N]>) {
+    let a = *x;
+    let b = *y;
+    *x = b;
+    *y = a;
+}
+
+#[inline]
+unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, bytes: NonZero<usize>) {
+    // Same as `swap_nonoverlapping::<[u8; N]>`.
+    unsafe fn swap_nonoverlapping_chunks<const N: usize>(
+        x: *mut MaybeUninit<[u8; N]>,
+        y: *mut MaybeUninit<[u8; N]>,
+        chunks: NonZero<usize>,
+    ) {
+        let chunks = chunks.get();
+        for i in 0..chunks {
+            // SAFETY: i is in [0, chunks) so the adds and dereferences are in-bounds.
+            unsafe { swap_chunk(&mut *x.add(i), &mut *y.add(i)) };
+        }
+    }
+
+    // Same as `swap_nonoverlapping_bytes`, but accepts at most 1+2+4=7 bytes
+    #[inline]
+    unsafe fn swap_nonoverlapping_short(x: *mut u8, y: *mut u8, bytes: NonZero<usize>) {
+        // Tail handling for auto-vectorized code sometimes has element-at-a-time behaviour,
+        // see <https://github.com/rust-lang/rust/issues/134946>.
+        // By swapping as different sizes, rather than as a loop over bytes,
+        // we make sure not to end up with, say, seven byte-at-a-time copies.
+
+        let bytes = bytes.get();
+        let mut i = 0;
+        macro_rules! swap_prefix {
+            ($($n:literal)+) => {$(
+                if (bytes & $n) != 0 {
+                    // SAFETY: `i` can only have the same bits set as those in bytes,
+                    // so these `add`s are in-bounds of `bytes`.  But the bit for
+                    // `$n` hasn't been set yet, so the `$n` bytes that `swap_chunk`
+                    // will read and write are within the usable range.
+                    unsafe { swap_chunk::<$n>(&mut*x.add(i).cast(), &mut*y.add(i).cast()) };
+                    i |= $n;
+                }
+            )+};
+        }
+        swap_prefix!(4 2 1);
+        debug_assert_eq!(i, bytes);
+    }
+
+    const CHUNK_SIZE: usize = size_of::<*const ()>();
+    let bytes = bytes.get();
+
+    let chunks = bytes / CHUNK_SIZE;
+    let tail = bytes % CHUNK_SIZE;
+    if let Some(chunks) = NonZero::new(chunks) {
+        // SAFETY: this is bytes/CHUNK_SIZE*CHUNK_SIZE bytes, which is <= bytes,
+        // so it's within the range of our non-overlapping bytes.
+        unsafe { swap_nonoverlapping_chunks::<CHUNK_SIZE>(x.cast(), y.cast(), chunks) };
+    }
+    if let Some(tail) = NonZero::new(tail) {
+        const { assert!(CHUNK_SIZE <= 8) };
+        let delta = chunks * CHUNK_SIZE;
+        // SAFETY: the tail length is below CHUNK SIZE because of the remainder,
+        // and CHUNK_SIZE is at most 8 by the const assert, so tail <= 7
+        unsafe { swap_nonoverlapping_short(x.add(delta), y.add(delta), tail) };
+    }
+}
+
 /// Moves `src` into the pointed `dst`, returning the previous `dst` value.
 ///
 /// Neither value is dropped.
diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs
index 6091926084a35..cc5f7946863a6 100644
--- a/library/coretests/tests/ptr.rs
+++ b/library/coretests/tests/ptr.rs
@@ -984,3 +984,39 @@ fn test_ptr_metadata_in_const() {
     assert_eq!(SLICE_META, 3);
     assert_eq!(DYN_META.size_of(), 42);
 }
+
+// See <https://github.com/rust-lang/rust/issues/134713>
+const fn ptr_swap_nonoverlapping_is_untyped_inner() {
+    #[repr(C)]
+    struct HasPadding(usize, u8);
+
+    let buf1: [usize; 2] = [1000, 2000];
+    let buf2: [usize; 2] = [3000, 4000];
+
+    // HasPadding and [usize; 2] have the same size and alignment,
+    // so swap_nonoverlapping should treat them the same
+    assert!(size_of::<HasPadding>() == size_of::<[usize; 2]>());
+    assert!(align_of::<HasPadding>() == align_of::<[usize; 2]>());
+
+    let mut b1 = buf1;
+    let mut b2 = buf2;
+    // Safety: b1 and b2 are distinct local variables,
+    // with the same size and alignment as HasPadding.
+    unsafe {
+        std::ptr::swap_nonoverlapping(
+            b1.as_mut_ptr().cast::<HasPadding>(),
+            b2.as_mut_ptr().cast::<HasPadding>(),
+            1,
+        );
+    }
+    assert!(b1[0] == buf2[0]);
+    assert!(b1[1] == buf2[1]);
+    assert!(b2[0] == buf1[0]);
+    assert!(b2[1] == buf1[1]);
+}
+
+#[test]
+fn test_ptr_swap_nonoverlapping_is_untyped() {
+    ptr_swap_nonoverlapping_is_untyped_inner();
+    const { ptr_swap_nonoverlapping_is_untyped_inner() };
+}
diff --git a/tests/assembly/x86_64-typed-swap.rs b/tests/assembly/x86_64-typed-swap.rs
index dfd6ee565bccb..a6753011d3620 100644
--- a/tests/assembly/x86_64-typed-swap.rs
+++ b/tests/assembly/x86_64-typed-swap.rs
@@ -51,3 +51,31 @@ pub fn swap_simd(x: &mut __m128, y: &mut __m128) {
     // CHECK-NEXT: retq
     swap(x, y)
 }
+
+// CHECK-LABEL: swap_string:
+#[no_mangle]
+pub fn swap_string(x: &mut String, y: &mut String) {
+    // CHECK-NOT: mov
+    // CHECK-COUNT-4: movups
+    // CHECK-NOT: mov
+    // CHECK-COUNT-4: movq
+    // CHECK-NOT: mov
+    swap(x, y)
+}
+
+// CHECK-LABEL: swap_44_bytes:
+#[no_mangle]
+pub fn swap_44_bytes(x: &mut [u8; 44], y: &mut [u8; 44]) {
+    // Ensure we do better than a long run of byte copies,
+    // see <https://github.com/rust-lang/rust/issues/134946>
+
+    // CHECK-NOT: movb
+    // CHECK-COUNT-8: movups{{.+}}xmm
+    // CHECK-NOT: movb
+    // CHECK-COUNT-4: movq
+    // CHECK-NOT: movb
+    // CHECK-COUNT-4: movl
+    // CHECK-NOT: movb
+    // CHECK: retq
+    swap(x, y)
+}
diff --git a/tests/codegen/simd/swap-simd-types.rs b/tests/codegen/simd/swap-simd-types.rs
index 69767d0a75580..c063cc683a616 100644
--- a/tests/codegen/simd/swap-simd-types.rs
+++ b/tests/codegen/simd/swap-simd-types.rs
@@ -23,8 +23,8 @@ pub fn swap_single_m256(x: &mut __m256, y: &mut __m256) {
 #[no_mangle]
 pub fn swap_m256_slice(x: &mut [__m256], y: &mut [__m256]) {
     // CHECK-NOT: alloca
-    // CHECK: load <8 x float>{{.+}}align 32
-    // CHECK: store <8 x float>{{.+}}align 32
+    // CHECK-COUNT-2: load <4 x i64>{{.+}}align 32
+    // CHECK-COUNT-2: store <4 x i64>{{.+}}align 32
     if x.len() == y.len() {
         x.swap_with_slice(y);
     }
@@ -34,7 +34,7 @@ pub fn swap_m256_slice(x: &mut [__m256], y: &mut [__m256]) {
 #[no_mangle]
 pub fn swap_bytes32(x: &mut [u8; 32], y: &mut [u8; 32]) {
     // CHECK-NOT: alloca
-    // CHECK: load <32 x i8>{{.+}}align 1
-    // CHECK: store <32 x i8>{{.+}}align 1
+    // CHECK-COUNT-2: load <4 x i64>{{.+}}align 1
+    // CHECK-COUNT-2: store <4 x i64>{{.+}}align 1
     swap(x, y)
 }
diff --git a/tests/codegen/swap-large-types.rs b/tests/codegen/swap-large-types.rs
index 49a41bb14692f..08c486affd949 100644
--- a/tests/codegen/swap-large-types.rs
+++ b/tests/codegen/swap-large-types.rs
@@ -12,6 +12,16 @@ type KeccakBuffer = [[u64; 5]; 5];
 // to stack for large types, which is completely unnecessary as the lack of
 // overlap means we can just do whatever fits in registers at a time.
 
+// The tests here (after the first one showing that the problem still exists)
+// are less about testing *exactly* what the codegen is, and more about testing
+// 1) That things are swapped directly from one argument to the other,
+//    never going through stack along the way, and
+// 2) That we're doing the swapping for big things using large vector types,
+//    rather then `i64` or `<8 x i8>` (or, even worse, `i8`) at a time.
+//
+// (There are separate tests for intrinsics::typed_swap_nonoverlapping that
+//  check that it, as an intrinsic, are emitting exactly what it should.)
+
 // CHECK-LABEL: @swap_basic
 #[no_mangle]
 pub fn swap_basic(x: &mut KeccakBuffer, y: &mut KeccakBuffer) {
@@ -26,55 +36,55 @@ pub fn swap_basic(x: &mut KeccakBuffer, y: &mut KeccakBuffer) {
     }
 }
 
-// This test verifies that the library does something smarter, and thus
-// doesn't need any scratch space on the stack.
-
 // CHECK-LABEL: @swap_std
 #[no_mangle]
 pub fn swap_std(x: &mut KeccakBuffer, y: &mut KeccakBuffer) {
     // CHECK-NOT: alloca
-    // CHECK: load <{{[0-9]+}} x i64>
-    // CHECK: store <{{[0-9]+}} x i64>
+    // CHECK: load <{{2|4}} x i64>
+    // CHECK: store <{{2|4}} x i64>
     swap(x, y)
 }
 
-// Verify that types with usize alignment are swapped via vectored usizes,
-// not falling back to byte-level code.
-
 // CHECK-LABEL: @swap_slice
 #[no_mangle]
 pub fn swap_slice(x: &mut [KeccakBuffer], y: &mut [KeccakBuffer]) {
     // CHECK-NOT: alloca
-    // CHECK: load <{{[0-9]+}} x i64>
-    // CHECK: store <{{[0-9]+}} x i64>
+    // CHECK: load <{{2|4}} x i64>
+    // CHECK: store <{{2|4}} x i64>
     if x.len() == y.len() {
         x.swap_with_slice(y);
     }
 }
 
-// But for a large align-1 type, vectorized byte copying is what we want.
-
 type OneKilobyteBuffer = [u8; 1024];
 
 // CHECK-LABEL: @swap_1kb_slices
 #[no_mangle]
 pub fn swap_1kb_slices(x: &mut [OneKilobyteBuffer], y: &mut [OneKilobyteBuffer]) {
     // CHECK-NOT: alloca
-    // CHECK: load <{{[0-9]+}} x i8>
-    // CHECK: store <{{[0-9]+}} x i8>
+
+    // CHECK-NOT: load i32
+    // CHECK-NOT: store i32
+    // CHECK-NOT: load i16
+    // CHECK-NOT: store i16
+    // CHECK-NOT: load i8
+    // CHECK-NOT: store i8
+
+    // CHECK: load <{{2|4}} x i64>{{.+}}align 1,
+    // CHECK: store <{{2|4}} x i64>{{.+}}align 1,
+
+    // CHECK-NOT: load i32
+    // CHECK-NOT: store i32
+    // CHECK-NOT: load i16
+    // CHECK-NOT: store i16
+    // CHECK-NOT: load i8
+    // CHECK-NOT: store i8
+
     if x.len() == y.len() {
         x.swap_with_slice(y);
     }
 }
 
-// This verifies that the 2×read + 2×write optimizes to just 3 memcpys
-// for an unusual type like this.  It's not clear whether we should do anything
-// smarter in Rust for these, so for now it's fine to leave these up to the backend.
-// That's not as bad as it might seem, as for example, LLVM will lower the
-// memcpys below to VMOVAPS on YMMs if one enables the AVX target feature.
-// Eventually we'll be able to pass `align_of::<T>` to a const generic and
-// thus pick a smarter chunk size ourselves without huge code duplication.
-
 #[repr(align(64))]
 pub struct BigButHighlyAligned([u8; 64 * 3]);
 
@@ -82,9 +92,25 @@ pub struct BigButHighlyAligned([u8; 64 * 3]);
 #[no_mangle]
 pub fn swap_big_aligned(x: &mut BigButHighlyAligned, y: &mut BigButHighlyAligned) {
     // CHECK-NOT: call void @llvm.memcpy
-    // CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192)
-    // CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192)
-    // CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192)
+    // CHECK-NOT: load i32
+    // CHECK-NOT: store i32
+    // CHECK-NOT: load i16
+    // CHECK-NOT: store i16
+    // CHECK-NOT: load i8
+    // CHECK-NOT: store i8
+
+    // CHECK-COUNT-2: load <{{2|4}} x i64>{{.+}}align 64,
+    // CHECK-COUNT-2: store <{{2|4}} x i64>{{.+}}align 64,
+
+    // CHECK-COUNT-2: load <{{2|4}} x i64>{{.+}}align 32,
+    // CHECK-COUNT-2: store <{{2|4}} x i64>{{.+}}align 32,
+
+    // CHECK-NOT: load i32
+    // CHECK-NOT: store i32
+    // CHECK-NOT: load i16
+    // CHECK-NOT: store i16
+    // CHECK-NOT: load i8
+    // CHECK-NOT: store i8
     // CHECK-NOT: call void @llvm.memcpy
     swap(x, y)
 }
diff --git a/tests/codegen/swap-small-types.rs b/tests/codegen/swap-small-types.rs
index 76bb853e64238..ffa573c9a43ab 100644
--- a/tests/codegen/swap-small-types.rs
+++ b/tests/codegen/swap-small-types.rs
@@ -1,5 +1,6 @@
 //@ compile-flags: -Copt-level=3 -Z merge-functions=disabled
 //@ only-x86_64
+//@ min-llvm-version: 20
 
 #![crate_type = "lib"]
 
@@ -27,13 +28,19 @@ pub fn swap_rgb48_manually(x: &mut RGB48, y: &mut RGB48) {
 pub fn swap_rgb48(x: &mut RGB48, y: &mut RGB48) {
     // CHECK-NOT: alloca
 
-    // Whether `i8` is the best for this is unclear, but
-    // might as well record what's actually happening right now.
-
-    // CHECK: load i8
-    // CHECK: load i8
-    // CHECK: store i8
-    // CHECK: store i8
+    // Swapping `i48` might be cleaner in LLVM-IR here, but `i32`+`i16` isn't bad,
+    // and is closer to the assembly it generates anyway.
+
+    // CHECK-NOT: load{{ }}
+    // CHECK: load i32{{.+}}align 2
+    // CHECK-NEXT: load i32{{.+}}align 2
+    // CHECK-NEXT: store i32{{.+}}align 2
+    // CHECK-NEXT: store i32{{.+}}align 2
+    // CHECK: load i16{{.+}}align 2
+    // CHECK-NEXT: load i16{{.+}}align 2
+    // CHECK-NEXT: store i16{{.+}}align 2
+    // CHECK-NEXT: store i16{{.+}}align 2
+    // CHECK-NOT: store{{ }}
     swap(x, y)
 }
 
@@ -76,30 +83,49 @@ pub fn swap_slices<'a>(x: &mut &'a [u32], y: &mut &'a [u32]) {
     swap(x, y)
 }
 
-// LLVM doesn't vectorize a loop over 3-byte elements,
-// so we chunk it down to bytes and loop over those instead.
 type RGB24 = [u8; 3];
 
 // CHECK-LABEL: @swap_rgb24_slices
 #[no_mangle]
 pub fn swap_rgb24_slices(x: &mut [RGB24], y: &mut [RGB24]) {
     // CHECK-NOT: alloca
-    // CHECK: load <{{[0-9]+}} x i8>
-    // CHECK: store <{{[0-9]+}} x i8>
+
+    // CHECK: mul nuw nsw i64 %{{x|y}}.1, 3
+
+    // CHECK: load <{{[0-9]+}} x i64>
+    // CHECK: store <{{[0-9]+}} x i64>
+
+    // CHECK-COUNT-2: load i32
+    // CHECK-COUNT-2: store i32
+    // CHECK-COUNT-2: load i16
+    // CHECK-COUNT-2: store i16
+    // CHECK-COUNT-2: load i8
+    // CHECK-COUNT-2: store i8
     if x.len() == y.len() {
         x.swap_with_slice(y);
     }
 }
 
-// This one has a power-of-two size, so we iterate over it directly
 type RGBA32 = [u8; 4];
 
 // CHECK-LABEL: @swap_rgba32_slices
 #[no_mangle]
 pub fn swap_rgba32_slices(x: &mut [RGBA32], y: &mut [RGBA32]) {
     // CHECK-NOT: alloca
-    // CHECK: load <{{[0-9]+}} x i32>
-    // CHECK: store <{{[0-9]+}} x i32>
+
+    // Because the size in bytes in a multiple of 4, we can skip the smallest sizes.
+
+    // CHECK: load <{{[0-9]+}} x i64>
+    // CHECK: store <{{[0-9]+}} x i64>
+
+    // CHECK-COUNT-2: load i32
+    // CHECK-COUNT-2: store i32
+
+    // CHECK-NOT: load i16
+    // CHECK-NOT: store i16
+    // CHECK-NOT: load i8
+    // CHECK-NOT: store i8
+
     if x.len() == y.len() {
         x.swap_with_slice(y);
     }
@@ -113,8 +139,8 @@ const _: () = assert!(!std::mem::size_of::<String>().is_power_of_two());
 #[no_mangle]
 pub fn swap_string_slices(x: &mut [String], y: &mut [String]) {
     // CHECK-NOT: alloca
-    // CHECK: load <{{[0-9]+}} x i64>
-    // CHECK: store <{{[0-9]+}} x i64>
+    // CHECK: load <{{[0-9]+}} x i64>{{.+}}, align 8,
+    // CHECK: store <{{[0-9]+}} x i64>{{.+}}, align 8,
     if x.len() == y.len() {
         x.swap_with_slice(y);
     }
@@ -130,6 +156,26 @@ pub struct Packed {
 #[no_mangle]
 pub fn swap_packed_structs(x: &mut Packed, y: &mut Packed) {
     // CHECK-NOT: alloca
+
+    // CHECK-NOT: load
+    // CHECK-NOT: store
+
+    // CHECK: %[[A:.+]] = load i64, ptr %x, align 1,
+    // CHECK-NEXT: %[[B:.+]] = load i64, ptr %y, align 1,
+    // CHECK-NEXT: store i64 %[[B]], ptr %x, align 1,
+    // CHECK-NEXT: store i64 %[[A]], ptr %y, align 1,
+
+    // CHECK-NOT: load
+    // CHECK-NOT: store
+
+    // CHECK: %[[C:.+]] = load i8, ptr %[[X8:.+]], align 1,
+    // CHECK-NEXT: %[[D:.+]] = load i8, ptr %[[Y8:.+]], align 1,
+    // CHECK-NEXT: store i8 %[[D]], ptr %[[X8]], align 1,
+    // CHECK-NEXT: store i8 %[[C]], ptr %[[Y8]], align 1,
+
+    // CHECK-NOT: load
+    // CHECK-NOT: store
+
     // CHECK: ret void
     swap(x, y)
 }
diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr
index 2f3a65302bd57..aad3d76dd2697 100644
--- a/tests/ui/consts/missing_span_in_backtrace.stderr
+++ b/tests/ui/consts/missing_span_in_backtrace.stderr
@@ -12,10 +12,10 @@ note: inside `swap_nonoverlapping::<MaybeUninit<u8>>`
   --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
 note: inside `swap_nonoverlapping::compiletime::<MaybeUninit<u8>>`
   --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
-note: inside `std::ptr::swap_nonoverlapping_simple_untyped::<MaybeUninit<u8>>`
-  --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
-note: inside `std::ptr::read::<MaybeUninit<MaybeUninit<u8>>>`
+note: inside `std::ptr::swap_nonoverlapping_const::<MaybeUninit<u8>>`
   --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+note: inside `copy_nonoverlapping::<MaybeUninit<u8>>`
+  --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL
    = help: this code performed an operation that depends on the underlying bytes representing a pointer
    = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
    = note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info)

From b06a88ff61acb08dd4f7502908485a3eaa74d0a6 Mon Sep 17 00:00:00 2001
From: Scott McMurray <scottmcm@users.noreply.github.com>
Date: Sat, 22 Feb 2025 13:12:38 -0800
Subject: [PATCH 22/27] Add a Miri test for 134713

---
 ...ssue-134713-swap_nonoverlapping_untyped.rs | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 src/tools/miri/tests/pass/issues/issue-134713-swap_nonoverlapping_untyped.rs

diff --git a/src/tools/miri/tests/pass/issues/issue-134713-swap_nonoverlapping_untyped.rs b/src/tools/miri/tests/pass/issues/issue-134713-swap_nonoverlapping_untyped.rs
new file mode 100644
index 0000000000000..a1da60ce65c4d
--- /dev/null
+++ b/src/tools/miri/tests/pass/issues/issue-134713-swap_nonoverlapping_untyped.rs
@@ -0,0 +1,30 @@
+use std::mem::{size_of, align_of};
+
+// See <https://github.com/rust-lang/rust/issues/134713>
+
+#[repr(C)]
+struct Foo(usize, u8);
+
+fn main() {
+    let buf1: [usize; 2] = [1000, 2000];
+    let buf2: [usize; 2] = [3000, 4000];
+
+    // Foo and [usize; 2] have the same size and alignment,
+    // so swap_nonoverlapping should treat them the same
+    assert_eq!(size_of::<Foo>(), size_of::<[usize; 2]>());
+    assert_eq!(align_of::<Foo>(), align_of::<[usize; 2]>());
+
+    let mut b1 = buf1;
+    let mut b2 = buf2;
+    // Safety: b1 and b2 are distinct local variables,
+    // with the same size and alignment as Foo.
+    unsafe {
+        std::ptr::swap_nonoverlapping(
+            b1.as_mut_ptr().cast::<Foo>(),
+            b2.as_mut_ptr().cast::<Foo>(),
+            1,
+        );
+    }
+    assert_eq!(b1, buf2);
+    assert_eq!(b2, buf1);
+}

From 8b227a42fa2833581cd70cdc186ccc34e251a74e Mon Sep 17 00:00:00 2001
From: Noah Lev <camelidcamel@gmail.com>
Date: Wed, 9 Apr 2025 12:51:31 -0400
Subject: [PATCH 23/27] rustdoc: Enable Markdown extensions when looking for
 doctests

We should enable these to avoid misinterpreting uses of the extended
syntax as code blocks. This happens in practice with multi-paragraph
footnotes, as discovered in #139064.
---
 src/librustdoc/html/markdown.rs            |  2 +-
 tests/rustdoc-ui/multi-par-footnote.rs     | 18 ++++++++++++++++++
 tests/rustdoc-ui/multi-par-footnote.stdout |  5 +++++
 3 files changed, 24 insertions(+), 1 deletion(-)
 create mode 100644 tests/rustdoc-ui/multi-par-footnote.rs
 create mode 100644 tests/rustdoc-ui/multi-par-footnote.stdout

diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 334a7a86989a8..44134bda5ea39 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -721,7 +721,7 @@ pub(crate) fn find_codes<T: doctest::DocTestVisitor>(
     extra_info: Option<&ExtraInfo<'_>>,
     include_non_rust: bool,
 ) {
-    let mut parser = Parser::new(doc).into_offset_iter();
+    let mut parser = Parser::new_ext(doc, main_body_opts()).into_offset_iter();
     let mut prev_offset = 0;
     let mut nb_lines = 0;
     let mut register_header = None;
diff --git a/tests/rustdoc-ui/multi-par-footnote.rs b/tests/rustdoc-ui/multi-par-footnote.rs
new file mode 100644
index 0000000000000..bb6a85db0dbac
--- /dev/null
+++ b/tests/rustdoc-ui/multi-par-footnote.rs
@@ -0,0 +1,18 @@
+//@ check-pass
+//@ compile-flags:--test
+//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
+// Regression test for #139064.
+
+/// Example
+///
+/// Footnote with multiple paragraphs[^multiple]
+///
+/// [^multiple]:
+///     One
+///
+///     Two
+///
+///     Three
+pub fn add(left: u64, right: u64) -> u64 {
+    left + right
+}
diff --git a/tests/rustdoc-ui/multi-par-footnote.stdout b/tests/rustdoc-ui/multi-par-footnote.stdout
new file mode 100644
index 0000000000000..7326c0a25a069
--- /dev/null
+++ b/tests/rustdoc-ui/multi-par-footnote.stdout
@@ -0,0 +1,5 @@
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+

From 63dcac8423750369ee251e2bad0945ad765eaf5c Mon Sep 17 00:00:00 2001
From: Scott McMurray <scottmcm@users.noreply.github.com>
Date: Wed, 9 Apr 2025 10:44:49 -0700
Subject: [PATCH 24/27] skip `tests/codegen/swap-small-types` when debug
 assertions are on

In `swap_nonoverlapping_short` there's a new `debug_assert!`, and if that's enabled then the `alloca`s don't optimize out.
---
 tests/codegen/swap-small-types.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/codegen/swap-small-types.rs b/tests/codegen/swap-small-types.rs
index ffa573c9a43ab..7aa613ae9c227 100644
--- a/tests/codegen/swap-small-types.rs
+++ b/tests/codegen/swap-small-types.rs
@@ -1,6 +1,7 @@
 //@ compile-flags: -Copt-level=3 -Z merge-functions=disabled
 //@ only-x86_64
 //@ min-llvm-version: 20
+//@ ignore-std-debug-assertions (`ptr::swap_nonoverlapping` has one which blocks some optimizations)
 
 #![crate_type = "lib"]
 

From 7103aea39ab4a73f074a035eb4416e223bf4cde3 Mon Sep 17 00:00:00 2001
From: Josh Triplett <josh@joshtriplett.org>
Date: Wed, 9 Apr 2025 12:59:37 -0700
Subject: [PATCH 25/27] Tracking issue template: fine-grained information on
 style update status

Inspired by some of the communication issues around the stabilization of
`let`-chains, give more fine-grained information about the status of
updating style for any new syntax.

This does not change the process or blockers in any way; it only
*documents* the current state in the tracking issue. For instance, in
the case of `let`-chains, we would have checked the boxes for "Style
team decision" and "(non-blocking) Formatting has been implemented", and
not checked the box for the style guide. That would have then provided
better supporting information for any decisions.
---
 .github/ISSUE_TEMPLATE/tracking_issue.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.github/ISSUE_TEMPLATE/tracking_issue.md b/.github/ISSUE_TEMPLATE/tracking_issue.md
index 3a9d8408b3c9d..aedc15a54c274 100644
--- a/.github/ISSUE_TEMPLATE/tracking_issue.md
+++ b/.github/ISSUE_TEMPLATE/tracking_issue.md
@@ -41,7 +41,10 @@ for larger features an implementation could be broken up into multiple PRs.
 - [ ] Implement the RFC (cc @rust-lang/XXX -- can anyone write up mentoring
       instructions?)
 - [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide])
-- [ ] Formatting for new syntax has been added to the [Style Guide] ([nightly-style-procedure])
+- [ ] Style updates for any new syntax ([nightly-style-procedure])
+  - [ ] Style team decision on new formatting
+  - [ ] Formatting for new syntax has been added to the [Style Guide]
+  - [ ] (non-blocking) Formatting has been implemented in `rustfmt`
 - [ ] Stabilization PR ([see instructions on rustc-dev-guide][stabilization-guide])
 
 [stabilization-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#stabilization-pr

From d21ee7ef32731f8aaa280075c993a75d9e72af9e Mon Sep 17 00:00:00 2001
From: Trevor Gross <tmgross@umich.edu>
Date: Wed, 9 Apr 2025 20:32:31 +0000
Subject: [PATCH 26/27] Update `compiler-builtins` to 0.1.153

Includes the following changes:

* Avoid OOB access in `memcpy` and `memmove` [1]
* Enable intrinsics on AVR [2]
* `libm` updates to avoid using `core::arch` vector intrinsics [3]

[1]: https://github.com/rust-lang/compiler-builtins/pull/799
[2]: https://github.com/rust-lang/compiler-builtins/pull/791
[3]: https://github.com/rust-lang/compiler-builtins/pull/814
---
 library/Cargo.lock       | 4 ++--
 library/alloc/Cargo.toml | 2 +-
 library/std/Cargo.toml   | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/library/Cargo.lock b/library/Cargo.lock
index ad634e9f794a4..d035ca6c91f53 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -67,9 +67,9 @@ dependencies = [
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.152"
+version = "0.1.153"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2153cf213eb259361567720ce55f6446f17acd0ccca87fb6dc05360578228a58"
+checksum = "926ef6a360c15a911023352fd6969c51605d70495406f735beb1ca0257448e59"
 dependencies = [
  "cc",
  "rustc-std-workspace-core",
diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
index 1d2dd1e60819f..ee8cb9d25a393 100644
--- a/library/alloc/Cargo.toml
+++ b/library/alloc/Cargo.toml
@@ -16,7 +16,7 @@ bench = false
 
 [dependencies]
 core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.152", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.153", features = ['rustc-dep-of-std'] }
 
 [features]
 compiler-builtins-mem = ['compiler_builtins/mem']
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 176da603d58d7..6b70ff764d7a4 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
 panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.152" }
+compiler_builtins = { version = "=0.1.153" }
 unwind = { path = "../unwind" }
 hashbrown = { version = "0.15", default-features = false, features = [
     'rustc-dep-of-std',

From 99b048cf2e599bdb7929735bd82b59a3e5acd072 Mon Sep 17 00:00:00 2001
From: Jieyou Xu <jieyouxu@outlook.com>
Date: Thu, 10 Apr 2025 08:18:32 +0800
Subject: [PATCH 27/27] compiletest: update to Edition 2024

---
 src/tools/compiletest/Cargo.toml       |  2 +-
 src/tools/compiletest/src/debuggers.rs |  4 +++-
 src/tools/compiletest/src/lib.rs       | 12 ++++++++----
 3 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index 06e618c2d254b..3db34ed24cc20 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "compiletest"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [lib]
 doctest = false
diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs
index 20e3c8dfb9ee7..5126e55aea123 100644
--- a/src/tools/compiletest/src/debuggers.rs
+++ b/src/tools/compiletest/src/debuggers.rs
@@ -40,7 +40,9 @@ pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
         //
         // we should figure out how to lift this restriction! (run them all
         // on different ports allocated dynamically).
-        env::set_var("RUST_TEST_THREADS", "1");
+        //
+        // SAFETY: at this point we are still single-threaded.
+        unsafe { env::set_var("RUST_TEST_THREADS", "1") };
     }
 
     Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() }))
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 782f6e0f2d8d2..dfd678a7e2d65 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -529,10 +529,14 @@ pub fn run_tests(config: Arc<Config>) {
     }
     // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
     // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
-    env::set_var("__COMPAT_LAYER", "RunAsInvoker");
-
-    // Let tests know which target they're running as
-    env::set_var("TARGET", &config.target);
+    //
+    // SAFETY: at this point we're still single-threaded.
+    unsafe { env::set_var("__COMPAT_LAYER", "RunAsInvoker") };
+
+    // Let tests know which target they're running as.
+    //
+    // SAFETY: at this point we're still single-threaded.
+    unsafe { env::set_var("TARGET", &config.target) };
 
     let mut configs = Vec::new();
     if let Mode::DebugInfo = config.mode {