Auto merge of #129115 - jieyouxu:reenable-dump-ice, r=estebank

Re-enable `dump-ice-to-disk` for Windows

This test was previously flakey on `i686-mingw` (reason unknown), but since some modifications (quarantining each ICE test in separate tmp dirs, adding/removing `RUSTC_ICE` env vars as suitable to prevent any kind of environmental influence), I could no longer make it fail on `i686-mingw`.

I tried running this test (without the `ignore-windows` of course) a bunch of times via `i686-mingw` try jobs and it refused to fail (see #128958). I was also never able to reproduce the failure locally.

In any case, if this turns out to be still flakey on `i686-mingw`, we can revert the removal of `ignore-windows` but this time we'll have way more context for why the test failed.

Running the `i686-mingw` alongside some Windows jobs for basic santiy check. But the try jobs succeeding is insufficient to guarantee reproducibility.

cc #129115 for backlink.

try-job: x86_64-msvc
try-job: x86_64-mingw
try-job: i686-msvc
try-job: i686-mingw
diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml
index b137497..5e54cee 100644
--- a/.github/workflows/dependencies.yml
+++ b/.github/workflows/dependencies.yml
@@ -67,7 +67,7 @@
       - name: cargo update rustbook
         run: |
           echo -e "\nrustbook dependencies:" >> cargo_update.log
-          cargo update --manifest-path src/tools/rustbook 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
+          cargo update --manifest-path src/tools/rustbook/Cargo.toml 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
       - name: upload Cargo.lock artifact for use in PR
         uses: actions/upload-artifact@v4
         with:
diff --git a/Cargo.lock b/Cargo.lock
index 4bd99b7..0b546d6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1775,9 +1775,9 @@
 
 [[package]]
 name = "indexmap"
-version = "2.2.6"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
 dependencies = [
  "equivalent",
  "hashbrown",
@@ -3149,6 +3149,7 @@
  "gimli 0.31.0",
  "object 0.36.2",
  "regex",
+ "serde_json",
  "similar",
  "wasmparser 0.214.0",
 ]
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index 300bfa1..9c07313 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -189,7 +189,7 @@ fn lower_delegation_sig(
     ) -> hir::FnSig<'hir> {
         let header = if let Some(local_sig_id) = sig_id.as_local() {
             match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
-                Some(sig) => self.lower_fn_header(sig.header),
+                Some(sig) => self.lower_fn_header(sig.header, hir::Safety::Safe),
                 None => self.generate_header_error(),
             }
         } else {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 7af3945..f606525 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -237,7 +237,7 @@ fn lower_item_kind(
                         });
                     let sig = hir::FnSig {
                         decl,
-                        header: this.lower_fn_header(*header),
+                        header: this.lower_fn_header(*header, hir::Safety::Safe),
                         span: this.lower_span(*fn_sig_span),
                     };
                     hir::ItemKind::Fn(sig, generics, body_id)
@@ -668,7 +668,7 @@ fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir
                 ForeignItemKind::Fn(box Fn { sig, generics, .. }) => {
                     let fdec = &sig.decl;
                     let itctx = ImplTraitContext::Universal;
-                    let (generics, (fn_dec, fn_args)) =
+                    let (generics, (decl, fn_args)) =
                         self.lower_generics(generics, Const::No, false, i.id, itctx, |this| {
                             (
                                 // Disallow `impl Trait` in foreign items.
@@ -682,9 +682,15 @@ fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir
                                 this.lower_fn_params_to_names(fdec),
                             )
                         });
-                    let safety = self.lower_safety(sig.header.safety, hir::Safety::Unsafe);
 
-                    hir::ForeignItemKind::Fn(fn_dec, fn_args, generics, safety)
+                    // Unmarked safety in unsafe block defaults to unsafe.
+                    let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe);
+
+                    hir::ForeignItemKind::Fn(
+                        hir::FnSig { header, decl, span: self.lower_span(sig.span) },
+                        fn_args,
+                        generics,
+                    )
                 }
                 ForeignItemKind::Static(box StaticItem { ty, mutability, expr: _, safety }) => {
                     let ty = self
@@ -1390,7 +1396,7 @@ fn lower_method_sig(
         coroutine_kind: Option<CoroutineKind>,
         parent_constness: Const,
     ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
-        let header = self.lower_fn_header(sig.header);
+        let header = self.lower_fn_header(sig.header, hir::Safety::Safe);
         // Don't pass along the user-provided constness of trait associated functions; we don't want to
         // synthesize a host effect param for them. We reject `const` on them during AST validation.
         let constness =
@@ -1403,14 +1409,18 @@ fn lower_method_sig(
         (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
     }
 
-    pub(super) fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
+    pub(super) fn lower_fn_header(
+        &mut self,
+        h: FnHeader,
+        default_safety: hir::Safety,
+    ) -> hir::FnHeader {
         let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
             hir::IsAsync::Async(span)
         } else {
             hir::IsAsync::NotAsync
         };
         hir::FnHeader {
-            safety: self.lower_safety(h.safety, hir::Safety::Safe),
+            safety: self.lower_safety(h.safety, default_safety),
             asyncness: asyncness,
             constness: self.lower_constness(h.constness),
             abi: self.lower_extern(h.ext),
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index a353c79..837cb80 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -887,7 +887,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara
 
 impl<'a> Visitor<'a> for AstValidator<'a> {
     fn visit_attribute(&mut self, attr: &Attribute) {
-        validate_attr::check_attr(&self.features, &self.session.psess, attr);
+        validate_attr::check_attr(&self.session.psess, attr);
     }
 
     fn visit_ty(&mut self, ty: &'a Ty) {
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 3ceb8e0..214a37b 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -559,7 +559,6 @@ macro_rules! gate_all {
     gate_all!(mut_ref, "mutable by-reference bindings are experimental");
     gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental");
     gate_all!(global_registration, "global registration is experimental");
-    gate_all!(unsafe_attributes, "`#[unsafe()]` markers for attributes are experimental");
     gate_all!(return_type_notation, "return type notation is experimental");
 
     if !visitor.features.never_patterns {
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index ed54c0c..ae2627d 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -328,7 +328,7 @@ pub fn parse_asm_args<'a>(
 /// Otherwise, the suggestion will be incorrect.
 fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
     // Tool-only output
-    let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span };
+    let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
     p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
 }
 
@@ -338,7 +338,7 @@ fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
 /// Otherwise, the suggestion will be incorrect.
 fn err_unsupported_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
     // Tool-only output
-    let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span };
+    let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
     p.dcx().emit_err(errors::GlobalAsmUnsupportedOption { span, symbol, full_span });
 }
 
diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
index 006b6aa..3d3bd3a 100644
--- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
@@ -47,7 +47,6 @@ fn expand(
     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
         let template = AttributeTemplate { list: Some("path"), ..Default::default() };
         validate_attr::check_builtin_meta_item(
-            &ecx.ecfg.features,
             &ecx.sess.psess,
             meta_item,
             ast::AttrStyle::Outer,
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index 57bddf0..e8704bc 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -38,7 +38,6 @@ fn expand(
                 let template =
                     AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
                 validate_attr::check_builtin_meta_item(
-                    features,
                     &sess.psess,
                     meta_item,
                     ast::AttrStyle::Outer,
diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs
index 73cc8ff..0bcd5ae 100644
--- a/compiler/rustc_builtin_macros/src/util.rs
+++ b/compiler/rustc_builtin_macros/src/util.rs
@@ -17,7 +17,6 @@ pub(crate) fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaI
     // All the built-in macro attributes are "words" at the moment.
     let template = AttributeTemplate { word: true, ..Default::default() };
     validate_attr::check_builtin_meta_item(
-        &ecx.ecfg.features,
         &ecx.sess.psess,
         meta_item,
         AttrStyle::Outer,
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index a2ab19a..2120fc1 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -106,9 +106,11 @@ fn build(mut self: Box<Self>, output: &Path) -> bool {
 
 impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
     fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
-        // FIXME use ArArchiveBuilder on most targets again once reading thin archives is
-        // implemented
-        if true {
+        // Keeping LlvmArchiveBuilder around in case of a regression caused by using
+        // ArArchiveBuilder.
+        // FIXME(#128955) remove a couple of months after #128936 gets merged in case
+        // no regression is found.
+        if false {
             Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
         } else {
             Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
@@ -198,25 +200,11 @@ fn create_dll_import_lib(
     get_xcoff_member_alignment: DEFAULT_OBJECT_READER.get_xcoff_member_alignment,
 };
 
-fn should_use_llvm_reader(buf: &[u8]) -> bool {
-    let is_bitcode = unsafe { llvm::LLVMRustIsBitcode(buf.as_ptr(), buf.len()) };
-
-    // COFF bigobj file, msvc LTO file or import library. See
-    // https://github.com/llvm/llvm-project/blob/453f27bc9/llvm/lib/BinaryFormat/Magic.cpp#L38-L51
-    let is_unsupported_windows_obj_file = buf.get(0..4) == Some(b"\0\0\xFF\xFF");
-
-    is_bitcode || is_unsupported_windows_obj_file
-}
-
 #[deny(unsafe_op_in_unsafe_fn)]
 fn get_llvm_object_symbols(
     buf: &[u8],
     f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
 ) -> io::Result<bool> {
-    if !should_use_llvm_reader(buf) {
-        return (DEFAULT_OBJECT_READER.get_symbols)(buf, f);
-    }
-
     let mut state = Box::new(f);
 
     let err = unsafe {
@@ -253,18 +241,10 @@ fn get_llvm_object_symbols(
 }
 
 fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool {
-    if !should_use_llvm_reader(buf) {
-        return (DEFAULT_OBJECT_READER.is_64_bit_object_file)(buf);
-    }
-
     unsafe { llvm::LLVMRustIs64BitSymbolicFile(buf.as_ptr(), buf.len()) }
 }
 
 fn llvm_is_ec_object_file(buf: &[u8]) -> bool {
-    if !should_use_llvm_reader(buf) {
-        return (DEFAULT_OBJECT_READER.is_ec_object_file)(buf);
-    }
-
     unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) }
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
index efe6168..0a02c23 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
@@ -88,7 +88,7 @@ fn make_mir_scope<'ll, 'tcx>(
     let loc = cx.lookup_debug_loc(scope_data.span.lo());
     let file_metadata = file_metadata(cx, &loc.file);
 
-    let parent_dbg_scope = match scope_data.inlined {
+    let dbg_scope = match scope_data.inlined {
         Some((callee, _)) => {
             // FIXME(eddyb) this would be `self.monomorphize(&callee)`
             // if this is moved to `rustc_codegen_ssa::mir::debuginfo`.
@@ -102,17 +102,15 @@ fn make_mir_scope<'ll, 'tcx>(
                 cx.dbg_scope_fn(callee, callee_fn_abi, None)
             })
         }
-        None => parent_scope.dbg_scope,
-    };
-
-    let dbg_scope = unsafe {
-        llvm::LLVMRustDIBuilderCreateLexicalBlock(
-            DIB(cx),
-            parent_dbg_scope,
-            file_metadata,
-            loc.line,
-            loc.col,
-        )
+        None => unsafe {
+            llvm::LLVMRustDIBuilderCreateLexicalBlock(
+                DIB(cx),
+                parent_scope.dbg_scope,
+                file_metadata,
+                loc.line,
+                loc.col,
+            )
+        },
     };
 
     let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
index 1300663..1810220 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
@@ -2,7 +2,7 @@
 
 use libc::c_uint;
 use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name;
-use rustc_codegen_ssa::debuginfo::wants_c_like_enum_debuginfo;
+use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
 use rustc_codegen_ssa::traits::ConstMethods;
 use rustc_index::IndexVec;
 use rustc_middle::bug;
@@ -12,7 +12,7 @@
 use smallvec::smallvec;
 
 use crate::common::CodegenCx;
-use crate::debuginfo::metadata::enums::{tag_base_type, DiscrResult};
+use crate::debuginfo::metadata::enums::DiscrResult;
 use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId};
 use crate::debuginfo::metadata::{
     build_field_di_node, file_metadata, size_and_align_of, type_di_node, unknown_file_metadata,
@@ -190,7 +190,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
     let enum_type_and_layout = cx.layout_of(enum_type);
     let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
 
-    assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout));
+    assert!(!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout));
 
     type_map::build_type_with_children(
         cx,
@@ -265,7 +265,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
     let coroutine_type_and_layout = cx.layout_of(coroutine_type);
     let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false);
 
-    assert!(!wants_c_like_enum_debuginfo(coroutine_type_and_layout));
+    assert!(!wants_c_like_enum_debuginfo(cx.tcx, coroutine_type_and_layout));
 
     type_map::build_type_with_children(
         cx,
@@ -381,7 +381,7 @@ fn build_union_fields_for_enum<'ll, 'tcx>(
     tag_field: usize,
     untagged_variant_index: Option<VariantIdx>,
 ) -> SmallVec<&'ll DIType> {
-    let tag_base_type = super::tag_base_type(cx, enum_type_and_layout);
+    let tag_base_type = tag_base_type(cx.tcx, enum_type_and_layout);
 
     let variant_names_type_di_node = build_variant_names_type_di_node(
         cx,
@@ -676,7 +676,7 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>(
     let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx);
     let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len();
 
-    let tag_base_type = tag_base_type(cx, coroutine_type_and_layout);
+    let tag_base_type = tag_base_type(cx.tcx, coroutine_type_and_layout);
 
     let variant_names_type_di_node = build_variant_names_type_di_node(
         cx,
@@ -803,7 +803,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
 
     assert_eq!(
         cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
-        cx.size_and_align_of(super::tag_base_type(cx, enum_type_and_layout))
+        cx.size_and_align_of(self::tag_base_type(cx.tcx, enum_type_and_layout))
     );
 
     // ... and a field for the tag. If the tag is 128 bits wide, this will actually
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
index fc3adaf..77cbcd8 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
@@ -1,17 +1,15 @@
 use std::borrow::Cow;
 
 use rustc_codegen_ssa::debuginfo::type_names::{compute_debuginfo_type_name, cpp_like_debuginfo};
-use rustc_codegen_ssa::debuginfo::wants_c_like_enum_debuginfo;
+use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
 use rustc_hir::def::CtorKind;
 use rustc_index::IndexSlice;
 use rustc_middle::bug;
 use rustc_middle::mir::CoroutineLayout;
-use rustc_middle::ty::layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout};
+use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef};
 use rustc_span::Symbol;
-use rustc_target::abi::{
-    FieldIdx, HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants,
-};
+use rustc_target::abi::{FieldIdx, TagEncoding, VariantIdx, Variants};
 
 use super::type_map::{DINodeCreationResult, UniqueTypeId};
 use super::{size_and_align_of, SmallVec};
@@ -39,7 +37,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
 
     let enum_type_and_layout = cx.layout_of(enum_type);
 
-    if wants_c_like_enum_debuginfo(enum_type_and_layout) {
+    if wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout) {
         return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout);
     }
 
@@ -74,7 +72,7 @@ fn build_c_style_enum_di_node<'ll, 'tcx>(
         di_node: build_enumeration_type_di_node(
             cx,
             &compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false),
-            tag_base_type(cx, enum_type_and_layout),
+            tag_base_type(cx.tcx, enum_type_and_layout),
             enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
                 let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
                 (name, discr.val)
@@ -85,48 +83,6 @@ fn build_c_style_enum_di_node<'ll, 'tcx>(
     }
 }
 
-/// Extract the type with which we want to describe the tag of the given enum or coroutine.
-fn tag_base_type<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    enum_type_and_layout: TyAndLayout<'tcx>,
-) -> Ty<'tcx> {
-    assert!(match enum_type_and_layout.ty.kind() {
-        ty::Coroutine(..) => true,
-        ty::Adt(adt_def, _) => adt_def.is_enum(),
-        _ => false,
-    });
-
-    match enum_type_and_layout.layout.variants() {
-        // A single-variant enum has no discriminant.
-        Variants::Single { .. } => {
-            bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout)
-        }
-
-        Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
-            // Niche tags are always normalized to unsized integers of the correct size.
-            match tag.primitive() {
-                Primitive::Int(t, _) => t,
-                Primitive::Float(f) => Integer::from_size(f.size()).unwrap(),
-                // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
-                Primitive::Pointer(_) => {
-                    // If the niche is the NULL value of a reference, then `discr_enum_ty` will be
-                    // a RawPtr. CodeView doesn't know what to do with enums whose base type is a
-                    // pointer so we fix this up to just be `usize`.
-                    // DWARF might be able to deal with this but with an integer type we are on
-                    // the safe side there too.
-                    cx.data_layout().ptr_sized_integer()
-                }
-            }
-            .to_ty(cx.tcx, false)
-        }
-
-        Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
-            // Direct tags preserve the sign.
-            tag.primitive().to_ty(cx.tcx)
-        }
-    }
-}
-
 /// Build a DW_TAG_enumeration_type debuginfo node, with the given base type and variants.
 /// This is a helper function and does not register anything in the type map by itself.
 ///
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
index d7e3b47..238fbad 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
@@ -2,7 +2,7 @@
 
 use libc::c_uint;
 use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name;
-use rustc_codegen_ssa::debuginfo::wants_c_like_enum_debuginfo;
+use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
 use rustc_codegen_ssa::traits::ConstMethods;
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
@@ -11,7 +11,6 @@
 use smallvec::smallvec;
 
 use crate::common::CodegenCx;
-use crate::debuginfo::metadata::enums::tag_base_type;
 use crate::debuginfo::metadata::type_map::{self, Stub, StubInfo, UniqueTypeId};
 use crate::debuginfo::metadata::{
     file_metadata, size_and_align_of, type_di_node, unknown_file_metadata, visibility_di_flags,
@@ -54,7 +53,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
 
     let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did());
 
-    assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout));
+    assert!(!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout));
 
     type_map::build_type_with_children(
         cx,
@@ -131,7 +130,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
     let containing_scope = get_namespace_for_item(cx, coroutine_def_id);
     let coroutine_type_and_layout = cx.layout_of(coroutine_type);
 
-    assert!(!wants_c_like_enum_debuginfo(coroutine_type_and_layout));
+    assert!(!wants_c_like_enum_debuginfo(cx.tcx, coroutine_type_and_layout));
 
     let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false);
 
@@ -321,7 +320,7 @@ fn build_discr_member_di_node<'ll, 'tcx>(
         &Variants::Single { .. } => None,
 
         &Variants::Multiple { tag_field, .. } => {
-            let tag_base_type = tag_base_type(cx, enum_or_coroutine_type_and_layout);
+            let tag_base_type = tag_base_type(cx.tcx, enum_or_coroutine_type_and_layout);
             let (size, align) = cx.size_and_align_of(tag_base_type);
 
             unsafe {
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index ce55d99..8eb44d1 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -307,10 +307,15 @@ fn add_archive(
             let file_name = String::from_utf8(entry.name().to_vec())
                 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
             if !skip(&file_name) {
-                self.entries.push((
-                    file_name.into_bytes(),
-                    ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() },
-                ));
+                if entry.is_thin() {
+                    let member_path = archive_path.parent().unwrap().join(Path::new(&file_name));
+                    self.entries.push((file_name.into_bytes(), ArchiveEntry::File(member_path)));
+                } else {
+                    self.entries.push((
+                        file_name.into_bytes(),
+                        ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() },
+                    ));
+                }
             }
         }
 
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs
index 1eaf593..0918660 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs
@@ -1,6 +1,7 @@
-use rustc_middle::ty::layout::TyAndLayout;
-use rustc_middle::ty::{self};
-use rustc_target::abi::Size;
+use rustc_middle::bug;
+use rustc_middle::ty::layout::{IntegerExt, PrimitiveExt, TyAndLayout};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_target::abi::{Integer, Primitive, Size, TagEncoding, Variants};
 
 // FIXME(eddyb) find a place for this (or a way to replace it).
 pub mod type_names;
@@ -11,13 +12,25 @@
 /// NOTE: This is somewhat inconsistent right now: For empty enums and enums with a single
 ///       fieldless variant, we generate DW_TAG_struct_type, although a
 ///       DW_TAG_enumeration_type would be a better fit.
-pub fn wants_c_like_enum_debuginfo(enum_type_and_layout: TyAndLayout<'_>) -> bool {
+pub fn wants_c_like_enum_debuginfo<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+) -> bool {
     match enum_type_and_layout.ty.kind() {
         ty::Adt(adt_def, _) => {
             if !adt_def.is_enum() {
                 return false;
             }
 
+            if type_names::cpp_like_debuginfo(tcx)
+                && tag_base_type_opt(tcx, enum_type_and_layout)
+                    .map(|ty| ty.primitive_size(tcx).bits())
+                    == Some(128)
+            {
+                // C++-like debuginfo never uses the C-like representation for 128-bit enums.
+                return false;
+            }
+
             match adt_def.variants().len() {
                 0 => false,
                 1 => {
@@ -33,3 +46,51 @@ pub fn wants_c_like_enum_debuginfo(enum_type_and_layout: TyAndLayout<'_>) -> boo
         _ => false,
     }
 }
+
+/// Extract the type with which we want to describe the tag of the given enum or coroutine.
+pub fn tag_base_type<'tcx>(tcx: TyCtxt<'tcx>, enum_type_and_layout: TyAndLayout<'tcx>) -> Ty<'tcx> {
+    tag_base_type_opt(tcx, enum_type_and_layout).unwrap_or_else(|| {
+        bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout)
+    })
+}
+
+pub fn tag_base_type_opt<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+) -> Option<Ty<'tcx>> {
+    assert!(match enum_type_and_layout.ty.kind() {
+        ty::Coroutine(..) => true,
+        ty::Adt(adt_def, _) => adt_def.is_enum(),
+        _ => false,
+    });
+
+    match enum_type_and_layout.layout.variants() {
+        // A single-variant enum has no discriminant.
+        Variants::Single { .. } => None,
+
+        Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
+            // Niche tags are always normalized to unsized integers of the correct size.
+            Some(
+                match tag.primitive() {
+                    Primitive::Int(t, _) => t,
+                    Primitive::Float(f) => Integer::from_size(f.size()).unwrap(),
+                    // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+                    Primitive::Pointer(_) => {
+                        // If the niche is the NULL value of a reference, then `discr_enum_ty` will be
+                        // a RawPtr. CodeView doesn't know what to do with enums whose base type is a
+                        // pointer so we fix this up to just be `usize`.
+                        // DWARF might be able to deal with this but with an integer type we are on
+                        // the safe side there too.
+                        tcx.data_layout.ptr_sized_integer()
+                    }
+                }
+                .to_ty(tcx, false),
+            )
+        }
+
+        Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
+            // Direct tags preserve the sign.
+            Some(tag.primitive().to_ty(tcx))
+        }
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 359043a..f0bc435 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -85,7 +85,7 @@ fn push_debuginfo_type_name<'tcx>(
             let layout_for_cpp_like_fallback = if cpp_like_debuginfo && def.is_enum() {
                 match tcx.layout_of(ParamEnv::reveal_all().and(t)) {
                     Ok(layout) => {
-                        if !wants_c_like_enum_debuginfo(layout) {
+                        if !wants_c_like_enum_debuginfo(tcx, layout) {
                             Some(layout)
                         } else {
                             // This is a C-like enum so we don't want to use the fallback encoding
@@ -106,6 +106,7 @@ fn push_debuginfo_type_name<'tcx>(
 
             if let Some(ty_and_layout) = layout_for_cpp_like_fallback {
                 msvc_enum_fallback(
+                    tcx,
                     ty_and_layout,
                     &|output, visited| {
                         push_item_name(tcx, def.did(), true, output);
@@ -421,6 +422,7 @@ fn push_debuginfo_type_name<'tcx>(
             if cpp_like_debuginfo && t.is_coroutine() {
                 let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap();
                 msvc_enum_fallback(
+                    tcx,
                     ty_and_layout,
                     &|output, visited| {
                         push_closure_or_coroutine_name(tcx, def_id, args, true, output, visited);
@@ -455,12 +457,13 @@ fn push_debuginfo_type_name<'tcx>(
     // debugger. For more information, look in
     // rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs.
     fn msvc_enum_fallback<'tcx>(
+        tcx: TyCtxt<'tcx>,
         ty_and_layout: TyAndLayout<'tcx>,
         push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
         output: &mut String,
         visited: &mut FxHashSet<Ty<'tcx>>,
     ) {
-        assert!(!wants_c_like_enum_debuginfo(ty_and_layout));
+        assert!(!wants_c_like_enum_debuginfo(tcx, ty_and_layout));
         output.push_str("enum2$<");
         push_inner(output, visited);
         push_close_angle_bracket(true, output);
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 0e49597..7569254 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -1,3 +1,4 @@
+use std::collections::hash_map::Entry;
 use std::ops::Range;
 
 use rustc_data_structures::fx::FxHashMap;
@@ -447,6 +448,7 @@ pub fn compute_per_local_var_debug_info(
         }
 
         let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
+        let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default();
         for var in &self.mir.var_debug_info {
             let dbg_scope_and_span = if full_debug_info {
                 self.adjusted_span_and_dbg_scope(var.source_info)
@@ -491,7 +493,18 @@ pub fn compute_per_local_var_debug_info(
                     VariableKind::LocalVariable
                 };
 
-                self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
+                if let VariableKind::ArgumentVariable(arg_index) = var_kind {
+                    match params_seen.entry((dbg_scope, arg_index)) {
+                        Entry::Occupied(o) => o.get().clone(),
+                        Entry::Vacant(v) => v
+                            .insert(
+                                self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span),
+                            )
+                            .clone(),
+                    }
+                } else {
+                    self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
+                }
             });
 
             let fragment = if let Some(ref fragment) = var.composite {
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 4ce0726..de94d87 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -106,7 +106,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
     locals: locals::Locals<'tcx, Bx::Value>,
 
     /// All `VarDebugInfo` from the MIR body, partitioned by `Local`.
-    /// This is `None` if no var`#[non_exhaustive]`iable debuginfo/names are needed.
+    /// This is `None` if no variable debuginfo/names are needed.
     per_local_var_debug_info:
         Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>>,
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index 81e9641..c5e2d55 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -1,4 +1,5 @@
 use std::any::Any;
+use std::hash::Hash;
 
 use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_data_structures::fx::FxIndexMap;
@@ -30,7 +31,7 @@ pub trait BackendTypes {
 
     // FIXME(eddyb) find a common convention for all of the debuginfo-related
     // names (choose between `Dbg`, `Debug`, `DebugInfo`, `DI` etc.).
-    type DIScope: Copy;
+    type DIScope: Copy + Hash + PartialEq + Eq;
     type DILocation: Copy;
     type DIVariable: Copy;
 }
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 7804042..d825a47 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -6,7 +6,6 @@
 #![feature(box_patterns)]
 #![feature(decl_macro)]
 #![feature(if_let_guard)]
-#![feature(is_none_or)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 3794a6e..69cbf8c 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -10,7 +10,7 @@
 either = "1.0"
 elsa = "=1.7.1"
 ena = "0.14.3"
-indexmap = { version = "2.0.0" }
+indexmap = { version = "2.4.0" }
 jobserver_crate = { version = "0.1.28", package = "jobserver" }
 measureme = "11"
 rustc-hash = "1.1.0"
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index f6bf9f5..b0d3fec 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -265,12 +265,7 @@ fn process_cfg_attr(&self, attr: &Attribute) -> Vec<Attribute> {
     /// is in the original source file. Gives a compiler error if the syntax of
     /// the attribute is incorrect.
     pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
-        validate_attr::check_attribute_safety(
-            self.features.unwrap_or(&Features::default()),
-            &self.sess.psess,
-            AttributeSafety::Normal,
-            &cfg_attr,
-        );
+        validate_attr::check_attribute_safety(&self.sess.psess, AttributeSafety::Normal, &cfg_attr);
 
         let Some((cfg_predicate, expanded_attrs)) =
             rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
@@ -395,11 +390,7 @@ pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option<MetaItem>) {
             }
         };
 
-        validate_attr::deny_builtin_meta_unsafety(
-            self.features.unwrap_or(&Features::default()),
-            &self.sess.psess,
-            &meta_item,
-        );
+        validate_attr::deny_builtin_meta_unsafety(&self.sess.psess, &meta_item);
 
         (
             parse_cfg(&meta_item, self.sess).map_or(true, |meta_item| {
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 37679e1..cb6b132 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -1882,7 +1882,7 @@ fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) {
         let mut span: Option<Span> = None;
         while let Some(attr) = attrs.next() {
             rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features);
-            validate_attr::check_attr(features, &self.cx.sess.psess, attr);
+            validate_attr::check_attr(&self.cx.sess.psess, attr);
 
             let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span };
             span = Some(current_span);
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 6f17710..256713e 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -1154,7 +1154,7 @@ fn check_matcher_core<'tt>(
                         && matches!(kind, NonterminalKind::Pat(PatParam { inferred: true }))
                         && matches!(
                             next_token,
-                            TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or)
+                            TokenTree::Token(token) if *token == BinOp(token::BinOpToken::Or)
                         )
                     {
                         // It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param.
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index e5a1c6c..b2f7c8f 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -54,18 +54,24 @@ pub(super) fn parse(
 
     // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming
     // additional trees if need be.
-    let mut trees = input.trees();
+    let mut trees = input.trees().peekable();
     while let Some(tree) = trees.next() {
         // Given the parsed tree, if there is a metavar and we are expecting matchers, actually
         // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
         let tree = parse_tree(tree, &mut trees, parsing_patterns, sess, node_id, features, edition);
         match tree {
             TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
-                let span = match trees.next() {
+                // Not consuming the next token immediately, as it may not be a colon
+                let span = match trees.peek() {
                     Some(&tokenstream::TokenTree::Token(
                         Token { kind: token::Colon, span: colon_span },
                         _,
                     )) => {
+                        // Consume the colon first
+                        trees.next();
+
+                        // It's ok to consume the next tree no matter how,
+                        // since if it's not a token then it will be an invalid declaration.
                         match trees.next() {
                             Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
                                 Some((fragment, _)) => {
@@ -125,12 +131,13 @@ pub(super) fn parse(
                                 }
                                 _ => token.span,
                             },
-                            Some(tree) => tree.span(),
-                            None => colon_span,
+                            // Invalid, return a nice source location
+                            _ => colon_span.with_lo(start_sp.lo()),
                         }
                     }
-                    Some(tree) => tree.span(),
-                    None => start_sp,
+                    // Whether it's none or some other tree, it doesn't belong to
+                    // the current meta variable, returning the original span.
+                    _ => start_sp,
                 };
 
                 result.push(TokenTree::MetaVarDecl(span, ident, None));
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 03b40e2..3d5ecba 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -392,6 +392,8 @@ macro_rules! declare_features {
     (accepted, universal_impl_trait, "1.26.0", Some(34511)),
     /// Allows arbitrary delimited token streams in non-macro attributes.
     (accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208)),
+    /// Allows unsafe attributes.
+    (accepted, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)),
     /// The `unsafe_op_in_unsafe_fn` lint (allowed by default): no longer treat an unsafe function as an unsafe block.
     (accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668)),
     /// Allows unsafe on extern declarations and safety qualifiers over internal items.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index d593f05..d4c54b6 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -578,12 +578,6 @@ pub struct BuiltinAttribute {
         EncodeCrossCrate::No, coroutines, experimental!(coroutines)
     ),
 
-    // `#[pointee]` attribute to designate the pointee type in SmartPointer derive-macro
-    gated!(
-        pointee, Normal, template!(Word), ErrorFollowing,
-        EncodeCrossCrate::No, derive_smart_pointer, experimental!(pointee)
-    ),
-
     // RFC 3543
     // `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]`
     gated!(
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 24f691e..459df9e 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -622,8 +622,6 @@ pub fn internal(&self, feature: Symbol) -> bool {
     (unstable, type_changing_struct_update, "1.58.0", Some(86555)),
     /// Allows unnamed fields of struct and union type
     (incomplete, unnamed_fields, "1.74.0", Some(49804)),
-    /// Allows unsafe attributes.
-    (unstable, unsafe_attributes, "1.80.0", Some(123757)),
     /// Allows const generic parameters to be defined with types that
     /// are not `Sized`, e.g. `fn foo<const N: [u8]>() {`.
     (incomplete, unsized_const_params, "CURRENT_RUSTC_VERSION", Some(95174)),
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 33e8432..6599e70 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3586,7 +3586,7 @@ pub fn foreign_item_id(&self) -> ForeignItemId {
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub enum ForeignItemKind<'hir> {
     /// A foreign function.
-    Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>, Safety),
+    Fn(FnSig<'hir>, &'hir [Ident], &'hir Generics<'hir>),
     /// A foreign static item (`static ext: u8`).
     Static(&'hir Ty<'hir>, Mutability, Safety),
     /// A foreign type.
@@ -3645,7 +3645,10 @@ pub fn fn_sig(self) -> Option<&'hir FnSig<'hir>> {
         match self {
             OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
             | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
-            | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig),
+            | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. })
+            | OwnerNode::ForeignItem(ForeignItem {
+                kind: ForeignItemKind::Fn(fn_sig, _, _), ..
+            }) => Some(fn_sig),
             _ => None,
         }
     }
@@ -3654,11 +3657,10 @@ pub fn fn_decl(self) -> Option<&'hir FnDecl<'hir>> {
         match self {
             OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
             | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
-            | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl),
-            OwnerNode::ForeignItem(ForeignItem {
-                kind: ForeignItemKind::Fn(fn_decl, _, _, _),
-                ..
-            }) => Some(fn_decl),
+            | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. })
+            | OwnerNode::ForeignItem(ForeignItem {
+                kind: ForeignItemKind::Fn(fn_sig, _, _), ..
+            }) => Some(fn_sig.decl),
             _ => None,
         }
     }
@@ -3846,11 +3848,13 @@ pub fn fn_decl(self) -> Option<&'hir FnDecl<'hir>> {
         match self {
             Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
             | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
-            | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl),
-            Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. })
-            | Node::ForeignItem(ForeignItem {
-                kind: ForeignItemKind::Fn(fn_decl, _, _, _), ..
-            }) => Some(fn_decl),
+            | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. })
+            | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_sig, _, _), .. }) => {
+                Some(fn_sig.decl)
+            }
+            Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. }) => {
+                Some(fn_decl)
+            }
             _ => None,
         }
     }
@@ -3874,7 +3878,10 @@ pub fn fn_sig(self) -> Option<&'hir FnSig<'hir>> {
         match self {
             Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
             | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
-            | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig),
+            | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. })
+            | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_sig, _, _), .. }) => {
+                Some(fn_sig)
+            }
             _ => None,
         }
     }
@@ -3949,7 +3956,7 @@ pub fn body_id(&self) -> Option<BodyId> {
     pub fn generics(self) -> Option<&'hir Generics<'hir>> {
         match self {
             Node::ForeignItem(ForeignItem {
-                kind: ForeignItemKind::Fn(_, _, generics, _), ..
+                kind: ForeignItemKind::Fn(_, _, generics), ..
             })
             | Node::TraitItem(TraitItem { generics, .. })
             | Node::ImplItem(ImplItem { generics, .. }) => Some(generics),
@@ -4039,8 +4046,8 @@ mod size_asserts {
     static_assert_size!(Expr<'_>, 64);
     static_assert_size!(ExprKind<'_>, 48);
     static_assert_size!(FnDecl<'_>, 40);
-    static_assert_size!(ForeignItem<'_>, 72);
-    static_assert_size!(ForeignItemKind<'_>, 40);
+    static_assert_size!(ForeignItem<'_>, 88);
+    static_assert_size!(ForeignItemKind<'_>, 56);
     static_assert_size!(GenericArg<'_>, 16);
     static_assert_size!(GenericBound<'_>, 48);
     static_assert_size!(Generics<'_>, 56);
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index dd501f84..a54596e 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -611,9 +611,9 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(
     try_visit!(visitor.visit_ident(foreign_item.ident));
 
     match foreign_item.kind {
-        ForeignItemKind::Fn(ref function_declaration, param_names, ref generics, _) => {
+        ForeignItemKind::Fn(ref sig, param_names, ref generics) => {
             try_visit!(visitor.visit_generics(generics));
-            try_visit!(visitor.visit_fn_decl(function_declaration));
+            try_visit!(visitor.visit_fn_decl(sig.decl));
             walk_list!(visitor, visit_ident, param_names.iter().copied());
         }
         ForeignItemKind::Static(ref typ, _, _) => {
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 2e778fd..0135cdf 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -804,8 +804,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 
                         let item = tcx.hir().foreign_item(item.id);
                         match &item.kind {
-                            hir::ForeignItemKind::Fn(fn_decl, _, _, _) => {
-                                require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span);
+                            hir::ForeignItemKind::Fn(sig, _, _) => {
+                                require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span);
                             }
                             hir::ForeignItemKind::Static(..) => {
                                 check_static_inhabited(tcx, def_id);
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 4b45ced..c2b2f08 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -30,7 +30,7 @@ fn equate_intrinsic_type<'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, _),
+            kind: hir::ForeignItemKind::Fn(_, _, generics),
             ..
         }) => (tcx.generics_of(def_id), generics.span),
         _ => {
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index d3b928f..bdf2914 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -350,8 +350,8 @@ fn check_foreign_item<'tcx>(
     );
 
     match item.kind {
-        hir::ForeignItemKind::Fn(decl, ..) => {
-            check_item_fn(tcx, def_id, item.ident, item.span, decl)
+        hir::ForeignItemKind::Fn(sig, ..) => {
+            check_item_fn(tcx, def_id, item.ident, item.span, sig.decl)
         }
         hir::ForeignItemKind::Static(ty, ..) => {
             check_item_type(tcx, def_id, ty.span, UnsizedHandling::AllowIfForeignTail)
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 91fa066..f75954c 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1440,11 +1440,9 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
             icx.lowerer().lower_fn_ty(hir_id, header.safety, header.abi, decl, Some(generics), None)
         }
 
-        ForeignItem(&hir::ForeignItem {
-            kind: ForeignItemKind::Fn(fn_decl, _, _, safety), ..
-        }) => {
+        ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(sig, _, _), .. }) => {
             let abi = tcx.hir().get_foreign_abi(hir_id);
-            compute_sig_of_foreign_fn_decl(tcx, def_id, fn_decl, abi, safety)
+            compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety)
         }
 
         Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => {
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index e11d3c9..ae0c70d 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -604,7 +604,7 @@ fn visit_precise_capturing_arg(
 
     fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
         match item.kind {
-            hir::ForeignItemKind::Fn(_, _, generics, _) => {
+            hir::ForeignItemKind::Fn(_, _, generics) => {
                 self.visit_early_late(item.hir_id(), generics, |this| {
                     intravisit::walk_foreign_item(this, item);
                 })
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 089cee2..cff2117 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -352,16 +352,11 @@ fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
         self.maybe_print_comment(item.span.lo());
         self.print_outer_attributes(self.attrs(item.hir_id()));
         match item.kind {
-            hir::ForeignItemKind::Fn(decl, arg_names, generics, safety) => {
+            hir::ForeignItemKind::Fn(sig, arg_names, generics) => {
                 self.head("");
                 self.print_fn(
-                    decl,
-                    hir::FnHeader {
-                        safety,
-                        constness: hir::Constness::NotConst,
-                        abi: Abi::Rust,
-                        asyncness: hir::IsAsync::NotAsync,
-                    },
+                    sig.decl,
+                    sig.header,
                     Some(item.ident.name),
                     generics,
                     arg_names,
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 589a9c5..6b813dc 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -14,7 +14,7 @@
 use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
 use rustc_middle::ty::{self, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::error_reporting::traits::ArgKind;
 use rustc_trait_selection::traits;
@@ -539,6 +539,10 @@ fn extract_sig_from_projection(
     /// we identify the `FnOnce<Args, Output = ?Fut>` bound, and if the output type is
     /// an inference variable `?Fut`, we check if that is bounded by a `Future<Output = Ty>`
     /// projection.
+    ///
+    /// This function is actually best-effort with the return type; if we don't find a
+    /// `Future` projection, we still will return arguments that we extracted from the `FnOnce`
+    /// projection, and the output will be an unconstrained type variable instead.
     fn extract_sig_from_projection_and_future_bound(
         &self,
         cause_span: Option<Span>,
@@ -564,24 +568,43 @@ fn extract_sig_from_projection_and_future_bound(
         };
 
         // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare.
+        let mut return_ty = None;
         for bound in self.obligations_for_self_ty(return_vid) {
             if let Some(ret_projection) = bound.predicate.as_projection_clause()
                 && let Some(ret_projection) = ret_projection.no_bound_vars()
                 && self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput)
             {
-                let sig = projection.rebind(self.tcx.mk_fn_sig(
-                    input_tys,
-                    ret_projection.term.expect_type(),
-                    false,
-                    hir::Safety::Safe,
-                    Abi::Rust,
-                ));
-
-                return Some(ExpectedSig { cause_span, sig });
+                return_ty = Some(ret_projection.term.expect_type());
+                break;
             }
         }
 
-        None
+        // SUBTLE: If we didn't find a `Future<Output = ...>` bound for the return
+        // vid, we still want to attempt to provide inference guidance for the async
+        // closure's arguments. Instantiate a new vid to plug into the output type.
+        //
+        // You may be wondering, what if it's higher-ranked? Well, given that we
+        // found a type variable for the `FnOnce::Output` projection above, we know
+        // that the output can't mention any of the vars.
+        //
+        // Also note that we use a fresh var here for the signature since the signature
+        // records the output of the *future*, and `return_vid` above is the type
+        // variable of the future, not its output.
+        //
+        // FIXME: We probably should store this signature inference output in a way
+        // that does not misuse a `FnSig` type, but that can be done separately.
+        let return_ty =
+            return_ty.unwrap_or_else(|| self.next_ty_var(cause_span.unwrap_or(DUMMY_SP)));
+
+        let sig = projection.rebind(self.tcx.mk_fn_sig(
+            input_tys,
+            return_ty,
+            false,
+            hir::Safety::Safe,
+            Abi::Rust,
+        ));
+
+        return Some(ExpectedSig { cause_span, sig });
     }
 
     fn sig_of_closure(
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 758a1ce..9ec1011 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -5,7 +5,6 @@
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(if_let_guard)]
-#![feature(is_none_or)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 3b71e2f..b3cf73b 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -3448,6 +3448,7 @@ fn suggest_traits_to_import(
         trait_missing_method: bool,
     ) {
         let mut alt_rcvr_sugg = false;
+        let mut trait_in_other_version_found = false;
         if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
             debug!(
                 "suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}",
@@ -3489,8 +3490,17 @@ fn suggest_traits_to_import(
                         // self types and rely on the suggestion to `use` the trait from
                         // `suggest_valid_traits`.
                         let did = Some(pick.item.container_id(self.tcx));
-                        let skip = skippable.contains(&did);
-                        if pick.autoderefs == 0 && !skip {
+                        if skippable.contains(&did) {
+                            continue;
+                        }
+                        trait_in_other_version_found = self
+                            .detect_and_explain_multiple_crate_versions(
+                                err,
+                                pick.item.def_id,
+                                rcvr.hir_id,
+                                *rcvr_ty,
+                            );
+                        if pick.autoderefs == 0 && !trait_in_other_version_found {
                             err.span_label(
                                 pick.item.ident(self.tcx).span,
                                 format!("the method is available for `{rcvr_ty}` here"),
@@ -3675,7 +3685,32 @@ fn suggest_traits_to_import(
                 }
             }
         }
-        if self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true) {
+
+        if let SelfSource::QPath(ty) = source
+            && !valid_out_of_scope_traits.is_empty()
+            && let hir::TyKind::Path(path) = ty.kind
+            && let hir::QPath::Resolved(_, path) = path
+            && let Some(def_id) = path.res.opt_def_id()
+            && let Some(assoc) = self
+                .tcx
+                .associated_items(valid_out_of_scope_traits[0])
+                .filter_by_name_unhygienic(item_name.name)
+                .next()
+        {
+            // See if the `Type::function(val)` where `function` wasn't found corresponds to a
+            // `Trait` that is imported directly, but `Type` came from a different version of the
+            // same crate.
+            let rcvr_ty = self.tcx.type_of(def_id).instantiate_identity();
+            trait_in_other_version_found = self.detect_and_explain_multiple_crate_versions(
+                err,
+                assoc.def_id,
+                ty.hir_id,
+                rcvr_ty,
+            );
+        }
+        if !trait_in_other_version_found
+            && self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true)
+        {
             return;
         }
 
@@ -4040,6 +4075,62 @@ enum Introducer {
         }
     }
 
+    fn detect_and_explain_multiple_crate_versions(
+        &self,
+        err: &mut Diag<'_>,
+        item_def_id: DefId,
+        hir_id: hir::HirId,
+        rcvr_ty: Ty<'_>,
+    ) -> bool {
+        let hir_id = self.tcx.parent_hir_id(hir_id);
+        let Some(traits) = self.tcx.in_scope_traits(hir_id) else { return false };
+        if traits.is_empty() {
+            return false;
+        }
+        let trait_def_id = self.tcx.parent(item_def_id);
+        let krate = self.tcx.crate_name(trait_def_id.krate);
+        let name = self.tcx.item_name(trait_def_id);
+        let candidates: Vec<_> = traits
+            .iter()
+            .filter(|c| {
+                c.def_id.krate != trait_def_id.krate
+                    && self.tcx.crate_name(c.def_id.krate) == krate
+                    && self.tcx.item_name(c.def_id) == name
+            })
+            .map(|c| (c.def_id, c.import_ids.get(0).cloned()))
+            .collect();
+        if candidates.is_empty() {
+            return false;
+        }
+        let item_span = self.tcx.def_span(item_def_id);
+        let msg = format!(
+            "there are multiple different versions of crate `{krate}` in the dependency graph",
+        );
+        let trait_span = self.tcx.def_span(trait_def_id);
+        let mut multi_span: MultiSpan = trait_span.into();
+        multi_span.push_span_label(trait_span, format!("this is the trait that is needed"));
+        let descr = self.tcx.associated_item(item_def_id).descr();
+        multi_span
+            .push_span_label(item_span, format!("the {descr} is available for `{rcvr_ty}` here"));
+        for (def_id, import_def_id) in candidates {
+            if let Some(import_def_id) = import_def_id {
+                multi_span.push_span_label(
+                    self.tcx.def_span(import_def_id),
+                    format!(
+                        "`{name}` imported here doesn't correspond to the right version of crate \
+                         `{krate}`",
+                    ),
+                );
+            }
+            multi_span.push_span_label(
+                self.tcx.def_span(def_id),
+                format!("this is the trait that was imported"),
+            );
+        }
+        err.span_note(multi_span, msg);
+        true
+    }
+
     /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`
     /// FIXME: currently not working for suggesting `map_or_else`, see #102408
     pub(crate) fn suggest_else_fn_with_closure(
diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs
index 7438c97..1cb8bc8 100644
--- a/compiler/rustc_index/src/vec.rs
+++ b/compiler/rustc_index/src/vec.rs
@@ -188,6 +188,11 @@ pub fn resize_to_elem(&mut self, elem: I, fill_value: impl FnMut() -> T) {
         let min_new_len = elem.index() + 1;
         self.raw.resize_with(min_new_len, fill_value);
     }
+
+    #[inline]
+    pub fn append(&mut self, other: &mut Self) {
+        self.raw.append(&mut other.raw);
+    }
 }
 
 /// `IndexVec` is often used as a map, so it provides some map-like APIs.
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index e37b307..3492df6 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -1,7 +1,6 @@
 // tidy-alphabetical-start
 #![feature(decl_macro)]
 #![feature(let_chains)]
-#![feature(thread_spawn_unchecked)]
 #![feature(try_blocks)]
 // tidy-alphabetical-end
 
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 6b36944..5f6e7fb 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1853,7 +1853,7 @@ fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) {
                         if !prev_dollar {
                             self.check_ident_token(cx, UnderMacro(true), ident);
                         }
-                    } else if token.kind == TokenKind::Dollar {
+                    } else if *token == TokenKind::Dollar {
                         prev_dollar = true;
                         continue;
                     }
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 54bf73a..cb7a071 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1734,13 +1734,16 @@ fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'
         let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
 
         match it.kind {
-            hir::ForeignItemKind::Fn(decl, _, _, _) if !vis.is_internal_abi(abi) => {
-                vis.check_foreign_fn(it.owner_id.def_id, decl);
+            hir::ForeignItemKind::Fn(sig, _, _) => {
+                if vis.is_internal_abi(abi) {
+                    vis.check_fn(it.owner_id.def_id, sig.decl)
+                } else {
+                    vis.check_foreign_fn(it.owner_id.def_id, sig.decl);
+                }
             }
             hir::ForeignItemKind::Static(ty, _, _) if !vis.is_internal_abi(abi) => {
                 vis.check_foreign_static(it.owner_id, ty.span);
             }
-            hir::ForeignItemKind::Fn(decl, _, _, _) => vis.check_fn(it.owner_id.def_id, decl),
             hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
         }
     }
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index c731b03..56d77c9 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -4971,7 +4971,6 @@
     /// ### Example
     ///
     /// ```rust
-    /// #![feature(unsafe_attributes)]
     /// #![warn(unsafe_attr_outside_unsafe)]
     ///
     /// #[no_mangle]
diff --git a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
index ccf1a54..d625935 100644
--- a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp
@@ -77,22 +77,18 @@
   Expected<std::unique_ptr<object::SymbolicFile>> ObjOrErr =
       getSymbolicFile(Buf->getMemBufferRef(), Context);
   if (!ObjOrErr) {
-    Error E = ObjOrErr.takeError();
-    SmallString<0> ErrorBuf;
-    auto Error = raw_svector_ostream(ErrorBuf);
-    Error << E << '\0';
-    return ErrorCallback(Error.str().data());
+    return ErrorCallback(toString(ObjOrErr.takeError()).c_str());
   }
   std::unique_ptr<object::SymbolicFile> Obj = std::move(*ObjOrErr);
+  if (Obj == nullptr) {
+    return 0;
+  }
 
   for (const object::BasicSymbolRef &S : Obj->symbols()) {
     if (!isArchiveSymbol(S))
       continue;
     if (Error E = S.printName(SymName)) {
-      SmallString<0> ErrorBuf;
-      auto Error = raw_svector_ostream(ErrorBuf);
-      Error << E << '\0';
-      return ErrorCallback(Error.str().data());
+      return ErrorCallback(toString(std::move(E)).c_str());
     }
     SymName << '\0';
     if (void *E = Callback(State, SymNameBuf.str().data())) {
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index edab6b5..0f85998 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -554,53 +554,43 @@ pub fn is_inside_const_context(self, hir_id: HirId) -> bool {
     /// }
     /// ```
     pub fn get_fn_id_for_return_block(self, id: HirId) -> Option<HirId> {
-        let mut iter = self.parent_iter(id).peekable();
-        let mut ignore_tail = false;
-        if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = self.tcx.hir_node(id) {
-            // When dealing with `return` statements, we don't care about climbing only tail
-            // expressions.
-            ignore_tail = true;
-        }
+        let enclosing_body_owner = self.tcx.local_def_id_to_hir_id(self.enclosing_body_owner(id));
 
-        let mut prev_hir_id = None;
-        while let Some((hir_id, node)) = iter.next() {
-            if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) {
-                match next_node {
-                    Node::Block(Block { expr: None, .. }) => return None,
-                    // The current node is not the tail expression of its parent.
-                    Node::Block(Block { expr: Some(e), .. }) if hir_id != e.hir_id => return None,
+        // Return `None` if the `id` expression is not the returned value of the enclosing body
+        let mut iter = [id].into_iter().chain(self.parent_id_iter(id)).peekable();
+        while let Some(cur_id) = iter.next() {
+            if enclosing_body_owner == cur_id {
+                break;
+            }
+
+            // A return statement is always the value returned from the enclosing body regardless of
+            // what the parent expressions are.
+            if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = self.tcx.hir_node(cur_id) {
+                break;
+            }
+
+            // If the current expression's value doesnt get used as the parent expressions value then return `None`
+            if let Some(&parent_id) = iter.peek() {
+                match self.tcx.hir_node(parent_id) {
+                    // The current node is not the tail expression of the block expression parent expr.
+                    Node::Block(Block { expr: Some(e), .. }) if cur_id != e.hir_id => return None,
                     Node::Block(Block { expr: Some(e), .. })
                         if matches!(e.kind, ExprKind::If(_, _, None)) =>
                     {
                         return None;
                     }
+
+                    // The current expression's value does not pass up through these parent expressions
+                    Node::Block(Block { expr: None, .. })
+                    | Node::Expr(Expr { kind: ExprKind::Loop(..), .. })
+                    | Node::LetStmt(..) => return None,
+
                     _ => {}
                 }
             }
-            match node {
-                Node::Item(_)
-                | Node::ForeignItem(_)
-                | Node::TraitItem(_)
-                | Node::Expr(Expr { kind: ExprKind::Closure(_), .. })
-                | Node::ImplItem(_)
-                    // The input node `id` must be enclosed in the method's body as opposed
-                    // to some other place such as its return type (fixes #114918).
-                    // We verify that indirectly by checking that the previous node is the
-                    // current node's body
-                    if node.body_id().map(|b| b.hir_id) == prev_hir_id =>  {
-                        return Some(hir_id)
-                }
-                // Ignore `return`s on the first iteration
-                Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. })
-                | Node::LetStmt(_) => {
-                    return None;
-                }
-                _ => {}
-            }
-
-            prev_hir_id = Some(hir_id);
         }
-        None
+
+        Some(enclosing_body_owner)
     }
 
     /// Retrieves the `OwnerId` for `id`'s parent item, or `id` itself if no
@@ -826,6 +816,11 @@ fn named_span(item_span: Span, ident: Ident, generics: Option<&Generics<'_>>) ->
             })
             | Node::ImplItem(ImplItem {
                 kind: ImplItemKind::Fn(sig, ..), span: outer_span, ..
+            })
+            | Node::ForeignItem(ForeignItem {
+                kind: ForeignItemKind::Fn(sig, ..),
+                span: outer_span,
+                ..
             }) => {
                 // Ensure that the returned span has the item's SyntaxContext, and not the
                 // SyntaxContext of the visibility.
@@ -884,10 +879,7 @@ fn named_span(item_span: Span, ident: Ident, generics: Option<&Generics<'_>>) ->
             },
             Node::Variant(variant) => named_span(variant.span, variant.ident, None),
             Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)),
-            Node::ForeignItem(item) => match item.kind {
-                ForeignItemKind::Fn(decl, _, _, _) => until_within(item.span, decl.output.span()),
-                _ => named_span(item.span, item.ident, None),
-            },
+            Node::ForeignItem(item) => named_span(item.span, item.ident, None),
             Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)),
             Node::Expr(Expr {
                 kind: ExprKind::Closure(Closure { fn_decl_span, .. }),
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index fa521ab..596d9f0 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -202,7 +202,7 @@ pub fn provide(providers: &mut Providers) {
             ..
         })
         | Node::ForeignItem(&ForeignItem {
-            kind: ForeignItemKind::Fn(_, idents, _, _),
+            kind: ForeignItemKind::Fn(_, idents, _),
             ..
         }) = tcx.hir_node(tcx.local_def_id_to_hir_id(def_id))
         {
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index f2d8781..5dd0e69 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1418,21 +1418,19 @@ fn alloc_ids_from_alloc(
         alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())
     }
 
-    fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
+    fn alloc_id_from_const_val(val: ConstValue<'_>) -> Option<AllocId> {
         match val {
-            ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => {
-                Either::Left(std::iter::once(ptr.provenance.alloc_id()))
-            }
-            ConstValue::Scalar(interpret::Scalar::Int { .. }) => Either::Right(std::iter::empty()),
-            ConstValue::ZeroSized => Either::Right(std::iter::empty()),
+            ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),
+            ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,
+            ConstValue::ZeroSized => None,
             ConstValue::Slice { .. } => {
                 // `u8`/`str` slices, shouldn't contain pointers that we want to print.
-                Either::Right(std::iter::empty())
+                None
             }
             ConstValue::Indirect { alloc_id, .. } => {
                 // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
                 // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
-                Either::Left(std::iter::once(alloc_id))
+                Some(alloc_id)
             }
         }
     }
@@ -1443,7 +1441,9 @@ fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) {
             match c.const_ {
                 Const::Ty(_, _) | Const::Unevaluated(..) => {}
                 Const::Val(val, _) => {
-                    self.0.extend(alloc_ids_from_const_val(val));
+                    if let Some(id) = alloc_id_from_const_val(val) {
+                        self.0.insert(id);
+                    }
                 }
             }
         }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 075eae0..92c8d26 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -65,10 +65,9 @@
 };
 use crate::traits::query::{
     CanonicalAliasGoal, CanonicalPredicateGoal, CanonicalTyGoal,
-    CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
-    CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, DropckConstraint,
-    DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult,
-    OutlivesBound,
+    CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal,
+    CanonicalTypeOpProvePredicateGoal, DropckConstraint, DropckOutlivesResult,
+    MethodAutoderefStepsResult, NoSolution, NormalizationResult, OutlivesBound,
 };
 use crate::traits::{
     specialization_graph, CodegenObligationError, EvaluationResult, ImplSource,
@@ -2090,26 +2089,6 @@
         desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.value.value }
     }
 
-    /// Do not call this query directly: part of the `Eq` type-op
-    query type_op_eq(
-        goal: CanonicalTypeOpEqGoal<'tcx>
-    ) -> Result<
-        &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
-        NoSolution,
-    > {
-        desc { "evaluating `type_op_eq` `{:?}`", goal.value.value }
-    }
-
-    /// Do not call this query directly: part of the `Subtype` type-op
-    query type_op_subtype(
-        goal: CanonicalTypeOpSubtypeGoal<'tcx>
-    ) -> Result<
-        &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
-        NoSolution,
-    > {
-        desc { "evaluating `type_op_subtype` `{:?}`", goal.value.value }
-    }
-
     /// Do not call this query directly: part of the `ProvePredicate` type-op
     query type_op_prove_predicate(
         goal: CanonicalTypeOpProvePredicateGoal<'tcx>
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index c380019..e373292 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -305,6 +305,10 @@ fn try_from_lit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>) ->
             // mir.
             match tcx.at(expr.span).lit_to_const(lit_input) {
                 Ok(c) => return Some(c),
+                Err(_) if lit_input.ty.has_aliases() => {
+                    // allow the `ty` to be an alias type, though we cannot handle it here
+                    return None;
+                }
                 Err(e) => {
                     tcx.dcx().span_delayed_bug(
                         expr.span,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 0a277fe..d60bfb9 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -970,6 +970,10 @@ fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> {
 
 /// Type utilities
 impl<'tcx> Ty<'tcx> {
+    // It would be nicer if this returned the value instead of a reference,
+    // like how `Predicate::kind` and `Region::kind` do. (It would result in
+    // many fewer subsequent dereferences.) But that gives a small but
+    // noticeable performance hit. See #126069 for details.
     #[inline(always)]
     pub fn kind(self) -> &'tcx TyKind<'tcx> {
         self.0.0
diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
index 6120b14..5b5f97c 100644
--- a/compiler/rustc_mir_build/src/thir/cx/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -8,7 +8,7 @@
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{HirId, Node};
+use rustc_hir::HirId;
 use rustc_middle::bug;
 use rustc_middle::middle::region;
 use rustc_middle::thir::*;
@@ -110,11 +110,7 @@ fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Cx<'tcx> {
     }
 
     #[instrument(level = "debug", skip(self))]
-    fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Box<Pat<'tcx>> {
-        let p = match self.tcx.hir_node(p.hir_id) {
-            Node::Pat(p) => p,
-            node => bug!("pattern became {:?}", node),
-        };
+    fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
         pat_from_hir(self.tcx, self.param_env, self.typeck_results(), p)
     }
 
diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
index 69d21a6..7ea36f0 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -78,6 +78,8 @@
 use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt};
 use rustc_target::abi::{FieldIdx, VariantIdx};
 
+use crate::pass_manager::validate_body;
+
 pub struct ByMoveBody;
 
 impl<'tcx> MirPass<'tcx> for ByMoveBody {
@@ -131,20 +133,40 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
             |(parent_field_idx, parent_capture), (child_field_idx, child_capture)| {
                 // Store this set of additional projections (fields and derefs).
                 // We need to re-apply them later.
-                let child_precise_captures =
-                    &child_capture.place.projections[parent_capture.place.projections.len()..];
+                let mut child_precise_captures = child_capture.place.projections
+                    [parent_capture.place.projections.len()..]
+                    .to_vec();
 
-                // If the parent captures by-move, and the child captures by-ref, then we
-                // need to peel an additional `deref` off of the body of the child.
-                let needs_deref = child_capture.is_by_ref() && !parent_capture.is_by_ref();
-                if needs_deref {
-                    assert_ne!(
-                        coroutine_kind,
-                        ty::ClosureKind::FnOnce,
+                // If the parent capture is by-ref, then we need to apply an additional
+                // deref before applying any further projections to this place.
+                if parent_capture.is_by_ref() {
+                    child_precise_captures.insert(
+                        0,
+                        Projection { ty: parent_capture.place.ty(), kind: ProjectionKind::Deref },
+                    );
+                }
+                // If the child capture is by-ref, then we need to apply a "ref"
+                // projection (i.e. `&`) at the end. But wait! We don't have that
+                // as a projection kind. So instead, we can apply its dual and
+                // *peel* a deref off of the place when it shows up in the MIR body.
+                // Luckily, by construction this is always possible.
+                let peel_deref = if child_capture.is_by_ref() {
+                    assert!(
+                        parent_capture.is_by_ref() || coroutine_kind != ty::ClosureKind::FnOnce,
                         "`FnOnce` coroutine-closures return coroutines that capture from \
                         their body; it will always result in a borrowck error!"
                     );
-                }
+                    true
+                } else {
+                    false
+                };
+
+                // Regarding the behavior above, you may think that it's redundant to both
+                // insert a deref and then peel a deref if the parent and child are both
+                // captured by-ref. This would be correct, except for the case where we have
+                // precise capturing projections, since the inserted deref is to the *beginning*
+                // and the peeled deref is at the *end*. I cannot seem to actually find a
+                // case where this happens, though, but let's keep this code flexible.
 
                 // Finally, store the type of the parent's captured place. We need
                 // this when building the field projection in the MIR body later on.
@@ -164,7 +186,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
                     (
                         FieldIdx::from_usize(parent_field_idx + num_args),
                         parent_capture_ty,
-                        needs_deref,
+                        peel_deref,
                         child_precise_captures,
                     ),
                 )
@@ -192,6 +214,10 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
         let mut by_move_body = body.clone();
         MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body);
         dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(()));
+
+        // Let's just always validate this body.
+        validate_body(tcx, &mut by_move_body, "Initial coroutine_by_move body".to_string());
+
         // FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body.
         by_move_body.source = mir::MirSource::from_instance(InstanceKind::CoroutineKindShim {
             coroutine_def_id: coroutine_def_id.to_def_id(),
@@ -202,7 +228,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
 
 struct MakeByMoveBody<'tcx> {
     tcx: TyCtxt<'tcx>,
-    field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>,
+    field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, Vec<Projection<'tcx>>)>,
     by_move_coroutine_ty: Ty<'tcx>,
 }
 
@@ -223,14 +249,14 @@ fn visit_place(
         if place.local == ty::CAPTURE_STRUCT_LOCAL
             && let Some((&mir::ProjectionElem::Field(idx, _), projection)) =
                 place.projection.split_first()
-            && let Some(&(remapped_idx, remapped_ty, needs_deref, bridging_projections)) =
+            && let Some(&(remapped_idx, remapped_ty, peel_deref, ref bridging_projections)) =
                 self.field_remapping.get(&idx)
         {
             // As noted before, if the parent closure captures a field by value, and
             // the child captures a field by ref, then for the by-move body we're
             // generating, we also are taking that field by value. Peel off a deref,
             // since a layer of ref'ing has now become redundant.
-            let final_projections = if needs_deref {
+            let final_projections = if peel_deref {
                 let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first()
                 else {
                     bug!(
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 324ddc5..61fc5fc 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -726,7 +726,7 @@ fn dest_needs_borrow(place: Place<'_>) -> bool {
 
         // Insert all of the (mapped) parts of the callee body into the caller.
         caller_body.local_decls.extend(callee_body.drain_vars_and_temps());
-        caller_body.source_scopes.extend(&mut callee_body.source_scopes.drain(..));
+        caller_body.source_scopes.append(&mut callee_body.source_scopes);
         if self
             .tcx
             .sess
@@ -740,7 +740,7 @@ fn dest_needs_borrow(place: Place<'_>) -> bool {
             // still getting consistent results from the mir-opt tests.
             caller_body.var_debug_info.append(&mut callee_body.var_debug_info);
         }
-        caller_body.basic_blocks_mut().extend(callee_body.basic_blocks_mut().drain(..));
+        caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut());
 
         caller_body[callsite.block].terminator = Some(Terminator {
             source_info: callsite.source_info,
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index f96329a..610ad41 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -371,7 +371,7 @@ fn merge_codegen_units<'tcx>(
         // Move the items from `cgu_src` to `cgu_dst`. Some of them may be
         // duplicate inlined items, in which case the destination CGU is
         // unaffected. Recalculate size estimates afterwards.
-        cgu_dst.items_mut().extend(cgu_src.items_mut().drain(..));
+        cgu_dst.items_mut().append(cgu_src.items_mut());
         cgu_dst.compute_size_estimate();
 
         // Record that `cgu_dst` now contains all the stuff that was in
@@ -410,7 +410,7 @@ fn merge_codegen_units<'tcx>(
         // Move the items from `smallest` to `second_smallest`. Some of them
         // may be duplicate inlined items, in which case the destination CGU is
         // unaffected. Recalculate size estimates afterwards.
-        second_smallest.items_mut().extend(smallest.items_mut().drain(..));
+        second_smallest.items_mut().append(smallest.items_mut());
         second_smallest.compute_size_estimate();
 
         // Don't update `cgu_contents`, that's only for incremental builds.
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index fb4ed5b..e7a7105 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -229,7 +229,7 @@ fn bump(&mut self, glue: bool) -> (Token, Spacing) {
             } else {
                 let this_spacing = if next_tok.is_punct() {
                     Spacing::Joint
-                } else if next_tok.kind == token::Eof {
+                } else if next_tok == token::Eof {
                     Spacing::Alone
                 } else {
                     Spacing::JointHidden
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 8fdfbce..6391ff9 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -4,11 +4,14 @@
 use rustc_errors::codes::*;
 use rustc_errors::{Diag, PResult};
 use rustc_span::symbol::kw;
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{BytePos, Span};
 use thin_vec::ThinVec;
 use tracing::debug;
 
-use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle};
+use super::{
+    AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle, Trailing,
+    UsePreAttrPos,
+};
 use crate::{errors, fluent_generated as fluent, maybe_whole};
 
 // Public for rustfmt usage
@@ -162,7 +165,7 @@ fn annotate_following_item_if_applicable(
         }
         loop {
             // skip any other attributes, we want the item
-            if snapshot.token.kind == token::Pound {
+            if snapshot.token == token::Pound {
                 if let Err(err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
                     err.cancel();
                     return Some(replacement_span);
@@ -257,11 +260,11 @@ pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerA
     pub fn parse_attr_item(&mut self, force_collect: ForceCollect) -> PResult<'a, ast::AttrItem> {
         maybe_whole!(self, NtMeta, |attr| attr.into_inner());
 
-        let do_parse = |this: &mut Self, _empty_attrs| {
+        // Attr items don't have attributes.
+        self.collect_tokens(None, AttrWrapper::empty(), force_collect, |this, _empty_attrs| {
             let is_unsafe = this.eat_keyword(kw::Unsafe);
             let unsafety = if is_unsafe {
                 let unsafe_span = this.prev_token.span;
-                this.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
                 this.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
                 ast::Safety::Unsafe(unsafe_span)
             } else {
@@ -273,10 +276,12 @@ pub fn parse_attr_item(&mut self, force_collect: ForceCollect) -> PResult<'a, as
             if is_unsafe {
                 this.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
             }
-            Ok((ast::AttrItem { unsafety, path, args, tokens: None }, false))
-        };
-        // Attr items don't have attributes.
-        self.collect_tokens_trailing_token(AttrWrapper::empty(), force_collect, do_parse)
+            Ok((
+                ast::AttrItem { unsafety, path, args, tokens: None },
+                Trailing::No,
+                UsePreAttrPos::No,
+            ))
+        })
     }
 
     /// Parses attributes that appear after the opening of an item. These should
@@ -309,8 +314,8 @@ pub fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
             };
             if let Some(attr) = attr {
                 // If we are currently capturing tokens (i.e. we are within a call to
-                // `Parser::collect_tokens_trailing_tokens`) record the token positions of this
-                // inner attribute, for possible later processing in a `LazyAttrTokenStream`.
+                // `Parser::collect_tokens`) record the token positions of this inner attribute,
+                // for possible later processing in a `LazyAttrTokenStream`.
                 if let Capturing::Yes = self.capture_state.capturing {
                     let end_pos = self.num_bump_calls;
                     let parser_range = ParserRange(start_pos..end_pos);
@@ -343,7 +348,7 @@ pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrIt
 
         // Presumably, the majority of the time there will only be one attr.
         let mut expanded_attrs = Vec::with_capacity(1);
-        while self.token.kind != token::Eof {
+        while self.token != token::Eof {
             let lo = self.token.span;
             let item = self.parse_attr_item(ForceCollect::Yes)?;
             expanded_attrs.push((item, lo.to(self.prev_token.span)));
@@ -359,7 +364,7 @@ pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrIt
     pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec<ast::NestedMetaItem>> {
         // Presumably, the majority of the time there will only be one attr.
         let mut nmis = ThinVec::with_capacity(1);
-        while self.token.kind != token::Eof {
+        while self.token != token::Eof {
             nmis.push(self.parse_meta_item_inner()?);
             if !self.eat(&token::Comma) {
                 break;
@@ -400,7 +405,6 @@ pub fn parse_meta_item(
         };
         let unsafety = if is_unsafe {
             let unsafe_span = self.prev_token.span;
-            self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
             self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
 
             ast::Safety::Unsafe(unsafe_span)
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index abf6103..49df281 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -12,9 +12,23 @@
 
 use super::{
     Capturing, FlatToken, ForceCollect, NodeRange, NodeReplacement, Parser, ParserRange,
-    TokenCursor,
+    TokenCursor, Trailing,
 };
 
+// When collecting tokens, this fully captures the start point. Usually its
+// just after outer attributes, but occasionally it's before.
+#[derive(Clone, Debug)]
+pub(super) struct CollectPos {
+    start_token: (Token, Spacing),
+    cursor_snapshot: TokenCursor,
+    start_pos: u32,
+}
+
+pub(super) enum UsePreAttrPos {
+    No,
+    Yes,
+}
+
 /// A wrapper type to ensure that the parser handles outer attributes correctly.
 /// When we parse outer attributes, we need to ensure that we capture tokens
 /// for the attribute target. This allows us to perform cfg-expansion on
@@ -22,30 +36,32 @@
 ///
 /// This wrapper prevents direct access to the underlying `ast::AttrVec`.
 /// Parsing code can only get access to the underlying attributes
-/// by passing an `AttrWrapper` to `collect_tokens_trailing_token`.
+/// by passing an `AttrWrapper` to `collect_tokens`.
 /// This makes it difficult to accidentally construct an AST node
 /// (which stores an `ast::AttrVec`) without first collecting tokens.
 ///
 /// This struct has its own module, to ensure that the parser code
 /// cannot directly access the `attrs` field.
 #[derive(Debug, Clone)]
-pub struct AttrWrapper {
+pub(super) struct AttrWrapper {
     attrs: AttrVec,
     // The start of the outer attributes in the parser's token stream.
     // This lets us create a `NodeReplacement` for the entire attribute
-    // target, including outer attributes.
-    start_pos: u32,
+    // target, including outer attributes. `None` if there are no outer
+    // attributes.
+    start_pos: Option<u32>,
 }
 
 impl AttrWrapper {
     pub(super) fn new(attrs: AttrVec, start_pos: u32) -> AttrWrapper {
-        AttrWrapper { attrs, start_pos }
-    }
-    pub fn empty() -> AttrWrapper {
-        AttrWrapper { attrs: AttrVec::new(), start_pos: u32::MAX }
+        AttrWrapper { attrs, start_pos: Some(start_pos) }
     }
 
-    pub(crate) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec {
+    pub(super) fn empty() -> AttrWrapper {
+        AttrWrapper { attrs: AttrVec::new(), start_pos: None }
+    }
+
+    pub(super) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec {
         psess.dcx().span_delayed_bug(
             self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP),
             "AttrVec is taken for recovery but no error is produced",
@@ -56,12 +72,12 @@ pub(crate) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec {
 
     /// Prepend `self.attrs` to `attrs`.
     // FIXME: require passing an NT to prevent misuse of this method
-    pub(crate) fn prepend_to_nt_inner(mut self, attrs: &mut AttrVec) {
+    pub(super) fn prepend_to_nt_inner(mut self, attrs: &mut AttrVec) {
         mem::swap(attrs, &mut self.attrs);
         attrs.extend(self.attrs);
     }
 
-    pub fn is_empty(&self) -> bool {
+    pub(super) fn is_empty(&self) -> bool {
         self.attrs.is_empty()
     }
 }
@@ -77,7 +93,7 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
 }
 
 // From a value of this type we can reconstruct the `TokenStream` seen by the
-// `f` callback passed to a call to `Parser::collect_tokens_trailing_token`, by
+// `f` callback passed to a call to `Parser::collect_tokens`, by
 // replaying the getting of the tokens. This saves us producing a `TokenStream`
 // if it is never needed, e.g. a captured `macro_rules!` argument that is never
 // passed to a proc macro. In practice, token stream creation happens rarely
@@ -166,16 +182,30 @@ fn to_attr_token_stream(&self) -> AttrTokenStream {
 }
 
 impl<'a> Parser<'a> {
+    pub(super) fn collect_pos(&self) -> CollectPos {
+        CollectPos {
+            start_token: (self.token.clone(), self.token_spacing),
+            cursor_snapshot: self.token_cursor.clone(),
+            start_pos: self.num_bump_calls,
+        }
+    }
+
     /// Parses code with `f`. If appropriate, it records the tokens (in
     /// `LazyAttrTokenStream` form) that were parsed in the result, accessible
-    /// via the `HasTokens` trait. The second (bool) part of the callback's
+    /// via the `HasTokens` trait. The `Trailing` part of the callback's
     /// result indicates if an extra token should be captured, e.g. a comma or
-    /// semicolon.
+    /// semicolon. The `UsePreAttrPos` part of the callback's result indicates
+    /// if we should use `pre_attr_pos` as the collection start position (only
+    /// required in a few cases).
     ///
     /// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The
     /// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for
     /// details.
     ///
+    /// `pre_attr_pos` is the position before the outer attributes (or the node
+    /// itself, if no outer attributes are present). It is only needed if `f`
+    /// can return `UsePreAttrPos::Yes`.
+    ///
     /// Note: If your callback consumes an opening delimiter (including the
     /// case where `self.token` is an opening delimiter on entry to this
     /// function), you must also consume the corresponding closing delimiter.
@@ -197,11 +227,12 @@ impl<'a> Parser<'a> {
     ///     }                               //  32..33
     /// }                                   //  33..34
     /// ```
-    pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
+    pub(super) fn collect_tokens<R: HasAttrs + HasTokens>(
         &mut self,
+        pre_attr_pos: Option<CollectPos>,
         attrs: AttrWrapper,
         force_collect: ForceCollect,
-        f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>,
+        f: impl FnOnce(&mut Self, AttrVec) -> PResult<'a, (R, Trailing, UsePreAttrPos)>,
     ) -> PResult<'a, R> {
         // We must collect if anything could observe the collected tokens, i.e.
         // if any of the following conditions hold.
@@ -220,23 +251,20 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
             return Ok(f(self, attrs.attrs)?.0);
         }
 
-        let start_token = (self.token.clone(), self.token_spacing);
-        let cursor_snapshot = self.token_cursor.clone();
-        let start_pos = self.num_bump_calls;
+        let mut collect_pos = self.collect_pos();
         let has_outer_attrs = !attrs.attrs.is_empty();
         let parser_replacements_start = self.capture_state.parser_replacements.len();
 
         // We set and restore `Capturing::Yes` on either side of the call to
-        // `f`, so we can distinguish the outermost call to
-        // `collect_tokens_trailing_token` (e.g. parsing `m` in the example
-        // above) from any inner (indirectly recursive) calls (e.g. parsing `g`
-        // in the example above). This distinction is used below and in
-        // `Parser::parse_inner_attributes`.
-        let (mut ret, capture_trailing) = {
+        // `f`, so we can distinguish the outermost call to `collect_tokens`
+        // (e.g. parsing `m` in the example above) from any inner (indirectly
+        // recursive) calls (e.g. parsing `g` in the example above). This
+        // distinction is used below and in `Parser::parse_inner_attributes`.
+        let (mut ret, capture_trailing, use_pre_attr_pos) = {
             let prev_capturing = mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
-            let ret_and_trailing = f(self, attrs.attrs);
+            let res = f(self, attrs.attrs);
             self.capture_state.capturing = prev_capturing;
-            ret_and_trailing?
+            res?
         };
 
         // When we're not in `capture_cfg` mode, then skip collecting and
@@ -279,10 +307,18 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
             return Ok(ret);
         }
 
+        // Replace the post-attribute collection start position with the
+        // pre-attribute position supplied, if `f` indicated it is necessary.
+        // (The caller is responsible for providing a non-`None` `pre_attr_pos`
+        // if this is a possibility.)
+        if matches!(use_pre_attr_pos, UsePreAttrPos::Yes) {
+            collect_pos = pre_attr_pos.unwrap();
+        }
+
         let parser_replacements_end = self.capture_state.parser_replacements.len();
 
         assert!(
-            !(self.break_last_token && capture_trailing),
+            !(self.break_last_token && matches!(capture_trailing, Trailing::Yes)),
             "Cannot set break_last_token and have trailing token"
         );
 
@@ -294,7 +330,7 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
             // `AttrTokenStream`, we will create the proper token.
             + self.break_last_token as u32;
 
-        let num_calls = end_pos - start_pos;
+        let num_calls = end_pos - collect_pos.start_pos;
 
         // Take the captured `ParserRange`s for any inner attributes that we parsed in
         // `Parser::parse_inner_attributes`, and pair them in a `ParserReplacement` with `None`,
@@ -328,7 +364,9 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
                 .iter()
                 .cloned()
                 .chain(inner_attr_parser_replacements.iter().cloned())
-                .map(|(parser_range, data)| (NodeRange::new(parser_range, start_pos), data))
+                .map(|(parser_range, data)| {
+                    (NodeRange::new(parser_range, collect_pos.start_pos), data)
+                })
                 .collect()
         };
 
@@ -355,9 +393,9 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
         //     - `tokens`: lazy tokens for `g` (with its inner attr deleted).
 
         let tokens = LazyAttrTokenStream::new(LazyAttrTokenStreamImpl {
-            start_token,
+            start_token: collect_pos.start_token,
+            cursor_snapshot: collect_pos.cursor_snapshot,
             num_calls,
-            cursor_snapshot,
             break_last_token: self.break_last_token,
             node_replacements,
         });
@@ -368,9 +406,9 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
         }
 
         // If `capture_cfg` is set and we're inside a recursive call to
-        // `collect_tokens_trailing_token`, then we need to register a replace range
-        // if we have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager cfg-expansion
-        // on the captured token stream.
+        // `collect_tokens`, then we need to register a replace range if we
+        // have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager
+        // cfg-expansion on the captured token stream.
         if self.capture_cfg
             && matches!(self.capture_state.capturing, Capturing::Yes)
             && has_cfg_or_cfg_attr(ret.attrs())
@@ -389,7 +427,8 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
             // Set things up so that the entire AST node that we just parsed, including attributes,
             // will be replaced with `target` in the lazy token stream. This will allow us to
             // cfg-expand this AST node.
-            let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
+            let start_pos =
+                if has_outer_attrs { attrs.start_pos.unwrap() } else { collect_pos.start_pos };
             let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens };
             self.capture_state
                 .parser_replacements
@@ -490,7 +529,6 @@ mod size_asserts {
 
     use super::*;
     // tidy-alphabetical-start
-    static_assert_size!(AttrWrapper, 16);
     static_assert_size!(LazyAttrTokenStreamImpl, 96);
     // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 47ca85b..ef1387c 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -474,8 +474,8 @@ fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
                     // If this isn't the case however, and the suggestion is a token the
                     // content of which is the same as the found token's, we remove it as well.
                     if !eq {
-                        if let TokenType::Token(kind) = &token {
-                            if kind == &self.token.kind {
+                        if let TokenType::Token(kind) = token {
+                            if self.token == *kind {
                                 return false;
                             }
                         }
@@ -506,7 +506,7 @@ fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
             } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
                 // The current token is in the same line as the prior token, not recoverable.
             } else if [token::Comma, token::Colon].contains(&self.token.kind)
-                && self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis)
+                && self.prev_token == token::CloseDelim(Delimiter::Parenthesis)
             {
                 // Likely typo: The current token is on a new line and is expected to be
                 // `.`, `;`, `?`, or an operator after a close delimiter token.
@@ -518,7 +518,7 @@ fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
                 // https://github.com/rust-lang/rust/issues/72253
             } else if self.look_ahead(1, |t| {
                 t == &token::CloseDelim(Delimiter::Brace)
-                    || t.can_begin_expr() && t.kind != token::Colon
+                    || t.can_begin_expr() && *t != token::Colon
             }) && [token::Comma, token::Colon].contains(&self.token.kind)
             {
                 // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
@@ -562,7 +562,7 @@ fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
             }
         }
 
-        if self.token.kind == TokenKind::EqEq
+        if self.token == TokenKind::EqEq
             && self.prev_token.is_ident()
             && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Eq)))
         {
@@ -655,9 +655,9 @@ fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
         // positive for a `cr#` that wasn't intended to start a c-string literal, but identifying
         // that in the parser requires unbounded lookahead, so we only add a hint to the existing
         // error rather than replacing it entirely.
-        if ((self.prev_token.kind == TokenKind::Ident(sym::c, IdentIsRaw::No)
+        if ((self.prev_token == TokenKind::Ident(sym::c, IdentIsRaw::No)
             && matches!(&self.token.kind, TokenKind::Literal(token::Lit { kind: token::Str, .. })))
-            || (self.prev_token.kind == TokenKind::Ident(sym::cr, IdentIsRaw::No)
+            || (self.prev_token == TokenKind::Ident(sym::cr, IdentIsRaw::No)
                 && matches!(
                     &self.token.kind,
                     TokenKind::Literal(token::Lit { kind: token::Str, .. }) | token::Pound
@@ -673,7 +673,7 @@ fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool {
         // `pub` may be used for an item or `pub(crate)`
         if self.prev_token.is_ident_named(sym::public)
             && (self.token.can_begin_item()
-                || self.token.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
+                || self.token == TokenKind::OpenDelim(Delimiter::Parenthesis))
         {
             err.span_suggestion_short(
                 self.prev_token.span,
@@ -772,7 +772,7 @@ pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) -> ErrorGuaranteed {
             ),
         );
         if self.token == token::Pound
-            && self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Bracket))
+            && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
         {
             // We have
             // #[attr]
@@ -867,7 +867,7 @@ fn check_too_many_raw_str_terminators(&mut self, err: &mut Diag<'_>) -> bool {
                 let str_span = self.prev_token.span;
                 let mut span = self.token.span;
                 let mut count = 0;
-                while self.token.kind == TokenKind::Pound
+                while self.token == TokenKind::Pound
                     && !sm.is_multiline(span.shrink_to_hi().until(self.token.span.shrink_to_lo()))
                 {
                     span = span.with_hi(self.token.span.hi());
@@ -1167,7 +1167,7 @@ pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut Pa
             return;
         }
 
-        if token::PathSep == self.token.kind && segment.args.is_none() {
+        if self.token == token::PathSep && segment.args.is_none() {
             let snapshot = self.create_snapshot_for_diagnostic();
             self.bump();
             let lo = self.token.span;
@@ -1176,13 +1176,11 @@ pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut Pa
                     let span = lo.to(self.prev_token.span);
                     // Detect trailing `>` like in `x.collect::Vec<_>>()`.
                     let mut trailing_span = self.prev_token.span.shrink_to_hi();
-                    while self.token.kind == token::BinOp(token::Shr)
-                        || self.token.kind == token::Gt
-                    {
+                    while self.token == token::BinOp(token::Shr) || self.token == token::Gt {
                         trailing_span = trailing_span.to(self.token.span);
                         self.bump();
                     }
-                    if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
+                    if self.token == token::OpenDelim(Delimiter::Parenthesis) {
                         // Recover from bad turbofish: `foo.collect::Vec<_>()`.
                         segment.args = Some(AngleBracketedArgs { args, span }.into());
 
@@ -1430,7 +1428,7 @@ pub(super) fn check_no_chained_comparison(
                             self.restore_snapshot(snapshot);
                         }
                     }
-                    return if token::PathSep == self.token.kind {
+                    return if self.token == token::PathSep {
                         // We have some certainty that this was a bad turbofish at this point.
                         // `foo< bar >::`
                         if let ExprKind::Binary(o, ..) = inner_op.kind
@@ -1462,7 +1460,7 @@ pub(super) fn check_no_chained_comparison(
                                 Err(self.dcx().create_err(err))
                             }
                         }
-                    } else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind {
+                    } else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
                         // We have high certainty that this was a bad turbofish at this point.
                         // `foo< bar >(`
                         if let ExprKind::Binary(o, ..) = inner_op.kind
@@ -1528,7 +1526,7 @@ fn consume_fn_args(&mut self) -> Result<(), ()> {
         ];
         self.consume_tts(1, &modifiers);
 
-        if self.token.kind == token::Eof {
+        if self.token == token::Eof {
             // Not entirely sure that what we consumed were fn arguments, rollback.
             self.restore_snapshot(snapshot);
             Err(())
@@ -1811,7 +1809,7 @@ pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
     /// This function gets called in places where a semicolon is NOT expected and if there's a
     /// semicolon it emits the appropriate error and returns true.
     pub fn maybe_consume_incorrect_semicolon(&mut self, previous_item: Option<&Item>) -> bool {
-        if self.token.kind != TokenKind::Semi {
+        if self.token != TokenKind::Semi {
             return false;
         }
 
@@ -2405,10 +2403,10 @@ fn consume_tts(
         modifier: &[(token::TokenKind, i64)],
     ) {
         while acc > 0 {
-            if let Some((_, val)) = modifier.iter().find(|(t, _)| *t == self.token.kind) {
+            if let Some((_, val)) = modifier.iter().find(|(t, _)| self.token == *t) {
                 acc += *val;
             }
-            if self.token.kind == token::Eof {
+            if self.token == token::Eof {
                 break;
             }
             self.bump();
@@ -2489,13 +2487,14 @@ pub(super) fn handle_ambiguous_unbraced_const_arg(
     pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> {
         let start = self.token.span;
         let attrs = self.parse_outer_attributes()?;
-        let expr = self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
-            err.span_label(
-                start.shrink_to_lo(),
-                "while parsing a const generic argument starting here",
-            );
-            err
-        })?;
+        let (expr, _) =
+            self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
+                err.span_label(
+                    start.shrink_to_lo(),
+                    "while parsing a const generic argument starting here",
+                );
+                err
+            })?;
         if !self.expr_is_valid_const_arg(&expr) {
             self.dcx().emit_err(ConstGenericWithoutBraces {
                 span: expr.span,
@@ -2598,7 +2597,7 @@ pub(super) fn recover_const_arg(
                 }
             })
             .is_some()
-            || self.token.kind == TokenKind::Dot;
+            || self.token == TokenKind::Dot;
         // This will be true when a trait object type `Foo +` or a path which was a `const fn` with
         // type params has been parsed.
         let was_op =
@@ -2615,9 +2614,9 @@ pub(super) fn recover_const_arg(
             let attrs = self.parse_outer_attributes()?;
             self.parse_expr_res(Restrictions::CONST_EXPR, attrs)
         })() {
-            Ok(expr) => {
+            Ok((expr, _)) => {
                 // Find a mistake like `MyTrait<Assoc == S::Assoc>`.
-                if token::EqEq == snapshot.token.kind {
+                if snapshot.token == token::EqEq {
                     err.span_suggestion(
                         snapshot.token.span,
                         "if you meant to use an associated type binding, replace `==` with `=`",
@@ -2627,7 +2626,7 @@ pub(super) fn recover_const_arg(
                     let guar = err.emit();
                     let value = self.mk_expr_err(start.to(expr.span), guar);
                     return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
-                } else if token::Colon == snapshot.token.kind
+                } else if snapshot.token == token::Colon
                     && expr.span.lo() == snapshot.token.span.hi()
                     && matches!(expr.kind, ExprKind::Path(..))
                 {
@@ -2642,8 +2641,7 @@ pub(super) fn recover_const_arg(
                     return Ok(GenericArg::Type(
                         self.mk_ty(start.to(expr.span), TyKind::Err(guar)),
                     ));
-                } else if token::Comma == self.token.kind || self.token.kind.should_end_const_arg()
-                {
+                } else if self.token == token::Comma || self.token.kind.should_end_const_arg() {
                     // Avoid the following output by checking that we consumed a full const arg:
                     // help: expressions must be enclosed in braces to be used as const generic
                     //       arguments
@@ -2674,7 +2672,7 @@ pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty(
         })() {
             // Since we don't know the exact reason why we failed to parse the type or the
             // expression, employ a simple heuristic to weed out some pathological cases.
-            Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => {
+            Ok((expr, _)) if let token::Comma | token::Gt = snapshot.token.kind => {
                 self.restore_snapshot(snapshot);
                 Some(expr)
             }
@@ -2846,8 +2844,8 @@ pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
     pub(crate) fn maybe_recover_unexpected_block_label(&mut self) -> bool {
         // Check for `'a : {`
         if !(self.check_lifetime()
-            && self.look_ahead(1, |tok| tok.kind == token::Colon)
-            && self.look_ahead(2, |tok| tok.kind == token::OpenDelim(Delimiter::Brace)))
+            && self.look_ahead(1, |t| *t == token::Colon)
+            && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace)))
         {
             return false;
         }
@@ -3001,7 +2999,7 @@ pub(crate) fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> {
         // >>>>>>>
         let mut end = None;
         loop {
-            if self.token.kind == TokenKind::Eof {
+            if self.token == TokenKind::Eof {
                 break;
             }
             if let Some(span) = self.conflict_marker(&TokenKind::OrOr, &TokenKind::BinOp(token::Or))
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index cf5d657..e0917ba 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -36,7 +36,7 @@
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
 use super::{
     AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions,
-    SemiColonMode, SeqSep, TokenType, Trailing,
+    SemiColonMode, SeqSep, TokenType, Trailing, UsePreAttrPos,
 };
 use crate::{errors, maybe_recover_from_interpolated_ty_qpath};
 
@@ -59,15 +59,30 @@ pub fn parse_expr(&mut self) -> PResult<'a, P<Expr>> {
         self.current_closure.take();
 
         let attrs = self.parse_outer_attributes()?;
-        self.parse_expr_res(Restrictions::empty(), attrs)
+        self.parse_expr_res(Restrictions::empty(), attrs).map(|res| res.0)
     }
 
     /// Parses an expression, forcing tokens to be collected.
     pub fn parse_expr_force_collect(&mut self) -> PResult<'a, P<Expr>> {
         self.current_closure.take();
 
+        // If the expression is associative (e.g. `1 + 2`), then any preceding
+        // outer attribute actually belongs to the first inner sub-expression.
+        // In which case we must use the pre-attr pos to include the attribute
+        // in the collected tokens for the outer expression.
+        let pre_attr_pos = self.collect_pos();
         let attrs = self.parse_outer_attributes()?;
-        self.collect_tokens_no_attrs(|this| this.parse_expr_res(Restrictions::empty(), attrs))
+        self.collect_tokens(
+            Some(pre_attr_pos),
+            AttrWrapper::empty(),
+            ForceCollect::Yes,
+            |this, _empty_attrs| {
+                let (expr, is_assoc) = this.parse_expr_res(Restrictions::empty(), attrs)?;
+                let use_pre_attr_pos =
+                    if is_assoc { UsePreAttrPos::Yes } else { UsePreAttrPos::No };
+                Ok((expr, Trailing::No, use_pre_attr_pos))
+            },
+        )
     }
 
     pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> {
@@ -77,7 +92,7 @@ pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> {
     fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
         let attrs = self.parse_outer_attributes()?;
         match self.parse_expr_res(restrictions, attrs) {
-            Ok(expr) => Ok(expr),
+            Ok((expr, _)) => Ok(expr),
             Err(err) => match self.token.ident() {
                 Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No))
                     if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
@@ -104,18 +119,20 @@ pub(super) fn parse_expr_res(
         &mut self,
         r: Restrictions,
         attrs: AttrWrapper,
-    ) -> PResult<'a, P<Expr>> {
+    ) -> PResult<'a, (P<Expr>, bool)> {
         self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs))
     }
 
     /// Parses an associative expression with operators of at least `min_prec` precedence.
+    /// The `bool` in the return value indicates if it was an assoc expr, i.e. with an operator
+    /// followed by a subexpression (e.g. `1 + 2`).
     pub(super) fn parse_expr_assoc_with(
         &mut self,
         min_prec: usize,
         attrs: AttrWrapper,
-    ) -> PResult<'a, P<Expr>> {
+    ) -> PResult<'a, (P<Expr>, bool)> {
         let lhs = if self.token.is_range_separator() {
-            return self.parse_expr_prefix_range(attrs);
+            return self.parse_expr_prefix_range(attrs).map(|res| (res, false));
         } else {
             self.parse_expr_prefix(attrs)?
         };
@@ -123,15 +140,17 @@ pub(super) fn parse_expr_assoc_with(
     }
 
     /// Parses the rest of an associative expression (i.e. the part after the lhs) with operators
-    /// of at least `min_prec` precedence.
+    /// of at least `min_prec` precedence. The `bool` in the return value indicates if something
+    /// was actually parsed.
     pub(super) fn parse_expr_assoc_rest_with(
         &mut self,
         min_prec: usize,
         starts_stmt: bool,
         mut lhs: P<Expr>,
-    ) -> PResult<'a, P<Expr>> {
+    ) -> PResult<'a, (P<Expr>, bool)> {
+        let mut parsed_something = false;
         if !self.should_continue_as_assoc_expr(&lhs) {
-            return Ok(lhs);
+            return Ok((lhs, parsed_something));
         }
 
         self.expected_tokens.push(TokenType::Operator);
@@ -156,16 +175,17 @@ pub(super) fn parse_expr_assoc_rest_with(
                 self.err_larrow_operator(self.token.span);
             }
 
+            parsed_something = true;
             self.bump();
             if op.node.is_comparison() {
                 if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? {
-                    return Ok(expr);
+                    return Ok((expr, parsed_something));
                 }
             }
 
             // Look for JS' `===` and `!==` and recover
             if (op.node == AssocOp::Equal || op.node == AssocOp::NotEqual)
-                && self.token.kind == token::Eq
+                && self.token == token::Eq
                 && self.prev_token.span.hi() == self.token.span.lo()
             {
                 let sp = op.span.to(self.token.span);
@@ -190,7 +210,7 @@ pub(super) fn parse_expr_assoc_rest_with(
 
             // Look for PHP's `<>` and recover
             if op.node == AssocOp::Less
-                && self.token.kind == token::Gt
+                && self.token == token::Gt
                 && self.prev_token.span.hi() == self.token.span.lo()
             {
                 let sp = op.span.to(self.token.span);
@@ -208,7 +228,7 @@ pub(super) fn parse_expr_assoc_rest_with(
 
             // Look for C++'s `<=>` and recover
             if op.node == AssocOp::LessEqual
-                && self.token.kind == token::Gt
+                && self.token == token::Gt
                 && self.prev_token.span.hi() == self.token.span.lo()
             {
                 let sp = op.span.to(self.token.span);
@@ -263,7 +283,7 @@ pub(super) fn parse_expr_assoc_rest_with(
                 // the special cases. The code is here only for future convenience.
                 Fixity::None => 1,
             };
-            let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
+            let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
                 let attrs = this.parse_outer_attributes()?;
                 this.parse_expr_assoc_with(prec + prec_adjustment, attrs)
             })?;
@@ -319,7 +339,7 @@ pub(super) fn parse_expr_assoc_rest_with(
             }
         }
 
-        Ok(lhs)
+        Ok((lhs, parsed_something))
     }
 
     fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool {
@@ -441,7 +461,8 @@ fn parse_expr_range(
             let attrs = self.parse_outer_attributes()?;
             Some(
                 self.parse_expr_assoc_with(prec + 1, attrs)
-                    .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?,
+                    .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?
+                    .0,
             )
         } else {
             None
@@ -498,7 +519,7 @@ fn parse_expr_prefix_range(&mut self, attrs: AttrWrapper) -> PResult<'a, P<Expr>
                 // RHS must be parsed with more associativity than the dots.
                 let attrs = this.parse_outer_attributes()?;
                 this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs)
-                    .map(|x| (lo.to(x.span), Some(x)))
+                    .map(|(x, _)| (lo.to(x.span), Some(x)))
                     .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
             } else {
                 (lo, None)
@@ -882,7 +903,7 @@ pub(super) fn parse_expr_dot_or_call_with(
         let mut res = ensure_sufficient_stack(|| {
             loop {
                 let has_question =
-                    if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) {
+                    if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) {
                         // We are using noexpect here because we don't expect a `?` directly after
                         // a `return` which could be suggested otherwise.
                         self.eat_noexpect(&token::Question)
@@ -894,20 +915,19 @@ pub(super) fn parse_expr_dot_or_call_with(
                     e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e));
                     continue;
                 }
-                let has_dot =
-                    if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) {
-                        // We are using noexpect here because we don't expect a `.` directly after
-                        // a `return` which could be suggested otherwise.
-                        self.eat_noexpect(&token::Dot)
-                    } else if self.token.kind == TokenKind::RArrow && self.may_recover() {
-                        // Recovery for `expr->suffix`.
-                        self.bump();
-                        let span = self.prev_token.span;
-                        self.dcx().emit_err(errors::ExprRArrowCall { span });
-                        true
-                    } else {
-                        self.eat(&token::Dot)
-                    };
+                let has_dot = if self.prev_token == TokenKind::Ident(kw::Return, IdentIsRaw::No) {
+                    // We are using noexpect here because we don't expect a `.` directly after
+                    // a `return` which could be suggested otherwise.
+                    self.eat_noexpect(&token::Dot)
+                } else if self.token == TokenKind::RArrow && self.may_recover() {
+                    // Recovery for `expr->suffix`.
+                    self.bump();
+                    let span = self.prev_token.span;
+                    self.dcx().emit_err(errors::ExprRArrowCall { span });
+                    true
+                } else {
+                    self.eat(&token::Dot)
+                };
                 if has_dot {
                     // expr.f
                     e = self.parse_dot_suffix_expr(lo, e)?;
@@ -1206,7 +1226,7 @@ fn parse_floating_field_access(&mut self) -> PResult<'a, P<[Ident]>> {
     }
 
     fn mk_expr_tuple_field_access(
-        &mut self,
+        &self,
         lo: Span,
         ident_span: Span,
         base: P<Expr>,
@@ -1221,7 +1241,7 @@ fn mk_expr_tuple_field_access(
 
     /// Parse a function call expression, `expr(...)`.
     fn parse_expr_fn_call(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> {
-        let snapshot = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
+        let snapshot = if self.token == token::OpenDelim(Delimiter::Parenthesis) {
             Some((self.create_snapshot_for_diagnostic(), fun.kind.clone()))
         } else {
             None
@@ -1585,7 +1605,7 @@ fn parse_expr_path_start(&mut self) -> PResult<'a, P<Expr>> {
                 // Suggests using '<=' if there is an error parsing qpath when the previous token
                 // is an '=' token. Only emits suggestion if the '<' token and '=' token are
                 // directly adjacent (i.e. '=<')
-                if maybe_eq_tok.kind == TokenKind::Eq && maybe_eq_tok.span.hi() == lt_span.lo() {
+                if maybe_eq_tok == TokenKind::Eq && maybe_eq_tok.span.hi() == lt_span.lo() {
                     let eq_lt = maybe_eq_tok.span.to(lt_span);
                     err.span_suggestion(eq_lt, "did you mean", "<=", Applicability::Unspecified);
                 }
@@ -2230,7 +2250,7 @@ fn suggest_missing_semicolon_before_array(
             return Ok(());
         }
 
-        if self.token.kind == token::Comma {
+        if self.token == token::Comma {
             if !self.psess.source_map().is_multiline(prev_span.until(self.token.span)) {
                 return Ok(());
             }
@@ -2336,7 +2356,7 @@ fn parse_expr_closure(&mut self) -> PResult<'a, P<Expr>> {
                 let token = self.token.clone();
                 let attrs = self.parse_outer_attributes()?;
                 match self.parse_expr_res(restrictions, attrs) {
-                    Ok(expr) => expr,
+                    Ok((expr, _)) => expr,
                     Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
                 }
             }
@@ -2360,7 +2380,7 @@ fn parse_expr_closure(&mut self) -> PResult<'a, P<Expr>> {
             None => {}
         }
 
-        if self.token.kind == TokenKind::Semi
+        if self.token == TokenKind::Semi
             && matches!(self.token_cursor.stack.last(), Some((.., Delimiter::Parenthesis)))
             && self.may_recover()
         {
@@ -2446,7 +2466,7 @@ fn parse_fn_block_decl(&mut self) -> PResult<'a, (P<FnDecl>, Span)> {
     fn parse_fn_block_param(&mut self) -> PResult<'a, Param> {
         let lo = self.token.span;
         let attrs = self.parse_outer_attributes()?;
-        self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
+        self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
             let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?;
             let ty = if this.eat(&token::Colon) {
                 this.parse_ty()?
@@ -2463,7 +2483,8 @@ fn parse_fn_block_param(&mut self) -> PResult<'a, Param> {
                     id: DUMMY_NODE_ID,
                     is_placeholder: false,
                 },
-                this.token == token::Comma,
+                Trailing::from(this.token == token::Comma),
+                UsePreAttrPos::No,
             ))
         })
     }
@@ -2557,7 +2578,7 @@ fn parse_if_after_cond(&mut self, lo: Span, mut cond: P<Expr>) -> PResult<'a, P<
                             );
                         } else {
                             // Look for usages of '=>' where '>=' might be intended
-                            if maybe_fatarrow.kind == token::FatArrow {
+                            if maybe_fatarrow == token::FatArrow {
                                 err.span_suggestion(
                                     maybe_fatarrow.span,
                                     "you might have meant to write a \"greater than or equal to\" comparison",
@@ -2584,7 +2605,7 @@ fn parse_if_after_cond(&mut self, lo: Span, mut cond: P<Expr>) -> PResult<'a, P<
     /// Parses the condition of a `if` or `while` expression.
     fn parse_expr_cond(&mut self) -> PResult<'a, P<Expr>> {
         let attrs = self.parse_outer_attributes()?;
-        let mut cond =
+        let (mut cond, _) =
             self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?;
 
         CondChecker::new(self).visit_expr(&mut cond);
@@ -2606,7 +2627,7 @@ fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>>
                 missing_let: None,
                 comparison: None,
             };
-            if self.prev_token.kind == token::BinOp(token::Or) {
+            if self.prev_token == token::BinOp(token::Or) {
                 // This was part of a closure, the that part of the parser recover.
                 return Err(self.dcx().create_err(err));
             } else {
@@ -2633,7 +2654,7 @@ fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>>
             self.expect(&token::Eq)?;
         }
         let attrs = self.parse_outer_attributes()?;
-        let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?;
+        let (expr, _) = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?;
         let span = lo.to(expr.span);
         Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered)))
     }
@@ -2742,7 +2763,7 @@ fn error_on_extra_if(&mut self, cond: &P<Expr>) -> PResult<'a, ()> {
     }
 
     fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
-        let begin_paren = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
+        let begin_paren = if self.token == token::OpenDelim(Delimiter::Parenthesis) {
             // Record whether we are about to parse `for (`.
             // This is used below for recovery in case of `for ( $stuff ) $block`
             // in which case we will suggest `for $stuff $block`.
@@ -2767,7 +2788,7 @@ fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
                 // We know for sure we have seen `for ($SOMETHING in`. In the happy path this would
                 // happen right before the return of this method.
                 let attrs = self.parse_outer_attributes()?;
-                let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs) {
+                let (expr, _) = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs) {
                     Ok(expr) => expr,
                     Err(expr_err) => {
                         // We don't know what followed the `in`, so cancel and bubble up the
@@ -2776,7 +2797,7 @@ fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
                         return Err(err);
                     }
                 };
-                return if self.token.kind == token::CloseDelim(Delimiter::Parenthesis) {
+                return if self.token == token::CloseDelim(Delimiter::Parenthesis) {
                     // We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the
                     // parser state and emit a targeted suggestion.
                     let span = vec![start_span, self.token.span];
@@ -2802,7 +2823,7 @@ fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
         }
         self.check_for_for_in_in_typo(self.prev_token.span);
         let attrs = self.parse_outer_attributes()?;
-        let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
+        let (expr, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
         Ok((pat, expr))
     }
 
@@ -2922,7 +2943,7 @@ pub(crate) fn eat_label(&mut self) -> Option<Label> {
     fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> {
         let match_span = self.prev_token.span;
         let attrs = self.parse_outer_attributes()?;
-        let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
+        let (scrutinee, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
 
         self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix)
     }
@@ -2995,7 +3016,7 @@ fn parse_arm_body_missing_braces(
         first_expr: &P<Expr>,
         arrow_span: Span,
     ) -> Option<(Span, ErrorGuaranteed)> {
-        if self.token.kind != token::Semi {
+        if self.token != token::Semi {
             return None;
         }
         let start_snapshot = self.create_snapshot_for_diagnostic();
@@ -3024,18 +3045,18 @@ fn parse_arm_body_missing_braces(
         // We might have either a `,` -> `;` typo, or a block without braces. We need
         // a more subtle parsing strategy.
         loop {
-            if self.token.kind == token::CloseDelim(Delimiter::Brace) {
+            if self.token == token::CloseDelim(Delimiter::Brace) {
                 // We have reached the closing brace of the `match` expression.
                 return Some(err(self, stmts));
             }
-            if self.token.kind == token::Comma {
+            if self.token == token::Comma {
                 self.restore_snapshot(start_snapshot);
                 return None;
             }
             let pre_pat_snapshot = self.create_snapshot_for_diagnostic();
             match self.parse_pat_no_top_alt(None, None) {
                 Ok(_pat) => {
-                    if self.token.kind == token::FatArrow {
+                    if self.token == token::FatArrow {
                         // Reached arm end.
                         self.restore_snapshot(pre_pat_snapshot);
                         return Some(err(self, stmts));
@@ -3070,7 +3091,7 @@ fn parse_arm_body_missing_braces(
 
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
         let attrs = self.parse_outer_attributes()?;
-        self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
+        self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
             let lo = this.token.span;
             let (pat, guard) = this.parse_match_arm_pat_and_guard()?;
 
@@ -3127,7 +3148,7 @@ pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
                 let arm_start_span = this.token.span;
 
                 let attrs = this.parse_outer_attributes()?;
-                let expr =
+                let (expr, _) =
                     this.parse_expr_res(Restrictions::STMT_EXPR, attrs).map_err(|mut err| {
                         err.span_label(arrow_span, "while parsing the `match` arm starting here");
                         err
@@ -3244,7 +3265,8 @@ pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
                     id: DUMMY_NODE_ID,
                     is_placeholder: false,
                 },
-                false,
+                Trailing::No,
+                UsePreAttrPos::No,
             ))
         })
     }
@@ -3286,7 +3308,7 @@ fn check_let_expr(expr: &Expr) -> (bool, bool) {
     }
 
     fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> {
-        if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
+        if self.token == token::OpenDelim(Delimiter::Parenthesis) {
             // Detect and recover from `($pat if $cond) => $arm`.
             let left = self.token.span;
             match self.parse_pat_allow_top_alt(
@@ -3335,8 +3357,9 @@ fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Exp
 
     fn parse_match_guard_condition(&mut self) -> PResult<'a, P<Expr>> {
         let attrs = self.parse_outer_attributes()?;
-        self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs).map_err(
-            |mut err| {
+        match self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs) {
+            Ok((expr, _)) => Ok(expr),
+            Err(mut err) => {
                 if self.prev_token == token::OpenDelim(Delimiter::Brace) {
                     let sugg_sp = self.prev_token.span.shrink_to_lo();
                     // Consume everything within the braces, let's avoid further parse
@@ -3344,7 +3367,7 @@ fn parse_match_guard_condition(&mut self) -> PResult<'a, P<Expr>> {
                     self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
                     let msg = "you might have meant to start a match arm after the match guard";
                     if self.eat(&token::CloseDelim(Delimiter::Brace)) {
-                        let applicability = if self.token.kind != token::FatArrow {
+                        let applicability = if self.token != token::FatArrow {
                             // We have high confidence that we indeed didn't have a struct
                             // literal in the match guard, but rather we had some operation
                             // that ended in a path, immediately followed by a block that was
@@ -3356,9 +3379,9 @@ fn parse_match_guard_condition(&mut self) -> PResult<'a, P<Expr>> {
                         err.span_suggestion_verbose(sugg_sp, msg, "=> ", applicability);
                     }
                 }
-                err
-            },
-        )
+                Err(err)
+            }
+        }
     }
 
     pub(crate) fn is_builtin(&self) -> bool {
@@ -3565,7 +3588,7 @@ pub(super) fn parse_struct_fields(
                         && self.look_ahead(1, |t| {
                             AssocOp::from_token(t).is_some()
                                 || matches!(t.kind, token::OpenDelim(_))
-                                || t.kind == token::Dot
+                                || *t == token::Dot
                         })
                     {
                         // Looks like they tried to write a shorthand, complex expression.
@@ -3709,7 +3732,7 @@ fn recover_ident_into_label(&mut self, ident: Ident) -> Label {
     fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
         let attrs = self.parse_outer_attributes()?;
         self.recover_vcs_conflict_marker();
-        self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
+        self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
             let lo = this.token.span;
 
             // Check if a colon exists one ahead. This means we're parsing a fieldname.
@@ -3753,7 +3776,8 @@ fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
                     id: DUMMY_NODE_ID,
                     is_placeholder: false,
                 },
-                this.token == token::Comma,
+                Trailing::from(this.token == token::Comma),
+                UsePreAttrPos::No,
             ))
         })
     }
@@ -3847,15 +3871,17 @@ fn collect_tokens_for_expr(
         attrs: AttrWrapper,
         f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, P<Expr>>,
     ) -> PResult<'a, P<Expr>> {
-        self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
+        self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
             let res = f(this, attrs)?;
-            let trailing = (this.restrictions.contains(Restrictions::STMT_EXPR)
-                 && this.token.kind == token::Semi)
-            // FIXME: pass an additional condition through from the place
-            // where we know we need a comma, rather than assuming that
-            // `#[attr] expr,` always captures a trailing comma.
-            || this.token.kind == token::Comma;
-            Ok((res, trailing))
+            let trailing = Trailing::from(
+                this.restrictions.contains(Restrictions::STMT_EXPR)
+                     && this.token == token::Semi
+                // FIXME: pass an additional condition through from the place
+                // where we know we need a comma, rather than assuming that
+                // `#[attr] expr,` always captures a trailing comma.
+                || this.token == token::Comma,
+            );
+            Ok((res, trailing, UsePreAttrPos::No))
         })
     }
 }
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 9124c15..af3b6f7 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -7,7 +7,7 @@
 use rustc_span::Span;
 use thin_vec::ThinVec;
 
-use super::{ForceCollect, Parser};
+use super::{ForceCollect, Parser, Trailing, UsePreAttrPos};
 use crate::errors::{
     self, MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
     UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody,
@@ -169,94 +169,88 @@ pub(super) fn parse_generic_params(&mut self) -> PResult<'a, ThinVec<ast::Generi
         let mut done = false;
         while !done {
             let attrs = self.parse_outer_attributes()?;
-            let param =
-                self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
-                    if this.eat_keyword_noexpect(kw::SelfUpper) {
-                        // `Self` as a generic param is invalid. Here we emit the diagnostic and continue parsing
-                        // as if `Self` never existed.
-                        this.dcx().emit_err(UnexpectedSelfInGenericParameters {
-                            span: this.prev_token.span,
-                        });
+            let param = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
+                if this.eat_keyword_noexpect(kw::SelfUpper) {
+                    // `Self` as a generic param is invalid. Here we emit the diagnostic and continue parsing
+                    // as if `Self` never existed.
+                    this.dcx()
+                        .emit_err(UnexpectedSelfInGenericParameters { span: this.prev_token.span });
 
-                        // Eat a trailing comma, if it exists.
-                        let _ = this.eat(&token::Comma);
-                    }
+                    // Eat a trailing comma, if it exists.
+                    let _ = this.eat(&token::Comma);
+                }
 
-                    let param = if this.check_lifetime() {
-                        let lifetime = this.expect_lifetime();
-                        // Parse lifetime parameter.
-                        let (colon_span, bounds) = if this.eat(&token::Colon) {
-                            (Some(this.prev_token.span), this.parse_lt_param_bounds())
-                        } else {
-                            (None, Vec::new())
-                        };
-
-                        if this.check_noexpect(&token::Eq)
-                            && this.look_ahead(1, |t| t.is_lifetime())
-                        {
-                            let lo = this.token.span;
-                            // Parse `= 'lifetime`.
-                            this.bump(); // `=`
-                            this.bump(); // `'lifetime`
-                            let span = lo.to(this.prev_token.span);
-                            this.dcx().emit_err(
-                                UnexpectedDefaultValueForLifetimeInGenericParameters { span },
-                            );
-                        }
-
-                        Some(ast::GenericParam {
-                            ident: lifetime.ident,
-                            id: lifetime.id,
-                            attrs,
-                            bounds,
-                            kind: ast::GenericParamKind::Lifetime,
-                            is_placeholder: false,
-                            colon_span,
-                        })
-                    } else if this.check_keyword(kw::Const) {
-                        // Parse const parameter.
-                        Some(this.parse_const_param(attrs)?)
-                    } else if this.check_ident() {
-                        // Parse type parameter.
-                        Some(this.parse_ty_param(attrs)?)
-                    } else if this.token.can_begin_type() {
-                        // Trying to write an associated type bound? (#26271)
-                        let snapshot = this.create_snapshot_for_diagnostic();
-                        match this.parse_ty_where_predicate() {
-                            Ok(where_predicate) => {
-                                this.dcx().emit_err(errors::BadAssocTypeBounds {
-                                    span: where_predicate.span(),
-                                });
-                                // FIXME - try to continue parsing other generics?
-                                return Ok((None, false));
-                            }
-                            Err(err) => {
-                                err.cancel();
-                                // FIXME - maybe we should overwrite 'self' outside of `collect_tokens`?
-                                this.restore_snapshot(snapshot);
-                                return Ok((None, false));
-                            }
-                        }
+                let param = if this.check_lifetime() {
+                    let lifetime = this.expect_lifetime();
+                    // Parse lifetime parameter.
+                    let (colon_span, bounds) = if this.eat(&token::Colon) {
+                        (Some(this.prev_token.span), this.parse_lt_param_bounds())
                     } else {
-                        // Check for trailing attributes and stop parsing.
-                        if !attrs.is_empty() {
-                            if !params.is_empty() {
-                                this.dcx()
-                                    .emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
-                            } else {
-                                this.dcx()
-                                    .emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
-                            }
-                        }
-                        return Ok((None, false));
+                        (None, Vec::new())
                     };
 
-                    if !this.eat(&token::Comma) {
-                        done = true;
+                    if this.check_noexpect(&token::Eq) && this.look_ahead(1, |t| t.is_lifetime()) {
+                        let lo = this.token.span;
+                        // Parse `= 'lifetime`.
+                        this.bump(); // `=`
+                        this.bump(); // `'lifetime`
+                        let span = lo.to(this.prev_token.span);
+                        this.dcx().emit_err(UnexpectedDefaultValueForLifetimeInGenericParameters {
+                            span,
+                        });
                     }
-                    // We just ate the comma, so no need to capture the trailing token.
-                    Ok((param, false))
-                })?;
+
+                    Some(ast::GenericParam {
+                        ident: lifetime.ident,
+                        id: lifetime.id,
+                        attrs,
+                        bounds,
+                        kind: ast::GenericParamKind::Lifetime,
+                        is_placeholder: false,
+                        colon_span,
+                    })
+                } else if this.check_keyword(kw::Const) {
+                    // Parse const parameter.
+                    Some(this.parse_const_param(attrs)?)
+                } else if this.check_ident() {
+                    // Parse type parameter.
+                    Some(this.parse_ty_param(attrs)?)
+                } else if this.token.can_begin_type() {
+                    // Trying to write an associated type bound? (#26271)
+                    let snapshot = this.create_snapshot_for_diagnostic();
+                    match this.parse_ty_where_predicate() {
+                        Ok(where_predicate) => {
+                            this.dcx().emit_err(errors::BadAssocTypeBounds {
+                                span: where_predicate.span(),
+                            });
+                            // FIXME - try to continue parsing other generics?
+                        }
+                        Err(err) => {
+                            err.cancel();
+                            // FIXME - maybe we should overwrite 'self' outside of `collect_tokens`?
+                            this.restore_snapshot(snapshot);
+                        }
+                    }
+                    return Ok((None, Trailing::No, UsePreAttrPos::No));
+                } else {
+                    // Check for trailing attributes and stop parsing.
+                    if !attrs.is_empty() {
+                        if !params.is_empty() {
+                            this.dcx().emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
+                        } else {
+                            this.dcx()
+                                .emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
+                        }
+                    }
+                    return Ok((None, Trailing::No, UsePreAttrPos::No));
+                };
+
+                if !this.eat(&token::Comma) {
+                    done = true;
+                }
+                // We just ate the comma, so no need to capture the trailing token.
+                Ok((param, Trailing::No, UsePreAttrPos::No))
+            })?;
 
             if let Some(param) = param {
                 params.push(param);
@@ -393,7 +387,7 @@ fn parse_ty_where_predicate_or_recover_tuple_struct_body(
 
         if let Some(struct_) = struct_
             && self.may_recover()
-            && self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
+            && self.token == token::OpenDelim(Delimiter::Parenthesis)
         {
             snapshot = Some((struct_, self.create_snapshot_for_diagnostic()));
         };
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 8775d79..47820e9 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -20,7 +20,9 @@
 
 use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
-use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing};
+use super::{
+    AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos,
+};
 use crate::errors::{self, MacroExpandsToAdtField};
 use crate::{fluent_generated as fluent, maybe_whole};
 
@@ -127,7 +129,7 @@ pub(super) fn parse_item_common(
             Some(item.into_inner())
         });
 
-        self.collect_tokens_trailing_token(attrs, force_collect, |this, mut attrs| {
+        self.collect_tokens(None, attrs, force_collect, |this, mut attrs| {
             let lo = this.token.span;
             let vis = this.parse_visibility(FollowedByType::No)?;
             let mut def = this.parse_defaultness();
@@ -145,7 +147,7 @@ pub(super) fn parse_item_common(
                 let span = lo.to(this.prev_token.span);
                 let id = DUMMY_NODE_ID;
                 let item = Item { ident, attrs, id, kind, vis, span, tokens: None };
-                return Ok((Some(item), false));
+                return Ok((Some(item), Trailing::No, UsePreAttrPos::No));
             }
 
             // At this point, we have failed to parse an item.
@@ -160,7 +162,7 @@ pub(super) fn parse_item_common(
             if !attrs_allowed {
                 this.recover_attrs_no_item(&attrs)?;
             }
-            Ok((None, false))
+            Ok((None, Trailing::No, UsePreAttrPos::No))
         })
     }
 
@@ -354,7 +356,7 @@ pub(super) fn is_path_start_item(&mut self) -> bool {
     fn is_reuse_path_item(&mut self) -> bool {
         // no: `reuse ::path` for compatibility reasons with macro invocations
         self.token.is_keyword(kw::Reuse)
-            && self.look_ahead(1, |t| t.is_path_start() && t.kind != token::PathSep)
+            && self.look_ahead(1, |t| t.is_path_start() && *t != token::PathSep)
     }
 
     /// Are we sure this could not possibly be a macro invocation?
@@ -499,7 +501,7 @@ fn recover_attrs_no_item(&mut self, attrs: &[Attribute]) -> PResult<'a, ()> {
         let mut err = self.dcx().struct_span_err(end.span, msg);
         if end.is_doc_comment() {
             err.span_label(end.span, "this doc comment doesn't document anything");
-        } else if self.token.kind == TokenKind::Semi {
+        } else if self.token == TokenKind::Semi {
             err.span_suggestion_verbose(
                 self.token.span,
                 "consider removing this semicolon",
@@ -777,12 +779,12 @@ fn parse_item_list<T>(
                         && self
                             .span_to_snippet(self.prev_token.span)
                             .is_ok_and(|snippet| snippet == "}")
-                        && self.token.kind == token::Semi;
+                        && self.token == token::Semi;
                     let mut semicolon_span = self.token.span;
                     if !is_unnecessary_semicolon {
                         // #105369, Detect spurious `;` before assoc fn body
                         is_unnecessary_semicolon = self.token == token::OpenDelim(Delimiter::Brace)
-                            && self.prev_token.kind == token::Semi;
+                            && self.prev_token == token::Semi;
                         semicolon_span = self.prev_token.span;
                     }
                     // We have to bail or we'll potentially never make progress.
@@ -1194,7 +1196,7 @@ fn parse_item_foreign_mod(
         // FIXME: This recovery should be tested better.
         if safety == Safety::Default
             && self.token.is_keyword(kw::Unsafe)
-            && self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Brace))
+            && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
         {
             self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err().emit();
             safety = Safety::Unsafe(self.token.span);
@@ -1258,7 +1260,7 @@ fn is_unsafe_foreign_mod(&self) -> bool {
             && self.is_keyword_ahead(1, &[kw::Extern])
             && self.look_ahead(
                 2 + self.look_ahead(2, |t| t.can_begin_string_literal() as usize),
-                |t| t.kind == token::OpenDelim(Delimiter::Brace),
+                |t| *t == token::OpenDelim(Delimiter::Brace),
             )
     }
 
@@ -1343,7 +1345,7 @@ fn parse_static_item(
     ) -> PResult<'a, (Ident, StaticItem)> {
         let ident = self.parse_ident()?;
 
-        if self.token.kind == TokenKind::Lt && self.may_recover() {
+        if self.token == TokenKind::Lt && self.may_recover() {
             let generics = self.parse_generics()?;
             self.dcx().emit_err(errors::StaticWithGenerics { span: generics.span });
         }
@@ -1546,86 +1548,82 @@ fn parse_enum_variant(&mut self, span: Span) -> PResult<'a, Option<Variant>> {
         self.recover_vcs_conflict_marker();
         let help = "enum variants can be `Variant`, `Variant = <integer>`, \
                     `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`";
-        self.collect_tokens_trailing_token(
-            variant_attrs,
-            ForceCollect::No,
-            |this, variant_attrs| {
-                let vlo = this.token.span;
+        self.collect_tokens(None, variant_attrs, ForceCollect::No, |this, variant_attrs| {
+            let vlo = this.token.span;
 
-                let vis = this.parse_visibility(FollowedByType::No)?;
-                if !this.recover_nested_adt_item(kw::Enum)? {
-                    return Ok((None, false));
-                }
-                let ident = this.parse_field_ident("enum", vlo)?;
+            let vis = this.parse_visibility(FollowedByType::No)?;
+            if !this.recover_nested_adt_item(kw::Enum)? {
+                return Ok((None, Trailing::No, UsePreAttrPos::No));
+            }
+            let ident = this.parse_field_ident("enum", vlo)?;
 
-                if this.token == token::Not {
-                    if let Err(err) = this.unexpected() {
-                        err.with_note(fluent::parse_macro_expands_to_enum_variant).emit();
-                    }
-
-                    this.bump();
-                    this.parse_delim_args()?;
-
-                    return Ok((None, this.token == token::Comma));
+            if this.token == token::Not {
+                if let Err(err) = this.unexpected() {
+                    err.with_note(fluent::parse_macro_expands_to_enum_variant).emit();
                 }
 
-                let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) {
-                    // Parse a struct variant.
-                    let (fields, recovered) =
-                        match this.parse_record_struct_body("struct", ident.span, false) {
-                            Ok((fields, recovered)) => (fields, recovered),
-                            Err(mut err) => {
-                                if this.token == token::Colon {
-                                    // We handle `enum` to `struct` suggestion in the caller.
-                                    return Err(err);
-                                }
-                                this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]);
-                                this.bump(); // }
-                                err.span_label(span, "while parsing this enum");
-                                err.help(help);
-                                let guar = err.emit();
-                                (thin_vec![], Recovered::Yes(guar))
-                            }
-                        };
-                    VariantData::Struct { fields, recovered: recovered.into() }
-                } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
-                    let body = match this.parse_tuple_struct_body() {
-                        Ok(body) => body,
+                this.bump();
+                this.parse_delim_args()?;
+
+                return Ok((None, Trailing::from(this.token == token::Comma), UsePreAttrPos::No));
+            }
+
+            let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) {
+                // Parse a struct variant.
+                let (fields, recovered) =
+                    match this.parse_record_struct_body("struct", ident.span, false) {
+                        Ok((fields, recovered)) => (fields, recovered),
                         Err(mut err) => {
                             if this.token == token::Colon {
                                 // We handle `enum` to `struct` suggestion in the caller.
                                 return Err(err);
                             }
-                            this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]);
-                            this.bump(); // )
+                            this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]);
+                            this.bump(); // }
                             err.span_label(span, "while parsing this enum");
                             err.help(help);
-                            err.emit();
-                            thin_vec![]
+                            let guar = err.emit();
+                            (thin_vec![], Recovered::Yes(guar))
                         }
                     };
-                    VariantData::Tuple(body, DUMMY_NODE_ID)
-                } else {
-                    VariantData::Unit(DUMMY_NODE_ID)
+                VariantData::Struct { fields, recovered: recovered.into() }
+            } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
+                let body = match this.parse_tuple_struct_body() {
+                    Ok(body) => body,
+                    Err(mut err) => {
+                        if this.token == token::Colon {
+                            // We handle `enum` to `struct` suggestion in the caller.
+                            return Err(err);
+                        }
+                        this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]);
+                        this.bump(); // )
+                        err.span_label(span, "while parsing this enum");
+                        err.help(help);
+                        err.emit();
+                        thin_vec![]
+                    }
                 };
+                VariantData::Tuple(body, DUMMY_NODE_ID)
+            } else {
+                VariantData::Unit(DUMMY_NODE_ID)
+            };
 
-                let disr_expr =
-                    if this.eat(&token::Eq) { Some(this.parse_expr_anon_const()?) } else { None };
+            let disr_expr =
+                if this.eat(&token::Eq) { Some(this.parse_expr_anon_const()?) } else { None };
 
-                let vr = ast::Variant {
-                    ident,
-                    vis,
-                    id: DUMMY_NODE_ID,
-                    attrs: variant_attrs,
-                    data: struct_def,
-                    disr_expr,
-                    span: vlo.to(this.prev_token.span),
-                    is_placeholder: false,
-                };
+            let vr = ast::Variant {
+                ident,
+                vis,
+                id: DUMMY_NODE_ID,
+                attrs: variant_attrs,
+                data: struct_def,
+                disr_expr,
+                span: vlo.to(this.prev_token.span),
+                is_placeholder: false,
+            };
 
-                Ok((Some(vr), this.token == token::Comma))
-            },
-        )
+            Ok((Some(vr), Trailing::from(this.token == token::Comma), UsePreAttrPos::No))
+        })
         .map_err(|mut err| {
             err.help(help);
             err
@@ -1777,7 +1775,7 @@ pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, ThinVec<FieldDef
         // Unit like structs are handled in parse_item_struct function
         self.parse_paren_comma_seq(|p| {
             let attrs = p.parse_outer_attributes()?;
-            p.collect_tokens_trailing_token(attrs, ForceCollect::No, |p, attrs| {
+            p.collect_tokens(None, attrs, ForceCollect::No, |p, attrs| {
                 let mut snapshot = None;
                 if p.is_vcs_conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
                     // Account for `<<<<<<<` diff markers. We can't proactively error here because
@@ -1815,7 +1813,8 @@ pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, ThinVec<FieldDef
                         attrs,
                         is_placeholder: false,
                     },
-                    p.token == token::Comma,
+                    Trailing::from(p.token == token::Comma),
+                    UsePreAttrPos::No,
                 ))
             })
         })
@@ -1827,10 +1826,11 @@ fn parse_field_def(&mut self, adt_ty: &str) -> PResult<'a, FieldDef> {
         self.recover_vcs_conflict_marker();
         let attrs = self.parse_outer_attributes()?;
         self.recover_vcs_conflict_marker();
-        self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
+        self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
             let lo = this.token.span;
             let vis = this.parse_visibility(FollowedByType::No)?;
-            this.parse_single_struct_field(adt_ty, lo, vis, attrs).map(|field| (field, false))
+            this.parse_single_struct_field(adt_ty, lo, vis, attrs)
+                .map(|field| (field, Trailing::No, UsePreAttrPos::No))
         })
     }
 
@@ -1914,7 +1914,7 @@ fn parse_single_struct_field(
                 let mut err = self.dcx().struct_span_err(sp, msg);
 
                 if self.token.is_ident()
-                    || (self.token.kind == TokenKind::Pound
+                    || (self.token == TokenKind::Pound
                         && (self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Bracket))))
                 {
                     // This is likely another field, TokenKind::Pound is used for `#[..]`
@@ -1937,8 +1937,8 @@ fn parse_single_struct_field(
     fn expect_field_ty_separator(&mut self) -> PResult<'a, ()> {
         if let Err(err) = self.expect(&token::Colon) {
             let sm = self.psess.source_map();
-            let eq_typo = self.token.kind == token::Eq && self.look_ahead(1, |t| t.is_path_start());
-            let semi_typo = self.token.kind == token::Semi
+            let eq_typo = self.token == token::Eq && self.look_ahead(1, |t| t.is_path_start());
+            let semi_typo = self.token == token::Semi
                 && self.look_ahead(1, |t| {
                     t.is_path_start()
                     // We check that we are in a situation like `foo; bar` to avoid bad suggestions
@@ -1974,7 +1974,7 @@ fn parse_name_and_ty(
         attrs: AttrVec,
     ) -> PResult<'a, FieldDef> {
         let name = self.parse_field_ident(adt_ty, lo)?;
-        if self.token.kind == token::Not {
+        if self.token == token::Not {
             if let Err(mut err) = self.unexpected() {
                 // Encounter the macro invocation
                 err.subdiagnostic(MacroExpandsToAdtField { adt_ty });
@@ -1983,10 +1983,10 @@ fn parse_name_and_ty(
         }
         self.expect_field_ty_separator()?;
         let ty = self.parse_ty_for_field_def()?;
-        if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) {
+        if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) {
             self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span });
         }
-        if self.token.kind == token::Eq {
+        if self.token == token::Eq {
             self.bump();
             let const_expr = self.parse_expr_anon_const()?;
             let sp = ty.span.shrink_to_hi().to(const_expr.value.span);
@@ -2064,7 +2064,7 @@ fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
                         .parse_ident_common(false)
                         // Cancel this error, we don't need it.
                         .map_err(|err| err.cancel())
-                    && self.token.kind == TokenKind::Colon
+                    && self.token == TokenKind::Colon
                 {
                     err.span_suggestion(
                         removal_span,
@@ -2367,12 +2367,12 @@ fn error_fn_body_not_found(
         match self.expected_one_of_not_found(&[], expected) {
             Ok(error_guaranteed) => Ok(error_guaranteed),
             Err(mut err) => {
-                if self.token.kind == token::CloseDelim(Delimiter::Brace) {
+                if self.token == token::CloseDelim(Delimiter::Brace) {
                     // The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
                     // the AST for typechecking.
                     err.span_label(ident_span, "while parsing this `fn`");
                     Ok(err.emit())
-                } else if self.token.kind == token::RArrow
+                } else if self.token == token::RArrow
                     && let Some(fn_params_end) = fn_params_end
                 {
                     // Instead of a function body, the parser has encountered a right arrow
@@ -2445,7 +2445,7 @@ fn parse_fn_body(
         fn_params_end: Option<Span>,
     ) -> PResult<'a, Option<P<Block>>> {
         let has_semi = if req_body {
-            self.token.kind == TokenKind::Semi
+            self.token == TokenKind::Semi
         } else {
             // Only include `;` in list of expected tokens if body is not required
             self.check(&TokenKind::Semi)
@@ -2458,7 +2458,7 @@ fn parse_fn_body(
         } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
             self.parse_block_common(self.token.span, BlockCheckMode::Default, false)
                 .map(|(attrs, body)| (attrs, Some(body)))?
-        } else if self.token.kind == token::Eq {
+        } else if self.token == token::Eq {
             // Recover `fn foo() = $expr;`.
             self.bump(); // `=`
             let eq_sp = self.prev_token.span;
@@ -2761,7 +2761,7 @@ pub(super) fn parse_fn_decl(
     pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, ThinVec<Param>> {
         let mut first_param = true;
         // Parse the arguments, starting out with `self` being allowed...
-        if self.token.kind != TokenKind::OpenDelim(Delimiter::Parenthesis)
+        if self.token != TokenKind::OpenDelim(Delimiter::Parenthesis)
         // might be typo'd trait impl, handled elsewhere
         && !self.token.is_keyword(kw::For)
         {
@@ -2805,12 +2805,12 @@ pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, ThinV
     fn parse_param_general(&mut self, req_name: ReqName, first_param: bool) -> PResult<'a, Param> {
         let lo = self.token.span;
         let attrs = self.parse_outer_attributes()?;
-        self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
+        self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
             // Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
             if let Some(mut param) = this.parse_self_param()? {
                 param.attrs = attrs;
                 let res = if first_param { Ok(param) } else { this.recover_bad_self_param(param) };
-                return Ok((res?, false));
+                return Ok((res?, Trailing::No, UsePreAttrPos::No));
             }
 
             let is_name_required = match this.token.kind {
@@ -2826,7 +2826,7 @@ fn parse_param_general(&mut self, req_name: ReqName, first_param: bool) -> PResu
                         this.parameter_without_type(&mut err, pat, is_name_required, first_param)
                     {
                         let guar = err.emit();
-                        Ok((dummy_arg(ident, guar), false))
+                        Ok((dummy_arg(ident, guar), Trailing::No, UsePreAttrPos::No))
                     } else {
                         Err(err)
                     };
@@ -2869,7 +2869,8 @@ fn parse_param_general(&mut self, req_name: ReqName, first_param: bool) -> PResu
 
             Ok((
                 Param { attrs, id: ast::DUMMY_NODE_ID, is_placeholder: false, pat, span, ty },
-                false,
+                Trailing::No,
+                UsePreAttrPos::No,
             ))
         })
     }
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 9b3b6d5..61e3fa2 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -14,7 +14,7 @@
 use std::ops::Range;
 use std::{fmt, mem, slice};
 
-use attr_wrapper::AttrWrapper;
+use attr_wrapper::{AttrWrapper, UsePreAttrPos};
 pub use diagnostics::AttemptLocalParseRecovery;
 pub(crate) use expr::ForbiddenLetReason;
 pub(crate) use item::FnParseMode;
@@ -238,6 +238,7 @@ impl NodeRange {
     // is the position of the function's start token. This gives
     // `NodeRange(10..15)`.
     fn new(ParserRange(parser_range): ParserRange, start_pos: u32) -> NodeRange {
+        assert!(parser_range.start >= start_pos && parser_range.end >= start_pos);
         NodeRange((parser_range.start - start_pos)..(parser_range.end - start_pos))
     }
 }
@@ -253,7 +254,7 @@ enum Capturing {
     Yes,
 }
 
-// This state is used by `Parser::collect_tokens_trailing_token`.
+// This state is used by `Parser::collect_tokens`.
 #[derive(Clone, Debug)]
 struct CaptureState {
     capturing: Capturing,
@@ -388,6 +389,12 @@ enum Trailing {
     Yes,
 }
 
+impl From<bool> for Trailing {
+    fn from(b: bool) -> Trailing {
+        if b { Trailing::Yes } else { Trailing::No }
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub(super) enum TokenDescription {
     ReservedIdentifier,
@@ -459,8 +466,8 @@ pub fn new(
         parser.bump();
 
         // Change this from 1 back to 0 after the bump. This eases debugging of
-        // `Parser::collect_tokens_trailing_token` nicer because it makes the
-        // token positions 0-indexed which is nicer than 1-indexed.
+        // `Parser::collect_tokens` because 0-indexed token positions are nicer
+        // than 1-indexed token positions.
         parser.num_bump_calls = 0;
 
         parser
@@ -527,7 +534,7 @@ fn expect_one_of(
         } else if inedible.contains(&self.token.kind) {
             // leave it in the input
             Ok(Recovered::No)
-        } else if self.token.kind != token::Eof
+        } else if self.token != token::Eof
             && self.last_unexpected_token_span == Some(self.token.span)
         {
             FatalError.raise();
@@ -756,7 +763,7 @@ fn check_plus(&mut self) -> bool {
     /// compound tokens like multi-character operators in process.
     /// Returns `true` if the token was eaten.
     fn break_and_eat(&mut self, expected: TokenKind) -> bool {
-        if self.token.kind == expected {
+        if self.token == expected {
             self.bump();
             return true;
         }
@@ -882,7 +889,7 @@ fn parse_seq_to_before_tokens<T>(
                             let token_str = pprust::token_kind_to_string(t);
 
                             match self.current_closure.take() {
-                                Some(closure_spans) if self.token.kind == TokenKind::Semi => {
+                                Some(closure_spans) if self.token == TokenKind::Semi => {
                                     // Finding a semicolon instead of a comma
                                     // after a closure body indicates that the
                                     // closure body may be a block but the user
@@ -910,7 +917,7 @@ fn parse_seq_to_before_tokens<T>(
                             // If this was a missing `@` in a binding pattern
                             // bail with a suggestion
                             // https://github.com/rust-lang/rust/issues/72373
-                            if self.prev_token.is_ident() && self.token.kind == token::DotDot {
+                            if self.prev_token.is_ident() && self.token == token::DotDot {
                                 let msg = format!(
                                     "if you meant to bind the contents of the rest of the array \
                                      pattern into `{}`, use `@`",
@@ -1546,11 +1553,9 @@ fn collect_tokens_no_attrs<R: HasAttrs + HasTokens>(
     ) -> PResult<'a, R> {
         // The only reason to call `collect_tokens_no_attrs` is if you want tokens, so use
         // `ForceCollect::Yes`
-        self.collect_tokens_trailing_token(
-            AttrWrapper::empty(),
-            ForceCollect::Yes,
-            |this, _attrs| Ok((f(this)?, false)),
-        )
+        self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |this, _attrs| {
+            Ok((f(this)?, Trailing::No, UsePreAttrPos::No))
+        })
     }
 
     /// `::{` or `::*`
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 5bfb8bd..eb9a957 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -13,7 +13,7 @@
 use rustc_span::{BytePos, ErrorGuaranteed, Span};
 use thin_vec::{thin_vec, ThinVec};
 
-use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing};
+use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos};
 use crate::errors::{
     self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
     DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
@@ -369,7 +369,7 @@ fn maybe_recover_trailing_expr(
                     .and_then(|(ident, _)| ident.name.as_str().chars().next())
                     .is_some_and(char::is_lowercase)
             })
-            && self.look_ahead(2, |tok| tok.kind == token::OpenDelim(Delimiter::Parenthesis));
+            && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Parenthesis));
 
         // Check for operators.
         // `|` is excluded as it is used in pattern alternatives and lambdas,
@@ -377,9 +377,9 @@ fn maybe_recover_trailing_expr(
         // `[` is included for indexing operations,
         // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`)
         let has_trailing_operator = matches!(self.token.kind, token::BinOp(op) if op != BinOpToken::Or)
-            || self.token.kind == token::Question
-            || (self.token.kind == token::OpenDelim(Delimiter::Bracket)
-                && self.look_ahead(1, |tok| tok.kind != token::CloseDelim(Delimiter::Bracket)));
+            || self.token == token::Question
+            || (self.token == token::OpenDelim(Delimiter::Bracket)
+                && self.look_ahead(1, |t| *t != token::CloseDelim(Delimiter::Bracket)));
 
         if !has_trailing_method && !has_trailing_operator {
             // Nothing to recover here.
@@ -403,7 +403,7 @@ fn maybe_recover_trailing_expr(
 
             // Parse an associative expression such as `+ expr`, `% expr`, ...
             // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
-            if let Ok(expr) =
+            if let Ok((expr, _)) =
                 snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel())
             {
                 // We got a valid expression.
@@ -413,7 +413,7 @@ fn maybe_recover_trailing_expr(
                 let is_bound = is_end_bound
                     // is_start_bound: either `..` or `)..`
                     || self.token.is_range_separator()
-                    || self.token.kind == token::CloseDelim(Delimiter::Parenthesis)
+                    || self.token == token::CloseDelim(Delimiter::Parenthesis)
                         && self.look_ahead(1, Token::is_range_separator);
 
                 // Check that `parse_expr_assoc_with` didn't eat a rhs.
@@ -450,7 +450,7 @@ fn parse_pat_with_range_pat(
             lo = self.token.span;
         }
 
-        let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd {
+        let pat = if self.check(&token::BinOp(token::And)) || self.token == token::AndAnd {
             self.parse_pat_deref(expected)?
         } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
             self.parse_pat_tuple_or_parens()?
@@ -625,7 +625,7 @@ fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind {
     ///
     /// [and]: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching
     fn recover_intersection_pat(&mut self, lhs: P<Pat>) -> PResult<'a, P<Pat>> {
-        if self.token.kind != token::At {
+        if self.token != token::At {
             // Next token is not `@` so it's not going to be an intersection pattern.
             return Ok(lhs);
         }
@@ -958,14 +958,14 @@ fn is_pat_range_end_start(&self, dist: usize) -> bool {
         self.check_inline_const(dist)
             || self.look_ahead(dist, |t| {
                 t.is_path_start() // e.g. `MY_CONST`;
-                || t.kind == token::Dot // e.g. `.5` for recovery;
+                || *t == token::Dot // e.g. `.5` for recovery;
                 || matches!(t.kind, token::Literal(..) | token::BinOp(token::Minus))
                 || t.is_bool_lit()
                 || t.is_whole_expr()
                 || t.is_lifetime() // recover `'a` instead of `'a'`
                 || (self.may_recover() // recover leading `(`
-                    && t.kind == token::OpenDelim(Delimiter::Parenthesis)
-                    && self.look_ahead(dist + 1, |t| t.kind != token::OpenDelim(Delimiter::Parenthesis))
+                    && *t == token::OpenDelim(Delimiter::Parenthesis)
+                    && self.look_ahead(dist + 1, |t| *t != token::OpenDelim(Delimiter::Parenthesis))
                     && self.is_pat_range_end_start(dist + 1))
             })
     }
@@ -1302,24 +1302,23 @@ fn parse_pat_fields(&mut self) -> PResult<'a, (ThinVec<PatField>, PatFieldsRest)
                 }
             }
 
-            let field =
-                self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
-                    let field = match this.parse_pat_field(lo, attrs) {
-                        Ok(field) => Ok(field),
-                        Err(err) => {
-                            if let Some(delayed_err) = delayed_err.take() {
-                                delayed_err.emit();
-                            }
-                            return Err(err);
+            let field = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
+                let field = match this.parse_pat_field(lo, attrs) {
+                    Ok(field) => Ok(field),
+                    Err(err) => {
+                        if let Some(delayed_err) = delayed_err.take() {
+                            delayed_err.emit();
                         }
-                    }?;
-                    ate_comma = this.eat(&token::Comma);
+                        return Err(err);
+                    }
+                }?;
+                ate_comma = this.eat(&token::Comma);
 
-                    last_non_comma_dotdot_span = Some(this.prev_token.span);
+                last_non_comma_dotdot_span = Some(this.prev_token.span);
 
-                    // We just ate a comma, so there's no need to capture a trailing token.
-                    Ok((field, false))
-                })?;
+                // We just ate a comma, so there's no need to capture a trailing token.
+                Ok((field, Trailing::No, UsePreAttrPos::No))
+            })?;
 
             fields.push(field)
         }
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 6f82d6b..b58f398 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -358,9 +358,9 @@ pub(super) fn parse_path_segment(
                     })?;
                     let span = lo.to(self.prev_token.span);
                     AngleBracketedArgs { args, span }.into()
-                } else if self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
+                } else if self.token == token::OpenDelim(Delimiter::Parenthesis)
                     // FIXME(return_type_notation): Could also recover `...` here.
-                    && self.look_ahead(1, |tok| tok.kind == token::DotDot)
+                    && self.look_ahead(1, |t| *t == token::DotDot)
                 {
                     self.bump(); // (
                     self.bump(); // ..
@@ -384,7 +384,7 @@ pub(super) fn parse_path_segment(
                     let token_before_parsing = self.token.clone();
                     let mut snapshot = None;
                     if self.may_recover()
-                        && prev_token_before_parsing.kind == token::PathSep
+                        && prev_token_before_parsing == token::PathSep
                         && (style == PathStyle::Expr && self.token.can_begin_expr()
                             || style == PathStyle::Pat && self.token.can_begin_pattern())
                     {
@@ -393,7 +393,7 @@ pub(super) fn parse_path_segment(
 
                     let (inputs, _) = match self.parse_paren_comma_seq(|p| p.parse_ty()) {
                         Ok(output) => output,
-                        Err(mut error) if prev_token_before_parsing.kind == token::PathSep => {
+                        Err(mut error) if prev_token_before_parsing == token::PathSep => {
                             error.span_label(
                                 prev_token_before_parsing.span.to(token_before_parsing.span),
                                 "while parsing this parenthesized list of type arguments starting here",
@@ -913,7 +913,7 @@ pub(super) fn parse_generic_arg(
             let snapshot = self.create_snapshot_for_diagnostic();
             let attrs = self.parse_outer_attributes()?;
             match self.parse_expr_res(Restrictions::CONST_EXPR, attrs) {
-                Ok(expr) => {
+                Ok((expr, _)) => {
                     return Ok(Some(self.dummy_const_arg_needs_braces(
                         self.dcx().struct_span_err(expr.span, "invalid const generic expression"),
                         expr.span,
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index b206f13..6904419 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -21,6 +21,7 @@
 use super::path::PathStyle;
 use super::{
     AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
+    Trailing, UsePreAttrPos,
 };
 use crate::errors::MalformedLoopLabel;
 use crate::{errors, maybe_whole};
@@ -45,6 +46,7 @@ pub fn parse_stmt_without_recovery(
         capture_semi: bool,
         force_collect: ForceCollect,
     ) -> PResult<'a, Option<Stmt>> {
+        let pre_attr_pos = self.collect_pos();
         let attrs = self.parse_outer_attributes()?;
         let lo = self.token.span;
 
@@ -65,11 +67,15 @@ pub fn parse_stmt_without_recovery(
         }
 
         Ok(Some(if self.token.is_keyword(kw::Let) {
-            self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
+            self.collect_tokens(None, attrs, force_collect, |this, attrs| {
                 this.expect_keyword(kw::Let)?;
                 let local = this.parse_local(attrs)?;
-                let trailing = capture_semi && this.token.kind == token::Semi;
-                Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing))
+                let trailing = Trailing::from(capture_semi && this.token == token::Semi);
+                Ok((
+                    this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
+                    trailing,
+                    UsePreAttrPos::No,
+                ))
             })?
         } else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
             self.recover_stmt_local_after_let(
@@ -103,10 +109,18 @@ pub fn parse_stmt_without_recovery(
             // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
             // that starts like a path (1 token), but it fact not a path.
             // Also, we avoid stealing syntax from `parse_item_`.
-            let stmt = self.collect_tokens_trailing_token(
+            //
+            // `UsePreAttrPos::Yes` here means the attribute belongs unconditionally to the
+            // expression, not the statement. (But the statement attributes/tokens are obtained
+            // from the expression anyway, because `Stmt` delegates `HasAttrs`/`HasTokens` to
+            // the things within `StmtKind`.)
+            let stmt = self.collect_tokens(
+                Some(pre_attr_pos),
                 AttrWrapper::empty(),
                 force_collect,
-                |this, _empty_attrs| Ok((this.parse_stmt_path_start(lo, attrs)?, false)),
+                |this, _empty_attrs| {
+                    Ok((this.parse_stmt_path_start(lo, attrs)?, Trailing::No, UsePreAttrPos::Yes))
+                },
             );
             match stmt {
                 Ok(stmt) => stmt,
@@ -128,12 +142,15 @@ pub fn parse_stmt_without_recovery(
             self.error_outer_attrs(attrs);
             self.mk_stmt(lo, StmtKind::Empty)
         } else if self.token != token::CloseDelim(Delimiter::Brace) {
-            // Remainder are line-expr stmts.
-            let e = self.collect_tokens_trailing_token(
+            // Remainder are line-expr stmts. This is similar to the `parse_stmt_path_start` case
+            // above.
+            let e = self.collect_tokens(
+                Some(pre_attr_pos),
                 AttrWrapper::empty(),
                 force_collect,
                 |this, _empty_attrs| {
-                    Ok((this.parse_expr_res(Restrictions::STMT_EXPR, attrs)?, false))
+                    let (expr, _) = this.parse_expr_res(Restrictions::STMT_EXPR, attrs)?;
+                    Ok((expr, Trailing::No, UsePreAttrPos::Yes))
                 },
             )?;
             if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(kw::Else) {
@@ -150,12 +167,16 @@ pub fn parse_stmt_without_recovery(
     }
 
     fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
-        let stmt = self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
+        let stmt = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
             let path = this.parse_path(PathStyle::Expr)?;
 
             if this.eat(&token::Not) {
                 let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
-                return Ok((stmt_mac, this.token == token::Semi));
+                return Ok((
+                    stmt_mac,
+                    Trailing::from(this.token == token::Semi),
+                    UsePreAttrPos::No,
+                ));
             }
 
             let expr = if this.eat(&token::OpenDelim(Delimiter::Brace)) {
@@ -169,13 +190,17 @@ fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a,
                 this.parse_expr_dot_or_call_with(attrs, expr, lo)
             })?;
             // `DUMMY_SP` will get overwritten later in this function
-            Ok((this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)), false))
+            Ok((
+                this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)),
+                Trailing::No,
+                UsePreAttrPos::No,
+            ))
         })?;
 
         if let StmtKind::Expr(expr) = stmt.kind {
-            // Perform this outside of the `collect_tokens_trailing_token` closure,
-            // since our outer attributes do not apply to this part of the expression
-            let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
+            // Perform this outside of the `collect_tokens` closure, since our
+            // outer attributes do not apply to this part of the expression.
+            let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
                 this.parse_expr_assoc_rest_with(0, true, expr)
             })?;
             Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
@@ -209,7 +234,7 @@ fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResu
             let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
             let e = self.maybe_recover_from_bad_qpath(e)?;
             let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
-            let e = self.parse_expr_assoc_rest_with(0, false, e)?;
+            let (e, _) = self.parse_expr_assoc_rest_with(0, false, e)?;
             StmtKind::Expr(e)
         };
         Ok(self.mk_stmt(lo.to(hi), kind))
@@ -239,10 +264,14 @@ fn recover_stmt_local_after_let(
         subdiagnostic: fn(Span) -> errors::InvalidVariableDeclarationSub,
         force_collect: ForceCollect,
     ) -> PResult<'a, Stmt> {
-        let stmt = self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
+        let stmt = self.collect_tokens(None, attrs, force_collect, |this, attrs| {
             let local = this.parse_local(attrs)?;
             // FIXME - maybe capture semicolon in recovery?
-            Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), false))
+            Ok((
+                this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
+                Trailing::No,
+                UsePreAttrPos::No,
+            ))
         })?;
         self.dcx()
             .emit_err(errors::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
@@ -760,7 +789,7 @@ pub fn parse_full_stmt(
                                     )
                                 ),
                             );
-                            let suggest_eq = if self.token.kind == token::Dot
+                            let suggest_eq = if self.token == token::Dot
                                 && let _ = self.bump()
                                 && let mut snapshot = self.create_snapshot_for_diagnostic()
                                 && let Ok(_) = snapshot
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 352ddd9..a8e8270 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -420,7 +420,7 @@ fn parse_ty_tuple_or_parens(&mut self, lo: Span, allow_plus: AllowPlus) -> PResu
         let mut trailing_plus = false;
         let (ts, trailing) = self.parse_paren_comma_seq(|p| {
             let ty = p.parse_ty()?;
-            trailing_plus = p.prev_token.kind == TokenKind::BinOp(token::Plus);
+            trailing_plus = p.prev_token == TokenKind::BinOp(token::Plus);
             Ok(ty)
         })?;
 
@@ -499,8 +499,8 @@ fn parse_array_or_slice_ty(&mut self) -> PResult<'a, TyKind> {
         let elt_ty = match self.parse_ty() {
             Ok(ty) => ty,
             Err(err)
-                if self.look_ahead(1, |t| t.kind == token::CloseDelim(Delimiter::Bracket))
-                    | self.look_ahead(1, |t| t.kind == token::Semi) =>
+                if self.look_ahead(1, |t| *t == token::CloseDelim(Delimiter::Bracket))
+                    | self.look_ahead(1, |t| *t == token::Semi) =>
             {
                 // Recover from `[LIT; EXPR]` and `[LIT]`
                 self.bump();
@@ -601,7 +601,7 @@ fn parse_ty_bare_fn(
         let span_start = self.token.span;
         let ast::FnHeader { ext, safety, constness, coroutine_kind } =
             self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?;
-        if self.may_recover() && self.token.kind == TokenKind::Lt {
+        if self.may_recover() && self.token == TokenKind::Lt {
             self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
         }
         let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
@@ -681,7 +681,7 @@ fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
         // Always parse bounds greedily for better error recovery.
         let bounds = self.parse_generic_bounds()?;
 
-        *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus);
+        *impl_dyn_multi = bounds.len() > 1 || self.prev_token == TokenKind::BinOp(token::Plus);
 
         Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
     }
@@ -727,8 +727,7 @@ fn is_explicit_dyn_type(&mut self) -> bool {
         self.check_keyword(kw::Dyn)
             && (self.token.uninterpolated_span().at_least_rust_2018()
                 || self.look_ahead(1, |t| {
-                    (can_begin_dyn_bound_in_edition_2015(t)
-                        || t.kind == TokenKind::BinOp(token::Star))
+                    (can_begin_dyn_bound_in_edition_2015(t) || *t == TokenKind::BinOp(token::Star))
                         && !can_continue_type_after_non_fn_ident(t)
                 }))
     }
@@ -750,7 +749,7 @@ fn parse_dyn_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
 
         // Always parse bounds greedily for better error recovery.
         let bounds = self.parse_generic_bounds()?;
-        *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus);
+        *impl_dyn_multi = bounds.len() > 1 || self.prev_token == TokenKind::BinOp(token::Plus);
         Ok(TyKind::TraitObject(bounds, syntax))
     }
 
@@ -1060,7 +1059,7 @@ fn parse_generic_ty_bound(
         }
 
         let mut path = if self.token.is_keyword(kw::Fn)
-            && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
+            && self.look_ahead(1, |t| *t == TokenKind::OpenDelim(Delimiter::Parenthesis))
             && let Some(path) = self.recover_path_from_fn()
         {
             path
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index a64c00f..fce41bd 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -7,9 +7,7 @@
     NestedMetaItem, Safety,
 };
 use rustc_errors::{Applicability, FatalError, PResult};
-use rustc_feature::{
-    AttributeSafety, AttributeTemplate, BuiltinAttribute, Features, BUILTIN_ATTRIBUTE_MAP,
-};
+use rustc_feature::{AttributeSafety, AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_session::errors::report_lit_error;
 use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE};
 use rustc_session::lint::BuiltinLintDiag;
@@ -18,7 +16,7 @@
 
 use crate::{errors, parse_in};
 
-pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) {
+pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
     if attr.is_doc_comment() {
         return;
     }
@@ -28,7 +26,7 @@ pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) {
 
     // All non-builtin attributes are considered safe
     let safety = attr_info.map(|x| x.safety).unwrap_or(AttributeSafety::Normal);
-    check_attribute_safety(features, psess, safety, attr);
+    check_attribute_safety(psess, safety, attr);
 
     // Check input tokens for built-in and key-value attributes.
     match attr_info {
@@ -36,9 +34,9 @@ pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) {
         Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
             match parse_meta(psess, attr) {
                 // Don't check safety again, we just did that
-                Ok(meta) => check_builtin_meta_item(
-                    features, psess, &meta, attr.style, *name, *template, false,
-                ),
+                Ok(meta) => {
+                    check_builtin_meta_item(psess, &meta, attr.style, *name, *template, false)
+                }
                 Err(err) => {
                     err.emit();
                 }
@@ -157,16 +155,7 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte
     }
 }
 
-pub fn check_attribute_safety(
-    features: &Features,
-    psess: &ParseSess,
-    safety: AttributeSafety,
-    attr: &Attribute,
-) {
-    if !features.unsafe_attributes {
-        return;
-    }
-
+pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr: &Attribute) {
     let attr_item = attr.get_normal_item();
 
     if safety == AttributeSafety::Unsafe {
@@ -215,21 +204,18 @@ pub fn check_attribute_safety(
 
 // Called by `check_builtin_meta_item` and code that manually denies
 // `unsafe(...)` in `cfg`
-pub fn deny_builtin_meta_unsafety(features: &Features, psess: &ParseSess, meta: &MetaItem) {
+pub fn deny_builtin_meta_unsafety(psess: &ParseSess, meta: &MetaItem) {
     // This only supports denying unsafety right now - making builtin attributes
     // support unsafety will requite us to thread the actual `Attribute` through
     // for the nice diagnostics.
-    if features.unsafe_attributes {
-        if let Safety::Unsafe(unsafe_span) = meta.unsafety {
-            psess
-                .dcx()
-                .emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: meta.path.clone() });
-        }
+    if let Safety::Unsafe(unsafe_span) = meta.unsafety {
+        psess
+            .dcx()
+            .emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: meta.path.clone() });
     }
 }
 
 pub fn check_builtin_meta_item(
-    features: &Features,
     psess: &ParseSess,
     meta: &MetaItem,
     style: ast::AttrStyle,
@@ -246,7 +232,7 @@ pub fn check_builtin_meta_item(
     }
 
     if deny_unsafety {
-        deny_builtin_meta_unsafety(features, psess, meta);
+        deny_builtin_meta_unsafety(psess, meta);
     }
 }
 
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 0318d34..1ea4ca3 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -430,6 +430,10 @@
     .warn = {-passes_previously_accepted}
     .label = not a function or static
 
+passes_linkage =
+    attribute should be applied to a function or static
+    .label = not a function definition or static
+
 passes_macro_export =
     `#[macro_export]` only has an effect on macro definitions
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index bd157d1..a47add9 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -243,6 +243,7 @@ fn check_attributes(
                 [sym::coroutine, ..] => {
                     self.check_coroutine(attr, target);
                 }
+                [sym::linkage, ..] => self.check_linkage(attr, span, target),
                 [
                     // ok
                     sym::allow
@@ -256,7 +257,6 @@ fn check_attributes(
                     | sym::cfi_encoding // FIXME(cfi_encoding)
                     | sym::may_dangle // FIXME(dropck_eyepatch)
                     | sym::pointee // FIXME(derive_smart_pointer)
-                    | sym::linkage // FIXME(linkage)
                     | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section)
                     | sym::used // handled elsewhere to restrict to static items
                     | sym::repr // handled elsewhere to restrict to type decls items
@@ -2347,6 +2347,19 @@ fn check_coroutine(&self, attr: &Attribute, target: Target) {
             }
         }
     }
+
+    fn check_linkage(&self, attr: &Attribute, span: Span, target: Target) {
+        match target {
+            Target::Fn
+            | Target::Method(..)
+            | Target::Static
+            | Target::ForeignStatic
+            | Target::ForeignFn => {}
+            _ => {
+                self.dcx().emit_err(errors::Linkage { attr_span: attr.span, span });
+            }
+        }
+    }
 }
 
 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 36dfc40..3a043e0 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -644,6 +644,15 @@ pub struct CoroutineOnNonClosure {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_linkage)]
+pub struct Linkage {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_empty_confusables)]
 pub(crate) struct EmptyConfusables {
     #[primary_span]
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index bcd3d30..d7885e0 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -774,17 +774,16 @@ fn hoist_pat_range_bdy(
         }
     }
 
-    /// Convert to a [`print::Pat`] for diagnostic purposes.
-    fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> print::Pat<'tcx> {
-        use print::{Pat, PatKind};
+    /// Prints an [`IntRange`] to a string for diagnostic purposes.
+    fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
         use MaybeInfiniteInt::*;
         let cx = self;
-        let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
-            PatKind::Wild
+        if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
+            "_".to_string()
         } else if range.is_singleton() {
             let lo = cx.hoist_pat_range_bdy(range.lo, ty);
             let value = lo.as_finite().unwrap();
-            PatKind::Constant { value }
+            value.to_string()
         } else {
             // We convert to an inclusive range for diagnostics.
             let mut end = rustc_hir::RangeEnd::Included;
@@ -807,32 +806,24 @@ fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> print::Pat<
                 range.hi
             };
             let hi = cx.hoist_pat_range_bdy(hi, ty);
-            PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() }))
-        };
-
-        Pat { ty: ty.inner(), kind }
+            PatRange { lo, hi, end, ty: ty.inner() }.to_string()
+        }
     }
 
     /// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes.
+    ///
+    /// This panics for patterns that don't appear in diagnostics, like float ranges.
     pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
-        // This works by converting the witness pattern to a `print::Pat`
-        // and then printing that, but callers don't need to know that.
-        self.hoist_witness_pat(pat).to_string()
-    }
-
-    /// Convert to a [`print::Pat`] for diagnostic purposes. This panics for patterns that don't
-    /// appear in diagnostics, like float ranges.
-    fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
-        use print::{FieldPat, Pat, PatKind};
         let cx = self;
-        let hoist = |p| Box::new(cx.hoist_witness_pat(p));
-        let kind = match pat.ctor() {
-            Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) },
-            IntRange(range) => return self.hoist_pat_range(range, *pat.ty()),
+        let print = |p| cx.print_witness_pat(p);
+        match pat.ctor() {
+            Bool(b) => b.to_string(),
+            Str(s) => s.to_string(),
+            IntRange(range) => return self.print_pat_range(range, *pat.ty()),
             Struct if pat.ty().is_box() => {
                 // Outside of the `alloc` crate, the only way to create a struct pattern
                 // of type `Box` is to use a `box` pattern via #[feature(box_patterns)].
-                PatKind::Box { subpattern: hoist(&pat.fields[0]) }
+                format!("box {}", print(&pat.fields[0]))
             }
             Struct | Variant(_) | UnionField => {
                 let enum_info = match *pat.ty().kind() {
@@ -847,12 +838,29 @@ fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
                 let subpatterns = pat
                     .iter_fields()
                     .enumerate()
-                    .map(|(i, pat)| FieldPat { field: FieldIdx::new(i), pattern: hoist(pat) })
+                    .map(|(i, pat)| print::FieldPat {
+                        field: FieldIdx::new(i),
+                        pattern: print(pat),
+                        is_wildcard: would_print_as_wildcard(cx.tcx, pat),
+                    })
                     .collect::<Vec<_>>();
 
-                PatKind::StructLike { enum_info, subpatterns }
+                let mut s = String::new();
+                print::write_struct_like(
+                    &mut s,
+                    self.tcx,
+                    pat.ty().inner(),
+                    &enum_info,
+                    &subpatterns,
+                )
+                .unwrap();
+                s
             }
-            Ref => PatKind::Deref { subpattern: hoist(&pat.fields[0]) },
+            Ref => {
+                let mut s = String::new();
+                print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
+                s
+            }
             Slice(slice) => {
                 let (prefix_len, has_dot_dot) = match slice.kind {
                     SliceKind::FixedLen(len) => (len, false),
@@ -879,14 +887,15 @@ fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
                     }
                 }
 
-                let prefix = prefix.iter().map(hoist).collect();
-                let suffix = suffix.iter().map(hoist).collect();
+                let prefix = prefix.iter().map(print).collect::<Vec<_>>();
+                let suffix = suffix.iter().map(print).collect::<Vec<_>>();
 
-                PatKind::Slice { prefix, has_dot_dot, suffix }
+                let mut s = String::new();
+                print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
+                s
             }
-            &Str(value) => PatKind::Constant { value },
-            Never if self.tcx.features().never_patterns => PatKind::Never,
-            Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
+            Never if self.tcx.features().never_patterns => "!".to_string(),
+            Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
             Missing { .. } => bug!(
                 "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
                 `Missing` should have been processed in `apply_constructors`"
@@ -894,9 +903,7 @@ fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
             F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
                 bug!("can't convert to pattern: {:?}", pat)
             }
-        };
-
-        Pat { ty: pat.ty().inner(), kind }
+        }
     }
 }
 
@@ -972,7 +979,7 @@ fn lint_overlapping_range_endpoints(
         overlaps_on: IntRange,
         overlaps_with: &[&crate::pat::DeconstructedPat<Self>],
     ) {
-        let overlap_as_pat = self.hoist_pat_range(&overlaps_on, *pat.ty());
+        let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty());
         let overlaps: Vec<_> = overlaps_with
             .iter()
             .map(|pat| pat.data().span)
@@ -1012,7 +1019,7 @@ fn lint_non_contiguous_range_endpoints(
             suggested_range.end = rustc_hir::RangeEnd::Included;
             suggested_range.to_string()
         };
-        let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty());
+        let gap_as_pat = self.print_pat_range(&gap, *pat.ty());
         if gapped_with.is_empty() {
             // If `gapped_with` is empty, `gap == T::MAX`.
             self.tcx.emit_node_span_lint(
diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs
index 7d63871..17e389d 100644
--- a/compiler/rustc_pattern_analysis/src/rustc/print.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs
@@ -11,75 +11,16 @@
 
 use std::fmt;
 
-use rustc_middle::thir::PatRange;
+use rustc_middle::bug;
 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
-use rustc_middle::{bug, mir};
 use rustc_span::sym;
 use rustc_target::abi::{FieldIdx, VariantIdx};
 
 #[derive(Clone, Debug)]
-pub(crate) struct FieldPat<'tcx> {
+pub(crate) struct FieldPat {
     pub(crate) field: FieldIdx,
-    pub(crate) pattern: Box<Pat<'tcx>>,
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct Pat<'tcx> {
-    pub(crate) ty: Ty<'tcx>,
-    pub(crate) kind: PatKind<'tcx>,
-}
-
-#[derive(Clone, Debug)]
-pub(crate) enum PatKind<'tcx> {
-    Wild,
-
-    StructLike {
-        enum_info: EnumInfo<'tcx>,
-        subpatterns: Vec<FieldPat<'tcx>>,
-    },
-
-    Box {
-        subpattern: Box<Pat<'tcx>>,
-    },
-
-    Deref {
-        subpattern: Box<Pat<'tcx>>,
-    },
-
-    Constant {
-        value: mir::Const<'tcx>,
-    },
-
-    Range(Box<PatRange<'tcx>>),
-
-    Slice {
-        prefix: Box<[Box<Pat<'tcx>>]>,
-        /// True if this slice-like pattern should include a `..` between the
-        /// prefix and suffix.
-        has_dot_dot: bool,
-        suffix: Box<[Box<Pat<'tcx>>]>,
-    },
-
-    Never,
-}
-
-impl<'tcx> fmt::Display for Pat<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.kind {
-            PatKind::Wild => write!(f, "_"),
-            PatKind::Never => write!(f, "!"),
-            PatKind::Box { ref subpattern } => write!(f, "box {subpattern}"),
-            PatKind::StructLike { ref enum_info, ref subpatterns } => {
-                ty::tls::with(|tcx| write_struct_like(f, tcx, self.ty, enum_info, subpatterns))
-            }
-            PatKind::Deref { ref subpattern } => write_ref_like(f, self.ty, subpattern),
-            PatKind::Constant { value } => write!(f, "{value}"),
-            PatKind::Range(ref range) => write!(f, "{range}"),
-            PatKind::Slice { ref prefix, has_dot_dot, ref suffix } => {
-                write_slice_like(f, prefix, has_dot_dot, suffix)
-            }
-        }
-    }
+    pub(crate) pattern: String,
+    pub(crate) is_wildcard: bool,
 }
 
 /// Returns a closure that will return `""` when called the first time,
@@ -103,12 +44,12 @@ pub(crate) enum EnumInfo<'tcx> {
     NotEnum,
 }
 
-fn write_struct_like<'tcx>(
+pub(crate) fn write_struct_like<'tcx>(
     f: &mut impl fmt::Write,
     tcx: TyCtxt<'_>,
     ty: Ty<'tcx>,
     enum_info: &EnumInfo<'tcx>,
-    subpatterns: &[FieldPat<'tcx>],
+    subpatterns: &[FieldPat],
 ) -> fmt::Result {
     let variant_and_name = match *enum_info {
         EnumInfo::Enum { adt_def, variant_index } => {
@@ -139,12 +80,12 @@ fn write_struct_like<'tcx>(
             write!(f, " {{ ")?;
 
             let mut printed = 0;
-            for p in subpatterns {
-                if let PatKind::Wild = p.pattern.kind {
+            for &FieldPat { field, ref pattern, is_wildcard } in subpatterns {
+                if is_wildcard {
                     continue;
                 }
-                let name = variant.fields[p.field].name;
-                write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
+                let field_name = variant.fields[field].name;
+                write!(f, "{}{field_name}: {pattern}", start_or_comma())?;
                 printed += 1;
             }
 
@@ -184,10 +125,10 @@ fn write_struct_like<'tcx>(
     Ok(())
 }
 
-fn write_ref_like<'tcx>(
+pub(crate) fn write_ref_like<'tcx>(
     f: &mut impl fmt::Write,
     ty: Ty<'tcx>,
-    subpattern: &Pat<'tcx>,
+    subpattern: &str,
 ) -> fmt::Result {
     match ty.kind() {
         ty::Ref(_, _, mutbl) => {
@@ -198,11 +139,11 @@ fn write_ref_like<'tcx>(
     write!(f, "{subpattern}")
 }
 
-fn write_slice_like<'tcx>(
+pub(crate) fn write_slice_like(
     f: &mut impl fmt::Write,
-    prefix: &[Box<Pat<'tcx>>],
+    prefix: &[String],
     has_dot_dot: bool,
-    suffix: &[Box<Pat<'tcx>>],
+    suffix: &[String],
 ) -> fmt::Result {
     let mut start_or_comma = start_or_comma();
     write!(f, "[")?;
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 672dddf..693867c 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1188,7 +1188,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
 
     // Sanitizers can only be used on platforms that we know have working sanitizer codegen.
     let supported_sanitizers = sess.target.options.supported_sanitizers;
-    let unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers;
+    let mut unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers;
+    // Niche: if `fixed-x18`, or effectively switching on `reserved-x18` flag, is enabled
+    // we should allow Shadow Call Stack sanitizer.
+    if sess.opts.unstable_opts.fixed_x18 && sess.target.arch == "aarch64" {
+        unsupported_sanitizers -= SanitizerSet::SHADOWCALLSTACK;
+    }
     match unsupported_sanitizers.into_iter().count() {
         0 => {}
         1 => {
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 8ce51ba..80f89a0 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1561,6 +1561,7 @@ fn $module() {
     ("powerpc-unknown-linux-gnu", powerpc_unknown_linux_gnu),
     ("powerpc-unknown-linux-gnuspe", powerpc_unknown_linux_gnuspe),
     ("powerpc-unknown-linux-musl", powerpc_unknown_linux_musl),
+    ("powerpc-unknown-linux-muslspe", powerpc_unknown_linux_muslspe),
     ("powerpc64-ibm-aix", powerpc64_ibm_aix),
     ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu),
     ("powerpc64-unknown-linux-musl", powerpc64_unknown_linux_musl),
diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs
new file mode 100644
index 0000000..d190157
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs
@@ -0,0 +1,28 @@
+use crate::abi::Endian;
+use crate::spec::{base, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions};
+
+pub fn target() -> Target {
+    let mut base = base::linux_musl::opts();
+    base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-mspe"]);
+    base.max_atomic_width = Some(32);
+    base.stack_probes = StackProbeType::Inline;
+
+    Target {
+        llvm_target: "powerpc-unknown-linux-muslspe".into(),
+        metadata: crate::spec::TargetMetadata {
+            description: Some("PowerPC SPE Linux with musl".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(true),
+        },
+        pointer_width: 32,
+        data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(),
+        arch: "powerpc".into(),
+        options: TargetOptions {
+            abi: "spe".into(),
+            endian: Endian::Big,
+            mcount: "_mcount".into(),
+            ..base
+        },
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 326a0e4..adeabb3 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1689,11 +1689,11 @@ pub(super) fn report_similar_impl_candidates(
             err.highlighted_span_help(
                 span,
                 vec![
-                    StringPart::normal("you have ".to_string()),
+                    StringPart::normal("there are ".to_string()),
                     StringPart::highlighted("multiple different versions".to_string()),
                     StringPart::normal(" of crate `".to_string()),
                     StringPart::highlighted(format!("{name}")),
-                    StringPart::normal("` in your dependency graph".to_string()),
+                    StringPart::normal("` the your dependency graph".to_string()),
                 ],
             );
             let candidates = if impl_candidates.is_empty() {
@@ -2729,6 +2729,10 @@ pub fn get_fn_like_arguments(
             | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. })
             | Node::TraitItem(&hir::TraitItem {
                 kind: hir::TraitItemKind::Fn(ref sig, _), ..
+            })
+            | Node::ForeignItem(&hir::ForeignItem {
+                kind: hir::ForeignItemKind::Fn(ref sig, _, _),
+                ..
             }) => (
                 sig.span,
                 None,
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
deleted file mode 100644
index 656130c..0000000
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-pub use rustc_middle::traits::query::type_op::Eq;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
-
-use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::ObligationCtxt;
-
-impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> {
-    type QueryResponse = ();
-
-    fn try_fast_path(
-        _tcx: TyCtxt<'tcx>,
-        key: &ParamEnvAnd<'tcx, Eq<'tcx>>,
-    ) -> Option<Self::QueryResponse> {
-        if key.value.a == key.value.b { Some(()) } else { None }
-    }
-
-    fn perform_query(
-        tcx: TyCtxt<'tcx>,
-        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
-    ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
-        tcx.type_op_eq(canonicalized)
-    }
-
-    fn perform_locally_with_next_solver(
-        ocx: &ObligationCtxt<'_, 'tcx>,
-        key: ParamEnvAnd<'tcx, Self>,
-    ) -> Result<Self::QueryResponse, NoSolution> {
-        ocx.eq(&ObligationCause::dummy(), key.param_env, key.value.a, key.value.b)?;
-        Ok(())
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 2f64ed9..a765de9 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -16,12 +16,10 @@
 
 pub mod ascribe_user_type;
 pub mod custom;
-pub mod eq;
 pub mod implied_outlives_bounds;
 pub mod normalize;
 pub mod outlives;
 pub mod prove_predicate;
-pub mod subtype;
 
 pub use rustc_middle::traits::query::type_op::*;
 
@@ -170,44 +168,12 @@ fn fully_perform(
         // collecting region constraints via `region_constraints`.
         let (mut output, _) = scrape_region_constraints(
             infcx,
-            |_ocx| {
-                let (output, ei, mut obligations, _) =
+            |ocx| {
+                let (output, ei, obligations, _) =
                     Q::fully_perform_into(self, infcx, &mut region_constraints, span)?;
                 error_info = ei;
 
-                // Typically, instantiating NLL query results does not
-                // create obligations. However, in some cases there
-                // are unresolved type variables, and unify them *can*
-                // create obligations. In that case, we have to go
-                // fulfill them. We do this via a (recursive) query.
-                while !obligations.is_empty() {
-                    trace!("{:#?}", obligations);
-                    let mut progress = false;
-                    for obligation in std::mem::take(&mut obligations) {
-                        let obligation = infcx.resolve_vars_if_possible(obligation);
-                        match ProvePredicate::fully_perform_into(
-                            obligation.param_env.and(ProvePredicate::new(obligation.predicate)),
-                            infcx,
-                            &mut region_constraints,
-                            span,
-                        ) {
-                            Ok(((), _, new, certainty)) => {
-                                obligations.extend(new);
-                                progress = true;
-                                if let Certainty::Ambiguous = certainty {
-                                    obligations.push(obligation);
-                                }
-                            }
-                            Err(_) => obligations.push(obligation),
-                        }
-                    }
-                    if !progress {
-                        infcx.dcx().span_bug(
-                            span,
-                            format!("ambiguity processing {obligations:?} from {self:?}"),
-                        );
-                    }
-                }
+                ocx.register_obligations(obligations);
                 Ok(output)
             },
             "fully_perform",
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
deleted file mode 100644
index 892c2a1..0000000
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-pub use rustc_middle::traits::query::type_op::Subtype;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
-
-use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::ObligationCtxt;
-
-impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> {
-    type QueryResponse = ();
-
-    fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> {
-        if key.value.sub == key.value.sup { Some(()) } else { None }
-    }
-
-    fn perform_query(
-        tcx: TyCtxt<'tcx>,
-        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
-    ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
-        tcx.type_op_subtype(canonicalized)
-    }
-
-    fn perform_locally_with_next_solver(
-        ocx: &ObligationCtxt<'_, 'tcx>,
-        key: ParamEnvAnd<'tcx, Self>,
-    ) -> Result<Self::QueryResponse, NoSolution> {
-        ocx.sub(&ObligationCause::dummy(), key.param_env, key.value.sub, key.value.sup)?;
-        Ok(())
-    }
-}
diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs
index 5affada..f34adf8 100644
--- a/compiler/rustc_traits/src/type_op.rs
+++ b/compiler/rustc_traits/src/type_op.rs
@@ -10,18 +10,14 @@
 use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{
     type_op_ascribe_user_type_with_span, AscribeUserType,
 };
-use rustc_trait_selection::traits::query::type_op::eq::Eq;
 use rustc_trait_selection::traits::query::type_op::normalize::Normalize;
 use rustc_trait_selection::traits::query::type_op::prove_predicate::ProvePredicate;
-use rustc_trait_selection::traits::query::type_op::subtype::Subtype;
 use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt};
 
 pub(crate) fn provide(p: &mut Providers) {
     *p = Providers {
         type_op_ascribe_user_type,
-        type_op_eq,
         type_op_prove_predicate,
-        type_op_subtype,
         type_op_normalize_ty,
         type_op_normalize_clause,
         type_op_normalize_fn_sig,
@@ -39,16 +35,6 @@ fn type_op_ascribe_user_type<'tcx>(
     })
 }
 
-fn type_op_eq<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Eq<'tcx>>>,
-) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> {
-    tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| {
-        let (param_env, Eq { a, b }) = key.into_parts();
-        Ok(ocx.eq(&ObligationCause::dummy(), param_env, a, b)?)
-    })
-}
-
 fn type_op_normalize<'tcx, T>(
     ocx: &ObligationCtxt<'_, 'tcx>,
     key: ParamEnvAnd<'tcx, Normalize<T>>,
@@ -91,16 +77,6 @@ fn type_op_normalize_poly_fn_sig<'tcx>(
     tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize)
 }
 
-fn type_op_subtype<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Subtype<'tcx>>>,
-) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> {
-    tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| {
-        let (param_env, Subtype { sub, sup }) = key.into_parts();
-        Ok(ocx.sup(&ObligationCause::dummy(), param_env, sup, sub)?)
-    })
-}
-
 fn type_op_prove_predicate<'tcx>(
     tcx: TyCtxt<'tcx>,
     canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>,
diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs
index 76a89ea..2150463 100644
--- a/library/core/src/clone.rs
+++ b/library/core/src/clone.rs
@@ -36,8 +36,7 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
-use crate::mem::{self, MaybeUninit};
-use crate::ptr;
+mod uninit;
 
 /// A common trait for the ability to explicitly duplicate an object.
 ///
@@ -248,7 +247,7 @@ pub unsafe trait CloneToUninit {
     /// * `dst` must be properly aligned.
     /// * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`.
     ///
-    /// [valid]: ptr#safety
+    /// [valid]: crate::ptr#safety
     /// [pointer metadata]: crate::ptr::metadata()
     ///
     /// # Panics
@@ -272,124 +271,42 @@ pub unsafe trait CloneToUninit {
 
 #[unstable(feature = "clone_to_uninit", issue = "126799")]
 unsafe impl<T: Clone> CloneToUninit for T {
-    default unsafe fn clone_to_uninit(&self, dst: *mut Self) {
-        // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
-        // ptr::write().
-        unsafe {
-            // We hope the optimizer will figure out to create the cloned value in-place,
-            // skipping ever storing it on the stack and the copy to the destination.
-            ptr::write(dst, self.clone());
-        }
-    }
-}
-
-// Specialized implementation for types that are [`Copy`], not just [`Clone`],
-// and can therefore be copied bitwise.
-#[unstable(feature = "clone_to_uninit", issue = "126799")]
-unsafe impl<T: Copy> CloneToUninit for T {
+    #[inline]
     unsafe fn clone_to_uninit(&self, dst: *mut Self) {
-        // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
-        // ptr::copy_nonoverlapping().
-        unsafe {
-            ptr::copy_nonoverlapping(self, dst, 1);
-        }
+        // SAFETY: we're calling a specialization with the same contract
+        unsafe { <T as self::uninit::CopySpec>::clone_one(self, dst) }
     }
 }
 
 #[unstable(feature = "clone_to_uninit", issue = "126799")]
 unsafe impl<T: Clone> CloneToUninit for [T] {
+    #[inline]
     #[cfg_attr(debug_assertions, track_caller)]
-    default unsafe fn clone_to_uninit(&self, dst: *mut Self) {
-        let len = self.len();
-        // This is the most likely mistake to make, so check it as a debug assertion.
-        debug_assert_eq!(
-            len,
-            dst.len(),
-            "clone_to_uninit() source and destination must have equal lengths",
-        );
-
-        // SAFETY: The produced `&mut` is valid because:
-        // * The caller is obligated to provide a pointer which is valid for writes.
-        // * All bytes pointed to are in MaybeUninit, so we don't care about the memory's
-        //   initialization status.
-        let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit<T>]) };
-
-        // Copy the elements
-        let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref);
-        for element_ref in self.iter() {
-            // If the clone() panics, `initializing` will take care of the cleanup.
-            initializing.push(element_ref.clone());
-        }
-        // If we reach here, then the entire slice is initialized, and we've satisfied our
-        // responsibilities to the caller. Disarm the cleanup guard by forgetting it.
-        mem::forget(initializing);
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: we're calling a specialization with the same contract
+        unsafe { <T as self::uninit::CopySpec>::clone_slice(self, dst) }
     }
 }
 
 #[unstable(feature = "clone_to_uninit", issue = "126799")]
-unsafe impl<T: Copy> CloneToUninit for [T] {
+unsafe impl CloneToUninit for str {
+    #[inline]
     #[cfg_attr(debug_assertions, track_caller)]
     unsafe fn clone_to_uninit(&self, dst: *mut Self) {
-        let len = self.len();
-        // This is the most likely mistake to make, so check it as a debug assertion.
-        debug_assert_eq!(
-            len,
-            dst.len(),
-            "clone_to_uninit() source and destination must have equal lengths",
-        );
-
-        // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
-        // ptr::copy_nonoverlapping().
-        unsafe {
-            ptr::copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), len);
-        }
+        // SAFETY: str is just a [u8] with UTF-8 invariant
+        unsafe { self.as_bytes().clone_to_uninit(dst as *mut [u8]) }
     }
 }
 
-/// Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
-/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
-/// Its responsibility is to provide cleanup on unwind by dropping the values that *are*
-/// initialized, unless disarmed by forgetting.
-///
-/// This is a helper for `impl<T: Clone> CloneToUninit for [T]`.
-struct InitializingSlice<'a, T> {
-    data: &'a mut [MaybeUninit<T>],
-    /// Number of elements of `*self.data` that are initialized.
-    initialized_len: usize,
-}
-
-impl<'a, T> InitializingSlice<'a, T> {
-    #[inline]
-    fn from_fully_uninit(data: &'a mut [MaybeUninit<T>]) -> Self {
-        Self { data, initialized_len: 0 }
-    }
-
-    /// Push a value onto the end of the initialized part of the slice.
-    ///
-    /// # Panics
-    ///
-    /// Panics if the slice is already fully initialized.
-    #[inline]
-    fn push(&mut self, value: T) {
-        MaybeUninit::write(&mut self.data[self.initialized_len], value);
-        self.initialized_len += 1;
-    }
-}
-
-impl<'a, T> Drop for InitializingSlice<'a, T> {
-    #[cold] // will only be invoked on unwind
-    fn drop(&mut self) {
-        let initialized_slice = ptr::slice_from_raw_parts_mut(
-            MaybeUninit::slice_as_mut_ptr(self.data),
-            self.initialized_len,
-        );
-        // SAFETY:
-        // * the pointer is valid because it was made from a mutable reference
-        // * `initialized_len` counts the initialized elements as an invariant of this type,
-        //   so each of the pointed-to elements is initialized and may be dropped.
-        unsafe {
-            ptr::drop_in_place::<[T]>(initialized_slice);
-        }
+#[unstable(feature = "clone_to_uninit", issue = "126799")]
+unsafe impl CloneToUninit for crate::ffi::CStr {
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: For now, CStr is just a #[repr(trasnsparent)] [c_char] with some invariants.
+        // And we can cast [c_char] to [u8] on all supported platforms (see: to_bytes_with_nul).
+        // The pointer metadata properly preserves the length (NUL included).
+        // See: `cstr_metadata_is_length_with_nul` in tests.
+        unsafe { self.to_bytes_with_nul().clone_to_uninit(dst as *mut [u8]) }
     }
 }
 
diff --git a/library/core/src/clone/uninit.rs b/library/core/src/clone/uninit.rs
new file mode 100644
index 0000000..8b738be
--- /dev/null
+++ b/library/core/src/clone/uninit.rs
@@ -0,0 +1,128 @@
+use crate::mem::{self, MaybeUninit};
+use crate::ptr;
+
+/// Private specialization trait used by CloneToUninit, as per
+/// [the dev guide](https://std-dev-guide.rust-lang.org/policy/specialization.html).
+pub(super) unsafe trait CopySpec: Clone {
+    unsafe fn clone_one(src: &Self, dst: *mut Self);
+    unsafe fn clone_slice(src: &[Self], dst: *mut [Self]);
+}
+
+unsafe impl<T: Clone> CopySpec for T {
+    #[inline]
+    default unsafe fn clone_one(src: &Self, dst: *mut Self) {
+        // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
+        // ptr::write().
+        unsafe {
+            // We hope the optimizer will figure out to create the cloned value in-place,
+            // skipping ever storing it on the stack and the copy to the destination.
+            ptr::write(dst, src.clone());
+        }
+    }
+
+    #[inline]
+    #[cfg_attr(debug_assertions, track_caller)]
+    default unsafe fn clone_slice(src: &[Self], dst: *mut [Self]) {
+        let len = src.len();
+        // This is the most likely mistake to make, so check it as a debug assertion.
+        debug_assert_eq!(
+            len,
+            dst.len(),
+            "clone_to_uninit() source and destination must have equal lengths",
+        );
+
+        // SAFETY: The produced `&mut` is valid because:
+        // * The caller is obligated to provide a pointer which is valid for writes.
+        // * All bytes pointed to are in MaybeUninit, so we don't care about the memory's
+        //   initialization status.
+        let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit<T>]) };
+
+        // Copy the elements
+        let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref);
+        for element_ref in src {
+            // If the clone() panics, `initializing` will take care of the cleanup.
+            initializing.push(element_ref.clone());
+        }
+        // If we reach here, then the entire slice is initialized, and we've satisfied our
+        // responsibilities to the caller. Disarm the cleanup guard by forgetting it.
+        mem::forget(initializing);
+    }
+}
+
+// Specialized implementation for types that are [`Copy`], not just [`Clone`],
+// and can therefore be copied bitwise.
+unsafe impl<T: Copy> CopySpec for T {
+    #[inline]
+    unsafe fn clone_one(src: &Self, dst: *mut Self) {
+        // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
+        // ptr::copy_nonoverlapping().
+        unsafe {
+            ptr::copy_nonoverlapping(src, dst, 1);
+        }
+    }
+
+    #[inline]
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_slice(src: &[Self], dst: *mut [Self]) {
+        let len = src.len();
+        // This is the most likely mistake to make, so check it as a debug assertion.
+        debug_assert_eq!(
+            len,
+            dst.len(),
+            "clone_to_uninit() source and destination must have equal lengths",
+        );
+
+        // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of
+        // ptr::copy_nonoverlapping().
+        unsafe {
+            ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), len);
+        }
+    }
+}
+
+/// Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
+/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
+/// Its responsibility is to provide cleanup on unwind by dropping the values that *are*
+/// initialized, unless disarmed by forgetting.
+///
+/// This is a helper for `impl<T: Clone> CloneToUninit for [T]`.
+struct InitializingSlice<'a, T> {
+    data: &'a mut [MaybeUninit<T>],
+    /// Number of elements of `*self.data` that are initialized.
+    initialized_len: usize,
+}
+
+impl<'a, T> InitializingSlice<'a, T> {
+    #[inline]
+    fn from_fully_uninit(data: &'a mut [MaybeUninit<T>]) -> Self {
+        Self { data, initialized_len: 0 }
+    }
+
+    /// Push a value onto the end of the initialized part of the slice.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the slice is already fully initialized.
+    #[inline]
+    fn push(&mut self, value: T) {
+        MaybeUninit::write(&mut self.data[self.initialized_len], value);
+        self.initialized_len += 1;
+    }
+}
+
+impl<'a, T> Drop for InitializingSlice<'a, T> {
+    #[cold] // will only be invoked on unwind
+    fn drop(&mut self) {
+        let initialized_slice = ptr::slice_from_raw_parts_mut(
+            MaybeUninit::slice_as_mut_ptr(self.data),
+            self.initialized_len,
+        );
+        // SAFETY:
+        // * the pointer is valid because it was made from a mutable reference
+        // * `initialized_len` counts the initialized elements as an invariant of this type,
+        //   so each of the pointed-to elements is initialized and may be dropped.
+        unsafe {
+            ptr::drop_in_place::<[T]>(initialized_slice);
+        }
+    }
+}
diff --git a/library/core/src/future/ready.rs b/library/core/src/future/ready.rs
index a07b63f..6f6da8c 100644
--- a/library/core/src/future/ready.rs
+++ b/library/core/src/future/ready.rs
@@ -34,13 +34,12 @@ impl<T> Ready<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(ready_into_inner)]
     /// use std::future;
     ///
     /// let a = future::ready(1);
     /// assert_eq!(a.into_inner(), 1);
     /// ```
-    #[unstable(feature = "ready_into_inner", issue = "101196")]
+    #[stable(feature = "ready_into_inner", since = "CURRENT_RUSTC_VERSION")]
     #[must_use]
     #[inline]
     pub fn into_inner(self) -> T {
diff --git a/library/core/src/iter/adapters/take.rs b/library/core/src/iter/adapters/take.rs
index 297dd0a..4c8f9fe 100644
--- a/library/core/src/iter/adapters/take.rs
+++ b/library/core/src/iter/adapters/take.rs
@@ -317,3 +317,60 @@ fn spec_for_each<F: FnMut(Self::Item)>(mut self, mut f: F) {
         }
     }
 }
+
+#[stable(feature = "exact_size_take_repeat", since = "CURRENT_RUSTC_VERSION")]
+impl<T: Clone> DoubleEndedIterator for Take<crate::iter::Repeat<T>> {
+    #[inline]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        self.next()
+    }
+
+    #[inline]
+    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
+        self.nth(n)
+    }
+
+    #[inline]
+    fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
+    where
+        Self: Sized,
+        Fold: FnMut(Acc, Self::Item) -> R,
+        R: Try<Output = Acc>,
+    {
+        self.try_fold(init, fold)
+    }
+
+    #[inline]
+    fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
+    where
+        Self: Sized,
+        Fold: FnMut(Acc, Self::Item) -> Acc,
+    {
+        self.fold(init, fold)
+    }
+
+    #[inline]
+    #[rustc_inherit_overflow_checks]
+    fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
+        self.advance_by(n)
+    }
+}
+
+// Note: It may be tempting to impl DoubleEndedIterator for Take<RepeatWith>.
+// One must fight that temptation since such implementation wouldn’t be correct
+// because we have no way to return value of nth invocation of repeater followed
+// by n-1st without remembering all results.
+
+#[stable(feature = "exact_size_take_repeat", since = "CURRENT_RUSTC_VERSION")]
+impl<T: Clone> ExactSizeIterator for Take<crate::iter::Repeat<T>> {
+    fn len(&self) -> usize {
+        self.n
+    }
+}
+
+#[stable(feature = "exact_size_take_repeat", since = "CURRENT_RUSTC_VERSION")]
+impl<F: FnMut() -> A, A> ExactSizeIterator for Take<crate::iter::RepeatWith<F>> {
+    fn len(&self) -> usize {
+        self.n
+    }
+}
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 6a83ec2..374fa08 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -1060,7 +1060,7 @@ pub trait FnPtr: Copy + Clone {
 }
 
 /// Derive macro generating impls of traits related to smart pointers.
-#[rustc_builtin_macro]
+#[rustc_builtin_macro(SmartPointer, attributes(pointee))]
 #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
 #[unstable(feature = "derive_smart_pointer", issue = "123430")]
 pub macro SmartPointer($item:item) {
diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs
index 3e036b8..919f681 100644
--- a/library/core/src/net/ip_addr.rs
+++ b/library/core/src/net/ip_addr.rs
@@ -1,6 +1,7 @@
 use super::display_buffer::DisplayBuffer;
 use crate::cmp::Ordering;
 use crate::fmt::{self, Write};
+use crate::hash::{Hash, Hasher};
 use crate::iter;
 use crate::mem::transmute;
 use crate::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
@@ -67,12 +68,22 @@ pub enum IpAddr {
 /// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal
 /// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
 /// ```
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, PartialEq, Eq)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Ipv4Addr {
     octets: [u8; 4],
 }
 
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Hash for Ipv4Addr {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        // Hashers are often more efficient at hashing a fixed-width integer
+        // than a bytestring, so convert before hashing. We don't use to_bits()
+        // here as that may involve a byteswap which is unnecessary.
+        u32::from_ne_bytes(self.octets).hash(state);
+    }
+}
+
 /// An IPv6 address.
 ///
 /// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291].
@@ -149,12 +160,22 @@ pub struct Ipv4Addr {
 /// assert_eq!("::1".parse(), Ok(localhost));
 /// assert_eq!(localhost.is_loopback(), true);
 /// ```
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, PartialEq, Eq)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Ipv6Addr {
     octets: [u8; 16],
 }
 
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Hash for Ipv6Addr {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        // Hashers are often more efficient at hashing a fixed-width integer
+        // than a bytestring, so convert before hashing. We don't use to_bits()
+        // here as that may involve unnecessary byteswaps.
+        u128::from_ne_bytes(self.octets).hash(state);
+    }
+}
+
 /// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2].
 ///
 /// # Stability Guarantees
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index 0c04f47..38e69e7 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -290,7 +290,7 @@ pub const fn is_nan(self) -> bool {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f128 {
-        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
         unsafe {
             mem::transmute::<u128, f128>(mem::transmute::<f128, u128>(self) & !Self::SIGN_MASK)
         }
@@ -439,22 +439,12 @@ pub const fn is_normal(self) -> bool {
     #[unstable(feature = "f128", issue = "116909")]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub const fn classify(self) -> FpCategory {
-        // Other float types cannot use a bitwise classify because they may suffer a variety
-        // of errors if the backend chooses to cast to different float types (x87). `f128` cannot
-        // fit into any other float types so this is not a concern, and we rely on bit patterns.
+        // Other float types suffer from various platform bugs that violate the usual IEEE semantics
+        // and also make bitwise classification not always work reliably. However, `f128` cannot fit
+        // into any other float types so this is not a concern, and we can rely on bit patterns.
 
-        // SAFETY: POD bitcast, same as in `to_bits`.
-        let bits = unsafe { mem::transmute::<f128, u128>(self) };
-        Self::classify_bits(bits)
-    }
-
-    /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
-    /// FIXME(jubilee): In a just world, this would be the entire impl for classify,
-    /// plus a transmute. We do not live in a just world, but we can make it more so.
-    #[inline]
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const fn classify_bits(b: u128) -> FpCategory {
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+        let bits = self.to_bits();
+        match (bits & Self::MAN_MASK, bits & Self::EXP_MASK) {
             (0, Self::EXP_MASK) => FpCategory::Infinite,
             (_, Self::EXP_MASK) => FpCategory::Nan,
             (0, 0) => FpCategory::Zero,
@@ -922,48 +912,7 @@ pub unsafe fn to_int_unchecked<Int>(self) -> Int
     #[must_use = "this returns the result of the operation, without modifying the original"]
     pub const fn to_bits(self) -> u128 {
         // SAFETY: `u128` is a plain old datatype so we can always transmute to it.
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to a floating point mode that alters nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // we reject any of these possible situations from happening.
-        #[inline]
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_f128_to_u128(ct: f128) -> u128 {
-            // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that
-            // is not available on all platforms (needs `netf2` and `unordtf2`). So classify
-            // the bits instead.
-
-            // SAFETY: this is a POD transmutation
-            let bits = unsafe { mem::transmute::<f128, u128>(ct) };
-            match f128::classify_bits(bits) {
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f128::to_bits on a NaN")
-                }
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f128::to_bits on a subnormal number")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_f128_to_u128(x: f128) -> u128 {
-            // SAFETY: `u128` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((self,), ct_f128_to_u128, rt_f128_to_u128)
+        unsafe { mem::transmute(self) }
     }
 
     /// Raw transmutation from `u128`.
@@ -1011,49 +960,8 @@ fn rt_f128_to_u128(x: f128) -> u128 {
     #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
     pub const fn from_bits(v: u128) -> Self {
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        // SAFETY: `u128` is a plain old datatype so we can always transmute from it
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to floating point modes that alter nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem usually, but at least one tier2 platform for Rust
-        // actually exhibits this behavior by default: thumbv7neon
-        // aka "the Neon FPU in AArch32 state"
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // reject any of these possible situations from happening.
-        #[inline]
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_u128_to_f128(ct: u128) -> f128 {
-            match f128::classify_bits(ct) {
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f128::from_bits on a subnormal number")
-                }
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f128::from_bits on NaN")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: It's not a frumious number
-                    unsafe { mem::transmute::<u128, f128>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_u128_to_f128(x: u128) -> f128 {
-            // SAFETY: `u128` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((v,), ct_u128_to_f128, rt_u128_to_f128)
+        // SAFETY: `u128` is a plain old datatype so we can always transmute from it.
+        unsafe { mem::transmute(v) }
     }
 
     /// Returns the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index e5b1148..41bd34a 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -284,7 +284,7 @@ pub const fn is_nan(self) -> bool {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f16 {
-        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
         unsafe { mem::transmute::<u16, f16>(mem::transmute::<f16, u16>(self) & !Self::SIGN_MASK) }
     }
 
@@ -426,15 +426,15 @@ pub const fn is_normal(self) -> bool {
     pub const fn classify(self) -> FpCategory {
         // A previous implementation for f32/f64 tried to only use bitmask-based checks,
         // using `to_bits` to transmute the float to its bit repr and match on that.
-        // Unfortunately, floating point numbers can be much worse than that.
-        // This also needs to not result in recursive evaluations of `to_bits`.
+        // If we only cared about being "technically" correct, that's an entirely legit
+        // implementation.
         //
-
-        // Platforms without native support generally convert to `f32` to perform operations,
-        // and most of these platforms correctly round back to `f16` after each operation.
-        // However, some platforms have bugs where they keep the excess `f32` precision (e.g.
-        // WASM, see llvm/llvm-project#96437). This implementation makes a best-effort attempt
-        // to account for that excess precision.
+        // Unfortunately, there are platforms out there that do not correctly implement the IEEE
+        // float semantics Rust relies on: some hardware flushes denormals to zero, and some
+        // platforms convert to `f32` to perform operations without properly rounding back (e.g.
+        // WASM, see llvm/llvm-project#96437). These are platforms bugs, and Rust will misbehave on
+        // such platforms, but we can at least try to make things seem as sane as possible by being
+        // careful here.
         if self.is_infinite() {
             // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
             FpCategory::Infinite
@@ -446,49 +446,20 @@ pub const fn classify(self) -> FpCategory {
             // as correctness requires avoiding equality tests that may be Subnormal == -0.0
             // because it may be wrong under "denormals are zero" and "flush to zero" modes.
             // Most of std's targets don't use those, but they are used for thumbv7neon.
-            // So, this does use bitpattern matching for the rest.
-
-            // SAFETY: f16 to u16 is fine. Usually.
-            // If classify has gotten this far, the value is definitely in one of these categories.
-            unsafe { f16::partial_classify(self) }
-        }
-    }
-
-    /// This doesn't actually return a right answer for NaN on purpose,
-    /// seeing as how it cannot correctly discern between a floating point NaN,
-    /// and some normal floating point numbers truncated from an x87 FPU.
-    ///
-    /// # Safety
-    ///
-    /// This requires making sure you call this function for values it answers correctly on,
-    /// otherwise it returns a wrong answer. This is not important for memory safety per se,
-    /// but getting floats correct is important for not accidentally leaking const eval
-    /// runtime-deviating logic which may or may not be acceptable.
-    #[inline]
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const unsafe fn partial_classify(self) -> FpCategory {
-        // SAFETY: The caller is not asking questions for which this will tell lies.
-        let b = unsafe { mem::transmute::<f16, u16>(self) };
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
-        }
-    }
-
-    /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
-    /// FIXME(jubilee): In a just world, this would be the entire impl for classify,
-    /// plus a transmute. We do not live in a just world, but we can make it more so.
-    #[inline]
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const fn classify_bits(b: u16) -> FpCategory {
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (_, Self::EXP_MASK) => FpCategory::Nan,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
+            // So, this does use bitpattern matching for the rest. On x87, due to the incorrect
+            // float codegen on this hardware, this doesn't actually return a right answer for NaN
+            // because it cannot correctly discern between a floating point NaN, and some normal
+            // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
+            // we are fine.
+            // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
+            // like the f64 version does, but I need to run more checks on how things go on x86.
+            // I fear losing mantissa data that would have answered that differently.
+            let b = self.to_bits();
+            match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+                (0, 0) => FpCategory::Zero,
+                (_, 0) => FpCategory::Subnormal,
+                _ => FpCategory::Normal,
+            }
         }
     }
 
@@ -952,48 +923,7 @@ pub unsafe fn to_int_unchecked<Int>(self) -> Int
     #[must_use = "this returns the result of the operation, without modifying the original"]
     pub const fn to_bits(self) -> u16 {
         // SAFETY: `u16` is a plain old datatype so we can always transmute to it.
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to a floating point mode that alters nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // we reject any of these possible situations from happening.
-        #[inline]
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_f16_to_u16(ct: f16) -> u16 {
-            // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet
-            // want to rely on that on all platforms because it is nondeterministic (e.g. x86 has
-            // convention discrepancies calling intrinsics). So just classify the bits instead.
-
-            // SAFETY: this is a POD transmutation
-            let bits = unsafe { mem::transmute::<f16, u16>(ct) };
-            match f16::classify_bits(bits) {
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f16::to_bits on a NaN")
-                }
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f16::to_bits on a subnormal number")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_f16_to_u16(x: f16) -> u16 {
-            // SAFETY: `u16` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((self,), ct_f16_to_u16, rt_f16_to_u16)
+        unsafe { mem::transmute(self) }
     }
 
     /// Raw transmutation from `u16`.
@@ -1040,49 +970,8 @@ fn rt_f16_to_u16(x: f16) -> u16 {
     #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
     pub const fn from_bits(v: u16) -> Self {
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        // SAFETY: `u16` is a plain old datatype so we can always transmute from it
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to floating point modes that alter nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem usually, but at least one tier2 platform for Rust
-        // actually exhibits this behavior by default: thumbv7neon
-        // aka "the Neon FPU in AArch32 state"
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // reject any of these possible situations from happening.
-        #[inline]
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_u16_to_f16(ct: u16) -> f16 {
-            match f16::classify_bits(ct) {
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f16::from_bits on a subnormal number")
-                }
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f16::from_bits on NaN")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: It's not a frumious number
-                    unsafe { mem::transmute::<u16, f16>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_u16_to_f16(x: u16) -> f16 {
-            // SAFETY: `u16` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((v,), ct_u16_to_f16, rt_u16_to_f16)
+        // SAFETY: `u16` is a plain old datatype so we can always transmute from it.
+        unsafe { mem::transmute(v) }
     }
 
     /// Returns the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 7710e23..719727e 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -529,7 +529,7 @@ pub const fn is_nan(self) -> bool {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f32 {
-        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
         unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & !Self::SIGN_MASK) }
     }
 
@@ -654,18 +654,20 @@ pub const fn is_normal(self) -> bool {
     pub const fn classify(self) -> FpCategory {
         // A previous implementation tried to only use bitmask-based checks,
         // using f32::to_bits to transmute the float to its bit repr and match on that.
-        // Unfortunately, floating point numbers can be much worse than that.
-        // This also needs to not result in recursive evaluations of f64::to_bits.
+        // If we only cared about being "technically" correct, that's an entirely legit
+        // implementation.
         //
-        // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
-        // in spite of a request for them using f32 and f64, to things like x87 operations.
-        // These have an f64's mantissa, but can have a larger than normal exponent.
+        // Unfortunately, there is hardware out there that does not correctly implement the IEEE
+        // float semantics Rust relies on: x87 uses a too-large mantissa and exponent, and some
+        // hardware flushes subnormals to zero. These are platforms bugs, and Rust will misbehave on
+        // such hardware, but we can at least try to make things seem as sane as possible by being
+        // careful here.
+        //
         // FIXME(jubilee): Using x87 operations is never necessary in order to function
         // on x86 processors for Rust-to-Rust calls, so this issue should not happen.
         // Code generation should be adjusted to use non-C calling conventions, avoiding this.
-        //
         if self.is_infinite() {
-            // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
+            // A value may compare unequal to infinity, despite having a "full" exponent mask.
             FpCategory::Infinite
         } else if self.is_nan() {
             // And it may not be NaN, as it can simply be an "overextended" finite value.
@@ -675,48 +677,20 @@ pub const fn classify(self) -> FpCategory {
             // as correctness requires avoiding equality tests that may be Subnormal == -0.0
             // because it may be wrong under "denormals are zero" and "flush to zero" modes.
             // Most of std's targets don't use those, but they are used for thumbv7neon.
-            // So, this does use bitpattern matching for the rest.
-
-            // SAFETY: f32 to u32 is fine. Usually.
-            // If classify has gotten this far, the value is definitely in one of these categories.
-            unsafe { f32::partial_classify(self) }
-        }
-    }
-
-    // This doesn't actually return a right answer for NaN on purpose,
-    // seeing as how it cannot correctly discern between a floating point NaN,
-    // and some normal floating point numbers truncated from an x87 FPU.
-    // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
-    // like the f64 version does, but I need to run more checks on how things go on x86.
-    // I fear losing mantissa data that would have answered that differently.
-    //
-    // # Safety
-    // This requires making sure you call this function for values it answers correctly on,
-    // otherwise it returns a wrong answer. This is not important for memory safety per se,
-    // but getting floats correct is important for not accidentally leaking const eval
-    // runtime-deviating logic which may or may not be acceptable.
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const unsafe fn partial_classify(self) -> FpCategory {
-        // SAFETY: The caller is not asking questions for which this will tell lies.
-        let b = unsafe { mem::transmute::<f32, u32>(self) };
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
-        }
-    }
-
-    // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
-    // FIXME(jubilee): In a just world, this would be the entire impl for classify,
-    // plus a transmute. We do not live in a just world, but we can make it more so.
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const fn classify_bits(b: u32) -> FpCategory {
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (_, Self::EXP_MASK) => FpCategory::Nan,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
+            // So, this does use bitpattern matching for the rest. On x87, due to the incorrect
+            // float codegen on this hardware, this doesn't actually return a right answer for NaN
+            // because it cannot correctly discern between a floating point NaN, and some normal
+            // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
+            // we are fine.
+            // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
+            // like the f64 version does, but I need to run more checks on how things go on x86.
+            // I fear losing mantissa data that would have answered that differently.
+            let b = self.to_bits();
+            match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+                (0, 0) => FpCategory::Zero,
+                (_, 0) => FpCategory::Subnormal,
+                _ => FpCategory::Normal,
+            }
         }
     }
 
@@ -1143,51 +1117,7 @@ pub unsafe fn to_int_unchecked<Int>(self) -> Int
     #[inline]
     pub const fn to_bits(self) -> u32 {
         // SAFETY: `u32` is a plain old datatype so we can always transmute to it.
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to a floating point mode that alters nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem per se, but at least one tier2 platform for Rust
-        // actually exhibits this behavior by default.
-        //
-        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
-        // i.e. not soft-float, the way Rust does parameter passing can actually alter
-        // a number that is "not infinity" to have the same exponent as infinity,
-        // in a slightly unpredictable manner.
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // we reject any of these possible situations from happening.
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_f32_to_u32(ct: f32) -> u32 {
-            match ct.classify() {
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f32::to_bits on a NaN")
-                }
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f32::to_bits on a subnormal number")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
-                    unsafe { mem::transmute::<f32, u32>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_f32_to_u32(x: f32) -> u32 {
-            // SAFETY: `u32` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32)
+        unsafe { mem::transmute(self) }
     }
 
     /// Raw transmutation from `u32`.
@@ -1232,53 +1162,8 @@ fn rt_f32_to_u32(x: f32) -> u32 {
     #[inline]
     pub const fn from_bits(v: u32) -> Self {
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        // SAFETY: `u32` is a plain old datatype so we can always transmute from it
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to floating point modes that alter nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem usually, but at least one tier2 platform for Rust
-        // actually exhibits this behavior by default: thumbv7neon
-        // aka "the Neon FPU in AArch32 state"
-        //
-        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
-        // i.e. not soft-float, the way Rust does parameter passing can actually alter
-        // a number that is "not infinity" to have the same exponent as infinity,
-        // in a slightly unpredictable manner.
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // reject any of these possible situations from happening.
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_u32_to_f32(ct: u32) -> f32 {
-            match f32::classify_bits(ct) {
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f32::from_bits on a subnormal number")
-                }
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f32::from_bits on NaN")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: It's not a frumious number
-                    unsafe { mem::transmute::<u32, f32>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_u32_to_f32(x: u32) -> f32 {
-            // SAFETY: `u32` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute(x) }
-        }
-        intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32)
+        // SAFETY: `u32` is a plain old datatype so we can always transmute from it.
+        unsafe { mem::transmute(v) }
     }
 
     /// Returns the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index a89859b..85eb152 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -528,7 +528,7 @@ pub const fn is_nan(self) -> bool {
     #[inline]
     #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
     pub(crate) const fn abs_private(self) -> f64 {
-        // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+        // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
         unsafe { mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & !Self::SIGN_MASK) }
     }
 
@@ -653,12 +653,14 @@ pub const fn is_normal(self) -> bool {
     pub const fn classify(self) -> FpCategory {
         // A previous implementation tried to only use bitmask-based checks,
         // using f64::to_bits to transmute the float to its bit repr and match on that.
-        // Unfortunately, floating point numbers can be much worse than that.
-        // This also needs to not result in recursive evaluations of f64::to_bits.
+        // If we only cared about being "technically" correct, that's an entirely legit
+        // implementation.
         //
-        // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
-        // in spite of a request for them using f32 and f64, to things like x87 operations.
-        // These have an f64's mantissa, but can have a larger than normal exponent.
+        // Unfortunately, there is hardware out there that does not correctly implement the IEEE
+        // float semantics Rust relies on: x87 uses a too-large exponent, and some hardware flushes
+        // subnormals to zero. These are platforms bugs, and Rust will misbehave on such hardware,
+        // but we can at least try to make things seem as sane as possible by being careful here.
+        //
         // FIXME(jubilee): Using x87 operations is never necessary in order to function
         // on x86 processors for Rust-to-Rust calls, so this issue should not happen.
         // Code generation should be adjusted to use non-C calling conventions, avoiding this.
@@ -672,41 +674,18 @@ pub const fn classify(self) -> FpCategory {
             // as correctness requires avoiding equality tests that may be Subnormal == -0.0
             // because it may be wrong under "denormals are zero" and "flush to zero" modes.
             // Most of std's targets don't use those, but they are used for thumbv7neon.
-            // So, this does use bitpattern matching for the rest.
-
-            // SAFETY: f64 to u64 is fine. Usually.
-            // If control flow has gotten this far, the value is definitely in one of the categories
-            // that f64::partial_classify can correctly analyze.
-            unsafe { f64::partial_classify(self) }
-        }
-    }
-
-    // This doesn't actually return a right answer for NaN on purpose,
-    // seeing as how it cannot correctly discern between a floating point NaN,
-    // and some normal floating point numbers truncated from an x87 FPU.
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const unsafe fn partial_classify(self) -> FpCategory {
-        // SAFETY: The caller is not asking questions for which this will tell lies.
-        let b = unsafe { mem::transmute::<f64, u64>(self) };
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
-        }
-    }
-
-    // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
-    // FIXME(jubilee): In a just world, this would be the entire impl for classify,
-    // plus a transmute. We do not live in a just world, but we can make it more so.
-    #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
-    const fn classify_bits(b: u64) -> FpCategory {
-        match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
-            (0, Self::EXP_MASK) => FpCategory::Infinite,
-            (_, Self::EXP_MASK) => FpCategory::Nan,
-            (0, 0) => FpCategory::Zero,
-            (_, 0) => FpCategory::Subnormal,
-            _ => FpCategory::Normal,
+            // So, this does use bitpattern matching for the rest. On x87, due to the incorrect
+            // float codegen on this hardware, this doesn't actually return a right answer for NaN
+            // because it cannot correctly discern between a floating point NaN, and some normal
+            // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
+            // we are fine.
+            let b = self.to_bits();
+            match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
+                (0, Self::EXP_MASK) => FpCategory::Infinite,
+                (0, 0) => FpCategory::Zero,
+                (_, 0) => FpCategory::Subnormal,
+                _ => FpCategory::Normal,
+            }
         }
     }
 
@@ -1134,33 +1113,7 @@ pub unsafe fn to_int_unchecked<Int>(self) -> Int
     #[inline]
     pub const fn to_bits(self) -> u64 {
         // SAFETY: `u64` is a plain old datatype so we can always transmute to it.
-        // ...sorta.
-        //
-        // See the SAFETY comment in f64::from_bits for more.
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_f64_to_u64(ct: f64) -> u64 {
-            match ct.classify() {
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f64::to_bits on a NaN")
-                }
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f64::to_bits on a subnormal number")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
-                    unsafe { mem::transmute::<f64, u64>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_f64_to_u64(rt: f64) -> u64 {
-            // SAFETY: `u64` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute::<f64, u64>(rt) }
-        }
-        intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64)
+        unsafe { mem::transmute(self) }
     }
 
     /// Raw transmutation from `u64`.
@@ -1205,58 +1158,8 @@ fn rt_f64_to_u64(rt: f64) -> u64 {
     #[inline]
     pub const fn from_bits(v: u64) -> Self {
         // It turns out the safety issues with sNaN were overblown! Hooray!
-        // SAFETY: `u64` is a plain old datatype so we can always transmute from it
-        // ...sorta.
-        //
-        // It turns out that at runtime, it is possible for a floating point number
-        // to be subject to floating point modes that alter nonzero subnormal numbers
-        // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
-        // This is not a problem usually, but at least one tier2 platform for Rust
-        // actually exhibits an FTZ behavior by default: thumbv7neon
-        // aka "the Neon FPU in AArch32 state"
-        //
-        // Even with this, not all instructions exhibit the FTZ behaviors on thumbv7neon,
-        // so this should load the same bits if LLVM emits the "correct" instructions,
-        // but LLVM sometimes makes interesting choices about float optimization,
-        // and other FPUs may do similar. Thus, it is wise to indulge luxuriously in caution.
-        //
-        // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
-        // i.e. not soft-float, the way Rust does parameter passing can actually alter
-        // a number that is "not infinity" to have the same exponent as infinity,
-        // in a slightly unpredictable manner.
-        //
-        // And, of course evaluating to a NaN value is fairly nondeterministic.
-        // More precisely: when NaN should be returned is knowable, but which NaN?
-        // So far that's defined by a combination of LLVM and the CPU, not Rust.
-        // This function, however, allows observing the bitstring of a NaN,
-        // thus introspection on CTFE.
-        //
-        // In order to preserve, at least for the moment, const-to-runtime equivalence,
-        // reject any of these possible situations from happening.
-        #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
-        const fn ct_u64_to_f64(ct: u64) -> f64 {
-            match f64::classify_bits(ct) {
-                FpCategory::Subnormal => {
-                    panic!("const-eval error: cannot use f64::from_bits on a subnormal number")
-                }
-                FpCategory::Nan => {
-                    panic!("const-eval error: cannot use f64::from_bits on NaN")
-                }
-                FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
-                    // SAFETY: It's not a frumious number
-                    unsafe { mem::transmute::<u64, f64>(ct) }
-                }
-            }
-        }
-
-        #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
-        fn rt_u64_to_f64(rt: u64) -> f64 {
-            // SAFETY: `u64` is a plain old datatype so we can always... uh...
-            // ...look, just pretend you forgot what you just read.
-            // Stability concerns.
-            unsafe { mem::transmute::<u64, f64>(rt) }
-        }
-        intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64)
+        // SAFETY: `u64` is a plain old datatype so we can always transmute from it.
+        unsafe { mem::transmute(v) }
     }
 
     /// Returns the memory representation of this floating point number as a byte array in
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index 9cec79c..50cb22b7 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -656,8 +656,6 @@ pub const fn is_none(&self) -> bool {
     /// # Examples
     ///
     /// ```
-    /// #![feature(is_none_or)]
-    ///
     /// let x: Option<u32> = Some(2);
     /// assert_eq!(x.is_none_or(|x| x > 1), true);
     ///
@@ -669,7 +667,7 @@ pub const fn is_none(&self) -> bool {
     /// ```
     #[must_use]
     #[inline]
-    #[unstable(feature = "is_none_or", issue = "126383")]
+    #[stable(feature = "is_none_or", since = "CURRENT_RUSTC_VERSION")]
     pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool {
         match self {
             None => true,
diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs
index 29932c0..7e5c157 100644
--- a/library/core/src/task/wake.rs
+++ b/library/core/src/task/wake.rs
@@ -530,10 +530,18 @@ pub fn will_wake(&self, other: &Waker) -> bool {
 
     /// Returns a reference to a `Waker` that does nothing when used.
     ///
+    // Note!  Much of the documentation for this method is duplicated
+    // in the docs for `LocalWaker::noop`.
+    // If you edit it, consider editing the other copy too.
+    //
     /// This is mostly useful for writing tests that need a [`Context`] to poll
     /// some futures, but are not expecting those futures to wake the waker or
     /// do not need to do anything specific if it happens.
     ///
+    /// More generally, using `Waker::noop()` to poll a future
+    /// means discarding the notification of when the future should be polled again.
+    /// So it should only be used when such a notification will not be needed to make progress.
+    ///
     /// If an owned `Waker` is needed, `clone()` this one.
     ///
     /// # Examples
@@ -783,12 +791,22 @@ pub fn will_wake(&self, other: &LocalWaker) -> bool {
         Self { waker }
     }
 
-    /// Creates a new `LocalWaker` that does nothing when `wake` is called.
+    /// Returns a reference to a `LocalWaker` that does nothing when used.
     ///
+    // Note!  Much of the documentation for this method is duplicated
+    // in the docs for `Waker::noop`.
+    // If you edit it, consider editing the other copy too.
+    //
     /// This is mostly useful for writing tests that need a [`Context`] to poll
     /// some futures, but are not expecting those futures to wake the waker or
     /// do not need to do anything specific if it happens.
     ///
+    /// More generally, using `LocalWaker::noop()` to poll a future
+    /// means discarding the notification of when the future should be polled again,
+    /// So it should only be used when such a notification will not be needed to make progress.
+    ///
+    /// If an owned `LocalWaker` is needed, `clone()` this one.
+    ///
     /// # Examples
     ///
     /// ```
diff --git a/library/core/tests/clone.rs b/library/core/tests/clone.rs
index b7130f1..71a3287 100644
--- a/library/core/tests/clone.rs
+++ b/library/core/tests/clone.rs
@@ -1,5 +1,7 @@
 use core::clone::CloneToUninit;
+use core::ffi::CStr;
 use core::mem::MaybeUninit;
+use core::ptr;
 
 #[test]
 #[allow(suspicious_double_ref_op)]
@@ -81,3 +83,41 @@ fn drop(&mut self) {
     drop(a);
     assert_eq!(COUNTER.load(Relaxed), 0);
 }
+
+#[test]
+fn test_clone_to_uninit_str() {
+    let a = "hello";
+
+    let mut storage: MaybeUninit<[u8; 5]> = MaybeUninit::uninit();
+    unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut str) };
+    assert_eq!(a.as_bytes(), unsafe { storage.assume_init() }.as_slice());
+
+    let mut b: Box<str> = "world".into();
+    assert_eq!(a.len(), b.len());
+    assert_ne!(a, &*b);
+    unsafe { a.clone_to_uninit(ptr::from_mut::<str>(&mut b)) };
+    assert_eq!(a, &*b);
+}
+
+#[test]
+fn test_clone_to_uninit_cstr() {
+    let a = c"hello";
+
+    let mut storage: MaybeUninit<[u8; 6]> = MaybeUninit::uninit();
+    unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut CStr) };
+    assert_eq!(a.to_bytes_with_nul(), unsafe { storage.assume_init() }.as_slice());
+
+    let mut b: Box<CStr> = c"world".into();
+    assert_eq!(a.count_bytes(), b.count_bytes());
+    assert_ne!(a, &*b);
+    unsafe { a.clone_to_uninit(ptr::from_mut::<CStr>(&mut b)) };
+    assert_eq!(a, &*b);
+}
+
+#[test]
+fn cstr_metadata_is_length_with_nul() {
+    let s: &CStr = c"abcdef";
+    let p: *const CStr = ptr::from_ref(s);
+    let bytes: *const [u8] = p as *const [u8];
+    assert_eq!(s.to_bytes_with_nul().len(), bytes.len());
+}
diff --git a/library/core/tests/iter/adapters/take.rs b/library/core/tests/iter/adapters/take.rs
index 39afa2c..65a8a93 100644
--- a/library/core/tests/iter/adapters/take.rs
+++ b/library/core/tests/iter/adapters/take.rs
@@ -170,3 +170,93 @@ fn test_byref_take_consumed_items() {
     assert_eq!(count, 70);
     assert_eq!(inner, 90..90);
 }
+
+#[test]
+fn test_exact_size_take_repeat() {
+    let mut iter = core::iter::repeat(42).take(40);
+    assert_eq!((40, Some(40)), iter.size_hint());
+    assert_eq!(40, iter.len());
+
+    assert_eq!(Some(42), iter.next());
+    assert_eq!((39, Some(39)), iter.size_hint());
+    assert_eq!(39, iter.len());
+
+    assert_eq!(Some(42), iter.next_back());
+    assert_eq!((38, Some(38)), iter.size_hint());
+    assert_eq!(38, iter.len());
+
+    assert_eq!(Some(42), iter.nth(3));
+    assert_eq!((34, Some(34)), iter.size_hint());
+    assert_eq!(34, iter.len());
+
+    assert_eq!(Some(42), iter.nth_back(3));
+    assert_eq!((30, Some(30)), iter.size_hint());
+    assert_eq!(30, iter.len());
+
+    assert_eq!(Ok(()), iter.advance_by(10));
+    assert_eq!((20, Some(20)), iter.size_hint());
+    assert_eq!(20, iter.len());
+
+    assert_eq!(Ok(()), iter.advance_back_by(10));
+    assert_eq!((10, Some(10)), iter.size_hint());
+    assert_eq!(10, iter.len());
+}
+
+#[test]
+fn test_exact_size_take_repeat_with() {
+    let mut counter = 0;
+    let mut iter = core::iter::repeat_with(move || {
+        counter += 1;
+        counter
+    })
+    .take(40);
+    assert_eq!((40, Some(40)), iter.size_hint());
+    assert_eq!(40, iter.len());
+
+    assert_eq!(Some(1), iter.next());
+    assert_eq!((39, Some(39)), iter.size_hint());
+    assert_eq!(39, iter.len());
+
+    assert_eq!(Some(5), iter.nth(3));
+    assert_eq!((35, Some(35)), iter.size_hint());
+    assert_eq!(35, iter.len());
+
+    assert_eq!(Ok(()), iter.advance_by(10));
+    assert_eq!((25, Some(25)), iter.size_hint());
+    assert_eq!(25, iter.len());
+
+    assert_eq!(Some(16), iter.next());
+    assert_eq!((24, Some(24)), iter.size_hint());
+    assert_eq!(24, iter.len());
+}
+
+// This is https://github.com/rust-lang/rust/issues/104729 with all uses of
+// repeat(0) were replaced by repeat(0).take(20).
+#[test]
+fn test_reverse_on_zip() {
+    let vec_1 = [1; 10];
+
+    let zipped_iter = vec_1.iter().copied().zip(core::iter::repeat(0).take(20));
+
+    // Forward
+    for (one, zero) in zipped_iter {
+        assert_eq!((1, 0), (one, zero));
+    }
+
+    let rev_vec_iter = vec_1.iter().rev();
+    let rev_repeat_iter = std::iter::repeat(0).take(20).rev();
+
+    // Manual reversed zip
+    let rev_zipped_iter = rev_vec_iter.zip(rev_repeat_iter);
+
+    for (&one, zero) in rev_zipped_iter {
+        assert_eq!((1, 0), (one, zero));
+    }
+
+    let zipped_iter = vec_1.iter().zip(core::iter::repeat(0).take(20));
+
+    // Cannot call rev here for automatic reversed zip constuction
+    for (&one, zero) in zipped_iter.rev() {
+        assert_eq!((1, 0), (one, zero));
+    }
+}
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index a501bcc..918eec2 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -3,10 +3,13 @@
 #[cfg(test)]
 mod tests;
 
+use core::clone::CloneToUninit;
+
 use crate::borrow::{Borrow, Cow};
 use crate::collections::TryReserveError;
 use crate::hash::{Hash, Hasher};
 use crate::ops::{self, Range};
+use crate::ptr::addr_of_mut;
 use crate::rc::Rc;
 use crate::str::FromStr;
 use crate::sync::Arc;
@@ -1261,6 +1264,16 @@ fn clone(&self) -> Self {
     }
 }
 
+#[unstable(feature = "clone_to_uninit", issue = "126799")]
+unsafe impl CloneToUninit for OsStr {
+    #[inline]
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: we're just a wrapper around a platform-specific Slice
+        unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) }
+    }
+}
+
 #[stable(feature = "shared_from_slice2", since = "1.24.0")]
 impl From<OsString> for Arc<OsStr> {
     /// Converts an [`OsString`] into an <code>[Arc]<[OsStr]></code> by moving the [`OsString`]
diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs
index 5b39b9e..6714793 100644
--- a/library/std/src/ffi/os_str/tests.rs
+++ b/library/std/src/ffi/os_str/tests.rs
@@ -1,4 +1,6 @@
 use super::*;
+use crate::mem::MaybeUninit;
+use crate::ptr;
 
 #[test]
 fn test_os_string_with_capacity() {
@@ -286,3 +288,18 @@ fn slice_surrogate_edge() {
     assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀");
     assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate);
 }
+
+#[test]
+fn clone_to_uninit() {
+    let a = OsStr::new("hello.txt");
+
+    let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)];
+    unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()) as *mut OsStr) };
+    assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) });
+
+    let mut b: Box<OsStr> = OsStr::new("world.exe").into();
+    assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b));
+    assert_ne!(a, &*b);
+    unsafe { a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b)) };
+    assert_eq!(a, &*b);
+}
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 93a74ef..2530a37 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -319,6 +319,7 @@
 // tidy-alphabetical-start
 #![feature(c_str_module)]
 #![feature(char_internals)]
+#![feature(clone_to_uninit)]
 #![feature(core_intrinsics)]
 #![feature(core_io_borrowed_buf)]
 #![feature(duration_constants)]
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index c534236..4620244 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -109,13 +109,21 @@ unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
     /// Schedules a closure to be run just before the `exec` function is
     /// invoked.
     ///
-    /// This method is stable and usable, but it should be unsafe. To fix
-    /// that, it got deprecated in favor of the unsafe [`pre_exec`].
+    /// `before_exec` used to be a safe method, but it needs to be unsafe since the closure may only
+    /// perform operations that are *async-signal-safe*. Hence it got deprecated in favor of the
+    /// unsafe [`pre_exec`]. Meanwhile, Rust gained the ability to make an existing safe method
+    /// fully unsafe in a new edition, which is how `before_exec` became `unsafe`. It still also
+    /// remains deprecated; `pre_exec` should be used instead.
     ///
     /// [`pre_exec`]: CommandExt::pre_exec
     #[stable(feature = "process_exec", since = "1.15.0")]
     #[deprecated(since = "1.37.0", note = "should be unsafe, use `pre_exec` instead")]
-    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+    #[cfg_attr(bootstrap, rustc_deprecated_safe_2024)]
+    #[cfg_attr(
+        not(bootstrap),
+        rustc_deprecated_safe_2024(audit_that = "the closure is async-signal-safe")
+    )]
+    unsafe fn before_exec<F>(&mut self, f: F) -> &mut process::Command
     where
         F: FnMut() -> io::Result<()> + Send + Sync + 'static,
     {
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 8016366..9eaa0e0 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -70,6 +70,8 @@
 #[cfg(test)]
 mod tests;
 
+use core::clone::CloneToUninit;
+
 use crate::borrow::{Borrow, Cow};
 use crate::collections::TryReserveError;
 use crate::error::Error;
@@ -3109,6 +3111,16 @@ pub fn into_path_buf(self: Box<Path>) -> PathBuf {
     }
 }
 
+#[unstable(feature = "clone_to_uninit", issue = "126799")]
+unsafe impl CloneToUninit for Path {
+    #[inline]
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: Path is just a wrapper around OsStr
+        unsafe { self.inner.clone_to_uninit(core::ptr::addr_of_mut!((*dst).inner)) }
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl AsRef<OsStr> for Path {
     #[inline]
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index a12e42c..6436872 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -3,6 +3,8 @@
 use super::*;
 use crate::collections::{BTreeSet, HashSet};
 use crate::hash::DefaultHasher;
+use crate::mem::MaybeUninit;
+use crate::ptr;
 
 #[allow(unknown_lints, unused_macro_rules)]
 macro_rules! t (
@@ -2054,3 +2056,20 @@ fn bench_hash_path_long(b: &mut test::Bencher) {
 
     black_box(hasher.finish());
 }
+
+#[test]
+fn clone_to_uninit() {
+    let a = Path::new("hello.txt");
+
+    let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<Path>(a)];
+    unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()) as *mut Path) };
+    assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe {
+        MaybeUninit::slice_assume_init_ref(&storage)
+    });
+
+    let mut b: Box<Path> = Path::new("world.exe").into();
+    assert_eq!(size_of_val::<Path>(a), size_of_val::<Path>(&b));
+    assert_ne!(a, &*b);
+    unsafe { a.clone_to_uninit(ptr::from_mut::<Path>(&mut b)) };
+    assert_eq!(a, &*b);
+}
diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs
index 0f8bd64..9927672 100644
--- a/library/std/src/sys/os_str/bytes.rs
+++ b/library/std/src/sys/os_str/bytes.rs
@@ -1,6 +1,9 @@
 //! The underlying OsString/OsStr implementation on Unix and many other
 //! systems: just a `Vec<u8>`/`[u8]`.
 
+use core::clone::CloneToUninit;
+use core::ptr::addr_of_mut;
+
 use crate::borrow::Cow;
 use crate::collections::TryReserveError;
 use crate::fmt::Write;
@@ -345,3 +348,13 @@ pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
         self.inner.eq_ignore_ascii_case(&other.inner)
     }
 }
+
+#[unstable(feature = "clone_to_uninit", issue = "126799")]
+unsafe impl CloneToUninit for Slice {
+    #[inline]
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: we're just a wrapper around [u8]
+        unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) }
+    }
+}
diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs
index ed975ba..433237a 100644
--- a/library/std/src/sys/os_str/wtf8.rs
+++ b/library/std/src/sys/os_str/wtf8.rs
@@ -1,5 +1,8 @@
 //! The underlying OsString/OsStr implementation on Windows is a
 //! wrapper around the "WTF-8" encoding; see the `wtf8` module for more.
+use core::clone::CloneToUninit;
+use core::ptr::addr_of_mut;
+
 use crate::borrow::Cow;
 use crate::collections::TryReserveError;
 use crate::rc::Rc;
@@ -268,3 +271,13 @@ pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
         self.inner.eq_ignore_ascii_case(&other.inner)
     }
 }
+
+#[unstable(feature = "clone_to_uninit", issue = "126799")]
+unsafe impl CloneToUninit for Slice {
+    #[inline]
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: we're just a wrapper around Wtf8
+        unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) }
+    }
+}
diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs
index 277c950..063451a 100644
--- a/library/std/src/sys_common/wtf8.rs
+++ b/library/std/src/sys_common/wtf8.rs
@@ -19,12 +19,14 @@
 mod tests;
 
 use core::char::{encode_utf16_raw, encode_utf8_raw};
+use core::clone::CloneToUninit;
 use core::str::next_code_point;
 
 use crate::borrow::Cow;
 use crate::collections::TryReserveError;
 use crate::hash::{Hash, Hasher};
 use crate::iter::FusedIterator;
+use crate::ptr::addr_of_mut;
 use crate::rc::Rc;
 use crate::sync::Arc;
 use crate::sys_common::AsInner;
@@ -1046,3 +1048,13 @@ fn hash<H: Hasher>(&self, state: &mut H) {
         0xfeu8.hash(state)
     }
 }
+
+#[unstable(feature = "clone_to_uninit", issue = "126799")]
+unsafe impl CloneToUninit for Wtf8 {
+    #[inline]
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: we're just a wrapper around [u8]
+        unsafe { self.bytes.clone_to_uninit(addr_of_mut!((*dst).bytes)) }
+    }
+}
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 88b31cd..e29c28f 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -412,7 +412,6 @@ pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
     /// # Examples
     ///
     /// ```
-    /// #![feature(thread_spawn_unchecked)]
     /// use std::thread;
     ///
     /// let builder = thread::Builder::new();
@@ -433,7 +432,7 @@ pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
     /// ```
     ///
     /// [`io::Result`]: crate::io::Result
-    #[unstable(feature = "thread_spawn_unchecked", issue = "55132")]
+    #[stable(feature = "thread_spawn_unchecked", since = "CURRENT_RUSTC_VERSION")]
     pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
     where
         F: FnOnce() -> T,
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 36de832..bdd9fd7 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -36,6 +36,14 @@ macro_rules! check_ci_llvm {
     };
 }
 
+/// This file is embedded in the overlay directory of the tarball sources. It is
+/// useful in scenarios where developers want to see how the tarball sources were
+/// generated.
+///
+/// We also use this file to compare the host's config.toml against the CI rustc builder
+/// configuration to detect any incompatible options.
+pub(crate) const BUILDER_CONFIG_FILENAME: &str = "builder-config";
+
 #[derive(Clone, Default)]
 pub enum DryRun {
     /// This isn't a dry run.
@@ -47,7 +55,7 @@ pub enum DryRun {
     UserSelected,
 }
 
-#[derive(Copy, Clone, Default, PartialEq, Eq)]
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
 pub enum DebuginfoLevel {
     #[default]
     None,
@@ -117,7 +125,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 /// 2) MSVC
 /// - Self-contained: `-Clinker=<path to rust-lld>`
 /// - External: `-Clinker=lld`
-#[derive(Default, Copy, Clone)]
+#[derive(Copy, Clone, Default, Debug, PartialEq)]
 pub enum LldMode {
     /// Do not use LLD
     #[default]
@@ -1203,40 +1211,42 @@ pub fn default_opts() -> Config {
         }
     }
 
-    pub fn parse(flags: Flags) -> Config {
-        #[cfg(test)]
-        fn get_toml(_: &Path) -> TomlConfig {
-            TomlConfig::default()
-        }
-
-        #[cfg(not(test))]
-        fn get_toml(file: &Path) -> TomlConfig {
-            let contents =
-                t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
-            // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of
-            // TomlConfig and sub types to be monomorphized 5x by toml.
-            toml::from_str(&contents)
-                .and_then(|table: toml::Value| TomlConfig::deserialize(table))
-                .unwrap_or_else(|err| {
-                    if let Ok(Some(changes)) = toml::from_str(&contents)
-                        .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table)).map(|change_id| change_id.inner.map(crate::find_recent_config_change_ids))
-                    {
-                        if !changes.is_empty() {
-                            println!(
-                                "WARNING: There have been changes to x.py since you last updated:\n{}",
-                                crate::human_readable_changes(&changes)
-                            );
-                        }
-                    }
-
-                    eprintln!("failed to parse TOML configuration '{}': {err}", file.display());
-                    exit!(2);
-                })
-        }
-        Self::parse_inner(flags, get_toml)
+    #[cfg(test)]
+    fn get_toml(_: &Path) -> Result<TomlConfig, toml::de::Error> {
+        Ok(TomlConfig::default())
     }
 
-    pub(crate) fn parse_inner(mut flags: Flags, get_toml: impl Fn(&Path) -> TomlConfig) -> Config {
+    #[cfg(not(test))]
+    fn get_toml(file: &Path) -> Result<TomlConfig, toml::de::Error> {
+        let contents =
+            t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
+        // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of
+        // TomlConfig and sub types to be monomorphized 5x by toml.
+        toml::from_str(&contents)
+            .and_then(|table: toml::Value| TomlConfig::deserialize(table))
+            .inspect_err(|_| {
+                if let Ok(Some(changes)) = toml::from_str(&contents)
+                    .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table))
+                    .map(|change_id| change_id.inner.map(crate::find_recent_config_change_ids))
+                {
+                    if !changes.is_empty() {
+                        println!(
+                            "WARNING: There have been changes to x.py since you last updated:\n{}",
+                            crate::human_readable_changes(&changes)
+                        );
+                    }
+                }
+            })
+    }
+
+    pub fn parse(flags: Flags) -> Config {
+        Self::parse_inner(flags, Self::get_toml)
+    }
+
+    pub(crate) fn parse_inner(
+        mut flags: Flags,
+        get_toml: impl Fn(&Path) -> Result<TomlConfig, toml::de::Error>,
+    ) -> Config {
         let mut config = Config::default_opts();
 
         // Set flags.
@@ -1344,7 +1354,10 @@ pub(crate) fn parse_inner(mut flags: Flags, get_toml: impl Fn(&Path) -> TomlConf
             } else {
                 toml_path.clone()
             });
-            get_toml(&toml_path)
+            get_toml(&toml_path).unwrap_or_else(|e| {
+                eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display());
+                exit!(2);
+            })
         } else {
             config.config = None;
             TomlConfig::default()
@@ -1375,7 +1388,13 @@ pub(crate) fn parse_inner(mut flags: Flags, get_toml: impl Fn(&Path) -> TomlConf
             include_path.push("bootstrap");
             include_path.push("defaults");
             include_path.push(format!("config.{include}.toml"));
-            let included_toml = get_toml(&include_path);
+            let included_toml = get_toml(&include_path).unwrap_or_else(|e| {
+                eprintln!(
+                    "ERROR: Failed to parse default config profile at '{}': {e}",
+                    include_path.display()
+                );
+                exit!(2);
+            });
             toml.merge(included_toml, ReplaceOpt::IgnoreDuplicate);
         }
 
@@ -1591,24 +1610,6 @@ fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
         let mut is_user_configured_rust_channel = false;
 
         if let Some(rust) = toml.rust {
-            if let Some(commit) = config.download_ci_rustc_commit(rust.download_rustc.clone()) {
-                // Primarily used by CI runners to avoid handling download-rustc incompatible
-                // options one by one on shell scripts.
-                let disable_ci_rustc_if_incompatible =
-                    env::var_os("DISABLE_CI_RUSTC_IF_INCOMPATIBLE")
-                        .is_some_and(|s| s == "1" || s == "true");
-
-                if let Err(e) = check_incompatible_options_for_ci_rustc(&rust) {
-                    if disable_ci_rustc_if_incompatible {
-                        config.download_rustc_commit = None;
-                    } else {
-                        panic!("{}", e);
-                    }
-                } else {
-                    config.download_rustc_commit = Some(commit);
-                }
-            }
-
             let Rust {
                 optimize: optimize_toml,
                 debug: debug_toml,
@@ -1656,7 +1657,7 @@ fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
                 new_symbol_mangling,
                 profile_generate,
                 profile_use,
-                download_rustc: _,
+                download_rustc,
                 lto,
                 validate_mir_opts,
                 frame_pointers,
@@ -1668,6 +1669,8 @@ fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
             is_user_configured_rust_channel = channel.is_some();
             set(&mut config.channel, channel.clone());
 
+            config.download_rustc_commit = config.download_ci_rustc_commit(download_rustc);
+
             debug = debug_toml;
             debug_assertions = debug_assertions_toml;
             debug_assertions_std = debug_assertions_std_toml;
@@ -2345,6 +2348,45 @@ pub(crate) fn download_rustc_commit(&self) -> Option<&str> {
                 None => None,
                 Some(commit) => {
                     self.download_ci_rustc(commit);
+
+                    if let Some(config_path) = &self.config {
+                        let builder_config_path =
+                            self.out.join(self.build.triple).join("ci-rustc").join(BUILDER_CONFIG_FILENAME);
+
+                        let ci_config_toml = match Self::get_toml(&builder_config_path) {
+                            Ok(ci_config_toml) => ci_config_toml,
+                            Err(e) if e.to_string().contains("unknown field") => {
+                                println!("WARNING: CI rustc has some fields that are no longer supported in bootstrap; download-rustc will be disabled.");
+                                println!("HELP: Consider rebasing to a newer commit if available.");
+                                return None;
+                            },
+                            Err(e) => {
+                                eprintln!("ERROR: Failed to parse CI rustc config at '{}': {e}", builder_config_path.display());
+                                exit!(2);
+                            },
+                        };
+
+                        let current_config_toml = Self::get_toml(config_path).unwrap();
+
+                        // Check the config compatibility
+                        // FIXME: this doesn't cover `--set` flags yet.
+                        let res = check_incompatible_options_for_ci_rustc(
+                            current_config_toml,
+                            ci_config_toml,
+                        );
+
+                        let disable_ci_rustc_if_incompatible =
+                            env::var_os("DISABLE_CI_RUSTC_IF_INCOMPATIBLE")
+                            .is_some_and(|s| s == "1" || s == "true");
+
+                        if disable_ci_rustc_if_incompatible && res.is_err() {
+                            println!("WARNING: download-rustc is disabled with `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` env.");
+                            return None;
+                        }
+
+                        res.unwrap();
+                    }
+
                     Some(commit.clone())
                 }
             })
@@ -2662,31 +2704,52 @@ pub fn last_modified_commit(
     }
 }
 
-/// Checks the CI rustc incompatible options by destructuring the `Rust` instance
-/// and makes sure that no rust options from config.toml are missed.
-fn check_incompatible_options_for_ci_rustc(rust: &Rust) -> Result<(), String> {
+/// Compares the current Rust options against those in the CI rustc builder and detects any incompatible options.
+/// It does this by destructuring the `Rust` instance to make sure every `Rust` field is covered and not missing.
+fn check_incompatible_options_for_ci_rustc(
+    current_config_toml: TomlConfig,
+    ci_config_toml: TomlConfig,
+) -> Result<(), String> {
     macro_rules! err {
-        ($name:expr) => {
-            if $name.is_some() {
-                return Err(format!(
-                    "ERROR: Setting `rust.{}` is incompatible with `rust.download-rustc`.",
-                    stringify!($name).replace("_", "-")
-                ));
-            }
+        ($current:expr, $expected:expr) => {
+            if let Some(current) = &$current {
+                if Some(current) != $expected.as_ref() {
+                    return Err(format!(
+                        "ERROR: Setting `rust.{}` is incompatible with `rust.download-rustc`. \
+                        Current value: {:?}, Expected value(s): {}{:?}",
+                        stringify!($expected).replace("_", "-"),
+                        $current,
+                        if $expected.is_some() { "None/" } else { "" },
+                        $expected,
+                    ));
+                };
+            };
         };
     }
 
     macro_rules! warn {
-        ($name:expr) => {
-            if $name.is_some() {
-                println!(
-                    "WARNING: `rust.{}` has no effect with `rust.download-rustc`.",
-                    stringify!($name).replace("_", "-")
-                );
-            }
+        ($current:expr, $expected:expr) => {
+            if let Some(current) = &$current {
+                if Some(current) != $expected.as_ref() {
+                    println!(
+                        "WARNING: `rust.{}` has no effect with `rust.download-rustc`. \
+                        Current value: {:?}, Expected value(s): {}{:?}",
+                        stringify!($expected).replace("_", "-"),
+                        $current,
+                        if $expected.is_some() { "None/" } else { "" },
+                        $expected,
+                    );
+                };
+            };
         };
     }
 
+    let (Some(current_rust_config), Some(ci_rust_config)) =
+        (current_config_toml.rust, ci_config_toml.rust)
+    else {
+        return Ok(());
+    };
+
     let Rust {
         // Following options are the CI rustc incompatible ones.
         optimize,
@@ -2744,7 +2807,7 @@ macro_rules! warn {
         download_rustc: _,
         validate_mir_opts: _,
         frame_pointers: _,
-    } = rust;
+    } = ci_rust_config;
 
     // There are two kinds of checks for CI rustc incompatible options:
     //    1. Checking an option that may change the compiler behaviour/output.
@@ -2752,22 +2815,23 @@ macro_rules! warn {
     //
     // If the option belongs to the first category, we call `err` macro for a hard error;
     // otherwise, we just print a warning with `warn` macro.
-    err!(optimize);
-    err!(debug_logging);
-    err!(debuginfo_level_rustc);
-    err!(default_linker);
-    err!(rpath);
-    err!(strip);
-    err!(stack_protector);
-    err!(lld_mode);
-    err!(llvm_tools);
-    err!(llvm_bitcode_linker);
-    err!(jemalloc);
-    err!(lto);
 
-    warn!(channel);
-    warn!(description);
-    warn!(incremental);
+    err!(current_rust_config.optimize, optimize);
+    err!(current_rust_config.debug_logging, debug_logging);
+    err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc);
+    err!(current_rust_config.rpath, rpath);
+    err!(current_rust_config.strip, strip);
+    err!(current_rust_config.lld_mode, lld_mode);
+    err!(current_rust_config.llvm_tools, llvm_tools);
+    err!(current_rust_config.llvm_bitcode_linker, llvm_bitcode_linker);
+    err!(current_rust_config.jemalloc, jemalloc);
+    err!(current_rust_config.default_linker, default_linker);
+    err!(current_rust_config.stack_protector, stack_protector);
+    err!(current_rust_config.lto, lto);
+
+    warn!(current_rust_config.channel, channel);
+    warn!(current_rust_config.description, description);
+    warn!(current_rust_config.incremental, incremental);
 
     Ok(())
 }
diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs
index 40f3e5e..378d069 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -14,7 +14,7 @@
 fn parse(config: &str) -> Config {
     Config::parse_inner(
         Flags::parse(&["check".to_string(), "--config=/does/not/exist".to_string()]),
-        |&_| toml::from_str(&config).unwrap(),
+        |&_| toml::from_str(&config),
     )
 }
 
@@ -151,7 +151,6 @@ fn override_toml() {
 
                 "#,
             )
-            .unwrap()
         },
     );
     assert_eq!(config.change_id, Some(1), "setting top-level value");
@@ -208,13 +207,13 @@ fn override_toml_duplicate() {
             "--set=change-id=1".to_owned(),
             "--set=change-id=2".to_owned(),
         ]),
-        |&_| toml::from_str("change-id = 0").unwrap(),
+        |&_| toml::from_str("change-id = 0"),
     );
 }
 
 #[test]
 fn profile_user_dist() {
-    fn get_toml(file: &Path) -> TomlConfig {
+    fn get_toml(file: &Path) -> Result<TomlConfig, toml::de::Error> {
         let contents =
             if file.ends_with("config.toml") || env::var_os("RUST_BOOTSTRAP_CONFIG").is_some() {
                 "profile = \"user\"".to_owned()
@@ -223,9 +222,7 @@ fn get_toml(file: &Path) -> TomlConfig {
                 std::fs::read_to_string(file).unwrap()
             };
 
-        toml::from_str(&contents)
-            .and_then(|table: toml::Value| TomlConfig::deserialize(table))
-            .unwrap()
+        toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table))
     }
     Config::parse_inner(Flags::parse(&["check".to_owned()]), get_toml);
 }
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index fd85650..ae39afa 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -9,6 +9,7 @@
 use build_helper::ci::CiEnv;
 use xz2::bufread::XzDecoder;
 
+use crate::core::config::BUILDER_CONFIG_FILENAME;
 use crate::utils::exec::{command, BootstrapCommand};
 use crate::utils::helpers::{check_run, exe, hex_encode, move_file, program_out_of_date};
 use crate::{t, Config};
@@ -273,11 +274,12 @@ fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) {
 
         let mut tar = tar::Archive::new(decompressor);
 
+        let is_ci_rustc = dst.ends_with("ci-rustc");
+
         // `compile::Sysroot` needs to know the contents of the `rustc-dev` tarball to avoid adding
         // it to the sysroot unless it was explicitly requested. But parsing the 100 MB tarball is slow.
         // Cache the entries when we extract it so we only have to read it once.
-        let mut recorded_entries =
-            if dst.ends_with("ci-rustc") { recorded_entries(dst, pattern) } else { None };
+        let mut recorded_entries = if is_ci_rustc { recorded_entries(dst, pattern) } else { None };
 
         for member in t!(tar.entries()) {
             let mut member = t!(member);
@@ -287,10 +289,12 @@ fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) {
                 continue;
             }
             let mut short_path = t!(original_path.strip_prefix(directory_prefix));
-            if !short_path.starts_with(pattern) {
+            let is_builder_config = short_path.to_str() == Some(BUILDER_CONFIG_FILENAME);
+
+            if !(short_path.starts_with(pattern) || (is_ci_rustc && is_builder_config)) {
                 continue;
             }
-            short_path = t!(short_path.strip_prefix(pattern));
+            short_path = short_path.strip_prefix(pattern).unwrap_or(short_path);
             let dst_path = dst.join(short_path);
             self.verbose(|| {
                 println!("extracting {} to {}", original_path.display(), dst.display())
@@ -703,9 +707,7 @@ pub(crate) fn maybe_download_ci_llvm(&self) {
             let file_times = fs::FileTimes::new().set_accessed(now).set_modified(now);
 
             let llvm_config = llvm_root.join("bin").join(exe("llvm-config", self.build));
-            let llvm_config_file = t!(File::options().write(true).open(llvm_config));
-
-            t!(llvm_config_file.set_times(file_times));
+            t!(crate::utils::helpers::set_file_times(llvm_config, file_times));
 
             if self.should_fix_bins_and_dylibs() {
                 let llvm_lib = llvm_root.join("lib");
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index bfd0e42..784519a 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -37,7 +37,9 @@
 use crate::core::builder::{Builder, Kind};
 use crate::core::config::{flags, DryRun, LldMode, LlvmLibunwind, Target, TargetSelection};
 use crate::utils::exec::{command, BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode};
-use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir};
+use crate::utils::helpers::{
+    self, dir_is_empty, exe, libdir, mtime, output, set_file_times, symlink_dir,
+};
 
 mod core;
 mod utils;
@@ -1056,11 +1058,29 @@ fn run(
             }
         };
 
-        let fail = |message: &str| {
+        let fail = |message: &str, output: CommandOutput| -> ! {
             if self.is_verbose() {
                 println!("{message}");
             } else {
-                println!("Command has failed. Rerun with -v to see more details.");
+                let (stdout, stderr) = (output.stdout_if_present(), output.stderr_if_present());
+                // If the command captures output, the user would not see any indication that
+                // it has failed. In this case, print a more verbose error, since to provide more
+                // context.
+                if stdout.is_some() || stderr.is_some() {
+                    if let Some(stdout) =
+                        output.stdout_if_present().take_if(|s| !s.trim().is_empty())
+                    {
+                        println!("STDOUT:\n{stdout}\n");
+                    }
+                    if let Some(stderr) =
+                        output.stderr_if_present().take_if(|s| !s.trim().is_empty())
+                    {
+                        println!("STDERR:\n{stderr}\n");
+                    }
+                    println!("Command {command:?} has failed. Rerun with -v to see more details.");
+                } else {
+                    println!("Command has failed. Rerun with -v to see more details.");
+                }
             }
             exit!(1);
         };
@@ -1069,14 +1089,14 @@ fn run(
             match command.failure_behavior {
                 BehaviorOnFailure::DelayFail => {
                     if self.fail_fast {
-                        fail(&message);
+                        fail(&message, output);
                     }
 
                     let mut failures = self.delayed_failures.borrow_mut();
                     failures.push(message);
                 }
                 BehaviorOnFailure::Exit => {
-                    fail(&message);
+                    fail(&message, output);
                 }
                 BehaviorOnFailure::Ignore => {
                     // If failures are allowed, either the error has been printed already
@@ -1774,21 +1794,20 @@ fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool)
             }
         }
         if let Ok(()) = fs::hard_link(&src, dst) {
-            // Attempt to "easy copy" by creating a hard link
-            // (symlinks don't work on windows), but if that fails
-            // just fall back to a slow `copy` operation.
+            // Attempt to "easy copy" by creating a hard link (symlinks are priviledged on windows),
+            // but if that fails just fall back to a slow `copy` operation.
         } else {
             if let Err(e) = fs::copy(&src, dst) {
                 panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)
             }
             t!(fs::set_permissions(dst, metadata.permissions()));
 
+            // Restore file times because changing permissions on e.g. Linux using `chmod` can cause
+            // file access time to change.
             let file_times = fs::FileTimes::new()
                 .set_accessed(t!(metadata.accessed()))
                 .set_modified(t!(metadata.modified()));
-
-            let dst_file = t!(fs::File::open(dst));
-            t!(dst_file.set_times(file_times));
+            t!(set_file_times(dst, file_times));
         }
     }
 
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
index 9f0d0b7..530d760 100644
--- a/src/bootstrap/src/utils/exec.rs
+++ b/src/bootstrap/src/utils/exec.rs
@@ -292,6 +292,11 @@ pub fn stdout(&self) -> String {
     }
 
     #[must_use]
+    pub fn stdout_if_present(&self) -> Option<String> {
+        self.stdout.as_ref().and_then(|s| String::from_utf8(s.clone()).ok())
+    }
+
+    #[must_use]
     pub fn stdout_if_ok(&self) -> Option<String> {
         if self.is_success() { Some(self.stdout()) } else { None }
     }
@@ -303,6 +308,11 @@ pub fn stderr(&self) -> String {
         )
         .expect("Cannot parse process stderr as UTF-8")
     }
+
+    #[must_use]
+    pub fn stderr_if_present(&self) -> Option<String> {
+        self.stderr.as_ref().and_then(|s| String::from_utf8(s.clone()).ok())
+    }
 }
 
 impl Default for CommandOutput {
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 65e75f1..a856c99 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -544,3 +544,15 @@ pub fn get_closest_merge_base_commit(
 
     Ok(output_result(git.as_command_mut())?.trim().to_owned())
 }
+
+/// Sets the file times for a given file at `path`.
+pub fn set_file_times<P: AsRef<Path>>(path: P, times: fs::FileTimes) -> io::Result<()> {
+    // Windows requires file to be writable to modify file times. But on Linux CI the file does not
+    // need to be writable to modify file times and might be read-only.
+    let f = if cfg!(windows) {
+        fs::File::options().write(true).open(path)?
+    } else {
+        fs::File::open(path)?
+    };
+    f.set_times(times)
+}
diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs
index 103c4d2..86016a9 100644
--- a/src/bootstrap/src/utils/helpers/tests.rs
+++ b/src/bootstrap/src/utils/helpers/tests.rs
@@ -3,7 +3,8 @@
 use std::path::PathBuf;
 
 use crate::utils::helpers::{
-    check_cfg_arg, extract_beta_rev, hex_encode, make, program_out_of_date, symlink_dir,
+    check_cfg_arg, extract_beta_rev, hex_encode, make, program_out_of_date, set_file_times,
+    symlink_dir,
 };
 use crate::{Config, Flags};
 
@@ -92,3 +93,25 @@ fn test_symlink_dir() {
     #[cfg(not(windows))]
     fs::remove_file(link_path).unwrap();
 }
+
+#[test]
+fn test_set_file_times_sanity_check() {
+    let config =
+        Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]));
+    let tempfile = config.tempdir().join(".tmp-file");
+
+    {
+        File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap();
+        assert!(tempfile.exists());
+    }
+
+    // This might only fail on Windows (if file is default read-only then we try to modify file
+    // times).
+    let unix_epoch = std::time::SystemTime::UNIX_EPOCH;
+    let target_time = fs::FileTimes::new().set_accessed(unix_epoch).set_modified(unix_epoch);
+    set_file_times(&tempfile, target_time).unwrap();
+
+    let found_metadata = fs::metadata(tempfile).unwrap();
+    assert_eq!(found_metadata.accessed().unwrap(), unix_epoch);
+    assert_eq!(found_metadata.modified().unwrap(), unix_epoch)
+}
diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs
index 3f7f621..3c6c7a7 100644
--- a/src/bootstrap/src/utils/tarball.rs
+++ b/src/bootstrap/src/utils/tarball.rs
@@ -9,6 +9,7 @@
 
 use crate::core::build_steps::dist::distdir;
 use crate::core::builder::{Builder, Kind};
+use crate::core::config::BUILDER_CONFIG_FILENAME;
 use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::{move_file, t};
 use crate::utils::{channel, helpers};
@@ -320,7 +321,7 @@ fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut BootstrapCommand)) -> Gen
 
         // Add config file if present.
         if let Some(config) = &self.builder.config.config {
-            self.add_renamed_file(config, &self.overlay_dir, "builder-config");
+            self.add_renamed_file(config, &self.overlay_dir, BUILDER_CONFIG_FILENAME);
         }
 
         for file in self.overlay.legal_and_readme() {
diff --git a/src/doc/book b/src/doc/book
index 67fa536..04bc139 160000
--- a/src/doc/book
+++ b/src/doc/book
@@ -1 +1 @@
-Subproject commit 67fa536768013d9d5a13f3a06790521d511ef711
+Subproject commit 04bc1396bb857f35b5dda1d773c9571e1f253304
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
index 5454de3..aeeb287 160000
--- a/src/doc/edition-guide
+++ b/src/doc/edition-guide
@@ -1 +1 @@
-Subproject commit 5454de3d12b9ccc6375b629cf7ccda8264640aac
+Subproject commit aeeb287d41a0332c210da122bea8e0e91844ab3e
diff --git a/src/doc/nomicon b/src/doc/nomicon
index 0ebdaca..6ecf95c 160000
--- a/src/doc/nomicon
+++ b/src/doc/nomicon
@@ -1 +1 @@
-Subproject commit 0ebdacadbda8ce2cd8fbf93985e15af61a7ab895
+Subproject commit 6ecf95c5f2bfa0e6314dfe282bf775fd1405f7e9
diff --git a/src/doc/reference b/src/doc/reference
index 2e19181..62cd0df 160000
--- a/src/doc/reference
+++ b/src/doc/reference
@@ -1 +1 @@
-Subproject commit 2e191814f163ee1e77e2d6094eee4dd78a289c5b
+Subproject commit 62cd0df95061ba0ac886333f5cd7f3012f149da1
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
index 89aecb6..8f94061 160000
--- a/src/doc/rust-by-example
+++ b/src/doc/rust-by-example
@@ -1 +1 @@
-Subproject commit 89aecb6951b77bc746da73df8c9f2b2ceaad494a
+Subproject commit 8f94061936e492159f4f6c09c0f917a7521893ff
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
index 0c4d55c..43d8378 160000
--- a/src/doc/rustc-dev-guide
+++ b/src/doc/rustc-dev-guide
@@ -1 +1 @@
-Subproject commit 0c4d55cb59fe440d1a630e4e5774d043968edb3f
+Subproject commit 43d83780db545a1ed6d45773312fc578987e3968
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 467fd6f..cb0b2e6 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -61,6 +61,7 @@
     - [mipsisa\*r6\*-unknown-linux-gnu\*](platform-support/mips-release-6.md)
     - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md)
     - [powerpc-unknown-openbsd](platform-support/powerpc-unknown-openbsd.md)
+    - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md)
     - [powerpc64-ibm-aix](platform-support/aix.md)
     - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md)
     - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index bd12172..c3f73a8 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -332,6 +332,7 @@
 `msp430-none-elf` | * |  | 16-bit MSP430 microcontrollers
 `powerpc-unknown-linux-gnuspe` | ✓ |  | PowerPC SPE Linux
 `powerpc-unknown-linux-musl` | ? |  | PowerPC Linux with musl 1.2.3
+[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? |  | PowerPC SPE Linux
 [`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems
 [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * |  |
 [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ |  |
diff --git a/src/doc/rustc/src/platform-support/android.md b/src/doc/rustc/src/platform-support/android.md
index 9ddf00e..96499b0 100644
--- a/src/doc/rustc/src/platform-support/android.md
+++ b/src/doc/rustc/src/platform-support/android.md
@@ -61,3 +61,8 @@
 * `Zba` (address calculation instructions)
 * `Zbb` (base instructions)
 * `Zbs` (single-bit instructions)
+
+### aarch64-linux-android on Nightly compilers
+
+As soon as `-Zfixed-x18` compiler flag is supplied, the [`ShadowCallStack` sanitizer](https://releases.llvm.org/7.0.1/tools/clang/docs/ShadowCallStack.html)
+instrumentation is also made avaiable by supplying the second compiler flag `-Zsanitizer=shadow-call-stack`.
diff --git a/src/doc/rustc/src/platform-support/powerpc-unknown-linux-muslspe.md b/src/doc/rustc/src/platform-support/powerpc-unknown-linux-muslspe.md
new file mode 100644
index 0000000..4c416b5
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/powerpc-unknown-linux-muslspe.md
@@ -0,0 +1,32 @@
+# powerpc-unknown-linux-muslspe
+
+**Tier: 3**
+
+This target is very similar to already existing ones like `powerpc_unknown_linux_musl` and `powerpc_unknown_linux_gnuspe`.
+This one has PowerPC SPE support for musl. Unfortunately, the last supported gcc version with PowerPC SPE is 8.4.0.
+
+## Target maintainers
+
+- [@BKPepe](https://github.com/BKPepe)
+
+## Requirements
+
+This target is cross-compiled. There is no support for `std`. There is no
+default allocator, but it's possible to use `alloc` by supplying an allocator.
+
+This target generated binaries in the ELF format.
+
+## Building the target
+
+This target was tested and used within the `OpenWrt` build system for CZ.NIC Turris 1.x routers using Freescale P2020.
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you will either need to build Rust with the target enabled (see
+"Building the target" above), or build your own copy of `core` by using
+`build-std` or similar.
+
+## Testing
+
+This is a cross-compiled target and there is no support to run rustc test suite.
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index 39f24a1..ebbe141 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -515,6 +515,9 @@
 
 Note that the third item is the crate root, which in this case is undocumented.
 
+If you want the JSON output to be displayed on `stdout` instead of having a file generated, you can
+use `-o -`.
+
 ### `-w`/`--output-format`: output format
 
 `--output-format json` emits documentation in the experimental
diff --git a/src/doc/unstable-book/src/compiler-flags/fixed-x18.md b/src/doc/unstable-book/src/compiler-flags/fixed-x18.md
index 8c8bff5..b215bc8 100644
--- a/src/doc/unstable-book/src/compiler-flags/fixed-x18.md
+++ b/src/doc/unstable-book/src/compiler-flags/fixed-x18.md
@@ -1,7 +1,7 @@
 # `fixed-x18`
 
 This option prevents the compiler from using the x18 register. It is only
-supported on aarch64.
+supported on `aarch64`.
 
 From the [ABI spec][arm-abi]:
 
@@ -23,6 +23,11 @@
 platforms that always treat x18 as a reserved register, and the `-Zfixed-x18`
 flag is not required to use the sanitizer on such platforms. However, the
 sanitizer may be supported on targets where this is not the case in the future.
+One way to do so now on Nightly compilers is to explicitly supply this `-Zfixed-x18`
+flag with `aarch64` targets, so that the sanitizer is available for instrumentation
+on targets like `aarch64-unknown-none`, for instance. However, discretion is still
+required to make sure that the runtime support is in place for this sanitizer
+to be effective.
 
 It is undefined behavior for `-Zsanitizer=shadow-call-stack` code to call into
 code where x18 is a temporary register. On the other hand, when you are *not*
diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
index 72b44e0..edc63a2 100644
--- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md
+++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
@@ -787,6 +787,10 @@
 
 See the [Clang ShadowCallStack documentation][clang-scs] for more details.
 
+* `aarch64-unknown-none`
+
+In addition to support from a runtime by the application or operating system, the `-Zfixed-x18` flag is also mandatory.
+
 # ThreadSanitizer
 
 ThreadSanitizer is a data race detection tool. It is supported on the following
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 5375734..db81b4c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -3094,16 +3094,10 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
     let def_id = item.owner_id.to_def_id();
     cx.with_param_env(def_id, |cx| {
         let kind = match item.kind {
-            hir::ForeignItemKind::Fn(decl, names, generics, safety) => {
-                let (generics, decl) = enter_impl_trait(cx, |cx| {
-                    // NOTE: generics must be cleaned before args
-                    let generics = clean_generics(generics, cx);
-                    let args = clean_args_from_types_and_names(cx, decl.inputs, names);
-                    let decl = clean_fn_decl_with_args(cx, decl, None, args);
-                    (generics, decl)
-                });
-                ForeignFunctionItem(Box::new(Function { decl, generics }), safety)
-            }
+            hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem(
+                clean_function(cx, &sig, generics, FunctionArgs::Names(names)),
+                sig.header.safety,
+            ),
             hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem(
                 Static { type_: Box::new(clean_ty(ty, cx)), mutability, expr: None },
                 safety,
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index d599fea..9e9d8f0 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -286,6 +286,9 @@ pub(crate) struct RenderOptions {
     pub(crate) no_emit_shared: bool,
     /// If `true`, HTML source code pages won't be generated.
     pub(crate) html_no_source: bool,
+    /// This field is only used for the JSON output. If it's set to true, no file will be created
+    /// and content will be displayed in stdout directly.
+    pub(crate) output_to_stdout: bool,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -548,16 +551,17 @@ fn println_condition(condition: Condition) {
             dcx.fatal("the `--test` flag must be passed to enable `--no-run`");
         }
 
+        let mut output_to_stdout = false;
         let test_builder_wrappers =
             matches.opt_strs("test-builder-wrapper").iter().map(PathBuf::from).collect();
-        let out_dir = matches.opt_str("out-dir").map(|s| PathBuf::from(&s));
-        let output = matches.opt_str("output").map(|s| PathBuf::from(&s));
-        let output = match (out_dir, output) {
+        let output = match (matches.opt_str("out-dir"), matches.opt_str("output")) {
             (Some(_), Some(_)) => {
                 dcx.fatal("cannot use both 'out-dir' and 'output' at once");
             }
-            (Some(out_dir), None) => out_dir,
-            (None, Some(output)) => output,
+            (Some(out_dir), None) | (None, Some(out_dir)) => {
+                output_to_stdout = out_dir == "-";
+                PathBuf::from(out_dir)
+            }
             (None, None) => PathBuf::from("doc"),
         };
 
@@ -818,6 +822,7 @@ fn println_condition(condition: Condition) {
             call_locations,
             no_emit_shared: false,
             html_no_source,
+            output_to_stdout,
         };
         Some((options, render_options))
     }
diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs
index b91333e..d49fa3ac 100644
--- a/src/librustdoc/doctest/runner.rs
+++ b/src/librustdoc/doctest/runner.rs
@@ -75,7 +75,6 @@ pub(crate) fn run_merged_tests(
 #![allow(internal_features)]
 #![feature(test)]
 #![feature(rustc_attrs)]
-#![feature(coverage_attribute)]
 "
         .to_string();
 
@@ -135,7 +134,6 @@ pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> Result<(), Strin
 }}
 
 #[rustc_main]
-#[coverage(off)]
 fn main() -> std::process::ExitCode {{
 const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}];
 let bin_marker = std::ffi::OsStr::new(__doctest_mod::BIN_OPTION);
@@ -235,11 +233,9 @@ fn main() {returns_result} {{
     writeln!(
         output,
         "
-#[rustc_test_marker = {test_name:?}]
 pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest(
 {test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic},
 test::StaticTestFn(
-    #[coverage(off)]
     || {{{runner}}},
 ));
 }}",
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index ea191dc..e286029 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -9,16 +9,19 @@
 
 use std::cell::RefCell;
 use std::fs::{create_dir_all, File};
-use std::io::{BufWriter, Write};
+use std::io::{stdout, BufWriter, Write};
 use std::path::PathBuf;
 use std::rc::Rc;
 
-use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustdoc_json_types as types;
+// It's important to use the FxHashMap from rustdoc_json_types here, instead of
+// the one from rustc_data_structures, as they're different types due to sysroots.
+// See #110051 and #127456 for details
+use rustdoc_json_types::FxHashMap;
 
 use crate::clean::types::{ExternalCrate, ExternalLocation};
 use crate::clean::ItemKind;
@@ -36,8 +39,10 @@ pub(crate) struct JsonRenderer<'tcx> {
     /// A mapping of IDs that contains all local items for this crate which gets output as a top
     /// level field of the JSON blob.
     index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>,
-    /// The directory where the blob will be written to.
-    out_path: PathBuf,
+    /// The directory where the JSON blob should be written to.
+    ///
+    /// If this is `None`, the blob will be printed to `stdout` instead.
+    out_dir: Option<PathBuf>,
     cache: Rc<Cache>,
     imported_items: DefIdSet,
 }
@@ -97,6 +102,22 @@ fn get_impls(&mut self, id: DefId) -> Vec<types::Id> {
             })
             .unwrap_or_default()
     }
+
+    fn serialize_and_write<T: Write>(
+        &self,
+        output_crate: types::Crate,
+        mut writer: BufWriter<T>,
+        path: &str,
+    ) -> Result<(), Error> {
+        self.sess().time("rustdoc_json_serialize_and_write", || {
+            try_err!(
+                serde_json::ser::to_writer(&mut writer, &output_crate).map_err(|e| e.to_string()),
+                path
+            );
+            try_err!(writer.flush(), path);
+            Ok(())
+        })
+    }
 }
 
 impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
@@ -120,7 +141,7 @@ fn init(
             JsonRenderer {
                 tcx,
                 index: Rc::new(RefCell::new(FxHashMap::default())),
-                out_path: options.output,
+                out_dir: if options.output_to_stdout { None } else { Some(options.output) },
                 cache: Rc::new(cache),
                 imported_items,
             },
@@ -220,14 +241,11 @@ fn after_krate(&mut self) -> Result<(), Error> {
         let index = (*self.index).clone().into_inner();
 
         debug!("Constructing Output");
-        // This needs to be the default HashMap for compatibility with the public interface for
-        // rustdoc-json-types
-        #[allow(rustc::default_hash_types)]
-        let output = types::Crate {
+        let output_crate = types::Crate {
             root: types::Id(format!("0:0:{}", e.name(self.tcx).as_u32())),
             crate_version: self.cache.crate_version.clone(),
             includes_private: self.cache.document_private,
-            index: index.into_iter().collect(),
+            index,
             paths: self
                 .cache
                 .paths
@@ -264,20 +282,21 @@ fn after_krate(&mut self) -> Result<(), Error> {
                 .collect(),
             format_version: types::FORMAT_VERSION,
         };
-        let out_dir = self.out_path.clone();
-        try_err!(create_dir_all(&out_dir), out_dir);
+        if let Some(ref out_dir) = self.out_dir {
+            try_err!(create_dir_all(&out_dir), out_dir);
 
-        let mut p = out_dir;
-        p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
-        p.set_extension("json");
-        let mut file = BufWriter::new(try_err!(File::create(&p), p));
-        self.tcx
-            .sess
-            .time("rustdoc_json_serialization", || serde_json::ser::to_writer(&mut file, &output))
-            .unwrap();
-        try_err!(file.flush(), p);
+            let mut p = out_dir.clone();
+            p.push(output_crate.index.get(&output_crate.root).unwrap().name.clone().unwrap());
+            p.set_extension("json");
 
-        Ok(())
+            self.serialize_and_write(
+                output_crate,
+                BufWriter::new(try_err!(File::create(&p), p)),
+                &p.display().to_string(),
+            )
+        } else {
+            self.serialize_and_write(output_crate, BufWriter::new(stdout().lock()), "<stdout>")
+        }
     }
 
     fn cache(&self) -> &Cache {
diff --git a/src/llvm-project b/src/llvm-project
index 57ae1a3..ccf4c38 160000
--- a/src/llvm-project
+++ b/src/llvm-project
@@ -1 +1 @@
-Subproject commit 57ae1a3474057fead2c438928ed368b3740bf0ec
+Subproject commit ccf4c38bdd73f1a37ec266c73bdaef80e39f8cf6
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 999134a..40a90c1 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -5,7 +5,7 @@
 
 use std::path::PathBuf;
 
-use rustc_hash::FxHashMap;
+pub use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
 
 /// The version of JSON output that this crate represents.
diff --git a/src/tools/cargo b/src/tools/cargo
index 0d8d22f..ba8b394 160000
--- a/src/tools/cargo
+++ b/src/tools/cargo
@@ -1 +1 @@
-Subproject commit 0d8d22f83b066503f6b2b755925197e959e58b4f
+Subproject commit ba8b39413c74d08494f94a7542fe79aa636e1661
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index de91233..8711783 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -470,7 +470,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
     });
 
     // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
-    while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
+    while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token == TokenKind::Ident) {
         let mut iter = iter
             .by_ref()
             .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
@@ -480,7 +480,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
                 // matches `!{`
                 match_tokens!(iter, Bang OpenBrace);
                 if let Some(LintDeclSearchResult { range, .. }) =
-                    iter.find(|result| result.token_kind == TokenKind::CloseBrace)
+                    iter.find(|result| result.token == TokenKind::CloseBrace)
                 {
                     last_decl_curly_offset = Some(range.end);
                 }
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 966d385..7a11e35 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -12,7 +12,6 @@
 #![feature(let_chains)]
 #![feature(trait_upcasting)]
 #![feature(strict_overflow_ops)]
-#![feature(is_none_or)]
 // Configure clippy and other lints
 #![allow(
     clippy::collapsible_else_if,
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index c4d5446..eae6022 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -11,3 +11,4 @@
 regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
 gimli = "0.31.0"
 build_helper = { path = "../build_helper" }
+serde_json = "1.0"
diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs
index 7af7944..e931585 100644
--- a/src/tools/run-make-support/src/external_deps/llvm.rs
+++ b/src/tools/run-make-support/src/external_deps/llvm.rs
@@ -285,12 +285,24 @@ pub fn obj_to_ar(&mut self) -> &mut Self {
         self
     }
 
+    /// Like `obj_to_ar` except creating a thin archive.
+    pub fn obj_to_thin_ar(&mut self) -> &mut Self {
+        self.cmd.arg("rcus").arg("--thin");
+        self
+    }
+
     /// Extract archive members back to files.
     pub fn extract(&mut self) -> &mut Self {
         self.cmd.arg("x");
         self
     }
 
+    /// Print the table of contents.
+    pub fn table_of_contents(&mut self) -> &mut Self {
+        self.cmd.arg("t");
+        self
+    }
+
     /// Provide an output, then an input file. Bundled in one function, as llvm-ar has
     /// no "--output"-style flag.
     pub fn output_input(&mut self, out: impl AsRef<Path>, input: impl AsRef<Path>) -> &mut Self {
diff --git a/src/tools/run-make-support/src/external_deps/rustdoc.rs b/src/tools/run-make-support/src/external_deps/rustdoc.rs
index 96c2218..96b1c71 100644
--- a/src/tools/run-make-support/src/external_deps/rustdoc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs
@@ -71,14 +71,8 @@ pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
         self
     }
 
-    /// Specify path to the output folder.
-    pub fn output<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
-        self.cmd.arg("-o");
-        self.cmd.arg(path.as_ref());
-        self
-    }
-
     /// Specify output directory.
+    #[doc(alias = "output")]
     pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
         self.cmd.arg("--out-dir").arg(path.as_ref());
         self
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 4bef4f0..fc20fd3 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -38,6 +38,7 @@ pub mod rfs {
 pub use gimli;
 pub use object;
 pub use regex;
+pub use serde_json;
 pub use wasmparser;
 
 // Re-exports of external dependencies.
diff --git a/src/tools/run-make-support/src/path_helpers.rs b/src/tools/run-make-support/src/path_helpers.rs
index b788bc6..1e6e44c 100644
--- a/src/tools/run-make-support/src/path_helpers.rs
+++ b/src/tools/run-make-support/src/path_helpers.rs
@@ -84,3 +84,18 @@ pub fn has_suffix<P: AsRef<Path>>(path: P, suffix: &str) -> bool {
 pub fn filename_contains<P: AsRef<Path>>(path: P, needle: &str) -> bool {
     path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(needle))
 }
+
+/// Helper for reading entries in a given directory and its children.
+pub fn read_dir_entries_recursive<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F) {
+    fn read_dir_entries_recursive_inner<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, callback: &mut F) {
+        for entry in rfs::read_dir(dir) {
+            let path = entry.unwrap().path();
+            callback(&path);
+            if path.is_dir() {
+                read_dir_entries_recursive_inner(path, callback);
+            }
+        }
+    }
+
+    read_dir_entries_recursive_inner(dir, &mut callback);
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 7c48195..f406666 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -1462,7 +1462,7 @@ fn generic_args_sans_defaults<'ga>(
                     // otherwise, if the arg is equal to the param default, hide it (unless the
                     // default is an error which can happen for the trait Self type)
                     #[allow(unstable_name_collisions)]
-                    default_parameters.get(i).is_none_or(|default_parameter| {
+                    IsNoneOr::is_none_or(default_parameters.get(i), |default_parameter| {
                         // !is_err(default_parameter.skip_binders())
                         //     &&
                         arg != &default_parameter.clone().substitute(Interner, &parameters)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 4c9e0a1..21c8451 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -15,8 +15,10 @@
 #[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_abi as rustc_abi;
 
-// Use the crates.io version unconditionally until the API settles enough that we can switch to
-// using the in-tree one.
+#[cfg(feature = "in-rust-tree")]
+extern crate rustc_pattern_analysis;
+
+#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
 
 mod builder;
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index df051ed..3b859fe 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -304,6 +304,12 @@
 ]
 
 [[package]]
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+
+[[package]]
 name = "elasticlunr-rs"
 version = "3.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -466,6 +472,21 @@
 ]
 
 [[package]]
+name = "html_parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f56db07b6612644f6f7719f8ef944f75fff9d6378fdf3d316fd32194184abd"
+dependencies = [
+ "doc-comment",
+ "pest",
+ "pest_derive",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "thiserror",
+]
+
+[[package]]
 name = "humantime"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -680,13 +701,13 @@
 version = "0.1.0"
 dependencies = [
  "clap",
+ "html_parser",
  "mdbook",
  "pulldown-cmark",
  "pulldown-cmark-to-cmark",
  "serde_json",
  "thiserror",
  "toml 0.8.14",
- "xmlparser",
 ]
 
 [[package]]
@@ -1768,12 +1789,6 @@
 ]
 
 [[package]]
-name = "xmlparser"
-version = "0.13.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
-
-[[package]]
 name = "yaml-rust"
 version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs
index 60c827f..8d5f7f9 100644
--- a/src/tools/rustfmt/src/parse/macros/mod.rs
+++ b/src/tools/rustfmt/src/parse/macros/mod.rs
@@ -84,9 +84,7 @@ pub(crate) struct ParsedMacroArgs {
 fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
     for &keyword in RUST_KW.iter() {
         if parser.token.is_keyword(keyword)
-            && parser.look_ahead(1, |t| {
-                t.kind == TokenKind::Eof || t.kind == TokenKind::Comma
-            })
+            && parser.look_ahead(1, |t| *t == TokenKind::Eof || *t == TokenKind::Comma)
         {
             parser.bump();
             return Some(MacroArg::Keyword(
@@ -131,7 +129,7 @@ pub(crate) fn parse_macro_args(
                                 Some(arg) => {
                                     args.push(arg);
                                     parser.bump();
-                                    if parser.token.kind == TokenKind::Eof && args.len() == 2 {
+                                    if parser.token == TokenKind::Eof && args.len() == 2 {
                                         vec_with_semi = true;
                                         break;
                                     }
@@ -150,7 +148,7 @@ pub(crate) fn parse_macro_args(
 
             parser.bump();
 
-            if parser.token.kind == TokenKind::Eof {
+            if parser.token == TokenKind::Eof {
                 trailing_comma = true;
                 break;
             }
diff --git a/src/tools/rustfmt/tests/target/unsafe_attributes.rs b/src/tools/rustfmt/tests/target/unsafe_attributes.rs
index a05bedc..d79c56f 100644
--- a/src/tools/rustfmt/tests/target/unsafe_attributes.rs
+++ b/src/tools/rustfmt/tests/target/unsafe_attributes.rs
@@ -1,4 +1,3 @@
-#![feature(unsafe_attributes)]
 // https://github.com/rust-lang/rust/issues/123757
 //
 #![simple_ident]
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index bc44655..f55abb5 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -1,21 +1,13 @@
 run-make/branch-protection-check-IBT/Makefile
 run-make/cat-and-grep-sanity-check/Makefile
-run-make/dep-info-doesnt-run-much/Makefile
-run-make/dep-info-spaces/Makefile
-run-make/dep-info/Makefile
 run-make/emit-to-stdout/Makefile
 run-make/extern-fn-reachable/Makefile
 run-make/incr-add-rust-src-component/Makefile
 run-make/issue-84395-lto-embed-bitcode/Makefile
 run-make/jobserver-error/Makefile
 run-make/libs-through-symlinks/Makefile
-run-make/libtest-json/Makefile
-run-make/libtest-junit/Makefile
 run-make/libtest-thread-limit/Makefile
 run-make/macos-deployment-target/Makefile
-run-make/native-link-modifier-bundle/Makefile
-run-make/reproducible-build/Makefile
-run-make/rlib-format-packed-bundled-libs/Makefile
 run-make/split-debuginfo/Makefile
 run-make/symbol-mangling-hashed/Makefile
 run-make/translation/Makefile
diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs
index 762df40..c3a0833 100644
--- a/tests/assembly/targets/targets-elf.rs
+++ b/tests/assembly/targets/targets-elf.rs
@@ -345,6 +345,9 @@
 //@ revisions: powerpc_unknown_linux_musl
 //@ [powerpc_unknown_linux_musl] compile-flags: --target powerpc-unknown-linux-musl
 //@ [powerpc_unknown_linux_musl] needs-llvm-components: powerpc
+//@ revisions: powerpc_unknown_linux_muslspe
+//@ [powerpc_unknown_linux_muslspe] compile-flags: --target powerpc-unknown-linux-muslspe
+//@ [powerpc_unknown_linux_muslspe] needs-llvm-components: powerpc
 //@ revisions: powerpc_unknown_netbsd
 //@ [powerpc_unknown_netbsd] compile-flags: --target powerpc-unknown-netbsd
 //@ [powerpc_unknown_netbsd] needs-llvm-components: powerpc
diff --git a/tests/codegen/debuginfo-inline-callsite-location.rs b/tests/codegen/debuginfo-inline-callsite-location.rs
index aee07b4..c31788d 100644
--- a/tests/codegen/debuginfo-inline-callsite-location.rs
+++ b/tests/codegen/debuginfo-inline-callsite-location.rs
@@ -9,13 +9,12 @@
 // CHECK:       tail call void @{{[A-Za-z0-9_]+4core6option13unwrap_failed}}
 // CHECK-SAME:  !dbg ![[#second_dbg:]]
 
-// CHECK-DAG:   ![[#func_dbg:]] = distinct !DISubprogram(name: "unwrap<i32>"
-// CHECK-DAG:   ![[#first_scope:]] = distinct !DILexicalBlock(scope: ![[#func_dbg]],
-// CHECK:       ![[#second_scope:]] = distinct !DILexicalBlock(scope: ![[#func_dbg]],
+// CHECK-DAG:   ![[#func_scope:]] = distinct !DISubprogram(name: "unwrap<i32>"
+// CHECK-DAG:   ![[#]] = !DILocalVariable(name: "self",{{( arg: 1,)?}} scope: ![[#func_scope]]
 // CHECK:       ![[#first_dbg]] = !DILocation(line: [[#]]
-// CHECK-SAME:  scope: ![[#first_scope]], inlinedAt: ![[#]])
+// CHECK-SAME:  scope: ![[#func_scope]], inlinedAt: ![[#]])
 // CHECK:       ![[#second_dbg]] = !DILocation(line: [[#]]
-// CHECK-SAME:  scope: ![[#second_scope]], inlinedAt: ![[#]])
+// CHECK-SAME:  scope: ![[#func_scope]], inlinedAt: ![[#]])
 
 #![crate_type = "lib"]
 
diff --git a/tests/codegen/inline-function-args-debug-info.rs b/tests/codegen/inline-function-args-debug-info.rs
index 7263374..53a1791 100644
--- a/tests/codegen/inline-function-args-debug-info.rs
+++ b/tests/codegen/inline-function-args-debug-info.rs
@@ -15,6 +15,7 @@ pub fn outer_function(x: usize, y: usize) -> usize {
 fn inner_function(aaaa: usize, bbbb: usize) -> usize {
     // CHECK: !DILocalVariable(name: "aaaa", arg: 1
     // CHECK-SAME: line: 15
+    // CHECK-NOT: !DILexicalBlock(
     // CHECK: !DILocalVariable(name: "bbbb", arg: 2
     // CHECK-SAME: line: 15
     aaaa + bbbb
diff --git a/tests/codegen/sanitizer/aarch64-shadow-call-stack-with-fixed-x18.rs b/tests/codegen/sanitizer/aarch64-shadow-call-stack-with-fixed-x18.rs
new file mode 100644
index 0000000..e2e14ab
--- /dev/null
+++ b/tests/codegen/sanitizer/aarch64-shadow-call-stack-with-fixed-x18.rs
@@ -0,0 +1,19 @@
+//@ revisions: aarch64 android
+//@[aarch64] compile-flags: --target aarch64-unknown-none -Zfixed-x18 -Zsanitizer=shadow-call-stack
+//@[aarch64] needs-llvm-components: aarch64
+//@[android] compile-flags: --target aarch64-linux-android -Zsanitizer=shadow-call-stack
+//@[android] needs-llvm-components: aarch64
+
+#![allow(internal_features)]
+#![crate_type = "rlib"]
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+// CHECK: ; Function Attrs:{{.*}}shadowcallstack
+#[no_mangle]
+pub fn foo() {}
+
+// CHECK: attributes #0 = {{.*}}shadowcallstack{{.*}}
diff --git a/tests/crashes/128695.rs b/tests/crashes/128695.rs
new file mode 100644
index 0000000..661f427
--- /dev/null
+++ b/tests/crashes/128695.rs
@@ -0,0 +1,11 @@
+//@ known-bug: rust-lang/rust#128695
+//@ edition: 2021
+
+use core::pin::{pin, Pin};
+
+fn main() {
+    let fut = pin!(async {
+        let async_drop_fut = pin!(core::future::async_drop(async {}));
+        (async_drop_fut).await;
+    });
+}
diff --git a/tests/crashes/128848.rs b/tests/crashes/128848.rs
new file mode 100644
index 0000000..636811f
--- /dev/null
+++ b/tests/crashes/128848.rs
@@ -0,0 +1,5 @@
+//@ known-bug: rust-lang/rust#128848
+
+fn f<T>(a: T, b: T, c: T)  {
+    f.call_once()
+}
diff --git a/tests/crashes/128870.rs b/tests/crashes/128870.rs
new file mode 100644
index 0000000..2b73196
--- /dev/null
+++ b/tests/crashes/128870.rs
@@ -0,0 +1,18 @@
+//@ known-bug: rust-lang/rust#128870
+//@ compile-flags: -Zvalidate-mir
+
+#[repr(packed)]
+#[repr(u32)]
+enum E {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    union InvalidTag {
+        int: u32,
+        e: E,
+    }
+    let _invalid_tag = InvalidTag { int: 4 };
+}
diff --git a/tests/crashes/129075.rs b/tests/crashes/129075.rs
new file mode 100644
index 0000000..4a0e920
--- /dev/null
+++ b/tests/crashes/129075.rs
@@ -0,0 +1,16 @@
+//@ known-bug: rust-lang/rust#129075
+//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes
+
+struct Foo<T>([T; 2]);
+
+impl<T: Default + Copy> Default for Foo<T> {
+    fn default(&mut self) -> Self {
+        Foo([Default::default(); 2])
+    }
+}
+
+fn field_array() {
+    let a: i32;
+    let b;
+    Foo([a, b]) = Default::default();
+}
diff --git a/tests/crashes/129095.rs b/tests/crashes/129095.rs
new file mode 100644
index 0000000..ea70c05
--- /dev/null
+++ b/tests/crashes/129095.rs
@@ -0,0 +1,10 @@
+//@ known-bug: rust-lang/rust#129095
+//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
+
+pub fn function_with_bytes<const BYTES: &'static [u8; 4]>() -> &'static [u8] {
+    BYTES
+}
+
+pub fn main() {
+    assert_eq!(function_with_bytes::<b"AAAAb">(), &[0x41, 0x41, 0x41, 0x41]);
+}
diff --git a/tests/crashes/129099.rs b/tests/crashes/129099.rs
new file mode 100644
index 0000000..9aaab75
--- /dev/null
+++ b/tests/crashes/129099.rs
@@ -0,0 +1,15 @@
+//@ known-bug: rust-lang/rust#129099
+
+#![feature(type_alias_impl_trait)]
+
+fn dyn_hoops<T: Sized>() -> dyn for<'a> Iterator<Item = impl Captures<'a>> {
+    loop {}
+}
+
+pub fn main() {
+    type Opaque = impl Sized;
+    fn define() -> Opaque {
+        let x: Opaque = dyn_hoops::<()>(0);
+        x
+    }
+}
diff --git a/tests/crashes/129109.rs b/tests/crashes/129109.rs
new file mode 100644
index 0000000..8b9ebdf
--- /dev/null
+++ b/tests/crashes/129109.rs
@@ -0,0 +1,10 @@
+//@ known-bug: rust-lang/rust#129109
+//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
+
+extern "C" {
+    pub static mut symbol: [i8];
+}
+
+fn main() {
+    println!("C", unsafe { &symbol });
+}
diff --git a/tests/crashes/129127.rs b/tests/crashes/129127.rs
new file mode 100644
index 0000000..8ec848d
--- /dev/null
+++ b/tests/crashes/129127.rs
@@ -0,0 +1,21 @@
+//@ known-bug: rust-lang/rust#129127
+//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir -Zcross-crate-inline-threshold=always
+
+
+
+
+pub struct Rows<'a>();
+
+impl<'a> Iterator for Rows<'a> {
+    type Item = ();
+
+    fn next() -> Option<Self::Item> {
+        let mut rows = Rows();
+        rows.map(|row| row).next()
+    }
+}
+
+fn main() {
+    let mut rows = Rows();
+    rows.next();
+}
diff --git a/tests/debuginfo/basic-types-globals-metadata.rs b/tests/debuginfo/basic-types-globals-metadata.rs
index d346b40..13678c3 100644
--- a/tests/debuginfo/basic-types-globals-metadata.rs
+++ b/tests/debuginfo/basic-types-globals-metadata.rs
@@ -1,5 +1,4 @@
 //@ min-lldb-version: 310
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ compile-flags:-g
 // gdb-command:run
diff --git a/tests/debuginfo/basic-types-metadata.rs b/tests/debuginfo/basic-types-metadata.rs
index 5f953c8..3aebf25 100644
--- a/tests/debuginfo/basic-types-metadata.rs
+++ b/tests/debuginfo/basic-types-metadata.rs
@@ -1,5 +1,4 @@
 //@ min-lldb-version: 310
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ compile-flags:-g
 // gdb-command:run
@@ -36,12 +35,12 @@
 // gdb-command:whatis f64
 // gdb-check:type = f64
 // gdb-command:whatis fnptr
-// gdb-check:type = [...] (*)([...])
+// gdb-check:type = *mut fn ()
 // gdb-command:info functions _yyy
 // gdbg-check:[...]![...]_yyy([...]);
-// gdbr-check:static fn basic_types_metadata::_yyy() -> !;
+// gdbr-check:static fn basic_types_metadata::_yyy();
 // gdb-command:ptype closure_0
-// gdbr-check: type = struct closure
+// gdbr-check: type = struct basic_types_metadata::main::{closure_env#0}
 // gdbg-check: type = struct closure {
 // gdbg-check:     <no data fields>
 // gdbg-check: }
@@ -49,18 +48,18 @@
 // gdbg-check: type = struct closure {
 // gdbg-check:     bool *__0;
 // gdbg-check: }
-// gdbr-check: type = struct closure (
-// gdbr-check:     bool *,
-// gdbr-check: )
+// gdbr-check: type = struct basic_types_metadata::main::{closure_env#1} {
+// gdbr-check:     *mut bool,
+// gdbr-check: }
 // gdb-command:ptype closure_2
 // gdbg-check: type = struct closure {
 // gdbg-check:     bool *__0;
 // gdbg-check:     isize *__1;
 // gdbg-check: }
-// gdbr-check: type = struct closure (
-// gdbr-check:     bool *,
-// gdbr-check:     isize *,
-// gdbr-check: )
+// gdbr-check: type = struct basic_types_metadata::main::{closure_env#2} {
+// gdbr-check:     *mut bool,
+// gdbr-check:     *mut isize,
+// gdbr-check: }
 
 //
 // gdb-command:continue
diff --git a/tests/debuginfo/basic-types.rs b/tests/debuginfo/basic-types.rs
index 10ffd74..d836525 100644
--- a/tests/debuginfo/basic-types.rs
+++ b/tests/debuginfo/basic-types.rs
@@ -6,9 +6,6 @@
 
 //@ min-lldb-version: 310
 
-// This fails on lldb 6.0.1 on x86-64 Fedora 28; so ignore Linux for now.
-//@ ignore-linux
-
 //@ compile-flags:-g
 
 // === GDB TESTS ===================================================================================
diff --git a/tests/debuginfo/by-value-non-immediate-argument.rs b/tests/debuginfo/by-value-non-immediate-argument.rs
index e0ae445..68717b7 100644
--- a/tests/debuginfo/by-value-non-immediate-argument.rs
+++ b/tests/debuginfo/by-value-non-immediate-argument.rs
@@ -1,6 +1,5 @@
-//@ ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
-//@ min-lldb-version: 310
-
+//@ min-lldb-version: 1800
+//@ min-gdb-version: 13.0
 //@ compile-flags:-g
 
 // === GDB TESTS ===================================================================================
@@ -42,11 +41,11 @@
 // lldb-command:run
 
 // lldb-command:v s
-// lldb-check:[...] Struct { a: 1, b: 2.5 }
+// lldb-check:[...] Struct { a = 1 b = 2.5 }
 // lldb-command:continue
 
 // lldb-command:v x
-// lldb-check:[...] Struct { a: 3, b: 4.5 }
+// lldb-check:[...] Struct { a = 3 b = 4.5 }
 // lldb-command:v y
 // lldb-check:[...] 5
 // lldb-command:v z
diff --git a/tests/debuginfo/c-style-enum.rs b/tests/debuginfo/c-style-enum.rs
index 395b94c..2ab5d5c 100644
--- a/tests/debuginfo/c-style-enum.rs
+++ b/tests/debuginfo/c-style-enum.rs
@@ -1,5 +1,4 @@
 //@ ignore-aarch64
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 //@ min-lldb-version: 310
 
 //@ compile-flags:-g
diff --git a/tests/debuginfo/cross-crate-spans.rs b/tests/debuginfo/cross-crate-spans.rs
index cf3f8e1..a93c8f4 100644
--- a/tests/debuginfo/cross-crate-spans.rs
+++ b/tests/debuginfo/cross-crate-spans.rs
@@ -3,10 +3,6 @@
 
 //@ min-lldb-version: 310
 
-// This fails on lldb 6.0.1 on x86-64 Fedora 28; so mark it macOS-only
-// for now.
-//@ only-macos
-
 //@ aux-build:cross_crate_spans.rs
 extern crate cross_crate_spans;
 
diff --git a/tests/debuginfo/destructured-for-loop-variable.rs b/tests/debuginfo/destructured-for-loop-variable.rs
index 1cad8bc..a8c7cc1 100644
--- a/tests/debuginfo/destructured-for-loop-variable.rs
+++ b/tests/debuginfo/destructured-for-loop-variable.rs
@@ -1,9 +1,5 @@
 //@ min-lldb-version: 310
 
-// This fails on lldb 6.0.1 on x86-64 Fedora 28; so mark it macOS-only
-// for now.
-//@ only-macos
-
 //@ compile-flags:-g
 
 // === GDB TESTS ===================================================================================
diff --git a/tests/debuginfo/drop-locations.rs b/tests/debuginfo/drop-locations.rs
index 15b2d0d..0777313 100644
--- a/tests/debuginfo/drop-locations.rs
+++ b/tests/debuginfo/drop-locations.rs
@@ -1,12 +1,11 @@
 //@ ignore-windows
 //@ ignore-android
-//@ ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 //@ min-lldb-version: 310
+//@ ignore-test: #128971
 
 #![allow(unused)]
 
-//@ compile-flags:-g -O -C no-prepopulate-passes
-// -O -C no-prepopulate-passes added to work around https://bugs.llvm.org/show_bug.cgi?id=32123
+//@ compile-flags:-g
 
 // This test checks that drop glue code gets attributed to scope's closing brace,
 // and function epilogues - to function's closing brace.
diff --git a/tests/debuginfo/function-arg-initialization.rs b/tests/debuginfo/function-arg-initialization.rs
index 05935c3..c641a35 100644
--- a/tests/debuginfo/function-arg-initialization.rs
+++ b/tests/debuginfo/function-arg-initialization.rs
@@ -1,6 +1,3 @@
-//@ ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
-//@ min-lldb-version: 310
-
 // This test case checks if function arguments already have the correct value
 // when breaking at the first line of the function, that is if the function
 // prologue has already been executed at the first line. Note that because of
@@ -8,7 +5,9 @@
 // arguments have been properly loaded when setting the breakpoint via the
 // function name.
 
-//@ compile-flags:-g
+//@ min-lldb-version: 1800
+//@ compile-flags:-g -Zmir-enable-passes=-SingleUseConsts
+// SingleUseConsts shouldn't need to be disabled, see #128945
 
 // === GDB TESTS ===================================================================================
 
diff --git a/tests/debuginfo/function-prologue-stepping-regular.rs b/tests/debuginfo/function-prologue-stepping-regular.rs
index a1f4410..07b9356 100644
--- a/tests/debuginfo/function-prologue-stepping-regular.rs
+++ b/tests/debuginfo/function-prologue-stepping-regular.rs
@@ -1,9 +1,8 @@
 // This test case checks if function arguments already have the correct value when breaking at the
 // beginning of a function.
 
-//@ min-lldb-version: 310
+//@ min-lldb-version: 1800
 //@ ignore-gdb
-//@ ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 //@ compile-flags:-g
 
 // lldb-command:breakpoint set --name immediate_args
diff --git a/tests/debuginfo/lexical-scopes-in-block-expression.rs b/tests/debuginfo/lexical-scopes-in-block-expression.rs
index 5ff7027..bd5a607 100644
--- a/tests/debuginfo/lexical-scopes-in-block-expression.rs
+++ b/tests/debuginfo/lexical-scopes-in-block-expression.rs
@@ -1,5 +1,4 @@
 //@ min-lldb-version: 310
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ compile-flags:-g
 
diff --git a/tests/debuginfo/limited-debuginfo.rs b/tests/debuginfo/limited-debuginfo.rs
index 32f1495..2e49acd 100644
--- a/tests/debuginfo/limited-debuginfo.rs
+++ b/tests/debuginfo/limited-debuginfo.rs
@@ -1,5 +1,4 @@
 //@ ignore-lldb
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ compile-flags:-C debuginfo=1
 
diff --git a/tests/debuginfo/macro-stepping.rs b/tests/debuginfo/macro-stepping.rs
index 5f8d828..3edac3c 100644
--- a/tests/debuginfo/macro-stepping.rs
+++ b/tests/debuginfo/macro-stepping.rs
@@ -1,8 +1,8 @@
 //@ ignore-windows
 //@ ignore-android
 //@ ignore-aarch64
-//@ min-lldb-version: 310
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
+//@ min-lldb-version: 1800
+//@ min-gdb-version: 13.0
 
 //@ aux-build:macro-stepping.rs
 
@@ -11,7 +11,8 @@
 #[macro_use]
 extern crate macro_stepping; // exports new_scope!()
 
-//@ compile-flags:-g
+//@ compile-flags:-g -Zmir-enable-passes=-SingleUseConsts
+// SingleUseConsts shouldn't need to be disabled, see #128945
 
 // === GDB TESTS ===================================================================================
 
diff --git a/tests/debuginfo/method-on-enum.rs b/tests/debuginfo/method-on-enum.rs
index 8a57060..7bee544 100644
--- a/tests/debuginfo/method-on-enum.rs
+++ b/tests/debuginfo/method-on-enum.rs
@@ -1,5 +1,5 @@
-//@ min-lldb-version: 310
-//@ ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
+//@ min-lldb-version: 1800
+//@ min-gdb-version: 13.0
 
 //@ compile-flags:-g
 
diff --git a/tests/debuginfo/msvc-pretty-enums.rs b/tests/debuginfo/msvc-pretty-enums.rs
index a6032cc..a2e2b32 100644
--- a/tests/debuginfo/msvc-pretty-enums.rs
+++ b/tests/debuginfo/msvc-pretty-enums.rs
@@ -206,6 +206,30 @@
 // cdb-command: dx -r2 arbitrary_discr2,d
 // cdb-check: arbitrary_discr2,d : Def [Type: enum2$<msvc_pretty_enums::ArbitraryDiscr>]
 // cdb-check:     [+0x[...]] __0              : 5678 [Type: unsigned int]
+//
+// cdb-command: dx c_style_u128_a
+// cdb-check: c_style_u128_a   : A [Type: enum2$<msvc_pretty_enums::CStyleU128>]
+//
+// cdb-command: dx c_style_u128_b
+// cdb-check: c_style_u128_b   : B [Type: enum2$<msvc_pretty_enums::CStyleU128>]
+//
+// cdb-command: dx c_style_u128_c
+// cdb-check: c_style_u128_c   : C [Type: enum2$<msvc_pretty_enums::CStyleU128>]
+//
+// cdb-command: dx c_style_u128_d
+// cdb-check: c_style_u128_d   : D [Type: enum2$<msvc_pretty_enums::CStyleU128>]
+//
+// cdb-command: dx c_style_i128_a
+// cdb-check: c_style_i128_a   : A [Type: enum2$<msvc_pretty_enums::CStyleI128>]
+//
+// cdb-command: dx c_style_i128_b
+// cdb-check: c_style_i128_b   : B [Type: enum2$<msvc_pretty_enums::CStyleI128>]
+//
+// cdb-command: dx c_style_i128_c
+// cdb-check: c_style_i128_c   : C [Type: enum2$<msvc_pretty_enums::CStyleI128>]
+//
+// cdb-command: dx c_style_i128_d
+// cdb-check: c_style_i128_d   : D [Type: enum2$<msvc_pretty_enums::CStyleI128>]
 #![feature(rustc_attrs)]
 #![feature(repr128)]
 #![feature(arbitrary_enum_discriminant)]
@@ -270,6 +294,22 @@ enum ArbitraryDiscr {
     Def(u32) = 5000_000,
 }
 
+#[repr(u128)]
+pub enum CStyleU128 {
+    A = 0_u128,
+    B = 1_u128,
+    C = u64::MAX as u128 + 1,
+    D = u128::MAX,
+}
+
+#[repr(i128)]
+pub enum CStyleI128 {
+    A = 0_i128,
+    B = -1_i128,
+    C = i128::MIN,
+    D = i128::MAX,
+}
+
 fn main() {
     let a = Some(CStyleEnum::Low);
     let b = Option::<CStyleEnum>::None;
@@ -313,6 +353,16 @@ fn main() {
     let arbitrary_discr1 = ArbitraryDiscr::Abc(1234);
     let arbitrary_discr2 = ArbitraryDiscr::Def(5678);
 
+    let c_style_u128_a = CStyleU128::A;
+    let c_style_u128_b = CStyleU128::B;
+    let c_style_u128_c = CStyleU128::C;
+    let c_style_u128_d = CStyleU128::D;
+
+    let c_style_i128_a = CStyleI128::A;
+    let c_style_i128_b = CStyleI128::B;
+    let c_style_i128_c = CStyleI128::C;
+    let c_style_i128_d = CStyleI128::D;
+
     zzz(); // #break
 }
 
diff --git a/tests/debuginfo/option-like-enum.rs b/tests/debuginfo/option-like-enum.rs
index c782796..da556d6 100644
--- a/tests/debuginfo/option-like-enum.rs
+++ b/tests/debuginfo/option-like-enum.rs
@@ -1,6 +1,5 @@
-//@ ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
-
-//@ min-lldb-version: 310
+//@ min-lldb-version: 1800
+//@ min-gdb-version: 13.0
 
 //@ compile-flags:-g
 
@@ -22,7 +21,7 @@
 
 // gdbg-command:print empty_gdb->discr
 // gdbr-command:print empty_gdb.discr
-// gdb-check:$4 = (isize *) 0x0
+// gdb-check:$4 = (*mut isize) 0x1
 
 // gdb-command:print droid
 // gdbg-check:$5 = {RUST$ENCODED$ENUM$2$Void = {id = 675675, range = 10000001, internals = 0x43218765}}
@@ -30,11 +29,11 @@
 
 // gdbg-command:print void_droid_gdb->internals
 // gdbr-command:print void_droid_gdb.internals
-// gdb-check:$6 = (isize *) 0x0
+// gdb-check:$6 = (*mut isize) 0x1
 
 // gdb-command:print nested_non_zero_yep
 // gdbg-check:$7 = {RUST$ENCODED$ENUM$1$2$Nope = {__0 = 10.5, __1 = {a = 10, b = 20, c = [...]}}}
-// gdbr-check:$7 = option_like_enum::NestedNonZero::Yep(10.5, option_like_enum::NestedNonZeroField {a: 10, b: 20, c: 0x[...] "x[...]"})
+// gdbr-check:$7 = option_like_enum::NestedNonZero::Yep(10.5, option_like_enum::NestedNonZeroField {a: 10, b: 20, c: 0x[...]})
 
 // gdb-command:print nested_non_zero_nope
 // gdbg-check:$8 = {RUST$ENCODED$ENUM$1$2$Nope = {__0 = [...], __1 = {a = [...], b = [...], c = 0x0}}}
diff --git a/tests/debuginfo/pretty-huge-vec.rs b/tests/debuginfo/pretty-huge-vec.rs
index f4b5345..dcf3521 100644
--- a/tests/debuginfo/pretty-huge-vec.rs
+++ b/tests/debuginfo/pretty-huge-vec.rs
@@ -1,5 +1,4 @@
 //@ ignore-windows failing on win32 bot
-//@ ignore-freebsd: gdb package too new
 //@ ignore-android: FIXME(#10381)
 //@ compile-flags:-g
 //@ min-gdb-version: 8.1
diff --git a/tests/debuginfo/pretty-std-collections.rs b/tests/debuginfo/pretty-std-collections.rs
index e9c2c4f..3d5a30d 100644
--- a/tests/debuginfo/pretty-std-collections.rs
+++ b/tests/debuginfo/pretty-std-collections.rs
@@ -1,6 +1,6 @@
 //@ ignore-windows failing on win32 bot
-//@ ignore-freebsd: gdb package too new
 //@ ignore-android: FIXME(#10381)
+//@ ignore-windows-gnu: #128981
 //@ compile-flags:-g
 
 // The pretty printers being tested here require the patch from
diff --git a/tests/debuginfo/pretty-std.rs b/tests/debuginfo/pretty-std.rs
index 45c6dbf..933be97 100644
--- a/tests/debuginfo/pretty-std.rs
+++ b/tests/debuginfo/pretty-std.rs
@@ -1,10 +1,9 @@
 // ignore-tidy-linelength
-//@ ignore-freebsd: gdb package too new
-//@ only-cdb // "Temporarily" ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155
+//@ ignore-windows-gnu: #128981
 //@ ignore-android: FIXME(#10381)
 //@ compile-flags:-g
 //@ min-gdb-version: 7.7
-//@ min-lldb-version: 310
+//@ min-lldb-version: 1800
 //@ min-cdb-version: 10.0.18317.1001
 
 // === GDB TESTS ===================================================================================
@@ -12,10 +11,10 @@
 // gdb-command: run
 
 // gdb-command: print slice
-// gdb-check:$1 = &[i32](len: 4) = {0, 1, 2, 3}
+// gdb-check:$1 = &[i32](size=4) = {0, 1, 2, 3}
 
 // gdb-command: print vec
-// gdb-check:$2 = Vec<u64, alloc::alloc::Global>(len: 4, cap: [...]) = {4, 5, 6, 7}
+// gdb-check:$2 = Vec(size=4) = {4, 5, 6, 7}
 
 // gdb-command: print str_slice
 // gdb-check:$3 = "IAMA string slice!"
@@ -24,37 +23,37 @@
 // gdb-check:$4 = "IAMA string!"
 
 // gdb-command: print some
-// gdb-check:$5 = Some = {8}
+// gdb-check:$5 = core::option::Option<i16>::Some(8)
 
 // gdb-command: print none
 // gdbg-check:$6 = None
-// gdbr-check:$6 = core::option::Option::None
+// gdbr-check:$6 = core::option::Option<i64>::None
 
 // gdb-command: print os_string
 // gdb-check:$7 = "IAMA OS string 😃"
 
 // gdb-command: print some_string
-// gdb-check:$8 = Some = {"IAMA optional string!"}
+// gdb-check:$8 = core::option::Option<alloc::string::String>::Some("IAMA optional string!")
 
-// gdb-command: set print length 5
+// gdb-command: set print elements 5
 // gdb-command: print some_string
-// gdb-check:$8 = Some = {"IAMA "...}
+// gdb-check:$9 = core::option::Option<alloc::string::String>::Some("IAMA "...)
 
 // === LLDB TESTS ==================================================================================
 
 // lldb-command:run
 
 // lldb-command:v slice
-// lldb-check:[...] slice = &[0, 1, 2, 3]
+// lldb-check:[...] slice = size=4 { [0] = 0 [1] = 1 [2] = 2 [3] = 3 }
 
 // lldb-command:v vec
-// lldb-check:[...] vec = vec![4, 5, 6, 7]
+// lldb-check:[...] vec = size=4 { [0] = 4 [1] = 5 [2] = 6 [3] = 7 }
 
 // lldb-command:v str_slice
-// lldb-check:[...] str_slice = "IAMA string slice!"
+// lldb-check:[...] str_slice = "IAMA string slice!" { [0] = 'I' [1] = 'A' [2] = 'M' [3] = 'A' [4] = ' ' [5] = 's' [6] = 't' [7] = 'r' [8] = 'i' [9] = 'n' [10] = 'g' [11] = ' ' [12] = 's' [13] = 'l' [14] = 'i' [15] = 'c' [16] = 'e' [17] = '!' }
 
 // lldb-command:v string
-// lldb-check:[...] string = "IAMA string!"
+// lldb-check:[...] string = "IAMA string!" { vec = size=12 { [0] = 'I' [1] = 'A' [2] = 'M' [3] = 'A' [4] = ' ' [5] = 's' [6] = 't' [7] = 'r' [8] = 'i' [9] = 'n' [10] = 'g' [11] = '!' } }
 
 // lldb-command:v some
 // lldb-check:[...] some = Some(8)
@@ -63,7 +62,7 @@
 // lldb-check:[...] none = None
 
 // lldb-command:v os_string
-// lldb-check:[...] os_string = "IAMA OS string 😃"[...]
+// lldb-check:[...] os_string = "IAMA OS string 😃" { inner = { inner = size=19 { [0] = 'I' [1] = 'A' [2] = 'M' [3] = 'A' [4] = ' ' [5] = 'O' [6] = 'S' [7] = ' ' [8] = 's' [9] = 't' [10] = 'r' [11] = 'i' [12] = 'n' [13] = 'g' [14] = ' ' [15] = '\xf0' [16] = '\x9f' [17] = '\x98' [18] = '\x83' } } }
 
 // === CDB TESTS ==================================================================================
 
diff --git a/tests/debuginfo/pretty-uninitialized-vec.rs b/tests/debuginfo/pretty-uninitialized-vec.rs
index 225b4a6..5206ff2 100644
--- a/tests/debuginfo/pretty-uninitialized-vec.rs
+++ b/tests/debuginfo/pretty-uninitialized-vec.rs
@@ -1,5 +1,4 @@
 //@ ignore-windows failing on win32 bot
-//@ ignore-freebsd: gdb package too new
 //@ ignore-android: FIXME(#10381)
 //@ compile-flags:-g
 //@ min-gdb-version: 8.1
diff --git a/tests/debuginfo/rc_arc.rs b/tests/debuginfo/rc_arc.rs
index ca0feb1..688dc62 100644
--- a/tests/debuginfo/rc_arc.rs
+++ b/tests/debuginfo/rc_arc.rs
@@ -1,4 +1,4 @@
-//@ ignore-windows-gnu: pretty-printers are not loaded
+//@ ignore-windows-gnu: #128981
 //@ compile-flags:-g
 
 //@ min-gdb-version: 8.1
diff --git a/tests/debuginfo/simple-struct.rs b/tests/debuginfo/simple-struct.rs
index 968a5c6..aaa654a 100644
--- a/tests/debuginfo/simple-struct.rs
+++ b/tests/debuginfo/simple-struct.rs
@@ -1,5 +1,4 @@
 //@ min-lldb-version: 310
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ compile-flags: -g -Zmir-enable-passes=-CheckAlignment
 
diff --git a/tests/debuginfo/simple-tuple.rs b/tests/debuginfo/simple-tuple.rs
index 8600310..9d809a1 100644
--- a/tests/debuginfo/simple-tuple.rs
+++ b/tests/debuginfo/simple-tuple.rs
@@ -1,5 +1,4 @@
 //@ min-lldb-version: 310
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ compile-flags:-g
 
diff --git a/tests/debuginfo/struct-in-enum.rs b/tests/debuginfo/struct-in-enum.rs
index 52e419e..e4b647a 100644
--- a/tests/debuginfo/struct-in-enum.rs
+++ b/tests/debuginfo/struct-in-enum.rs
@@ -1,6 +1,5 @@
-//@ min-lldb-version: 310
+//@ min-lldb-version: 1800
 //@ ignore-gdb-version: 7.11.90 - 7.12.9
-//@ ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ compile-flags:-g
 
diff --git a/tests/debuginfo/union-smoke.rs b/tests/debuginfo/union-smoke.rs
index 9b1cf6e..d786ba6 100644
--- a/tests/debuginfo/union-smoke.rs
+++ b/tests/debuginfo/union-smoke.rs
@@ -1,5 +1,4 @@
 //@ min-lldb-version: 310
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ ignore-gdb-version: 7.11.90 - 7.12.9
 
diff --git a/tests/debuginfo/vec.rs b/tests/debuginfo/vec.rs
index cf7de0b..20cfd78 100644
--- a/tests/debuginfo/vec.rs
+++ b/tests/debuginfo/vec.rs
@@ -1,5 +1,4 @@
 //@ min-lldb-version: 310
-//@ ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
 
 //@ compile-flags:-g
 
diff --git a/tests/run-make/crate-loading/multiple-dep-versions-1.rs b/tests/run-make/crate-loading/multiple-dep-versions-1.rs
index 2d35163..d814625 100644
--- a/tests/run-make/crate-loading/multiple-dep-versions-1.rs
+++ b/tests/run-make/crate-loading/multiple-dep-versions-1.rs
@@ -1,6 +1,12 @@
 #![crate_name = "dependency"]
 #![crate_type = "rlib"]
-pub struct Type;
-pub trait Trait {}
-impl Trait for Type {}
+pub struct Type(pub i32);
+pub trait Trait {
+    fn foo(&self);
+    fn bar();
+}
+impl Trait for Type {
+    fn foo(&self) {}
+    fn bar() {}
+}
 pub fn do_something<X: Trait>(_: X) {}
diff --git a/tests/run-make/crate-loading/multiple-dep-versions-2.rs b/tests/run-make/crate-loading/multiple-dep-versions-2.rs
index a5df3dc..0a566fe 100644
--- a/tests/run-make/crate-loading/multiple-dep-versions-2.rs
+++ b/tests/run-make/crate-loading/multiple-dep-versions-2.rs
@@ -1,6 +1,12 @@
 #![crate_name = "dependency"]
 #![crate_type = "rlib"]
-pub struct Type(pub i32);
-pub trait Trait {}
-impl Trait for Type {}
+pub struct Type;
+pub trait Trait {
+    fn foo(&self);
+    fn bar();
+}
+impl Trait for Type {
+    fn foo(&self) {}
+    fn bar() {}
+}
 pub fn do_something<X: Trait>(_: X) {}
diff --git a/tests/run-make/crate-loading/multiple-dep-versions-3.rs b/tests/run-make/crate-loading/multiple-dep-versions-3.rs
new file mode 100644
index 0000000..07d888e
--- /dev/null
+++ b/tests/run-make/crate-loading/multiple-dep-versions-3.rs
@@ -0,0 +1,5 @@
+#![crate_name = "foo"]
+#![crate_type = "rlib"]
+
+extern crate dependency;
+pub use dependency::Type;
diff --git a/tests/run-make/crate-loading/multiple-dep-versions.rs b/tests/run-make/crate-loading/multiple-dep-versions.rs
index 5a6cb03..8ef042b 100644
--- a/tests/run-make/crate-loading/multiple-dep-versions.rs
+++ b/tests/run-make/crate-loading/multiple-dep-versions.rs
@@ -1,8 +1,10 @@
 extern crate dep_2_reexport;
 extern crate dependency;
-use dep_2_reexport::do_something;
-use dependency::Type;
+use dep_2_reexport::Type;
+use dependency::{do_something, Trait};
 
 fn main() {
     do_something(Type);
+    Type.foo();
+    Type::bar();
 }
diff --git a/tests/run-make/crate-loading/rmake.rs b/tests/run-make/crate-loading/rmake.rs
index d7abd58..13585ed 100644
--- a/tests/run-make/crate-loading/rmake.rs
+++ b/tests/run-make/crate-loading/rmake.rs
@@ -1,26 +1,100 @@
 //@ only-linux
 //@ ignore-wasm32
 //@ ignore-wasm64
+// ignore-tidy-linelength
 
 use run_make_support::{rust_lib_name, rustc};
 
 fn main() {
     rustc().input("multiple-dep-versions-1.rs").run();
     rustc().input("multiple-dep-versions-2.rs").extra_filename("2").metadata("2").run();
+    rustc()
+        .input("multiple-dep-versions-3.rs")
+        .extern_("dependency", rust_lib_name("dependency2"))
+        .run();
 
     rustc()
         .input("multiple-dep-versions.rs")
         .extern_("dependency", rust_lib_name("dependency"))
-        .extern_("dep_2_reexport", rust_lib_name("dependency2"))
+        .extern_("dep_2_reexport", rust_lib_name("foo"))
         .run_fail()
         .assert_stderr_contains(
-            "you have multiple different versions of crate `dependency` in your dependency graph",
+            r#"error[E0277]: the trait bound `dep_2_reexport::Type: Trait` is not satisfied
+  --> multiple-dep-versions.rs:7:18
+   |
+7  |     do_something(Type);
+   |     ------------ ^^^^ the trait `Trait` is not implemented for `dep_2_reexport::Type`
+   |     |
+   |     required by a bound introduced by this call
+   |
+help: there are multiple different versions of crate `dependency` the your dependency graph
+  --> multiple-dep-versions.rs:1:1
+   |
+1  | extern crate dep_2_reexport;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a dependency of crate `foo`
+2  | extern crate dependency;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `dependency` is used here, as a direct dependency of the current crate"#,
         )
         .assert_stderr_contains(
-            "two types coming from two different versions of the same crate are different types \
-             even if they look the same",
+            r#"
+3  | pub struct Type(pub i32);
+   | ^^^^^^^^^^^^^^^ this type implements the required trait
+4  | pub trait Trait {
+   | --------------- this is the required trait"#,
         )
-        .assert_stderr_contains("this type doesn't implement the required trait")
-        .assert_stderr_contains("this type implements the required trait")
-        .assert_stderr_contains("this is the required trait");
+        .assert_stderr_contains(
+            r#"
+3  | pub struct Type;
+   | ^^^^^^^^^^^^^^^ this type doesn't implement the required trait"#,
+        )
+        .assert_stderr_contains(
+            r#"
+error[E0599]: no method named `foo` found for struct `dep_2_reexport::Type` in the current scope
+ --> multiple-dep-versions.rs:8:10
+  |
+8 |     Type.foo();
+  |          ^^^ method not found in `Type`
+  |
+note: there are multiple different versions of crate `dependency` in the dependency graph"#,
+        )
+        .assert_stderr_contains(
+            r#"
+4 | pub trait Trait {
+  | ^^^^^^^^^^^^^^^ this is the trait that is needed
+5 |     fn foo(&self);
+  |     -------------- the method is available for `dep_2_reexport::Type` here
+  |
+ ::: multiple-dep-versions.rs:4:32
+  |
+4 | use dependency::{do_something, Trait};
+  |                                ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#,
+        )
+        .assert_stderr_contains(
+            r#"
+4 | pub trait Trait {
+  | --------------- this is the trait that was imported"#,
+        )
+        .assert_stderr_contains(
+            r#"
+error[E0599]: no function or associated item named `bar` found for struct `dep_2_reexport::Type` in the current scope
+ --> multiple-dep-versions.rs:9:11
+  |
+9 |     Type::bar();
+  |           ^^^ function or associated item not found in `Type`
+  |
+note: there are multiple different versions of crate `dependency` in the dependency graph"#,
+        )
+        .assert_stderr_contains(
+            r#"
+4 | pub trait Trait {
+  | ^^^^^^^^^^^^^^^ this is the trait that is needed
+5 |     fn foo(&self);
+6 |     fn bar();
+  |     --------- the associated function is available for `dep_2_reexport::Type` here
+  |
+ ::: multiple-dep-versions.rs:4:32
+  |
+4 | use dependency::{do_something, Trait};
+  |                                ----- `Trait` imported here doesn't correspond to the right version of crate `dependency`"#,
+        );
 }
diff --git a/tests/run-make/dep-info-doesnt-run-much/Makefile b/tests/run-make/dep-info-doesnt-run-much/Makefile
deleted file mode 100644
index b4dc44a..0000000
--- a/tests/run-make/dep-info-doesnt-run-much/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-include ../tools.mk
-
-all:
-	$(RUSTC) foo.rs --emit dep-info
diff --git a/tests/run-make/dep-info-spaces/Makefile b/tests/run-make/dep-info-spaces/Makefile
deleted file mode 100644
index 0cfe513..0000000
--- a/tests/run-make/dep-info-spaces/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-include ../tools.mk
-
-# ignore-windows
-# ignore-freebsd
-# FIXME: (windows: see `../dep-info/Makefile`)
-
-all:
-	cp lib.rs $(TMPDIR)/
-	cp 'foo foo.rs' $(TMPDIR)/
-	cp bar.rs $(TMPDIR)/
-	$(RUSTC) --emit link,dep-info --crate-type=lib $(TMPDIR)/lib.rs
-	sleep 1
-	touch $(TMPDIR)/'foo foo.rs'
-	-rm -f $(TMPDIR)/done
-	$(MAKE) -drf Makefile.foo
-	rm $(TMPDIR)/done
-	pwd
-	$(MAKE) -drf Makefile.foo
-	rm $(TMPDIR)/done && exit 1 || exit 0
diff --git a/tests/run-make/dep-info-spaces/Makefile.foo b/tests/run-make/dep-info-spaces/Makefile.foo
deleted file mode 100644
index 80a5d43..0000000
--- a/tests/run-make/dep-info-spaces/Makefile.foo
+++ /dev/null
@@ -1,7 +0,0 @@
-LIB := $(shell $(RUSTC) --print file-names --crate-type=lib $(TMPDIR)/lib.rs)
-
-$(TMPDIR)/$(LIB):
-	$(RUSTC) --emit link,dep-info --crate-type=lib $(TMPDIR)/lib.rs
-	touch $(TMPDIR)/done
-
--include $(TMPDIR)/lib.d
diff --git a/tests/run-make/dep-info-spaces/bar.rs b/tests/run-make/dep-info-spaces/bar.rs
deleted file mode 100644
index c5c0bc6..0000000
--- a/tests/run-make/dep-info-spaces/bar.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub fn bar() {}
diff --git a/tests/run-make/dep-info/Makefile b/tests/run-make/dep-info/Makefile
deleted file mode 100644
index c76f43a..0000000
--- a/tests/run-make/dep-info/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-include ../tools.mk
-
-# ignore-windows
-# ignore-freebsd
-# FIXME: on windows `rustc --dep-info` produces Makefile dependency with
-# windows native paths (e.g. `c:\path\to\libfoo.a`)
-# but msys make seems to fail to recognize such paths, so test fails.
-
-all:
-	cp *.rs $(TMPDIR)
-	$(RUSTC) --emit dep-info,link --crate-type=lib $(TMPDIR)/lib.rs
-	sleep 2
-	touch $(TMPDIR)/foo.rs
-	-rm -f $(TMPDIR)/done
-	$(MAKE) -drf Makefile.foo
-	sleep 2
-	rm $(TMPDIR)/done
-	pwd
-	$(MAKE) -drf Makefile.foo
-	rm $(TMPDIR)/done && exit 1 || exit 0
-
-	# When a source file is deleted `make` should still work
-	rm $(TMPDIR)/bar.rs
-	cp $(TMPDIR)/lib2.rs $(TMPDIR)/lib.rs
-	$(MAKE) -drf Makefile.foo
diff --git a/tests/run-make/dep-info/Makefile.foo b/tests/run-make/dep-info/Makefile.foo
deleted file mode 100644
index e5df31f..0000000
--- a/tests/run-make/dep-info/Makefile.foo
+++ /dev/null
@@ -1,7 +0,0 @@
-LIB := $(shell $(RUSTC) --print file-names --crate-type=lib lib.rs)
-
-$(TMPDIR)/$(LIB):
-	$(RUSTC) --emit dep-info,link --crate-type=lib lib.rs
-	touch $(TMPDIR)/done
-
--include $(TMPDIR)/foo.d
diff --git a/tests/run-make/dep-info-doesnt-run-much/foo.rs b/tests/run-make/dep-info/erroneous.rs
similarity index 100%
rename from tests/run-make/dep-info-doesnt-run-much/foo.rs
rename to tests/run-make/dep-info/erroneous.rs
diff --git a/tests/run-make/dep-info-spaces/foo foo.rs b/tests/run-make/dep-info/foo foo.rs
similarity index 100%
rename from tests/run-make/dep-info-spaces/foo foo.rs
rename to tests/run-make/dep-info/foo foo.rs
diff --git a/tests/run-make/dep-info-spaces/lib.rs b/tests/run-make/dep-info/lib_foofoo.rs
similarity index 100%
rename from tests/run-make/dep-info-spaces/lib.rs
rename to tests/run-make/dep-info/lib_foofoo.rs
diff --git a/tests/run-make/dep-info/rmake.rs b/tests/run-make/dep-info/rmake.rs
new file mode 100644
index 0000000..508569b
--- /dev/null
+++ b/tests/run-make/dep-info/rmake.rs
@@ -0,0 +1,37 @@
+// This is a simple smoke test for rustc's `--emit dep-info` feature. It prints out
+// information about dependencies in a Makefile-compatible format, as a `.d` file.
+// Note that this test does not check that the `.d` file is Makefile-compatible.
+
+// This test first checks that emitting dep-info disables static analysis, preventing
+// compilation of `erroneous.rs` from causing a compilation failure.
+// Then, it checks that compilation using the flag is successful in general, even with
+// empty source files or source files that contain a whitespace character.
+
+// Finally, it removes one dependency and checks that compilation is still successful.
+// See https://github.com/rust-lang/rust/pull/10698
+
+use run_make_support::{rfs, rustc};
+
+fn main() {
+    // We're only emitting dep info, so we shouldn't be running static analysis to
+    // figure out that this program is erroneous.
+    rustc().input("erroneous.rs").emit("dep-info").run();
+
+    rustc().input("lib.rs").emit("dep-info,link").crate_type("lib").run();
+    rfs::remove_file("foo.rs");
+    rfs::create_file("foo.rs");
+    // Compilation should succeed even if `foo.rs` is empty.
+    rustc().input("lib.rs").emit("dep-info,link").crate_type("lib").run();
+
+    // Again, with a space in the filename this time around.
+    rustc().input("lib_foofoo.rs").emit("dep-info,link").crate_type("lib").run();
+    rfs::remove_file("foo foo.rs");
+    rfs::create_file("foo foo.rs");
+    // Compilation should succeed even if `foo foo.rs` is empty.
+    rustc().input("lib_foofoo.rs").emit("dep-info,link").crate_type("lib").run();
+
+    // When a source file is deleted, compilation should still succeed if the library
+    // also loses this source file dependency.
+    rfs::remove_file("bar.rs");
+    rustc().input("lib2.rs").emit("dep-info,link").crate_type("lib").run();
+}
diff --git a/tests/run-make/deref-impl-rustdoc-ice/rmake.rs b/tests/run-make/deref-impl-rustdoc-ice/rmake.rs
index 91fc0a9..0ad5934 100644
--- a/tests/run-make/deref-impl-rustdoc-ice/rmake.rs
+++ b/tests/run-make/deref-impl-rustdoc-ice/rmake.rs
@@ -12,5 +12,5 @@
 fn main() {
     rustc().input("foo.rs").run();
     rustc().input("bar.rs").run();
-    rustdoc().input("baz.rs").library_search_path(cwd()).output(cwd()).run();
+    rustdoc().input("baz.rs").library_search_path(cwd()).out_dir(cwd()).run();
 }
diff --git a/tests/run-make/emit-shared-files/rmake.rs b/tests/run-make/emit-shared-files/rmake.rs
index 8ac9073..e5482af 100644
--- a/tests/run-make/emit-shared-files/rmake.rs
+++ b/tests/run-make/emit-shared-files/rmake.rs
@@ -13,7 +13,7 @@ fn main() {
     rustdoc()
         .arg("-Zunstable-options")
         .arg("--emit=invocation-specific")
-        .output("invocation-only")
+        .out_dir("invocation-only")
         .arg("--resource-suffix=-xxx")
         .args(&["--theme", "y.css"])
         .args(&["--extend-css", "z.css"])
@@ -34,7 +34,7 @@ fn main() {
     rustdoc()
         .arg("-Zunstable-options")
         .arg("--emit=toolchain-shared-resources")
-        .output("toolchain-only")
+        .out_dir("toolchain-only")
         .arg("--resource-suffix=-xxx")
         .args(&["--extend-css", "z.css"])
         .input("x.rs")
@@ -68,7 +68,7 @@ fn main() {
     rustdoc()
         .arg("-Zunstable-options")
         .arg("--emit=toolchain-shared-resources,unversioned-shared-resources")
-        .output("all-shared")
+        .out_dir("all-shared")
         .arg("--resource-suffix=-xxx")
         .args(&["--extend-css", "z.css"])
         .input("x.rs")
diff --git a/tests/run-make/exit-code/rmake.rs b/tests/run-make/exit-code/rmake.rs
index f290554..d3dcc04 100644
--- a/tests/run-make/exit-code/rmake.rs
+++ b/tests/run-make/exit-code/rmake.rs
@@ -16,7 +16,7 @@ fn main() {
         .run_fail()
         .assert_exit_code(101);
 
-    rustdoc().arg("success.rs").output("exit-code").run();
+    rustdoc().arg("success.rs").out_dir("exit-code").run();
 
     rustdoc().arg("--invalid-arg-foo").run_fail().assert_exit_code(1);
 
diff --git a/tests/run-make/libtest-json/Makefile b/tests/run-make/libtest-json/Makefile
deleted file mode 100644
index c8bc7b5..0000000
--- a/tests/run-make/libtest-json/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# ignore-cross-compile
-# needs-unwind
-include ../tools.mk
-
-# Test expected libtest's JSON output
-
-OUTPUT_FILE_DEFAULT := $(TMPDIR)/libtest-json-output-default.json
-OUTPUT_FILE_STDOUT_SUCCESS := $(TMPDIR)/libtest-json-output-stdout-success.json
-
-all: f.rs validate_json.py output-default.json output-stdout-success.json
-	$(RUSTC) --test f.rs
-	RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=json > $(OUTPUT_FILE_DEFAULT) || true
-	RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=json --show-output > $(OUTPUT_FILE_STDOUT_SUCCESS) || true
-
-	cat $(OUTPUT_FILE_DEFAULT) | "$(PYTHON)" validate_json.py
-	cat $(OUTPUT_FILE_STDOUT_SUCCESS) | "$(PYTHON)" validate_json.py
-
-	# Normalize the actual output and compare to expected output file
-	cat $(OUTPUT_FILE_DEFAULT) | sed 's/"exec_time": [0-9.]*/"exec_time": $$TIME/' | diff output-default.json -
-	cat $(OUTPUT_FILE_STDOUT_SUCCESS) | sed 's/"exec_time": [0-9.]*/"exec_time": $$TIME/' | diff output-stdout-success.json -
diff --git a/tests/run-make/libtest-json/output-default.json b/tests/run-make/libtest-json/output-default.json
index 01710f5..a2293a0 100644
--- a/tests/run-make/libtest-json/output-default.json
+++ b/tests/run-make/libtest-json/output-default.json
@@ -7,4 +7,4 @@
 { "type": "test", "name": "c", "event": "ok" }
 { "type": "test", "event": "started", "name": "d" }
 { "type": "test", "name": "d", "event": "ignored", "message": "msg" }
-{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
+{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": "$EXEC_TIME" }
diff --git a/tests/run-make/libtest-json/output-stdout-success.json b/tests/run-make/libtest-json/output-stdout-success.json
index 878eb6c..cf92f01 100644
--- a/tests/run-make/libtest-json/output-stdout-success.json
+++ b/tests/run-make/libtest-json/output-stdout-success.json
@@ -7,4 +7,4 @@
 { "type": "test", "name": "c", "event": "ok", "stdout": "thread 'c' panicked at f.rs:15:5:\nassertion failed: false\n" }
 { "type": "test", "event": "started", "name": "d" }
 { "type": "test", "name": "d", "event": "ignored", "message": "msg" }
-{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
+{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": "$EXEC_TIME" }
diff --git a/tests/run-make/libtest-json/rmake.rs b/tests/run-make/libtest-json/rmake.rs
new file mode 100644
index 0000000..acbd88d
--- /dev/null
+++ b/tests/run-make/libtest-json/rmake.rs
@@ -0,0 +1,31 @@
+// Check libtest's JSON output against snapshots.
+
+//@ ignore-cross-compile
+//@ needs-unwind (test file contains #[should_panic] test)
+
+use run_make_support::{cmd, diff, python_command, rustc};
+
+fn main() {
+    rustc().arg("--test").input("f.rs").run();
+
+    run_tests(&[], "output-default.json");
+    run_tests(&["--show-output"], "output-stdout-success.json");
+}
+
+#[track_caller]
+fn run_tests(extra_args: &[&str], expected_file: &str) {
+    let cmd_out = cmd("./f")
+        .env("RUST_BACKTRACE", "0")
+        .args(&["-Zunstable-options", "--test-threads=1", "--format=json"])
+        .args(extra_args)
+        .run_fail();
+    let test_stdout = &cmd_out.stdout_utf8();
+
+    python_command().arg("validate_json.py").stdin(test_stdout).run();
+
+    diff()
+        .expected_file(expected_file)
+        .actual_text("stdout", test_stdout)
+        .normalize(r#"(?<prefix>"exec_time": )[0-9.]+"#, r#"${prefix}"$$EXEC_TIME""#)
+        .run();
+}
diff --git a/tests/run-make/libtest-junit/Makefile b/tests/run-make/libtest-junit/Makefile
deleted file mode 100644
index 26e5624..0000000
--- a/tests/run-make/libtest-junit/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# ignore-cross-compile
-# needs-unwind contains should_panic test
-include ../tools.mk
-
-# Test expected libtest's junit output
-
-OUTPUT_FILE_DEFAULT := $(TMPDIR)/libtest-junit-output-default.xml
-OUTPUT_FILE_STDOUT_SUCCESS := $(TMPDIR)/libtest-junit-output-stdout-success.xml
-
-all: f.rs validate_junit.py output-default.xml output-stdout-success.xml
-	$(RUSTC) --test f.rs
-	RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=junit > $(OUTPUT_FILE_DEFAULT) || true
-	RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=junit --show-output > $(OUTPUT_FILE_STDOUT_SUCCESS) || true
-
-	cat $(OUTPUT_FILE_DEFAULT) | "$(PYTHON)" validate_junit.py
-	cat $(OUTPUT_FILE_STDOUT_SUCCESS) | "$(PYTHON)" validate_junit.py
-
-	# Normalize the actual output and compare to expected output file
-	cat $(OUTPUT_FILE_DEFAULT) | sed 's/time="[0-9.]*"/time="$$TIME"/g' | diff output-default.xml -
-	cat $(OUTPUT_FILE_STDOUT_SUCCESS) | sed 's/time="[0-9.]*"/time="$$TIME"/g' | diff output-stdout-success.xml -
diff --git a/tests/run-make/libtest-junit/rmake.rs b/tests/run-make/libtest-junit/rmake.rs
new file mode 100644
index 0000000..d631313
--- /dev/null
+++ b/tests/run-make/libtest-junit/rmake.rs
@@ -0,0 +1,31 @@
+// Check libtest's JUnit (XML) output against snapshots.
+
+//@ ignore-cross-compile
+//@ needs-unwind (test file contains #[should_panic] test)
+
+use run_make_support::{cmd, diff, python_command, rustc};
+
+fn main() {
+    rustc().arg("--test").input("f.rs").run();
+
+    run_tests(&[], "output-default.xml");
+    run_tests(&["--show-output"], "output-stdout-success.xml");
+}
+
+#[track_caller]
+fn run_tests(extra_args: &[&str], expected_file: &str) {
+    let cmd_out = cmd("./f")
+        .env("RUST_BACKTRACE", "0")
+        .args(&["-Zunstable-options", "--test-threads=1", "--format=junit"])
+        .args(extra_args)
+        .run_fail();
+    let test_stdout = &cmd_out.stdout_utf8();
+
+    python_command().arg("validate_junit.py").stdin(test_stdout).run();
+
+    diff()
+        .expected_file(expected_file)
+        .actual_text("stdout", test_stdout)
+        .normalize(r#"\btime="[0-9.]+""#, r#"time="$$TIME""#)
+        .run();
+}
diff --git a/tests/run-make/native-link-modifier-bundle/Makefile b/tests/run-make/native-link-modifier-bundle/Makefile
deleted file mode 100644
index 5277209..0000000
--- a/tests/run-make/native-link-modifier-bundle/Makefile
+++ /dev/null
@@ -1,38 +0,0 @@
-# ignore-cross-compile
-# ignore-windows-msvc
-
-include ../tools.mk
-
-# We're using the llvm-nm instead of the system nm to ensure it is compatible
-# with the LLVM bitcode generated by rustc.
-# Except on Windows where piping/IO redirection under MSYS2 is wonky with llvm-nm.
-ifndef IS_WINDOWS
-NM = "$(LLVM_BIN_DIR)"/llvm-nm
-else
-NM = nm
-endif
-
-all: $(call NATIVE_STATICLIB,native-staticlib)
-	# Build a staticlib and a rlib, the `native_func` symbol will be bundled into them
-	$(RUSTC) bundled.rs --crate-type=staticlib --crate-type=rlib
-	$(NM) $(TMPDIR)/libbundled.a | $(CGREP) -e "T _*native_func"
-	$(NM) $(TMPDIR)/libbundled.a | $(CGREP) -e "U _*native_func"
-	$(NM) $(TMPDIR)/libbundled.rlib | $(CGREP) -e "T _*native_func"
-	$(NM) $(TMPDIR)/libbundled.rlib | $(CGREP) -e "U _*native_func"
-
-	# Build a staticlib and a rlib, the `native_func` symbol will not be bundled into it
-	$(RUSTC) non-bundled.rs --crate-type=staticlib --crate-type=rlib
-	$(NM) $(TMPDIR)/libnon_bundled.a | $(CGREP) -ve "T _*native_func"
-	$(NM) $(TMPDIR)/libnon_bundled.a | $(CGREP) -e "U _*native_func"
-	$(NM) $(TMPDIR)/libnon_bundled.rlib | $(CGREP) -ve "T _*native_func"
-	$(NM) $(TMPDIR)/libnon_bundled.rlib | $(CGREP) -e "U _*native_func"
-
-	# Build a cdylib, `native-staticlib` will not appear on the linker line because it was bundled previously
-	# The cdylib will contain the `native_func` symbol in the end
-	$(RUSTC) cdylib-bundled.rs --crate-type=cdylib --print link-args | $(CGREP) -ve '-l[" ]*native-staticlib'
-	$(NM) $(call DYLIB,cdylib_bundled) | $(CGREP) -e "[Tt] _*native_func"
-
-	# Build a cdylib, `native-staticlib` will appear on the linker line because it was not bundled previously
-	# The cdylib will contain the `native_func` symbol in the end
-	$(RUSTC) cdylib-non-bundled.rs --crate-type=cdylib --print link-args | $(CGREP) -e '-l[" ]*native-staticlib'
-	$(NM) $(call DYLIB,cdylib_non_bundled) | $(CGREP) -e "[Tt] _*native_func"
diff --git a/tests/run-make/native-link-modifier-bundle/rmake.rs b/tests/run-make/native-link-modifier-bundle/rmake.rs
new file mode 100644
index 0000000..058b66b
--- /dev/null
+++ b/tests/run-make/native-link-modifier-bundle/rmake.rs
@@ -0,0 +1,90 @@
+// This test exercises the `bundle` link argument, which can be turned on or off.
+
+// When building a rlib or staticlib, +bundle means that all object files from the native static
+// library will be added to the rlib or staticlib archive, and then used from it during linking of
+// the final binary.
+
+// When building a rlib -bundle means that the native static library is registered as a dependency
+// of that rlib "by name", and object files from it are included only during linking of the final
+// binary, the file search by that name is also performed during final linking.
+// When building a staticlib -bundle means that the native static library is simply not included
+// into the archive and some higher level build system will need to add it later during linking of
+// the final binary.
+
+// This modifier has no effect when building other targets like executables or dynamic libraries.
+
+// The default for this modifier is +bundle.
+// See https://github.com/rust-lang/rust/pull/95818
+
+//@ ignore-cross-compile
+// Reason: cross-compilation fails to export native symbols
+
+use run_make_support::{
+    build_native_static_lib, dynamic_lib_name, is_msvc, llvm_nm, rust_lib_name, rustc,
+    static_lib_name,
+};
+
+fn main() {
+    build_native_static_lib("native-staticlib");
+    // Build a staticlib and a rlib, the `native_func` symbol will be bundled into them
+    rustc().input("bundled.rs").crate_type("staticlib").crate_type("rlib").run();
+    llvm_nm()
+        .input(static_lib_name("bundled"))
+        .run()
+        .assert_stdout_contains_regex("T _*native_func");
+    llvm_nm()
+        .input(static_lib_name("bundled"))
+        .run()
+        .assert_stdout_contains_regex("U _*native_func");
+    llvm_nm().input(rust_lib_name("bundled")).run().assert_stdout_contains_regex("T _*native_func");
+    llvm_nm().input(rust_lib_name("bundled")).run().assert_stdout_contains_regex("U _*native_func");
+
+    // Build a staticlib and a rlib, the `native_func` symbol will not be bundled into it
+    build_native_static_lib("native-staticlib");
+    rustc().input("non-bundled.rs").crate_type("staticlib").crate_type("rlib").run();
+    llvm_nm()
+        .input(static_lib_name("non_bundled"))
+        .run()
+        .assert_stdout_not_contains_regex("T _*native_func");
+    llvm_nm()
+        .input(static_lib_name("non_bundled"))
+        .run()
+        .assert_stdout_contains_regex("U _*native_func");
+    llvm_nm()
+        .input(rust_lib_name("non_bundled"))
+        .run()
+        .assert_stdout_not_contains_regex("T _*native_func");
+    llvm_nm()
+        .input(rust_lib_name("non_bundled"))
+        .run()
+        .assert_stdout_contains_regex("U _*native_func");
+
+    // This part of the test does not function on Windows MSVC - no symbols are printed.
+    if !is_msvc() {
+        // Build a cdylib, `native-staticlib` will not appear on the linker line because it was
+        // bundled previously. The cdylib will contain the `native_func` symbol in the end.
+        rustc()
+            .input("cdylib-bundled.rs")
+            .crate_type("cdylib")
+            .print("link-args")
+            .run()
+            .assert_stdout_not_contains(r#"-l[" ]*native-staticlib"#);
+        llvm_nm()
+            .input(dynamic_lib_name("cdylib_bundled"))
+            .run()
+            .assert_stdout_contains_regex("[Tt] _*native_func");
+
+        // Build a cdylib, `native-staticlib` will appear on the linker line because it was not
+        // bundled previously. The cdylib will contain the `native_func` symbol in the end
+        rustc()
+            .input("cdylib-non-bundled.rs")
+            .crate_type("cdylib")
+            .print("link-args")
+            .run()
+            .assert_stdout_contains_regex(r#"-l[" ]*native-staticlib"#);
+        llvm_nm()
+            .input(dynamic_lib_name("cdylib_non_bundled"))
+            .run()
+            .assert_stdout_contains_regex("[Tt] _*native_func");
+    }
+}
diff --git a/tests/run-make/reproducible-build/Makefile b/tests/run-make/reproducible-build/Makefile
deleted file mode 100644
index f5d17a2..0000000
--- a/tests/run-make/reproducible-build/Makefile
+++ /dev/null
@@ -1,140 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-# ignore-musl
-# Objects are reproducible but their path is not.
-
-all:  \
-	smoke \
-	debug \
-	opt \
-	link_paths \
-	remap_paths \
-	different_source_dirs_rlib \
-	remap_cwd_rlib \
-	remap_cwd_to_empty \
-	extern_flags
-
-# TODO: Builds of `bin` crate types are not deterministic with debuginfo=2 on
-# Windows.
-# See: https://github.com/rust-lang/rust/pull/87320#issuecomment-920105533
-# Issue: https://github.com/rust-lang/rust/issues/88982
-#
-#	different_source_dirs_bin \
-#	remap_cwd_bin \
-
-smoke:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) linker.rs -O
-	$(RUSTC) reproducible-build-aux.rs
-	$(RUSTC) reproducible-build.rs -C linker=$(call RUN_BINFILE,linker)
-	$(RUSTC) reproducible-build.rs -C linker=$(call RUN_BINFILE,linker)
-	diff -u "$(TMPDIR)/linker-arguments1" "$(TMPDIR)/linker-arguments2"
-
-debug:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) linker.rs -O
-	$(RUSTC) reproducible-build-aux.rs -g
-	$(RUSTC) reproducible-build.rs -C linker=$(call RUN_BINFILE,linker) -g
-	$(RUSTC) reproducible-build.rs -C linker=$(call RUN_BINFILE,linker) -g
-	diff -u "$(TMPDIR)/linker-arguments1" "$(TMPDIR)/linker-arguments2"
-
-opt:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) linker.rs -O
-	$(RUSTC) reproducible-build-aux.rs -O
-	$(RUSTC) reproducible-build.rs -C linker=$(call RUN_BINFILE,linker) -O
-	$(RUSTC) reproducible-build.rs -C linker=$(call RUN_BINFILE,linker) -O
-	diff -u "$(TMPDIR)/linker-arguments1" "$(TMPDIR)/linker-arguments2"
-
-link_paths:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) reproducible-build-aux.rs
-	$(RUSTC) reproducible-build.rs --crate-type rlib -L /b
-	cp $(TMPDIR)/libreproducible_build.rlib $(TMPDIR)/libfoo.rlib
-	$(RUSTC) reproducible-build.rs --crate-type rlib -L /a
-	cmp "$(TMPDIR)/libreproducible_build.rlib" "$(TMPDIR)/libfoo.rlib" || exit 1
-
-remap_paths:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) reproducible-build-aux.rs
-	$(RUSTC) reproducible-build.rs --crate-type rlib --remap-path-prefix=/a=/c
-	cp $(TMPDIR)/libreproducible_build.rlib $(TMPDIR)/libfoo.rlib
-	$(RUSTC) reproducible-build.rs --crate-type rlib --remap-path-prefix=/b=/c
-	cmp "$(TMPDIR)/libreproducible_build.rlib" "$(TMPDIR)/libfoo.rlib" || exit 1
-
-different_source_dirs_bin:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) reproducible-build-aux.rs
-	mkdir $(TMPDIR)/test
-	cp reproducible-build.rs $(TMPDIR)/test
-	$(RUSTC) reproducible-build.rs --crate-type bin --remap-path-prefix=$$PWD=/b
-	cp $(TMPDIR)/reproducible-build $(TMPDIR)/foo
-	(cd $(TMPDIR)/test && $(RUSTC) reproducible-build.rs \
-		--remap-path-prefix=$(TMPDIR)/test=/b \
-		--crate-type bin)
-	cmp "$(TMPDIR)/reproducible-build" "$(TMPDIR)/foo" || exit 1
-
-different_source_dirs_rlib:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) reproducible-build-aux.rs
-	mkdir $(TMPDIR)/test
-	cp reproducible-build.rs $(TMPDIR)/test
-	$(RUSTC) reproducible-build.rs --crate-type rlib --remap-path-prefix=$$PWD=/b
-	cp $(TMPDIR)/libreproducible_build.rlib $(TMPDIR)/libfoo.rlib
-	(cd $(TMPDIR)/test && $(RUSTC) reproducible-build.rs \
-		--remap-path-prefix=$(TMPDIR)/test=/b \
-		--crate-type rlib)
-	cmp "$(TMPDIR)/libreproducible_build.rlib" "$(TMPDIR)/libfoo.rlib" || exit 1
-
-remap_cwd_bin:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) reproducible-build-aux.rs
-	mkdir $(TMPDIR)/test
-	cp reproducible-build.rs $(TMPDIR)/test
-	$(RUSTC) reproducible-build.rs --crate-type bin -C debuginfo=2 \
-	  -Z remap-cwd-prefix=.
-	cp $(TMPDIR)/reproducible-build $(TMPDIR)/first
-	(cd $(TMPDIR)/test && \
-	 $(RUSTC) reproducible-build.rs --crate-type bin -C debuginfo=2 \
-	   -Z remap-cwd-prefix=.)
-	cmp "$(TMPDIR)/first" "$(TMPDIR)/reproducible-build" || exit 1
-
-remap_cwd_rlib:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) reproducible-build-aux.rs
-	mkdir $(TMPDIR)/test
-	cp reproducible-build.rs $(TMPDIR)/test
-	$(RUSTC) reproducible-build.rs --crate-type rlib -C debuginfo=2 \
-	  -Z remap-cwd-prefix=.
-	cp $(TMPDIR)/libreproducible_build.rlib $(TMPDIR)/libfirst.rlib
-	(cd $(TMPDIR)/test && \
-	 $(RUSTC) reproducible-build.rs --crate-type rlib -C debuginfo=2 \
-	   -Z remap-cwd-prefix=.)
-	cmp "$(TMPDIR)/libfirst.rlib" "$(TMPDIR)/libreproducible_build.rlib" || exit 1
-
-remap_cwd_to_empty:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) reproducible-build-aux.rs
-	mkdir $(TMPDIR)/test
-	cp reproducible-build.rs $(TMPDIR)/test
-	$(RUSTC) reproducible-build.rs --crate-type rlib -C debuginfo=2 \
-	  -Z remap-cwd-prefix=
-	cp $(TMPDIR)/libreproducible_build.rlib $(TMPDIR)/libfirst.rlib
-	(cd $(TMPDIR)/test && \
-	 $(RUSTC) reproducible-build.rs --crate-type rlib -C debuginfo=2 \
-	   -Z remap-cwd-prefix=)
-	cmp "$(TMPDIR)/libfirst.rlib" "$(TMPDIR)/libreproducible_build.rlib" || exit 1
-
-extern_flags:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) reproducible-build-aux.rs
-	$(RUSTC) reproducible-build.rs \
-		--extern reproducible_build_aux=$(TMPDIR)/libreproducible_build_aux.rlib \
-		--crate-type rlib
-	cp $(TMPDIR)/libreproducible_build_aux.rlib $(TMPDIR)/libbar.rlib
-	cp $(TMPDIR)/libreproducible_build.rlib $(TMPDIR)/libfoo.rlib
-	$(RUSTC) reproducible-build.rs \
-		--extern reproducible_build_aux=$(TMPDIR)/libbar.rlib \
-		--crate-type rlib
-	cmp "$(TMPDIR)/libreproducible_build.rlib" "$(TMPDIR)/libfoo.rlib" || exit 1
diff --git a/tests/run-make/reproducible-build/rmake.rs b/tests/run-make/reproducible-build/rmake.rs
new file mode 100644
index 0000000..34410d2
--- /dev/null
+++ b/tests/run-make/reproducible-build/rmake.rs
@@ -0,0 +1,240 @@
+// This test case makes sure that two identical invocations of the compiler
+// (i.e. same code base, same compile-flags, same compiler-versions, etc.)
+// produce the same output. In the past, symbol names of monomorphized functions
+// were not deterministic (which we want to avoid).
+//
+// The test tries to exercise as many different paths into symbol name
+// generation as possible:
+//
+// - regular functions
+// - generic functions
+// - methods
+// - statics
+// - closures
+// - enum variant constructors
+// - tuple struct constructors
+// - drop glue
+// - FnOnce adapters
+// - Trait object shims
+// - Fn Pointer shims
+// See https://github.com/rust-lang/rust/pull/32293
+// Tracking Issue: https://github.com/rust-lang/rust/issues/129080
+
+use run_make_support::{
+    bin_name, cwd, diff, is_darwin, is_windows, rfs, run_in_tmpdir, rust_lib_name, rustc,
+};
+
+fn main() {
+    // Smoke tests. Simple flags, build should be reproducible.
+    eprintln!("smoke_test => None");
+    smoke_test(None);
+    eprintln!("smoke_test => SmokeFlag::Debug");
+    smoke_test(Some(SmokeFlag::Debug));
+    eprintln!("smoke_test => SmokeFlag::Opt");
+    smoke_test(Some(SmokeFlag::Opt));
+
+    // Builds should be reproducible even through custom library search paths
+    // or remap path prefixes.
+    eprintln!("paths_test => PathsFlag::Link");
+    paths_test(PathsFlag::Link);
+    eprintln!("paths_test => PathsFlag::Remap");
+    paths_test(PathsFlag::Remap);
+
+    // Builds should be reproducible even if each build is done in a different directory,
+    // with both --remap-path-prefix and -Z remap-cwd-prefix.
+
+    // FIXME(Oneirical): Building with crate type set to `bin` AND having -Cdebuginfo=2
+    // (or `-g`, the shorthand form) enabled will cause reproducibility failures.
+    // See https://github.com/rust-lang/rust/issues/89911
+
+    if !is_darwin() && !is_windows() {
+        // FIXME(Oneirical): Bin builds are not reproducible on non-Linux targets.
+        eprintln!("diff_dir_test => Bin, Path");
+        diff_dir_test(CrateType::Bin, RemapType::Path);
+    }
+
+    eprintln!("diff_dir_test => Rlib, Path");
+    diff_dir_test(CrateType::Rlib, RemapType::Path);
+
+    // FIXME(Oneirical): This specific case would fail on Linux, should -Cdebuginfo=2
+    // be added.
+    // FIXME(Oneirical): Bin builds are not reproducible on non-Linux targets.
+    // See https://github.com/rust-lang/rust/issues/89911
+    if !is_darwin() && !is_windows() {
+        eprintln!("diff_dir_test => Bin, Cwd false");
+        diff_dir_test(CrateType::Bin, RemapType::Cwd { is_empty: false });
+    }
+
+    eprintln!("diff_dir_test => Rlib, Cwd false");
+    diff_dir_test(CrateType::Rlib, RemapType::Cwd { is_empty: false });
+    eprintln!("diff_dir_test => Rlib, Cwd true");
+    diff_dir_test(CrateType::Rlib, RemapType::Cwd { is_empty: true });
+
+    eprintln!("final extern test");
+    // Builds should be reproducible when using the --extern flag.
+    run_in_tmpdir(|| {
+        rustc().input("reproducible-build-aux.rs").run();
+        rustc()
+            .input("reproducible-build.rs")
+            .crate_type("rlib")
+            .extern_("reproducible_build_aux", rust_lib_name("reproducible_build_aux"))
+            .run();
+        rfs::copy(rust_lib_name("reproducible_build"), rust_lib_name("foo"));
+        rfs::copy(rust_lib_name("reproducible_build_aux"), rust_lib_name("bar"));
+        rustc()
+            .input("reproducible-build.rs")
+            .crate_type("rlib")
+            .extern_("reproducible_build_aux", rust_lib_name("bar"))
+            .run();
+        assert!(rfs::read(rust_lib_name("foo")) == rfs::read(rust_lib_name("reproducible_build")))
+    });
+}
+
+#[track_caller]
+fn smoke_test(flag: Option<SmokeFlag>) {
+    run_in_tmpdir(|| {
+        rustc().input("linker.rs").opt().run();
+        rustc().input("reproducible-build-aux.rs").run();
+        let mut compiler1 = rustc();
+        let mut compiler2 = rustc();
+        if let Some(flag) = flag {
+            match flag {
+                SmokeFlag::Debug => {
+                    compiler1.arg("-g");
+                    compiler2.arg("-g");
+                }
+                SmokeFlag::Opt => {
+                    compiler1.opt();
+                    compiler2.opt();
+                }
+            };
+        };
+        compiler1
+            .input("reproducible-build.rs")
+            .linker(&cwd().join(bin_name("linker")).display().to_string())
+            .run();
+        compiler2
+            .input("reproducible-build.rs")
+            .linker(&cwd().join(bin_name("linker")).display().to_string())
+            .run();
+        diff().actual_file("linker-arguments1").expected_file("linker-arguments2").run();
+    });
+}
+
+#[track_caller]
+fn paths_test(flag: PathsFlag) {
+    run_in_tmpdir(|| {
+        rustc().input("reproducible-build-aux.rs").run();
+        let mut compiler1 = rustc();
+        let mut compiler2 = rustc();
+        match flag {
+            PathsFlag::Link => {
+                compiler1.library_search_path("a");
+                compiler2.library_search_path("b");
+            }
+            PathsFlag::Remap => {
+                compiler1.arg("--remap-path-prefix=/a=/c");
+                compiler2.arg("--remap-path-prefix=/b=/c");
+            }
+        }
+        compiler1.input("reproducible-build.rs").crate_type("rlib").run();
+        rfs::rename(rust_lib_name("reproducible_build"), rust_lib_name("foo"));
+        compiler2.input("reproducible-build.rs").crate_type("rlib").run();
+        assert!(rfs::read(rust_lib_name("foo")) == rfs::read(rust_lib_name("reproducible_build")))
+    });
+}
+
+#[track_caller]
+fn diff_dir_test(crate_type: CrateType, remap_type: RemapType) {
+    run_in_tmpdir(|| {
+        let base_dir = cwd();
+        rustc().input("reproducible-build-aux.rs").run();
+        rfs::create_dir("test");
+        rfs::copy("reproducible-build.rs", "test/reproducible-build.rs");
+        let mut compiler1 = rustc();
+        let mut compiler2 = rustc();
+        match crate_type {
+            CrateType::Bin => {
+                compiler1.crate_type("bin");
+                compiler2.crate_type("bin");
+            }
+            CrateType::Rlib => {
+                compiler1.crate_type("rlib");
+                compiler2.crate_type("rlib");
+            }
+        }
+        match remap_type {
+            RemapType::Path => {
+                compiler1.arg(&format!("--remap-path-prefix={}=/b", cwd().display()));
+                compiler2
+                    .arg(format!("--remap-path-prefix={}=/b", base_dir.join("test").display()));
+            }
+            RemapType::Cwd { is_empty } => {
+                // FIXME(Oneirical): Building with crate type set to `bin` AND having -Cdebuginfo=2
+                // (or `-g`, the shorthand form) enabled will cause reproducibility failures
+                // for multiple platforms.
+                // See https://github.com/rust-lang/rust/issues/89911
+                // FIXME(#129117): Windows rlib + `-Cdebuginfo=2` + `-Z remap-cwd-prefix=.` seems
+                // to be unreproducible.
+                if !matches!(crate_type, CrateType::Bin) && !is_windows() {
+                    compiler1.arg("-Cdebuginfo=2");
+                    compiler2.arg("-Cdebuginfo=2");
+                }
+                if is_empty {
+                    compiler1.arg("-Zremap-cwd-prefix=");
+                    compiler2.arg("-Zremap-cwd-prefix=");
+                } else {
+                    compiler1.arg("-Zremap-cwd-prefix=.");
+                    compiler2.arg("-Zremap-cwd-prefix=.");
+                }
+            }
+        }
+        compiler1.input("reproducible-build.rs").run();
+        match crate_type {
+            CrateType::Bin => {
+                rfs::rename(bin_name("reproducible-build"), bin_name("foo"));
+            }
+            CrateType::Rlib => {
+                rfs::rename(rust_lib_name("reproducible_build"), rust_lib_name("foo"));
+            }
+        }
+        std::env::set_current_dir("test").unwrap();
+        compiler2
+            .input("reproducible-build.rs")
+            .library_search_path(&base_dir)
+            .out_dir(&base_dir)
+            .run();
+        std::env::set_current_dir(&base_dir).unwrap();
+        match crate_type {
+            CrateType::Bin => {
+                assert!(rfs::read(bin_name("reproducible-build")) == rfs::read(bin_name("foo")));
+            }
+            CrateType::Rlib => {
+                assert!(
+                    rfs::read(rust_lib_name("foo"))
+                        == rfs::read(rust_lib_name("reproducible_build"))
+                );
+            }
+        }
+    });
+}
+
+enum SmokeFlag {
+    Debug,
+    Opt,
+}
+
+enum PathsFlag {
+    Link,
+    Remap,
+}
+
+enum CrateType {
+    Bin,
+    Rlib,
+}
+
+enum RemapType {
+    Path,
+    Cwd { is_empty: bool },
+}
diff --git a/tests/run-make/rlib-format-packed-bundled-libs/Makefile b/tests/run-make/rlib-format-packed-bundled-libs/Makefile
deleted file mode 100644
index f454da6..0000000
--- a/tests/run-make/rlib-format-packed-bundled-libs/Makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-include ../tools.mk
-
-# ignore-cross-compile
-
-# Make sure rlib format with -Zpacked_bundled_libs is correct.
-
-# We're using the llvm-nm instead of the system nm to ensure it is compatible
-# with the LLVM bitcode generated by rustc.
-# Except on Windows where piping/IO redirection under MSYS2 is wonky with llvm-nm.
-ifndef IS_WINDOWS
-NM = "$(LLVM_BIN_DIR)"/llvm-nm
-else
-NM = nm
-endif
-
-all: $(call NATIVE_STATICLIB,native_dep_1) $(call NATIVE_STATICLIB,native_dep_2) $(call NATIVE_STATICLIB,native_dep_3)
-	$(RUSTC) rust_dep_up.rs --crate-type=rlib -Zpacked_bundled_libs
-	$(NM) $(TMPDIR)/librust_dep_up.rlib | $(CGREP) -e "U.*native_f2"
-	$(NM) $(TMPDIR)/librust_dep_up.rlib | $(CGREP) -e "U.*native_f3"
-	$(NM) $(TMPDIR)/librust_dep_up.rlib | $(CGREP) -e "T.*rust_dep_up"
-	$(AR) t $(TMPDIR)/librust_dep_up.rlib | $(CGREP) "native_dep_2"
-	$(AR) t $(TMPDIR)/librust_dep_up.rlib | $(CGREP) "native_dep_3"
-	$(RUSTC) rust_dep_local.rs --extern rlib=$(TMPDIR)/librust_dep_up.rlib -Zpacked_bundled_libs --crate-type=rlib
-	$(NM) $(TMPDIR)/librust_dep_local.rlib | $(CGREP) -e "U.*native_f1"
-	$(NM) $(TMPDIR)/librust_dep_local.rlib | $(CGREP) -e "T.*rust_dep_local"
-	$(AR) t $(TMPDIR)/librust_dep_local.rlib | $(CGREP) "native_dep_1"
-
-	# Make sure compiler doesn't use files, that it shouldn't know about.
-	rm $(TMPDIR)/*native_dep_*
-
-	$(RUSTC) main.rs --extern lib=$(TMPDIR)/librust_dep_local.rlib -o $(TMPDIR)/main.exe -Zpacked_bundled_libs --print link-args | $(CGREP) -e "native_dep_1.*native_dep_2.*native_dep_3"
-
-ifndef IS_MSVC
-	$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*native_f1"
-	$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*native_f2"
-	$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*native_f3"
-	$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*rust_dep_local"
-	$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*rust_dep_up"
-endif
diff --git a/tests/run-make/rlib-format-packed-bundled-libs/rmake.rs b/tests/run-make/rlib-format-packed-bundled-libs/rmake.rs
new file mode 100644
index 0000000..ff0438a
--- /dev/null
+++ b/tests/run-make/rlib-format-packed-bundled-libs/rmake.rs
@@ -0,0 +1,84 @@
+// `-Z packed_bundled_libs` is an unstable rustc flag that makes the compiler
+// only require a native library and no supplementary object files to compile.
+// Output files compiled with this flag should still contain all expected symbols -
+// that is what this test checks.
+// See https://github.com/rust-lang/rust/pull/100101
+
+//@ ignore-cross-compile
+// Reason: cross-compilation fails to export native symbols
+
+use run_make_support::{
+    bin_name, build_native_static_lib, cwd, filename_contains, is_msvc, llvm_ar, llvm_nm, rfs,
+    rust_lib_name, rustc, shallow_find_files,
+};
+
+fn main() {
+    build_native_static_lib("native_dep_1");
+    build_native_static_lib("native_dep_2");
+    build_native_static_lib("native_dep_3");
+    rustc().input("rust_dep_up.rs").crate_type("rlib").arg("-Zpacked_bundled_libs").run();
+    llvm_nm()
+        .input(rust_lib_name("rust_dep_up"))
+        .run()
+        .assert_stdout_contains_regex("U.*native_f2");
+    llvm_nm()
+        .input(rust_lib_name("rust_dep_up"))
+        .run()
+        .assert_stdout_contains_regex("U.*native_f3");
+    llvm_nm()
+        .input(rust_lib_name("rust_dep_up"))
+        .run()
+        .assert_stdout_contains_regex("T.*rust_dep_up");
+    llvm_ar()
+        .table_of_contents()
+        .arg(rust_lib_name("rust_dep_up"))
+        .run()
+        .assert_stdout_contains("native_dep_2");
+    llvm_ar()
+        .table_of_contents()
+        .arg(rust_lib_name("rust_dep_up"))
+        .run()
+        .assert_stdout_contains("native_dep_3");
+    rustc()
+        .input("rust_dep_local.rs")
+        .extern_("rlib", rust_lib_name("rust_dep_up"))
+        .arg("-Zpacked_bundled_libs")
+        .crate_type("rlib")
+        .run();
+    llvm_nm()
+        .input(rust_lib_name("rust_dep_local"))
+        .run()
+        .assert_stdout_contains_regex("U.*native_f1");
+    llvm_nm()
+        .input(rust_lib_name("rust_dep_local"))
+        .run()
+        .assert_stdout_contains_regex("T.*rust_dep_local");
+    llvm_ar()
+        .table_of_contents()
+        .arg(rust_lib_name("rust_dep_local"))
+        .run()
+        .assert_stdout_contains("native_dep_1");
+
+    // Ensure the compiler will not use files it should not know about.
+    for file in shallow_find_files(cwd(), |path| filename_contains(path, "native_dep_")) {
+        rfs::remove_file(file);
+    }
+
+    rustc()
+        .input("main.rs")
+        .extern_("lib", rust_lib_name("rust_dep_local"))
+        .output(bin_name("main"))
+        .arg("-Zpacked_bundled_libs")
+        .print("link-args")
+        .run()
+        .assert_stdout_contains_regex("native_dep_1.*native_dep_2.*native_dep_3");
+
+    // The binary "main" will not contain any symbols on MSVC.
+    if !is_msvc() {
+        llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f1");
+        llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f2");
+        llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f3");
+        llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*rust_dep_local");
+        llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*rust_dep_up");
+    }
+}
diff --git a/tests/run-make/rustdoc-determinism/rmake.rs b/tests/run-make/rustdoc-determinism/rmake.rs
index aa80901..ce08850 100644
--- a/tests/run-make/rustdoc-determinism/rmake.rs
+++ b/tests/run-make/rustdoc-determinism/rmake.rs
@@ -7,12 +7,12 @@
 
 fn main() {
     let foo_first = Path::new("foo_first");
-    rustdoc().input("foo.rs").output(&foo_first).run();
-    rustdoc().input("bar.rs").output(&foo_first).run();
+    rustdoc().input("foo.rs").out_dir(&foo_first).run();
+    rustdoc().input("bar.rs").out_dir(&foo_first).run();
 
     let bar_first = Path::new("bar_first");
-    rustdoc().input("bar.rs").output(&bar_first).run();
-    rustdoc().input("foo.rs").output(&bar_first).run();
+    rustdoc().input("bar.rs").out_dir(&bar_first).run();
+    rustdoc().input("foo.rs").out_dir(&bar_first).run();
 
     diff()
         .expected_file(foo_first.join("search-index.js"))
diff --git a/tests/run-make/rustdoc-io-error/rmake.rs b/tests/run-make/rustdoc-io-error/rmake.rs
index a5fae36..31441d7 100644
--- a/tests/run-make/rustdoc-io-error/rmake.rs
+++ b/tests/run-make/rustdoc-io-error/rmake.rs
@@ -25,7 +25,7 @@ fn main() {
     permissions.set_readonly(true);
     rfs::set_permissions(&out_dir, permissions);
 
-    let output = rustdoc().input("foo.rs").output(&out_dir).env("RUST_BACKTRACE", "1").run_fail();
+    let output = rustdoc().input("foo.rs").out_dir(&out_dir).env("RUST_BACKTRACE", "1").run_fail();
 
     rfs::set_permissions(&out_dir, original_permissions);
 
diff --git a/tests/run-make/rustdoc-map-file/rmake.rs b/tests/run-make/rustdoc-map-file/rmake.rs
index 08f9595..d7e3510 100644
--- a/tests/run-make/rustdoc-map-file/rmake.rs
+++ b/tests/run-make/rustdoc-map-file/rmake.rs
@@ -1,13 +1,54 @@
-use run_make_support::{python_command, rustdoc};
+// This test ensures that all items from `foo` are correctly generated into the `redirect-map.json`
+// file with `--generate-redirect-map` rustdoc option.
+
+use std::path::Path;
+
+use run_make_support::rfs::read_to_string;
+use run_make_support::{path, rustdoc, serde_json};
 
 fn main() {
     let out_dir = "out";
+    let crate_name = "foo";
     rustdoc()
         .input("foo.rs")
+        .crate_name(crate_name)
         .arg("-Zunstable-options")
         .arg("--generate-redirect-map")
-        .output(&out_dir)
+        .out_dir(&out_dir)
         .run();
-    // FIXME (GuillaumeGomez): Port the python script to Rust as well.
-    python_command().arg("validate_json.py").arg(&out_dir).run();
+
+    let generated = read_to_string(path(out_dir).join(crate_name).join("redirect-map.json"));
+    let expected = read_to_string("expected.json");
+    let generated: serde_json::Value =
+        serde_json::from_str(&generated).expect("failed to parse JSON");
+    let expected: serde_json::Value =
+        serde_json::from_str(&expected).expect("failed to parse JSON");
+    let expected = expected.as_object().unwrap();
+
+    let mut differences = Vec::new();
+    for (key, expected_value) in expected.iter() {
+        match generated.get(key) {
+            Some(value) => {
+                if expected_value != value {
+                    differences.push(format!(
+                        "values for key `{key}` don't match: `{expected_value:?}` != `{value:?}`"
+                    ));
+                }
+            }
+            None => differences.push(format!("missing key `{key}`")),
+        }
+    }
+    for (key, data) in generated.as_object().unwrap().iter() {
+        if !expected.contains_key(key) {
+            differences.push(format!("Extra data not expected: key: `{key}`, data: `{data}`"));
+        }
+    }
+
+    if !differences.is_empty() {
+        eprintln!("Found differences in JSON files:");
+        for diff in differences {
+            eprintln!("=> {diff}");
+        }
+        panic!("Found differences in JSON files");
+    }
 }
diff --git a/tests/run-make/rustdoc-map-file/validate_json.py b/tests/run-make/rustdoc-map-file/validate_json.py
deleted file mode 100755
index 912dea3..0000000
--- a/tests/run-make/rustdoc-map-file/validate_json.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python
-
-import os
-import sys
-import json
-
-
-def find_redirect_map_file(folder, errors):
-    for root, _dirs, files in os.walk(folder):
-        for name in files:
-            if not name.endswith("redirect-map.json"):
-                continue
-            with open(os.path.join(root, name)) as f:
-                data = json.load(f)
-            with open("expected.json") as f:
-                expected = json.load(f)
-            for key in expected:
-                if expected[key] != data.get(key):
-                    errors.append("Expected `{}` for key `{}`, found: `{}`".format(
-                        expected[key], key, data.get(key)))
-                else:
-                    del data[key]
-            for key in data:
-                errors.append("Extra data not expected: key: `{}`, data: `{}`".format(
-                    key, data[key]))
-            return True
-    return False
-
-
-if len(sys.argv) != 2:
-    print("Expected doc directory to check!")
-    sys.exit(1)
-
-errors = []
-if not find_redirect_map_file(sys.argv[1], errors):
-    print("Didn't find the map file in `{}`...".format(sys.argv[1]))
-    sys.exit(1)
-for err in errors:
-    print("=> {}".format(err))
-if len(errors) != 0:
-    sys.exit(1)
diff --git a/tests/run-make/rustdoc-output-path/rmake.rs b/tests/run-make/rustdoc-output-path/rmake.rs
index 3c1ccd3..181239e 100644
--- a/tests/run-make/rustdoc-output-path/rmake.rs
+++ b/tests/run-make/rustdoc-output-path/rmake.rs
@@ -6,6 +6,6 @@
 
 fn main() {
     let out_dir = Path::new("foo/bar/doc");
-    rustdoc().input("foo.rs").output(&out_dir).run();
+    rustdoc().input("foo.rs").out_dir(&out_dir).run();
     assert!(out_dir.exists());
 }
diff --git a/tests/run-make/rustdoc-output-stdout/foo.rs b/tests/run-make/rustdoc-output-stdout/foo.rs
new file mode 100644
index 0000000..4a83567
--- /dev/null
+++ b/tests/run-make/rustdoc-output-stdout/foo.rs
@@ -0,0 +1 @@
+pub struct Foo;
diff --git a/tests/run-make/rustdoc-output-stdout/rmake.rs b/tests/run-make/rustdoc-output-stdout/rmake.rs
new file mode 100644
index 0000000..dbc9892
--- /dev/null
+++ b/tests/run-make/rustdoc-output-stdout/rmake.rs
@@ -0,0 +1,25 @@
+// This test verifies that rustdoc `-o -` prints JSON on stdout and doesn't generate
+// a JSON file.
+
+use std::path::PathBuf;
+
+use run_make_support::path_helpers::{cwd, has_extension, read_dir_entries_recursive};
+use run_make_support::rustdoc;
+
+fn main() {
+    // First we check that we generate the JSON in the stdout.
+    rustdoc()
+        .input("foo.rs")
+        .out_dir("-")
+        .arg("-Zunstable-options")
+        .output_format("json")
+        .run()
+        .assert_stdout_contains("{\"");
+
+    // Then we check it didn't generate any JSON file.
+    read_dir_entries_recursive(cwd(), |path| {
+        if path.is_file() && has_extension(path, "json") {
+            panic!("Found a JSON file {path:?}");
+        }
+    });
+}
diff --git a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
index b77df7a..546a068 100644
--- a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
+++ b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
@@ -35,7 +35,7 @@ fn main() {
         .input("examples/ex.rs")
         .crate_name("ex")
         .crate_type("bin")
-        .output(&out_dir)
+        .out_dir(&out_dir)
         .extern_(crate_name, rust_lib_name(crate_name))
         .extern_(proc_crate_name, dylib_name.trim())
         .arg("-Zunstable-options")
@@ -49,7 +49,7 @@ fn main() {
         .input("src/lib.rs")
         .crate_name(crate_name)
         .crate_type("lib")
-        .output(&out_dir)
+        .out_dir(&out_dir)
         .arg("-Zunstable-options")
         .arg("--with-examples")
         .arg(&ex_dir)
diff --git a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
index eca0704..c4d7814 100644
--- a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
+++ b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
@@ -20,7 +20,7 @@ pub fn scrape(extra_args: &[&str]) {
             .input(&dep)
             .crate_name(&dep_stem)
             .crate_type("bin")
-            .output(&out_dir)
+            .out_dir(&out_dir)
             .extern_(crate_name, format!("lib{crate_name}.rmeta"))
             .arg("-Zunstable-options")
             .arg("--scrape-examples-output-path")
@@ -35,7 +35,7 @@ pub fn scrape(extra_args: &[&str]) {
     let mut rustdoc = rustdoc();
     rustdoc
         .input("src/lib.rs")
-        .output(&out_dir)
+        .out_dir(&out_dir)
         .crate_name(crate_name)
         .crate_type("lib")
         .arg("-Zunstable-options");
diff --git a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
index 3246fc5..fe9587f 100644
--- a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
+++ b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
@@ -7,7 +7,7 @@ fn main() {
     rustc().crate_type("lib").input("dummy_core.rs").target("target.json").run();
     rustdoc()
         .input("my_crate.rs")
-        .output(out_dir)
+        .out_dir(out_dir)
         .library_search_path(cwd())
         .target("target.json")
         .run();
diff --git a/tests/run-make/rustdoc-themes/rmake.rs b/tests/run-make/rustdoc-themes/rmake.rs
index 8a961be..4577e47 100644
--- a/tests/run-make/rustdoc-themes/rmake.rs
+++ b/tests/run-make/rustdoc-themes/rmake.rs
@@ -27,6 +27,6 @@ fn main() {
     rfs::create_dir_all(&out_dir);
     rfs::write(&test_css, test_content);
 
-    rustdoc().output(&out_dir).input("foo.rs").arg("--theme").arg(&test_css).run();
+    rustdoc().out_dir(&out_dir).input("foo.rs").arg("--theme").arg(&test_css).run();
     htmldocck().arg(out_dir).arg("foo.rs").run();
 }
diff --git a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
index ded89c9..a82a196 100644
--- a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
@@ -2,6 +2,6 @@
 
 fn main() {
     let out_dir = "rustdoc";
-    rustdoc().input("src/lib.rs").crate_name("foobar").crate_type("lib").output(&out_dir).run();
+    rustdoc().input("src/lib.rs").crate_name("foobar").crate_type("lib").out_dir(&out_dir).run();
     htmldocck().arg(out_dir).arg("src/lib.rs").run();
 }
diff --git a/tests/run-make/staticlib-thin-archive/bin.rs b/tests/run-make/staticlib-thin-archive/bin.rs
new file mode 100644
index 0000000..97a2751
--- /dev/null
+++ b/tests/run-make/staticlib-thin-archive/bin.rs
@@ -0,0 +1,5 @@
+fn main() {
+    unsafe {
+        rust_lib::simple_fn();
+    }
+}
diff --git a/tests/run-make/staticlib-thin-archive/rmake.rs b/tests/run-make/staticlib-thin-archive/rmake.rs
new file mode 100644
index 0000000..955c50d
--- /dev/null
+++ b/tests/run-make/staticlib-thin-archive/rmake.rs
@@ -0,0 +1,23 @@
+// Regression test for https://github.com/rust-lang/rust/issues/107407 which
+// checks that rustc can read thin archive. Before the object crate added thin
+// archive support rustc would add emit object files to the staticlib and after
+// the object crate added thin archive support it would previously crash the
+// compiler due to a missing special case for thin archive members.
+use run_make_support::{llvm_ar, path, rfs, rust_lib_name, rustc, static_lib_name};
+
+fn main() {
+    rfs::create_dir("archive");
+
+    // Build a thin archive
+    rustc().input("simple_obj.rs").emit("obj").output("archive/simple_obj.o").run();
+    llvm_ar()
+        .obj_to_thin_ar()
+        .output_input(path("archive").join(static_lib_name("thin_archive")), "archive/simple_obj.o")
+        .run();
+
+    // Build an rlib which includes the members of this thin archive
+    rustc().input("rust_lib.rs").library_search_path("archive").run();
+
+    // Build a binary which requires a symbol from the thin archive
+    rustc().input("bin.rs").extern_("rust_lib", rust_lib_name("rust_lib")).run();
+}
diff --git a/tests/run-make/staticlib-thin-archive/rust_lib.rs b/tests/run-make/staticlib-thin-archive/rust_lib.rs
new file mode 100644
index 0000000..c76b0f2
--- /dev/null
+++ b/tests/run-make/staticlib-thin-archive/rust_lib.rs
@@ -0,0 +1,6 @@
+#![crate_type = "rlib"]
+
+#[link(name = "thin_archive", kind = "static")]
+extern "C" {
+    pub fn simple_fn();
+}
diff --git a/tests/run-make/staticlib-thin-archive/simple_obj.rs b/tests/run-make/staticlib-thin-archive/simple_obj.rs
new file mode 100644
index 0000000..a120c9b
--- /dev/null
+++ b/tests/run-make/staticlib-thin-archive/simple_obj.rs
@@ -0,0 +1,4 @@
+#![crate_type = "staticlib"]
+
+#[no_mangle]
+extern "C" fn simple_fn() {}
diff --git a/tests/run-make/sysroot-crates-are-unstable/rmake.rs b/tests/run-make/sysroot-crates-are-unstable/rmake.rs
index 24da387..2240d87 100644
--- a/tests/run-make/sysroot-crates-are-unstable/rmake.rs
+++ b/tests/run-make/sysroot-crates-are-unstable/rmake.rs
@@ -1,5 +1,102 @@
-use run_make_support::python_command;
+// Check that crates in the sysroot are treated as unstable, unless they are
+// on a list of known-stable sysroot crates.
+
+use std::path::{Path, PathBuf};
+use std::str;
+
+use run_make_support::{rfs, rustc, target};
+
+fn is_stable_crate(name: &str) -> bool {
+    matches!(name, "std" | "alloc" | "core" | "proc_macro")
+}
 
 fn main() {
-    python_command().arg("test.py").run();
+    for cr in get_unstable_sysroot_crates() {
+        check_crate_is_unstable(&cr);
+    }
+    println!("Done");
+}
+
+#[derive(Debug)]
+struct Crate {
+    name: String,
+    path: PathBuf,
+}
+
+fn check_crate_is_unstable(cr: &Crate) {
+    let Crate { name, path } = cr;
+
+    print!("- Verifying that sysroot crate '{name}' is an unstable crate ...");
+
+    // Trying to use this crate from a user program should fail.
+    let output = rustc()
+        .crate_type("rlib")
+        .target(target())
+        .extern_(name, path)
+        .input("-")
+        .stdin(format!("extern crate {name};"))
+        .run_fail();
+
+    // Make sure it failed for the intended reason, not some other reason.
+    // (The actual feature required varies between crates.)
+    output.assert_stderr_contains("use of unstable library feature");
+
+    println!(" OK");
+}
+
+fn get_unstable_sysroot_crates() -> Vec<Crate> {
+    let sysroot = PathBuf::from(rustc().print("sysroot").run().stdout_utf8().trim());
+    let sysroot_libs_dir = sysroot.join("lib").join("rustlib").join(target()).join("lib");
+    println!("Sysroot libs dir: {sysroot_libs_dir:?}");
+
+    // Generate a list of all library crates in the sysroot.
+    let sysroot_crates = get_all_crates_in_dir(&sysroot_libs_dir);
+    println!(
+        "Found {} sysroot crates: {:?}",
+        sysroot_crates.len(),
+        sysroot_crates.iter().map(|cr| &cr.name).collect::<Vec<_>>()
+    );
+
+    // Self-check: If we didn't find `core`, we probably checked the wrong directory.
+    assert!(
+        sysroot_crates.iter().any(|cr| cr.name == "core"),
+        "Couldn't find `core` in {sysroot_libs_dir:?}"
+    );
+
+    let unstable_sysroot_crates =
+        sysroot_crates.into_iter().filter(|cr| !is_stable_crate(&cr.name)).collect::<Vec<_>>();
+    // Self-check: There should be at least one unstable crate in the directory.
+    assert!(
+        !unstable_sysroot_crates.is_empty(),
+        "Couldn't find any unstable crates in {sysroot_libs_dir:?}"
+    );
+    unstable_sysroot_crates
+}
+
+fn get_all_crates_in_dir(libs_dir: &Path) -> Vec<Crate> {
+    let mut libs = vec![];
+    rfs::read_dir_entries(libs_dir, |path| {
+        if !path.is_file() {
+            return;
+        }
+        if let Some(name) = crate_name_from_path(path) {
+            libs.push(Crate { name, path: path.to_owned() });
+        }
+    });
+    libs.sort_by(|a, b| a.name.cmp(&b.name));
+    libs
+}
+
+/// Treat a file as a crate if its name begins with `lib` and ends with `.rlib`.
+/// The crate name is the part before the first hyphen (if any).
+fn crate_name_from_path(path: &Path) -> Option<String> {
+    let name = path
+        .file_name()?
+        .to_str()?
+        .strip_prefix("lib")?
+        .strip_suffix(".rlib")?
+        .split('-')
+        .next()
+        .expect("split always yields at least one string");
+    Some(name.to_owned())
 }
diff --git a/tests/run-make/sysroot-crates-are-unstable/test.py b/tests/run-make/sysroot-crates-are-unstable/test.py
deleted file mode 100644
index 45cfdd1..0000000
--- a/tests/run-make/sysroot-crates-are-unstable/test.py
+++ /dev/null
@@ -1,75 +0,0 @@
-import sys
-import os
-from os import listdir
-from os.path import isfile, join
-from subprocess import PIPE, Popen
-
-
-# This is n list of files which are stable crates or simply are not crates,
-# we don't check for the instability of these crates as they're all stable!
-STABLE_CRATES = ['std', 'alloc', 'core', 'proc_macro',
-                 'rsbegin.o', 'rsend.o', 'dllcrt2.o', 'crt2.o', 'clang_rt']
-
-
-def convert_to_string(s):
-    if s.__class__.__name__ == 'bytes':
-        return s.decode('utf-8')
-    return s
-
-
-def set_ld_lib_path():
-    var = os.environ.get("LD_LIB_PATH_ENVVAR")
-    rpath = os.environ.get("HOST_RPATH_DIR")
-    if var and rpath:
-        path = os.environ.get(var)
-        if path:
-            os.environ[var] = rpath + os.pathsep + path
-        else:
-            os.environ[var] = rpath
-
-
-def exec_command(command, to_input=None):
-    child = None
-    if to_input is None:
-        child = Popen(command, stdout=PIPE, stderr=PIPE)
-    else:
-        child = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
-    stdout, stderr = child.communicate(input=to_input)
-    return (convert_to_string(stdout), convert_to_string(stderr))
-
-
-def check_lib(lib):
-    if lib['name'] in STABLE_CRATES:
-        return True
-    print('verifying if {} is an unstable crate'.format(lib['name']))
-    stdout, stderr = exec_command([os.environ['RUSTC'], '-', '--crate-type', 'rlib',
-                                   '--target', os.environ['TARGET'],
-                                   '--extern', '{}={}'.format(lib['name'], lib['path'])],
-                                  to_input=('extern crate {};'.format(lib['name'])).encode('utf-8'))
-    if 'use of unstable library feature' not in '{}{}'.format(stdout, stderr):
-        print('crate {} "{}" is not unstable'.format(lib['name'], lib['path']))
-        print('{}{}'.format(stdout, stderr))
-        print('')
-        return False
-    return True
-
-# Generate a list of all crates in the sysroot. To do this we list all files in
-# rustc's sysroot, look at the filename, strip everything after the `-`, and
-# strip the leading `lib` (if present)
-def get_all_libs(dir_path):
-    return [{ 'path': join(dir_path, f), 'name': f[3:].split('-')[0] }
-            for f in listdir(dir_path)
-            if isfile(join(dir_path, f)) and f.endswith('.rlib') and f not in STABLE_CRATES]
-
-
-set_ld_lib_path()
-sysroot = exec_command([os.environ['RUSTC'], '--print', 'sysroot'])[0].replace('\n', '')
-assert sysroot, "Could not read the rustc sysroot!"
-libs = get_all_libs(join(sysroot, 'lib/rustlib/{}/lib'.format(os.environ['TARGET'])))
-
-ret = 0
-for lib in libs:
-    if not check_lib(lib):
-        # We continue so users can see all the not unstable crates.
-        ret = 1
-sys.exit(ret)
diff --git a/tests/ui/abi/shadow-call-stack-without-fixed-x18.rs b/tests/ui/abi/shadow-call-stack-without-fixed-x18.rs
new file mode 100644
index 0000000..d758c90
--- /dev/null
+++ b/tests/ui/abi/shadow-call-stack-without-fixed-x18.rs
@@ -0,0 +1,15 @@
+//@ compile-flags: --target aarch64-unknown-none -Zsanitizer=shadow-call-stack
+//@ error-pattern: shadow-call-stack sanitizer is not supported for this target
+//@ dont-check-compiler-stderr
+//@ needs-llvm-components: aarch64
+
+#![allow(internal_features)]
+#![crate_type = "rlib"]
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[no_mangle]
+pub fn foo() {}
diff --git a/tests/ui/async-await/async-closures/move-out-of-ref.rs b/tests/ui/async-await/async-closures/move-out-of-ref.rs
new file mode 100644
index 0000000..a054472
--- /dev/null
+++ b/tests/ui/async-await/async-closures/move-out-of-ref.rs
@@ -0,0 +1,16 @@
+//@ compile-flags: -Zvalidate-mir
+//@ edition: 2021
+
+#![feature(async_closure)]
+
+// NOT copy.
+struct Ty;
+
+fn hello(x: &Ty) {
+    let c = async || {
+        *x;
+        //~^ ERROR cannot move out of `*x` which is behind a shared reference
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/async-closures/move-out-of-ref.stderr b/tests/ui/async-await/async-closures/move-out-of-ref.stderr
new file mode 100644
index 0000000..294905a
--- /dev/null
+++ b/tests/ui/async-await/async-closures/move-out-of-ref.stderr
@@ -0,0 +1,18 @@
+error[E0507]: cannot move out of `*x` which is behind a shared reference
+  --> $DIR/move-out-of-ref.rs:11:9
+   |
+LL |         *x;
+   |         ^^ move occurs because `*x` has type `Ty`, which does not implement the `Copy` trait
+   |
+note: if `Ty` implemented `Clone`, you could clone the value
+  --> $DIR/move-out-of-ref.rs:7:1
+   |
+LL | struct Ty;
+   | ^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |         *x;
+   |         -- you could clone this value
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/tests/ui/async-await/async-closures/sig-from-bare-fn.rs b/tests/ui/async-await/async-closures/sig-from-bare-fn.rs
new file mode 100644
index 0000000..a679471
--- /dev/null
+++ b/tests/ui/async-await/async-closures/sig-from-bare-fn.rs
@@ -0,0 +1,49 @@
+//@ check-pass
+//@ edition: 2021
+
+// Make sure that we infer the args of an async closure even if it's passed to
+// a function that requires the async closure implement `Fn*` but does *not* have
+// a `Future` bound on the return type.
+
+#![feature(async_closure)]
+
+use std::future::Future;
+
+trait TryStream {
+    type Ok;
+    type Err;
+}
+
+trait TryFuture {
+    type Ok;
+    type Err;
+}
+
+impl<F, T, E> TryFuture for F where F: Future<Output = Result<T, E>> {
+    type Ok = T;
+    type Err = E;
+}
+
+trait TryStreamExt: TryStream {
+    fn try_for_each<F, Fut>(&self, f: F)
+    where
+        F: FnMut(Self::Ok) -> Fut,
+        Fut: TryFuture<Ok = (), Err = Self::Err>;
+}
+
+impl<S> TryStreamExt for S where S: TryStream {
+    fn try_for_each<F, Fut>(&self, f: F)
+    where
+        F: FnMut(Self::Ok) -> Fut,
+        Fut: TryFuture<Ok = (), Err = Self::Err>,
+    { }
+}
+
+fn test(stream: impl TryStream<Ok = &'static str, Err = ()>) {
+    stream.try_for_each(async |s| {
+        s.trim(); // Make sure we know the type of `s` at this point.
+        Ok(())
+    });
+}
+
+fn main() {}
diff --git a/tests/ui/attributes/assoc-expr.rs b/tests/ui/attributes/assoc-expr.rs
new file mode 100644
index 0000000..f39557d
--- /dev/null
+++ b/tests/ui/attributes/assoc-expr.rs
@@ -0,0 +1,42 @@
+//@ check-pass
+// This test triggered an assertion failure in token collection due to
+// mishandling of attributes on associative expressions.
+
+#![feature(cfg_eval)]
+#![feature(rustc_attrs)]
+#![feature(stmt_expr_attributes)]
+#![allow(internal_features)]
+
+fn main() {}
+
+#[cfg_eval]
+struct Foo1(
+    [ bool; {
+        let _x = 30;
+        #[cfg_attr(unix, rustc_dummy(aa))] 1
+    } ]
+);
+
+#[cfg_eval]
+struct Foo12(
+    [ bool; {
+        let _x = 30;
+        #[cfg_attr(unix, rustc_dummy(bb))] 1 + 2
+    } ]
+);
+
+#[cfg_eval]
+struct Foox(
+    [ bool; {
+        let _x = 30;
+        #[cfg_attr(unix, rustc_dummy(cc))] _x
+    } ]
+);
+
+#[cfg_eval]
+struct Foox2(
+    [ bool; {
+        let _x = 30;
+        #[cfg_attr(unix, rustc_dummy(dd))] _x + 2
+    } ]
+);
diff --git a/tests/ui/attributes/linkage.rs b/tests/ui/attributes/linkage.rs
new file mode 100644
index 0000000..0d5ce69
--- /dev/null
+++ b/tests/ui/attributes/linkage.rs
@@ -0,0 +1,42 @@
+#![feature(linkage)]
+#![feature(stmt_expr_attributes)]
+#![deny(unused_attributes)]
+#![allow(dead_code)]
+
+#[linkage = "weak"] //~ ERROR attribute should be applied to a function or static
+type InvalidTy = ();
+
+#[linkage = "weak"] //~ ERROR attribute should be applied to a function or static
+mod invalid_module {}
+
+#[linkage = "weak"] //~ ERROR attribute should be applied to a function or static
+struct F;
+
+#[linkage = "weak"] //~ ERROR attribute should be applied to a function or static
+impl F {
+    #[linkage = "weak"]
+    fn valid(&self) {}
+}
+
+#[linkage = "weak"]
+fn f() {
+    #[linkage = "weak"]
+    {
+        1
+    };
+    //~^^^^ ERROR attribute should be applied to a function or static
+}
+
+extern "C" {
+    #[linkage = "weak"]
+    static A: *const ();
+
+    #[linkage = "weak"]
+    fn bar();
+}
+
+fn main() {
+    let _ = #[linkage = "weak"]
+    (|| 1);
+    //~^^ ERROR attribute should be applied to a function or static
+}
diff --git a/tests/ui/attributes/linkage.stderr b/tests/ui/attributes/linkage.stderr
new file mode 100644
index 0000000..d559552
--- /dev/null
+++ b/tests/ui/attributes/linkage.stderr
@@ -0,0 +1,55 @@
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:6:1
+   |
+LL | #[linkage = "weak"]
+   | ^^^^^^^^^^^^^^^^^^^
+LL | type InvalidTy = ();
+   | -------------------- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:9:1
+   |
+LL | #[linkage = "weak"]
+   | ^^^^^^^^^^^^^^^^^^^
+LL | mod invalid_module {}
+   | --------------------- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:12:1
+   |
+LL | #[linkage = "weak"]
+   | ^^^^^^^^^^^^^^^^^^^
+LL | struct F;
+   | --------- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:15:1
+   |
+LL |   #[linkage = "weak"]
+   |   ^^^^^^^^^^^^^^^^^^^
+LL | / impl F {
+LL | |     #[linkage = "weak"]
+LL | |     fn valid(&self) {}
+LL | | }
+   | |_- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:23:5
+   |
+LL |       #[linkage = "weak"]
+   |       ^^^^^^^^^^^^^^^^^^^
+LL | /     {
+LL | |         1
+LL | |     };
+   | |_____- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:39:13
+   |
+LL |     let _ = #[linkage = "weak"]
+   |             ^^^^^^^^^^^^^^^^^^^
+LL |     (|| 1);
+   |     ------ not a function definition or static
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs
index ce365d1..6a9853b 100644
--- a/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs
@@ -1,5 +1,4 @@
 //@ build-pass
-#![feature(unsafe_attributes)]
 
 #[cfg_attr(all(), unsafe(no_mangle))]
 fn a() {}
diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
index b8edb4a..95fc19f 100644
--- a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
@@ -1,5 +1,3 @@
-#![feature(unsafe_attributes)]
-
 #[derive(unsafe(Debug))]
 //~^ ERROR: expected identifier, found keyword `unsafe`
 //~| ERROR: traits in `#[derive(...)]` don't accept arguments
diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
index c40a551..4002c93 100644
--- a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
@@ -1,5 +1,5 @@
 error: expected identifier, found keyword `unsafe`
-  --> $DIR/derive-unsafe-attributes.rs:3:10
+  --> $DIR/derive-unsafe-attributes.rs:1:10
    |
 LL | #[derive(unsafe(Debug))]
    |          ^^^^^^ expected identifier, found keyword
@@ -10,13 +10,13 @@
    |          ++
 
 error: traits in `#[derive(...)]` don't accept arguments
-  --> $DIR/derive-unsafe-attributes.rs:3:16
+  --> $DIR/derive-unsafe-attributes.rs:1:16
    |
 LL | #[derive(unsafe(Debug))]
    |                ^^^^^^^ help: remove the arguments
 
 error: `derive` is not an unsafe attribute
-  --> $DIR/derive-unsafe-attributes.rs:12:3
+  --> $DIR/derive-unsafe-attributes.rs:10:3
    |
 LL | #[unsafe(derive(Debug))]
    |   ^^^^^^ this is not an unsafe attribute
@@ -24,7 +24,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: expected identifier, found keyword `unsafe`
-  --> $DIR/derive-unsafe-attributes.rs:3:10
+  --> $DIR/derive-unsafe-attributes.rs:1:10
    |
 LL | #[derive(unsafe(Debug))]
    |          ^^^^^^ expected identifier, found keyword
@@ -36,7 +36,7 @@
    |          ++
 
 error: expected identifier, found keyword `unsafe`
-  --> $DIR/derive-unsafe-attributes.rs:3:10
+  --> $DIR/derive-unsafe-attributes.rs:1:10
    |
 LL | #[derive(unsafe(Debug))]
    |          ^^^^^^ expected identifier, found keyword
@@ -48,13 +48,13 @@
    |          ++
 
 error: cannot find derive macro `r#unsafe` in this scope
-  --> $DIR/derive-unsafe-attributes.rs:3:10
+  --> $DIR/derive-unsafe-attributes.rs:1:10
    |
 LL | #[derive(unsafe(Debug))]
    |          ^^^^^^
 
 error: cannot find derive macro `r#unsafe` in this scope
-  --> $DIR/derive-unsafe-attributes.rs:3:10
+  --> $DIR/derive-unsafe-attributes.rs:1:10
    |
 LL | #[derive(unsafe(Debug))]
    |          ^^^^^^
diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs
index a6c0ea5..894d132 100644
--- a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs
@@ -1,5 +1,3 @@
-#![feature(unsafe_attributes)]
-
 #[unsafe(unsafe(no_mangle))]
 //~^ ERROR expected identifier, found keyword `unsafe`
 //~| ERROR cannot find attribute `r#unsafe` in this scope
diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
index 950b263..0825cf7 100644
--- a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
@@ -1,5 +1,5 @@
 error: expected identifier, found keyword `unsafe`
-  --> $DIR/double-unsafe-attributes.rs:3:10
+  --> $DIR/double-unsafe-attributes.rs:1:10
    |
 LL | #[unsafe(unsafe(no_mangle))]
    |          ^^^^^^ expected identifier, found keyword
@@ -10,7 +10,7 @@
    |          ++
 
 error: `r#unsafe` is not an unsafe attribute
-  --> $DIR/double-unsafe-attributes.rs:3:3
+  --> $DIR/double-unsafe-attributes.rs:1:3
    |
 LL | #[unsafe(unsafe(no_mangle))]
    |   ^^^^^^ this is not an unsafe attribute
@@ -18,7 +18,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: cannot find attribute `r#unsafe` in this scope
-  --> $DIR/double-unsafe-attributes.rs:3:10
+  --> $DIR/double-unsafe-attributes.rs:1:10
    |
 LL | #[unsafe(unsafe(no_mangle))]
    |          ^^^^^^
diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs
index 0181add..b561550 100644
--- a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs
@@ -1,6 +1,5 @@
 //@ edition: 2024
 //@ compile-flags: -Zunstable-options
-#![feature(unsafe_attributes)]
 
 #[unsafe(cfg(any()))] //~ ERROR: is not an unsafe attribute
 fn a() {}
diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr
index f39074b..9fb7f06 100644
--- a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr
@@ -1,5 +1,5 @@
 error: `cfg` is not an unsafe attribute
-  --> $DIR/extraneous-unsafe-attributes.rs:5:3
+  --> $DIR/extraneous-unsafe-attributes.rs:4:3
    |
 LL | #[unsafe(cfg(any()))]
    |   ^^^^^^ this is not an unsafe attribute
@@ -7,7 +7,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `cfg_attr` is not an unsafe attribute
-  --> $DIR/extraneous-unsafe-attributes.rs:8:3
+  --> $DIR/extraneous-unsafe-attributes.rs:7:3
    |
 LL | #[unsafe(cfg_attr(any(), allow(dead_code)))]
    |   ^^^^^^ this is not an unsafe attribute
@@ -15,7 +15,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `test` is not an unsafe attribute
-  --> $DIR/extraneous-unsafe-attributes.rs:11:3
+  --> $DIR/extraneous-unsafe-attributes.rs:10:3
    |
 LL | #[unsafe(test)]
    |   ^^^^^^ this is not an unsafe attribute
@@ -23,7 +23,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `ignore` is not an unsafe attribute
-  --> $DIR/extraneous-unsafe-attributes.rs:14:3
+  --> $DIR/extraneous-unsafe-attributes.rs:13:3
    |
 LL | #[unsafe(ignore = "test")]
    |   ^^^^^^ this is not an unsafe attribute
@@ -31,7 +31,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `should_panic` is not an unsafe attribute
-  --> $DIR/extraneous-unsafe-attributes.rs:17:3
+  --> $DIR/extraneous-unsafe-attributes.rs:16:3
    |
 LL | #[unsafe(should_panic(expected = "test"))]
    |   ^^^^^^ this is not an unsafe attribute
@@ -39,7 +39,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `macro_use` is not an unsafe attribute
-  --> $DIR/extraneous-unsafe-attributes.rs:20:3
+  --> $DIR/extraneous-unsafe-attributes.rs:19:3
    |
 LL | #[unsafe(macro_use)]
    |   ^^^^^^ this is not an unsafe attribute
@@ -47,7 +47,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `macro_export` is not an unsafe attribute
-  --> $DIR/extraneous-unsafe-attributes.rs:22:7
+  --> $DIR/extraneous-unsafe-attributes.rs:21:7
    |
 LL |     #[unsafe(macro_export)]
    |       ^^^^^^ this is not an unsafe attribute
@@ -55,7 +55,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `used` is not an unsafe attribute
-  --> $DIR/extraneous-unsafe-attributes.rs:28:3
+  --> $DIR/extraneous-unsafe-attributes.rs:27:3
    |
 LL | #[unsafe(used)]
    |   ^^^^^^ this is not an unsafe attribute
diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
index f29a5b3..eaf8706 100644
--- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
@@ -1,5 +1,3 @@
-#![feature(unsafe_attributes)]
-
 #[unsafe(proc_macro)]
 //~^ ERROR: is not an unsafe attribute
 //~| ERROR attribute is only usable with crates of the `proc-macro` crate type
diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
index 79d34d4..9c5751c 100644
--- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
@@ -1,11 +1,11 @@
 error[E0452]: malformed lint attribute input
-  --> $DIR/proc-unsafe-attributes.rs:28:16
+  --> $DIR/proc-unsafe-attributes.rs:26:16
    |
 LL | #[unsafe(allow(unsafe(dead_code)))]
    |                ^^^^^^^^^^^^^^^^^ bad attribute argument
 
 error[E0452]: malformed lint attribute input
-  --> $DIR/proc-unsafe-attributes.rs:28:16
+  --> $DIR/proc-unsafe-attributes.rs:26:16
    |
 LL | #[unsafe(allow(unsafe(dead_code)))]
    |                ^^^^^^^^^^^^^^^^^ bad attribute argument
@@ -13,7 +13,7 @@
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: `proc_macro` is not an unsafe attribute
-  --> $DIR/proc-unsafe-attributes.rs:3:3
+  --> $DIR/proc-unsafe-attributes.rs:1:3
    |
 LL | #[unsafe(proc_macro)]
    |   ^^^^^^ this is not an unsafe attribute
@@ -21,7 +21,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `proc_macro_derive` is not an unsafe attribute
-  --> $DIR/proc-unsafe-attributes.rs:9:3
+  --> $DIR/proc-unsafe-attributes.rs:7:3
    |
 LL | #[unsafe(proc_macro_derive(Foo))]
    |   ^^^^^^ this is not an unsafe attribute
@@ -29,7 +29,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: expected identifier, found keyword `unsafe`
-  --> $DIR/proc-unsafe-attributes.rs:14:21
+  --> $DIR/proc-unsafe-attributes.rs:12:21
    |
 LL | #[proc_macro_derive(unsafe(Foo))]
    |                     ^^^^^^ expected identifier, found keyword
@@ -40,7 +40,7 @@
    |                     ++
 
 error: `proc_macro_attribute` is not an unsafe attribute
-  --> $DIR/proc-unsafe-attributes.rs:19:3
+  --> $DIR/proc-unsafe-attributes.rs:17:3
    |
 LL | #[unsafe(proc_macro_attribute)]
    |   ^^^^^^ this is not an unsafe attribute
@@ -48,7 +48,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `allow` is not an unsafe attribute
-  --> $DIR/proc-unsafe-attributes.rs:24:3
+  --> $DIR/proc-unsafe-attributes.rs:22:3
    |
 LL | #[unsafe(allow(dead_code))]
    |   ^^^^^^ this is not an unsafe attribute
@@ -56,7 +56,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: `allow` is not an unsafe attribute
-  --> $DIR/proc-unsafe-attributes.rs:28:3
+  --> $DIR/proc-unsafe-attributes.rs:26:3
    |
 LL | #[unsafe(allow(unsafe(dead_code)))]
    |   ^^^^^^ this is not an unsafe attribute
@@ -64,7 +64,7 @@
    = note: extraneous unsafe is not allowed in attributes
 
 error: expected identifier, found keyword `unsafe`
-  --> $DIR/proc-unsafe-attributes.rs:28:16
+  --> $DIR/proc-unsafe-attributes.rs:26:16
    |
 LL | #[unsafe(allow(unsafe(dead_code)))]
    |                ^^^^^^ expected identifier, found keyword
@@ -75,31 +75,31 @@
    |                ++
 
 error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type
-  --> $DIR/proc-unsafe-attributes.rs:3:1
+  --> $DIR/proc-unsafe-attributes.rs:1:1
    |
 LL | #[unsafe(proc_macro)]
    | ^^^^^^^^^^^^^^^^^^^^^
 
 error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
-  --> $DIR/proc-unsafe-attributes.rs:9:1
+  --> $DIR/proc-unsafe-attributes.rs:7:1
    |
 LL | #[unsafe(proc_macro_derive(Foo))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
-  --> $DIR/proc-unsafe-attributes.rs:14:1
+  --> $DIR/proc-unsafe-attributes.rs:12:1
    |
 LL | #[proc_macro_derive(unsafe(Foo))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type
-  --> $DIR/proc-unsafe-attributes.rs:19:1
+  --> $DIR/proc-unsafe-attributes.rs:17:1
    |
 LL | #[unsafe(proc_macro_attribute)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0452]: malformed lint attribute input
-  --> $DIR/proc-unsafe-attributes.rs:28:16
+  --> $DIR/proc-unsafe-attributes.rs:26:16
    |
 LL | #[unsafe(allow(unsafe(dead_code)))]
    |                ^^^^^^^^^^^^^^^^^ bad attribute argument
@@ -107,7 +107,7 @@
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0452]: malformed lint attribute input
-  --> $DIR/proc-unsafe-attributes.rs:28:16
+  --> $DIR/proc-unsafe-attributes.rs:26:16
    |
 LL | #[unsafe(allow(unsafe(dead_code)))]
    |                ^^^^^^^^^^^^^^^^^ bad attribute argument
diff --git a/tests/ui/attributes/unsafe/unsafe-attributes.rs b/tests/ui/attributes/unsafe/unsafe-attributes.rs
index 33a412a..5c57767 100644
--- a/tests/ui/attributes/unsafe/unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/unsafe-attributes.rs
@@ -1,5 +1,4 @@
 //@ build-pass
-#![feature(unsafe_attributes)]
 
 #[unsafe(no_mangle)]
 fn a() {}
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs
index 67db36a..5af03a2 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs
@@ -1,5 +1,3 @@
-#![feature(unsafe_attributes)]
-
 #[unsafe(repr(C))] //~ ERROR: is not an unsafe attribute
 struct Foo {}
 
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
index 584b0ea..55172c9 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
@@ -1,5 +1,5 @@
 error: `repr` is not an unsafe attribute
-  --> $DIR/unsafe-safe-attribute.rs:3:3
+  --> $DIR/unsafe-safe-attribute.rs:1:3
    |
 LL | #[unsafe(repr(C))]
    |   ^^^^^^ this is not an unsafe attribute
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs
index ff2eb61..0f241cc 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs
@@ -1,5 +1,3 @@
-#![feature(unsafe_attributes)]
-
 #[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute
     message = "testing",
 ))]
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
index 26b5e4e..3bc291d 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
@@ -1,5 +1,5 @@
 error: `diagnostic::on_unimplemented` is not an unsafe attribute
-  --> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3
+  --> $DIR/unsafe-safe-attribute_diagnostic.rs:1:3
    |
 LL | #[unsafe(diagnostic::on_unimplemented(
    |   ^^^^^^ this is not an unsafe attribute
diff --git a/tests/crashes/116308.rs b/tests/ui/const-generics/adt_const_params/116308.rs
similarity index 81%
rename from tests/crashes/116308.rs
rename to tests/ui/const-generics/adt_const_params/116308.rs
index cb96c80..9ea7022 100644
--- a/tests/crashes/116308.rs
+++ b/tests/ui/const-generics/adt_const_params/116308.rs
@@ -1,6 +1,8 @@
-//@ known-bug: #116308
+//@ check-pass
 #![feature(adt_const_params)]
 
+// Regression test for #116308
+
 pub trait Identity {
     type Identity;
 }
diff --git a/tests/ui/consts/const-float-bits-conv.rs b/tests/ui/consts/const-float-bits-conv.rs
index ba8db4c..45e8ea5 100644
--- a/tests/ui/consts/const-float-bits-conv.rs
+++ b/tests/ui/consts/const-float-bits-conv.rs
@@ -23,6 +23,11 @@ macro_rules! const_assert {
     };
 }
 
+fn has_broken_floats() -> bool {
+    // i586 targets are broken due to <https://github.com/rust-lang/rust/issues/114479>.
+    std::env::var("TARGET").is_ok_and(|v| v.contains("i586"))
+}
+
 fn f32() {
     const_assert!((1f32).to_bits(), 0x3f800000);
     const_assert!(u32::from_be_bytes(1f32.to_be_bytes()), 0x3f800000);
@@ -38,6 +43,19 @@ fn f32() {
     const_assert!(f32::from_bits(0x44a72000), 1337.0);
     const_assert!(f32::from_ne_bytes(0x44a72000u32.to_ne_bytes()), 1337.0);
     const_assert!(f32::from_bits(0xc1640000), -14.25);
+
+    // Check that NaNs roundtrip their bits regardless of signalingness
+    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+    // NOTE: These names assume `f{BITS}::NAN` is a quiet NAN and IEEE754-2008's NaN rules apply!
+    const QUIET_NAN: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
+    const SIGNALING_NAN: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
+
+    const_assert!(f32::from_bits(QUIET_NAN).is_nan());
+    const_assert!(f32::from_bits(SIGNALING_NAN).is_nan());
+    const_assert!(f32::from_bits(QUIET_NAN).to_bits(), QUIET_NAN);
+    if !has_broken_floats() {
+        const_assert!(f32::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN);
+    }
 }
 
 fn f64() {
@@ -55,6 +73,19 @@ fn f64() {
     const_assert!(f64::from_bits(0x4094e40000000000), 1337.0);
     const_assert!(f64::from_ne_bytes(0x4094e40000000000u64.to_ne_bytes()), 1337.0);
     const_assert!(f64::from_bits(0xc02c800000000000), -14.25);
+
+    // Check that NaNs roundtrip their bits regardless of signalingness
+    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+    // NOTE: These names assume `f{BITS}::NAN` is a quiet NAN and IEEE754-2008's NaN rules apply!
+    const QUIET_NAN: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
+    const SIGNALING_NAN: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
+
+    const_assert!(f64::from_bits(QUIET_NAN).is_nan());
+    const_assert!(f64::from_bits(SIGNALING_NAN).is_nan());
+    const_assert!(f64::from_bits(QUIET_NAN).to_bits(), QUIET_NAN);
+    if !has_broken_floats() {
+        const_assert!(f64::from_bits(SIGNALING_NAN).to_bits(), SIGNALING_NAN);
+    }
 }
 
 fn main() {
diff --git a/tests/ui/consts/const-float-bits-reject-conv.rs b/tests/ui/consts/const-float-bits-reject-conv.rs
deleted file mode 100644
index febb272..0000000
--- a/tests/ui/consts/const-float-bits-reject-conv.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-//@ compile-flags: -Zmir-opt-level=0
-//@ error-pattern: cannot use f32::to_bits on a NaN
-#![feature(const_float_bits_conv)]
-#![feature(const_float_classify)]
-
-// Don't promote
-const fn nop<T>(x: T) -> T { x }
-
-macro_rules! const_assert {
-    ($a:expr) => {
-        {
-            const _: () = assert!($a);
-            assert!(nop($a));
-        }
-    };
-    ($a:expr, $b:expr) => {
-        {
-            const _: () = assert!($a == $b);
-            assert_eq!(nop($a), nop($b));
-        }
-    };
-}
-
-fn f32() {
-    // Check that NaNs roundtrip their bits regardless of signalingness
-    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
-    // ...actually, let's just check that these break. :D
-    const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
-    //~^ inside
-    const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
-    //~^ inside
-
-    // The rest of the code is dead because the constants already fail to evaluate.
-
-    const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-    const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-
-    // LLVM does not guarantee that loads and stores of NaNs preserve their exact bit pattern.
-    // In practice, this seems to only cause a problem on x86, since the most widely used calling
-    // convention mandates that floating point values are returned on the x87 FPU stack. See #73328.
-    // However, during CTFE we still preserve bit patterns (though that is not a guarantee).
-    const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-    const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-}
-
-fn f64() {
-    // Check that NaNs roundtrip their bits regardless of signalingness
-    // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
-    // ...actually, let's just check that these break. :D
-    const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
-    //~^ inside
-    const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
-    //~^ inside
-
-    // The rest of the code is dead because the constants already fail to evaluate.
-
-    const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-    const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-
-    // See comment above.
-    const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-    const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-}
-
-fn main() {
-    f32();
-    f64();
-}
diff --git a/tests/ui/consts/const-float-bits-reject-conv.stderr b/tests/ui/consts/const-float-bits-reject-conv.stderr
deleted file mode 100644
index 1511dab..0000000
--- a/tests/ui/consts/const-float-bits-reject-conv.stderr
+++ /dev/null
@@ -1,115 +0,0 @@
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-   |
-   = note: the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
-   |
-note: inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32`
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-note: inside `core::f32::<impl f32>::to_bits`
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-note: inside `f32::MASKED_NAN1`
-  --> $DIR/const-float-bits-reject-conv.rs:28:30
-   |
-LL |     const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
-   |                              ^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-   |
-   = note: the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
-   |
-note: inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32`
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-note: inside `core::f32::<impl f32>::to_bits`
-  --> $SRC_DIR/core/src/num/f32.rs:LL:COL
-note: inside `f32::MASKED_NAN2`
-  --> $DIR/const-float-bits-reject-conv.rs:30:30
-   |
-LL |     const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
-   |                              ^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:35:34
-   |
-LL |     const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:36:34
-   |
-LL |     const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:42:34
-   |
-LL |     const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:43:34
-   |
-LL |     const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-   |                                  ^^^^^^^^^^^
-
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-   |
-   = note: the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
-   |
-note: inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64`
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-note: inside `core::f64::<impl f64>::to_bits`
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-note: inside `f64::MASKED_NAN1`
-  --> $DIR/const-float-bits-reject-conv.rs:50:30
-   |
-LL |     const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
-   |                              ^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-   |
-   = note: the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
-   |
-note: inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64`
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-note: inside `core::f64::<impl f64>::to_bits`
-  --> $SRC_DIR/core/src/num/f64.rs:LL:COL
-note: inside `f64::MASKED_NAN2`
-  --> $DIR/const-float-bits-reject-conv.rs:52:30
-   |
-LL |     const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
-   |                              ^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:57:34
-   |
-LL |     const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:58:34
-   |
-LL |     const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:61:34
-   |
-LL |     const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-   |                                  ^^^^^^^^^^^
-
-note: erroneous constant encountered
-  --> $DIR/const-float-bits-reject-conv.rs:62:34
-   |
-LL |     const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-   |                                  ^^^^^^^^^^^
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.rs b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.rs
new file mode 100644
index 0000000..0a7ec5a
--- /dev/null
+++ b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.rs
@@ -0,0 +1,34 @@
+#![feature(fn_delegation)]
+#![allow(incomplete_features)]
+
+use std::marker::PhantomData;
+
+pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
+
+impl<'a> InvariantRef<'a, ()> {
+    pub const NEW: Self = InvariantRef::new(&());
+    //~^ ERROR: no function or associated item named `new` found
+}
+
+trait Trait {
+    fn foo(&self) -> u8 { 0 }
+    fn bar(&self) -> u8 { 1 }
+    fn meh(&self) -> u8 { 2 }
+}
+
+struct Z(u8);
+
+impl Trait for Z {
+    reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+    //~^ ERROR: use of undeclared lifetime name `'a`
+    //~| ERROR: use of undeclared lifetime name `'a`
+    //~| ERROR: use of undeclared lifetime name `'a`
+    //~| ERROR: the trait bound `u8: Trait` is not satisfied
+    //~| ERROR: the trait bound `u8: Trait` is not satisfied
+    //~| ERROR: the trait bound `u8: Trait` is not satisfied
+    //~| ERROR: mismatched types
+    //~| ERROR: mismatched types
+    //~| ERROR: mismatched types
+}
+
+fn main() { }
diff --git a/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr
new file mode 100644
index 0000000..2ce3b38
--- /dev/null
+++ b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr
@@ -0,0 +1,113 @@
+error[E0261]: use of undeclared lifetime name `'a`
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:68
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |                                                                    ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'a` here
+   |
+LL |     reuse <u8 as Trait>::{foo'a, , bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |                              +++
+help: consider introducing lifetime `'a` here
+   |
+LL | impl<'a> Trait for Z {
+   |     ++++
+
+error[E0261]: use of undeclared lifetime name `'a`
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:68
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |                                                                    ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'a` here
+   |
+LL |     reuse <u8 as Trait>::{foo, bar'a, , meh} { &const { InvariantRef::<'a>::NEW } }
+   |                                   +++
+help: consider introducing lifetime `'a` here
+   |
+LL | impl<'a> Trait for Z {
+   |     ++++
+
+error[E0261]: use of undeclared lifetime name `'a`
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:68
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |                                                                    ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'a` here
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh'a, } { &const { InvariantRef::<'a>::NEW } }
+   |                                        +++
+help: consider introducing lifetime `'a` here
+   |
+LL | impl<'a> Trait for Z {
+   |     ++++
+
+error[E0599]: no function or associated item named `new` found for struct `InvariantRef` in the current scope
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:9:41
+   |
+LL | pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
+   | -------------------------------------- function or associated item `new` not found for this struct
+...
+LL |     pub const NEW: Self = InvariantRef::new(&());
+   |                                         ^^^ function or associated item not found in `InvariantRef<'_, _>`
+
+error[E0308]: mismatched types
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:53
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |                                                     ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>`
+   |
+   = note: expected type `u8`
+            found struct `InvariantRef<'_, ()>`
+
+error[E0277]: the trait bound `u8: Trait` is not satisfied
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:12
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |            ^^ the trait `Trait` is not implemented for `u8`
+   |
+   = help: the trait `Trait` is implemented for `Z`
+
+error[E0308]: mismatched types
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:53
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |                                                     ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>`
+   |
+   = note: expected type `u8`
+            found struct `InvariantRef<'_, ()>`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0277]: the trait bound `u8: Trait` is not satisfied
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:12
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |            ^^ the trait `Trait` is not implemented for `u8`
+   |
+   = help: the trait `Trait` is implemented for `Z`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0308]: mismatched types
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:53
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |                                                     ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>`
+   |
+   = note: expected type `u8`
+            found struct `InvariantRef<'_, ()>`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0277]: the trait bound `u8: Trait` is not satisfied
+  --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:12
+   |
+LL |     reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
+   |            ^^ the trait `Trait` is not implemented for `u8`
+   |
+   = help: the trait `Trait` is implemented for `Z`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 10 previous errors
+
+Some errors have detailed explanations: E0261, E0277, E0308, E0599.
+For more information about an error, try `rustc --explain E0261`.
diff --git a/tests/ui/deriving/auxiliary/another-proc-macro.rs b/tests/ui/deriving/auxiliary/another-proc-macro.rs
new file mode 100644
index 0000000..a05175c
--- /dev/null
+++ b/tests/ui/deriving/auxiliary/another-proc-macro.rs
@@ -0,0 +1,45 @@
+//@ force-host
+//@ no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(proc_macro_quote)]
+
+extern crate proc_macro;
+
+use proc_macro::{quote, TokenStream};
+
+#[proc_macro_derive(AnotherMacro, attributes(pointee))]
+pub fn derive(_input: TokenStream) -> TokenStream {
+    quote! {
+        const _: () = {
+            const ANOTHER_MACRO_DERIVED: () = ();
+        };
+    }
+    .into()
+}
+
+#[proc_macro_attribute]
+pub fn pointee(
+    _attr: proc_macro::TokenStream,
+    _item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    quote! {
+        const _: () = {
+            const POINTEE_MACRO_ATTR_DERIVED: () = ();
+        };
+    }
+    .into()
+}
+
+#[proc_macro_attribute]
+pub fn default(
+    _attr: proc_macro::TokenStream,
+    _item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+    quote! {
+        const _: () = {
+            const DEFAULT_MACRO_ATTR_DERIVED: () = ();
+        };
+    }
+    .into()
+}
diff --git a/tests/ui/deriving/built-in-proc-macro-scope.rs b/tests/ui/deriving/built-in-proc-macro-scope.rs
new file mode 100644
index 0000000..41c95f6
--- /dev/null
+++ b/tests/ui/deriving/built-in-proc-macro-scope.rs
@@ -0,0 +1,25 @@
+//@ check-pass
+//@ aux-build: another-proc-macro.rs
+//@ compile-flags: -Zunpretty=expanded
+
+#![feature(derive_smart_pointer)]
+
+#[macro_use]
+extern crate another_proc_macro;
+
+use another_proc_macro::{pointee, AnotherMacro};
+
+#[derive(core::marker::SmartPointer)]
+#[repr(transparent)]
+pub struct Ptr<'a, #[pointee] T: ?Sized> {
+    data: &'a mut T,
+}
+
+#[pointee]
+fn f() {}
+
+#[derive(AnotherMacro)]
+#[pointee]
+struct MyStruct;
+
+fn main() {}
diff --git a/tests/ui/deriving/built-in-proc-macro-scope.stdout b/tests/ui/deriving/built-in-proc-macro-scope.stdout
new file mode 100644
index 0000000..c649b7a
--- /dev/null
+++ b/tests/ui/deriving/built-in-proc-macro-scope.stdout
@@ -0,0 +1,43 @@
+#![feature(prelude_import)]
+#![no_std]
+//@ check-pass
+//@ aux-build: another-proc-macro.rs
+//@ compile-flags: -Zunpretty=expanded
+
+#![feature(derive_smart_pointer)]
+#[prelude_import]
+use ::std::prelude::rust_2015::*;
+#[macro_use]
+extern crate std;
+
+#[macro_use]
+extern crate another_proc_macro;
+
+use another_proc_macro::{pointee, AnotherMacro};
+
+#[repr(transparent)]
+pub struct Ptr<'a, #[pointee] T: ?Sized> {
+    data: &'a mut T,
+}
+#[automatically_derived]
+impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
+    ::core::ops::DispatchFromDyn<Ptr<'a, __S>> for Ptr<'a, T> {
+}
+#[automatically_derived]
+impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
+    ::core::ops::CoerceUnsized<Ptr<'a, __S>> for Ptr<'a, T> {
+}
+
+
+
+const _: () =
+    {
+        const POINTEE_MACRO_ATTR_DERIVED: () = ();
+    };
+#[pointee]
+struct MyStruct;
+const _: () =
+    {
+        const ANOTHER_MACRO_DERIVED: () = ();
+    };
+fn main() {}
diff --git a/tests/ui/deriving/proc-macro-attribute-mixing.rs b/tests/ui/deriving/proc-macro-attribute-mixing.rs
new file mode 100644
index 0000000..489665e
--- /dev/null
+++ b/tests/ui/deriving/proc-macro-attribute-mixing.rs
@@ -0,0 +1,20 @@
+// This test certify that we can mix attribute macros from Rust and external proc-macros.
+// For instance, `#[derive(Default)]` uses `#[default]` and `#[derive(SmartPointer)]` uses
+// `#[pointee]`.
+// The scoping rule should allow the use of the said two attributes when external proc-macros
+// are in scope.
+
+//@ check-pass
+//@ aux-build: another-proc-macro.rs
+//@ compile-flags: -Zunpretty=expanded
+
+#![feature(derive_smart_pointer)]
+
+#[macro_use]
+extern crate another_proc_macro;
+
+#[pointee]
+fn f() {}
+
+#[default]
+fn g() {}
diff --git a/tests/ui/deriving/proc-macro-attribute-mixing.stdout b/tests/ui/deriving/proc-macro-attribute-mixing.stdout
new file mode 100644
index 0000000..f314f6e
--- /dev/null
+++ b/tests/ui/deriving/proc-macro-attribute-mixing.stdout
@@ -0,0 +1,30 @@
+#![feature(prelude_import)]
+#![no_std]
+// This test certify that we can mix attribute macros from Rust and external proc-macros.
+// For instance, `#[derive(Default)]` uses `#[default]` and `#[derive(SmartPointer)]` uses
+// `#[pointee]`.
+// The scoping rule should allow the use of the said two attributes when external proc-macros
+// are in scope.
+
+//@ check-pass
+//@ aux-build: another-proc-macro.rs
+//@ compile-flags: -Zunpretty=expanded
+
+#![feature(derive_smart_pointer)]
+#[prelude_import]
+use ::std::prelude::rust_2015::*;
+#[macro_use]
+extern crate std;
+
+#[macro_use]
+extern crate another_proc_macro;
+
+
+const _: () =
+    {
+        const POINTEE_MACRO_ATTR_DERIVED: () = ();
+    };
+const _: () =
+    {
+        const DEFAULT_MACRO_ATTR_DERIVED: () = ();
+    };
diff --git a/tests/ui/extern/extern-main-issue-86110.stderr b/tests/ui/extern/extern-main-issue-86110.stderr
index 8a3262f..d69f4e6 100644
--- a/tests/ui/extern/extern-main-issue-86110.stderr
+++ b/tests/ui/extern/extern-main-issue-86110.stderr
@@ -2,7 +2,7 @@
   --> $DIR/extern-main-issue-86110.rs:4:5
    |
 LL |     fn main();
-   |     ^^^^^^^^^
+   |     ^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs b/tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs
index 3257a9c..7b4764e 100644
--- a/tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs
+++ b/tests/ui/feature-gates/feature-gate-derive-smart-pointer.rs
@@ -3,7 +3,6 @@
 #[derive(SmartPointer)] //~ ERROR use of unstable library feature 'derive_smart_pointer'
 #[repr(transparent)]
 struct MyPointer<'a, #[pointee] T: ?Sized> {
-    //~^ ERROR the `#[pointee]` attribute is an experimental feature
     ptr: &'a T,
 }
 
diff --git a/tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr b/tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr
index 1950193..ea4d127 100644
--- a/tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr
+++ b/tests/ui/feature-gates/feature-gate-derive-smart-pointer.stderr
@@ -8,16 +8,6 @@
    = help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0658]: the `#[pointee]` attribute is an experimental feature
-  --> $DIR/feature-gate-derive-smart-pointer.rs:5:22
-   |
-LL | struct MyPointer<'a, #[pointee] T: ?Sized> {
-   |                      ^^^^^^^^^^
-   |
-   = note: see issue #123430 <https://github.com/rust-lang/rust/issues/123430> for more information
-   = help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 error[E0658]: use of unstable library feature 'derive_smart_pointer'
   --> $DIR/feature-gate-derive-smart-pointer.rs:1:5
    |
@@ -28,6 +18,6 @@
    = help: add `#![feature(derive_smart_pointer)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs b/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs
deleted file mode 100644
index 9eba415..0000000
--- a/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-#[unsafe(no_mangle)] //~ ERROR [E0658]
-extern "C" fn foo() {
-
-}
-
-fn main() {
-    foo();
-}
diff --git a/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr b/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr
deleted file mode 100644
index dfcea75..0000000
--- a/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr
+++ /dev/null
@@ -1,13 +0,0 @@
-error[E0658]: `#[unsafe()]` markers for attributes are experimental
-  --> $DIR/feature-gate-unsafe-attributes.rs:1:3
-   |
-LL | #[unsafe(no_mangle)]
-   |   ^^^^^^
-   |
-   = note: see issue #123757 <https://github.com/rust-lang/rust/issues/123757> for more information
-   = help: add `#![feature(unsafe_attributes)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/foreign/foreign-safe-fn-arg-mismatch.rs b/tests/ui/foreign/foreign-safe-fn-arg-mismatch.rs
new file mode 100644
index 0000000..bb1052b
--- /dev/null
+++ b/tests/ui/foreign/foreign-safe-fn-arg-mismatch.rs
@@ -0,0 +1,13 @@
+// Make sure we don't ICE when a foreign fn doesn't implement `Fn` due to arg mismatch.
+
+unsafe extern "Rust" {
+    pub safe fn foo();
+    pub safe fn bar(x: u32);
+}
+
+fn test(_: impl Fn(i32)) {}
+
+fn main() {
+    test(foo); //~ ERROR function is expected to take 1 argument, but it takes 0 arguments
+    test(bar); //~ ERROR type mismatch in function arguments
+}
diff --git a/tests/ui/foreign/foreign-safe-fn-arg-mismatch.stderr b/tests/ui/foreign/foreign-safe-fn-arg-mismatch.stderr
new file mode 100644
index 0000000..73ccecf
--- /dev/null
+++ b/tests/ui/foreign/foreign-safe-fn-arg-mismatch.stderr
@@ -0,0 +1,44 @@
+error[E0593]: function is expected to take 1 argument, but it takes 0 arguments
+  --> $DIR/foreign-safe-fn-arg-mismatch.rs:11:10
+   |
+LL |     pub safe fn foo();
+   |     ------------------ takes 0 arguments
+...
+LL |     test(foo);
+   |     ---- ^^^ expected function that takes 1 argument
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `test`
+  --> $DIR/foreign-safe-fn-arg-mismatch.rs:8:17
+   |
+LL | fn test(_: impl Fn(i32)) {}
+   |                 ^^^^^^^ required by this bound in `test`
+
+error[E0631]: type mismatch in function arguments
+  --> $DIR/foreign-safe-fn-arg-mismatch.rs:12:10
+   |
+LL |     pub safe fn bar(x: u32);
+   |     ------------------------ found signature defined here
+...
+LL |     test(bar);
+   |     ---- ^^^ expected due to this
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = note: expected function signature `fn(i32) -> _`
+              found function signature `fn(u32) -> _`
+note: required by a bound in `test`
+  --> $DIR/foreign-safe-fn-arg-mismatch.rs:8:17
+   |
+LL | fn test(_: impl Fn(i32)) {}
+   |                 ^^^^^^^ required by this bound in `test`
+help: consider wrapping the function in a closure
+   |
+LL |     test(|arg0: i32| bar(/* u32 */));
+   |          +++++++++++    +++++++++++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0593, E0631.
+For more information about an error, try `rustc --explain E0593`.
diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.effects.stderr b/tests/ui/intrinsics/safe-intrinsic-mismatch.effects.stderr
index d9a4960..55983a4 100644
--- a/tests/ui/intrinsics/safe-intrinsic-mismatch.effects.stderr
+++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.effects.stderr
@@ -7,13 +7,13 @@
   --> $DIR/safe-intrinsic-mismatch.rs:11:5
    |
 LL |     fn size_of<T>() -> usize;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of`
   --> $DIR/safe-intrinsic-mismatch.rs:11:5
    |
 LL |     fn size_of<T>() -> usize;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.stock.stderr b/tests/ui/intrinsics/safe-intrinsic-mismatch.stock.stderr
index 6864c0f..c59e357 100644
--- a/tests/ui/intrinsics/safe-intrinsic-mismatch.stock.stderr
+++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.stock.stderr
@@ -2,13 +2,13 @@
   --> $DIR/safe-intrinsic-mismatch.rs:11:5
    |
 LL |     fn size_of<T>() -> usize;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of`
   --> $DIR/safe-intrinsic-mismatch.rs:11:5
    |
 LL |     fn size_of<T>() -> usize;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
diff --git a/tests/ui/issues/issue-16725.stderr b/tests/ui/issues/issue-16725.stderr
index a4a406b..dcb7d58 100644
--- a/tests/ui/issues/issue-16725.stderr
+++ b/tests/ui/issues/issue-16725.stderr
@@ -8,7 +8,7 @@
   --> $DIR/auxiliary/issue-16725.rs:2:5
    |
 LL |     fn bar();
-   |     ^^^^^^^^
+   |     ^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr
index 43c8cde..f75ff6d 100644
--- a/tests/ui/lint/clashing-extern-fn.stderr
+++ b/tests/ui/lint/clashing-extern-fn.stderr
@@ -21,10 +21,10 @@
   --> $DIR/clashing-extern-fn.rs:14:13
    |
 LL |             fn clash(x: u8);
-   |             --------------- `clash` previously declared here
+   |             ---------------- `clash` previously declared here
 ...
 LL |             fn clash(x: u64);
-   |             ^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn(u8)`
               found `unsafe extern "C" fn(u64)`
@@ -41,7 +41,7 @@
    |     --------------------------------- `extern_link_name` previously declared here
 ...
 LL |         fn extern_link_name(x: u32);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn(i16)`
               found `unsafe extern "C" fn(u32)`
@@ -50,7 +50,7 @@
   --> $DIR/clashing-extern-fn.rs:55:9
    |
 LL |     fn some_other_new_name(x: i16);
-   |     ------------------------------ `some_other_new_name` previously declared here
+   |     ------------------------------- `some_other_new_name` previously declared here
 ...
 LL |         #[link_name = "some_other_new_name"]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
@@ -74,10 +74,10 @@
   --> $DIR/clashing-extern-fn.rs:72:9
    |
 LL |         fn different_mod(x: u8);
-   |         ----------------------- `different_mod` previously declared here
+   |         ------------------------ `different_mod` previously declared here
 ...
 LL |         fn different_mod(x: u64);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn(u8)`
               found `unsafe extern "C" fn(u64)`
@@ -86,10 +86,10 @@
   --> $DIR/clashing-extern-fn.rs:82:9
    |
 LL |     fn variadic_decl(x: u8, ...);
-   |     ---------------------------- `variadic_decl` previously declared here
+   |     ----------------------------- `variadic_decl` previously declared here
 ...
 LL |         fn variadic_decl(x: u8);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn(u8, ...)`
               found `unsafe extern "C" fn(u8)`
@@ -98,10 +98,10 @@
   --> $DIR/clashing-extern-fn.rs:142:13
    |
 LL |             fn weigh_banana(count: *const Banana) -> u64;
-   |             -------------------------------------------- `weigh_banana` previously declared here
+   |             --------------------------------------------- `weigh_banana` previously declared here
 ...
 LL |             fn weigh_banana(count: *const Banana) -> u64;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn(*const one::Banana) -> u64`
               found `unsafe extern "C" fn(*const three::Banana) -> u64`
@@ -110,10 +110,10 @@
   --> $DIR/clashing-extern-fn.rs:171:13
    |
 LL |             fn draw_point(p: Point);
-   |             ----------------------- `draw_point` previously declared here
+   |             ------------------------ `draw_point` previously declared here
 ...
 LL |             fn draw_point(p: Point);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn(sameish_members::a::Point)`
               found `unsafe extern "C" fn(sameish_members::b::Point)`
@@ -122,10 +122,10 @@
   --> $DIR/clashing-extern-fn.rs:197:13
    |
 LL |             fn origin() -> Point3;
-   |             --------------------- `origin` previously declared here
+   |             ---------------------- `origin` previously declared here
 ...
 LL |             fn origin() -> Point3;
-   |             ^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> same_sized_members_clash::a::Point3`
               found `unsafe extern "C" fn() -> same_sized_members_clash::b::Point3`
@@ -134,10 +134,10 @@
   --> $DIR/clashing-extern-fn.rs:220:13
    |
 LL |             fn transparent_incorrect() -> T;
-   |             ------------------------------- `transparent_incorrect` previously declared here
+   |             -------------------------------- `transparent_incorrect` previously declared here
 ...
 LL |             fn transparent_incorrect() -> isize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> T`
               found `unsafe extern "C" fn() -> isize`
@@ -146,10 +146,10 @@
   --> $DIR/clashing-extern-fn.rs:259:13
    |
 LL |             fn missing_return_type() -> usize;
-   |             --------------------------------- `missing_return_type` previously declared here
+   |             ---------------------------------- `missing_return_type` previously declared here
 ...
 LL |             fn missing_return_type();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> usize`
               found `unsafe extern "C" fn()`
@@ -158,10 +158,10 @@
   --> $DIR/clashing-extern-fn.rs:277:13
    |
 LL |             fn non_zero_usize() -> core::num::NonZero<usize>;
-   |             ------------------------------------------------ `non_zero_usize` previously declared here
+   |             ------------------------------------------------- `non_zero_usize` previously declared here
 ...
 LL |             fn non_zero_usize() -> usize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> NonZero<usize>`
               found `unsafe extern "C" fn() -> usize`
@@ -170,10 +170,10 @@
   --> $DIR/clashing-extern-fn.rs:279:13
    |
 LL |             fn non_null_ptr() -> core::ptr::NonNull<usize>;
-   |             ---------------------------------------------- `non_null_ptr` previously declared here
+   |             ----------------------------------------------- `non_null_ptr` previously declared here
 ...
 LL |             fn non_null_ptr() -> *const usize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> NonNull<usize>`
               found `unsafe extern "C" fn() -> *const usize`
@@ -182,10 +182,10 @@
   --> $DIR/clashing-extern-fn.rs:373:13
    |
 LL |             fn option_non_zero_usize_incorrect() -> usize;
-   |             --------------------------------------------- `option_non_zero_usize_incorrect` previously declared here
+   |             ---------------------------------------------- `option_non_zero_usize_incorrect` previously declared here
 ...
 LL |             fn option_non_zero_usize_incorrect() -> isize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> usize`
               found `unsafe extern "C" fn() -> isize`
@@ -194,10 +194,10 @@
   --> $DIR/clashing-extern-fn.rs:375:13
    |
 LL |             fn option_non_null_ptr_incorrect() -> *const usize;
-   |             -------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here
+   |             --------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here
 ...
 LL |             fn option_non_null_ptr_incorrect() -> *const isize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> *const usize`
               found `unsafe extern "C" fn() -> *const isize`
@@ -206,10 +206,10 @@
   --> $DIR/clashing-extern-fn.rs:429:13
    |
 LL |             fn hidden_niche_transparent_no_niche() -> usize;
-   |             ----------------------------------------------- `hidden_niche_transparent_no_niche` previously declared here
+   |             ------------------------------------------------ `hidden_niche_transparent_no_niche` previously declared here
 ...
 LL |             fn hidden_niche_transparent_no_niche() -> Option<TransparentNoNiche>;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> usize`
               found `unsafe extern "C" fn() -> Option<TransparentNoNiche>`
@@ -218,10 +218,10 @@
   --> $DIR/clashing-extern-fn.rs:433:13
    |
 LL |             fn hidden_niche_unsafe_cell() -> usize;
-   |             -------------------------------------- `hidden_niche_unsafe_cell` previously declared here
+   |             --------------------------------------- `hidden_niche_unsafe_cell` previously declared here
 ...
 LL |             fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZero<usize>>>;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn() -> usize`
               found `unsafe extern "C" fn() -> Option<UnsafeCell<NonZero<usize>>>`
diff --git a/tests/ui/lint/issue-1866.stderr b/tests/ui/lint/issue-1866.stderr
index 36d3238..d19a134 100644
--- a/tests/ui/lint/issue-1866.stderr
+++ b/tests/ui/lint/issue-1866.stderr
@@ -2,10 +2,10 @@
   --> $DIR/issue-1866.rs:23:13
    |
 LL |             pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
-   |             ----------------------------------------------------------- `rust_task_is_unwinding` previously declared here
+   |             ------------------------------------------------------------ `rust_task_is_unwinding` previously declared here
 ...
 LL |             pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn(*const usize) -> bool`
               found `unsafe extern "C" fn(*const bool) -> bool`
diff --git a/tests/ui/lint/lint-attr-everywhere-late.stderr b/tests/ui/lint/lint-attr-everywhere-late.stderr
index ddc3190..1937b61 100644
--- a/tests/ui/lint/lint-attr-everywhere-late.stderr
+++ b/tests/ui/lint/lint-attr-everywhere-late.stderr
@@ -406,10 +406,10 @@
   --> $DIR/lint-attr-everywhere-late.rs:123:5
    |
 LL |         fn clashing1();
-   |         -------------- `clashing1` previously declared here
+   |         --------------- `clashing1` previously declared here
 ...
 LL |     fn clashing1(_: i32);
-   |     ^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |     ^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn()`
               found `unsafe extern "C" fn(i32)`
@@ -423,10 +423,10 @@
   --> $DIR/lint-attr-everywhere-late.rs:128:5
    |
 LL |         fn clashing2();
-   |         -------------- `clashing2` previously declared here
+   |         --------------- `clashing2` previously declared here
 ...
 LL |     fn clashing2(_: i32);
-   |     ^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |     ^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
    |
    = note: expected `unsafe extern "C" fn()`
               found `unsafe extern "C" fn(i32)`
diff --git a/tests/ui/lint/lint-missing-doc.stderr b/tests/ui/lint/lint-missing-doc.stderr
index 4e9ee4f..5165ccc 100644
--- a/tests/ui/lint/lint-missing-doc.stderr
+++ b/tests/ui/lint/lint-missing-doc.stderr
@@ -116,7 +116,7 @@
   --> $DIR/lint-missing-doc.rs:196:5
    |
 LL |     pub fn extern_fn_undocumented(f: f32) -> f32;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: missing documentation for a static
   --> $DIR/lint-missing-doc.rs:201:5
diff --git a/tests/ui/lint/unreachable_pub.stderr b/tests/ui/lint/unreachable_pub.stderr
index 705a537..65f45fb 100644
--- a/tests/ui/lint/unreachable_pub.stderr
+++ b/tests/ui/lint/unreachable_pub.stderr
@@ -130,7 +130,7 @@
   --> $DIR/unreachable_pub.rs:48:9
    |
 LL |         pub fn catalyze() -> bool;
-   |         ---^^^^^^^^^^^^^^^^^^^^^^
+   |         ---^^^^^^^^^^^^^^^^^^^^^^^
    |         |
    |         help: consider restricting its visibility: `pub(crate)`
    |
diff --git a/tests/ui/macros/macro-match-nonterminal.stderr b/tests/ui/macros/macro-match-nonterminal.stderr
index 831579c..f221f92 100644
--- a/tests/ui/macros/macro-match-nonterminal.stderr
+++ b/tests/ui/macros/macro-match-nonterminal.stderr
@@ -1,14 +1,14 @@
 error: missing fragment specifier
-  --> $DIR/macro-match-nonterminal.rs:2:8
+  --> $DIR/macro-match-nonterminal.rs:2:6
    |
 LL |     ($a, $b) => {
-   |        ^
+   |      ^^
 
 error: missing fragment specifier
-  --> $DIR/macro-match-nonterminal.rs:2:8
+  --> $DIR/macro-match-nonterminal.rs:2:6
    |
 LL |     ($a, $b) => {
-   |        ^
+   |      ^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
@@ -27,10 +27,10 @@
 
 Future incompatibility report: Future breakage diagnostic:
 error: missing fragment specifier
-  --> $DIR/macro-match-nonterminal.rs:2:8
+  --> $DIR/macro-match-nonterminal.rs:2:6
    |
 LL |     ($a, $b) => {
-   |        ^
+   |      ^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs
index f06c1f9..37409dd 100644
--- a/tests/ui/macros/stringify.rs
+++ b/tests/ui/macros/stringify.rs
@@ -33,8 +33,6 @@
 macro_rules! ty { ($ty:ty) => { stringify!($ty) }; }
 macro_rules! vis { ($vis:vis) => { stringify!($vis) }; }
 
-// Use this when AST pretty-printing and TokenStream pretty-printing give
-// the same result (which is preferable.)
 macro_rules! c1 {
     ($frag:ident, [$($tt:tt)*], $s:literal) => {
         // Prior to #125174:
@@ -66,6 +64,8 @@ fn test_block() {
         } ],
         "{ let _; true }"
     );
+
+    // Attributes are not allowed on vanilla blocks.
 }
 
 #[test]
@@ -332,6 +332,20 @@ fn test_expr() {
     // ExprKind::FormatArgs: untestable because this test works pre-expansion.
 
     // ExprKind::Err: untestable.
+
+    // Ones involving attributes.
+    c1!(expr, [ #[aa] 1 ], "#[aa] 1");
+    c1!(expr, [ #[aa] #[bb] x ], "#[aa] #[bb] x");
+    c1!(expr, [ #[aa] 1 + 2 ], "#[aa] 1 + 2");
+    c1!(expr, [ #[aa] x + 2 ], "#[aa] x + 2");
+    c1!(expr, [ #[aa] 1 / #[bb] 2 ], "#[aa] 1 / #[bb] 2");
+    c1!(expr, [ #[aa] x / #[bb] 2 ], "#[aa] x / #[bb] 2");
+    c1!(expr, [ 1 << #[bb] 2 ], "1 << #[bb] 2");
+    c1!(expr, [ x << #[bb] 2 ], "x << #[bb] 2");
+    c1!(expr, [ #[aa] (1 + 2) ], "#[aa] (1 + 2)");
+    c1!(expr, [ #[aa] #[bb] (x + 2) ], "#[aa] #[bb] (x + 2)");
+    c1!(expr, [ #[aa] x[0].p ], "#[aa] x[0].p");
+    c1!(expr, [ #[aa] { #![bb] 0 } ], "#[aa] { #![bb] 0 }");
 }
 
 #[test]
@@ -484,6 +498,11 @@ macro_rules! stringify {
         "macro_rules! stringify { () => {}; }"
     );
     c1!(item, [ pub macro stringify() {} ], "pub macro stringify() {}");
+
+    // Ones involving attributes.
+    c1!(item, [ #[aa] mod m; ], "#[aa] mod m;");
+    c1!(item, [ mod m { #![bb] } ], "mod m { #![bb] }");
+    c1!(item, [ #[aa] mod m { #![bb] } ], "#[aa] mod m { #![bb] }");
 }
 
 #[test]
@@ -492,6 +511,8 @@ fn test_meta() {
     c1!(meta, [ k = "v" ], "k = \"v\"");
     c1!(meta, [ list(k1, k2 = "v") ], "list(k1, k2 = \"v\")");
     c1!(meta, [ serde::k ], "serde::k");
+
+    // Attributes are not allowed on metas.
 }
 
 #[test]
@@ -580,6 +601,8 @@ fn test_pat() {
     c1!(pat, [ mac!(...) ], "mac!(...)");
     c1!(pat, [ mac![...] ], "mac![...]");
     c1!(pat, [ mac! { ... } ], "mac! { ... }");
+
+    // Attributes are not allowed on patterns.
 }
 
 #[test]
@@ -593,6 +616,8 @@ fn test_path() {
     c1!(path, [ Self::<'static> ], "Self::<'static>");
     c1!(path, [ Self() ], "Self()");
     c1!(path, [ Self() -> () ], "Self() -> ()");
+
+    // Attributes are not allowed on paths.
 }
 
 #[test]
@@ -622,6 +647,20 @@ fn test_stmt() {
     c1!(stmt, [ mac!(...) ], "mac!(...)");
     c1!(stmt, [ mac![...] ], "mac![...]");
     c1!(stmt, [ mac! { ... } ], "mac! { ... }");
+
+    // Ones involving attributes.
+    c1!(stmt, [ #[aa] 1 ], "#[aa] 1");
+    c1!(stmt, [ #[aa] #[bb] x ], "#[aa] #[bb] x");
+    c1!(stmt, [ #[aa] 1 as u32 ], "#[aa] 1 as u32");
+    c1!(stmt, [ #[aa] x as u32 ], "#[aa] x as u32");
+    c1!(stmt, [ #[aa] 1 .. #[bb] 2 ], "#[aa] 1 .. #[bb] 2");
+    c1!(stmt, [ #[aa] x .. #[bb] 2 ], "#[aa] x .. #[bb] 2");
+    c1!(stmt, [ 1 || #[bb] 2 ], "1 || #[bb] 2");
+    c1!(stmt, [ x || #[bb] 2 ], "x || #[bb] 2");
+    c1!(stmt, [ #[aa] (1 + 2) ], "#[aa] (1 + 2)");
+    c1!(stmt, [ #[aa] #[bb] (x + 2) ], "#[aa] #[bb] (x + 2)");
+    c1!(stmt, [ #[aa] x[0].p ], "#[aa] x[0].p");
+    c1!(stmt, [ #[aa] { #![bb] 0 } ], "#[aa] { #![bb] 0 }");
 }
 
 #[test]
@@ -708,6 +747,8 @@ fn test_ty() {
 
     // TyKind::CVarArgs
     // FIXME: todo
+
+    // Attributes are not allowed on types.
 }
 
 #[test]
@@ -732,6 +773,8 @@ fn test_vis() {
     macro_rules! inherited_vis { ($vis:vis struct) => { vis!($vis) }; }
     assert_eq!(inherited_vis!(struct), "");
     assert_eq!(stringify!(), "");
+
+    // Attributes are not allowed on visibilities.
 }
 
 macro_rules! p {
diff --git a/tests/ui/privacy/private-in-public-warn.stderr b/tests/ui/privacy/private-in-public-warn.stderr
index 3f7b8c2..3743879 100644
--- a/tests/ui/privacy/private-in-public-warn.stderr
+++ b/tests/ui/privacy/private-in-public-warn.stderr
@@ -100,7 +100,7 @@
   --> $DIR/private-in-public-warn.rs:28:9
    |
 LL |         pub fn ef1(arg: Priv);
-   |         ^^^^^^^^^^^^^^^^^^^^^ function `types::ef1` is reachable at visibility `pub(crate)`
+   |         ^^^^^^^^^^^^^^^^^^^^^^ function `types::ef1` is reachable at visibility `pub(crate)`
    |
 note: but type `types::Priv` is only usable at visibility `pub(self)`
   --> $DIR/private-in-public-warn.rs:9:5
@@ -112,7 +112,7 @@
   --> $DIR/private-in-public-warn.rs:29:9
    |
 LL |         pub fn ef2() -> Priv;
-   |         ^^^^^^^^^^^^^^^^^^^^ function `types::ef2` is reachable at visibility `pub(crate)`
+   |         ^^^^^^^^^^^^^^^^^^^^^ function `types::ef2` is reachable at visibility `pub(crate)`
    |
 note: but type `types::Priv` is only usable at visibility `pub(self)`
   --> $DIR/private-in-public-warn.rs:9:5
diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr
index 7866af5..b766b5c 100644
--- a/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr
+++ b/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr
@@ -2,7 +2,7 @@
   --> $DIR/multiple-declarations.rs:13:9
    |
 LL |         fn f(x: i32);
-   |         ^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr
index d7c7344..ef02240 100644
--- a/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr
+++ b/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr
@@ -2,7 +2,7 @@
   --> $DIR/unsupported-abi.rs:6:5
    |
 LL |     fn f(x: i32);
-   |     ^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.rs b/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.rs
index c6f9115..f3b8645 100644
--- a/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.rs
+++ b/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.rs
@@ -1,5 +1,4 @@
 #![deny(rust_2024_compatibility)]
-#![feature(unsafe_attributes)]
 
 #[no_mangle]
 //~^ ERROR: unsafe attribute used without unsafe
diff --git a/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.stderr b/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.stderr
index f0689d9..4629a15 100644
--- a/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.stderr
+++ b/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.stderr
@@ -1,5 +1,5 @@
 error: unsafe attribute used without unsafe
-  --> $DIR/in_2024_compatibility.rs:4:3
+  --> $DIR/in_2024_compatibility.rs:3:3
    |
 LL | #[no_mangle]
    |   ^^^^^^^^^ usage of unsafe attribute
diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attribute-marked.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attribute-marked.rs
index 279ced2..7c919fe 100644
--- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attribute-marked.rs
+++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attribute-marked.rs
@@ -4,7 +4,6 @@
 //@[edition2024] compile-flags: -Zunstable-options
 //@ check-pass
 
-#![feature(unsafe_attributes)]
 
 #[unsafe(no_mangle)]
 extern "C" fn foo() {}
diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed
index 6ebdff0..586881d 100644
--- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed
+++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed
@@ -1,5 +1,4 @@
 //@ run-rustfix
-#![feature(unsafe_attributes)]
 #![deny(unsafe_attr_outside_unsafe)]
 
 macro_rules! tt {
diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs
index c78ff45..03e122c 100644
--- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs
+++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs
@@ -1,5 +1,4 @@
 //@ run-rustfix
-#![feature(unsafe_attributes)]
 #![deny(unsafe_attr_outside_unsafe)]
 
 macro_rules! tt {
diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr
index c95984f..64debc5 100644
--- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr
+++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr
@@ -1,5 +1,5 @@
 error: unsafe attribute used without unsafe
-  --> $DIR/unsafe-attributes-fix.rs:44:6
+  --> $DIR/unsafe-attributes-fix.rs:43:6
    |
 LL | tt!([no_mangle]);
    |      ^^^^^^^^^ usage of unsafe attribute
@@ -7,7 +7,7 @@
    = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
    = note: for more information, see issue #123757 <https://github.com/rust-lang/rust/issues/123757>
 note: the lint level is defined here
-  --> $DIR/unsafe-attributes-fix.rs:3:9
+  --> $DIR/unsafe-attributes-fix.rs:2:9
    |
 LL | #![deny(unsafe_attr_outside_unsafe)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@
    |      +++++++         +
 
 error: unsafe attribute used without unsafe
-  --> $DIR/unsafe-attributes-fix.rs:14:11
+  --> $DIR/unsafe-attributes-fix.rs:13:11
    |
 LL |         #[$e]
    |           ^^ usage of unsafe attribute
@@ -34,7 +34,7 @@
    |           +++++++  +
 
 error: unsafe attribute used without unsafe
-  --> $DIR/unsafe-attributes-fix.rs:48:7
+  --> $DIR/unsafe-attributes-fix.rs:47:7
    |
 LL | meta!(no_mangle);
    |       ^^^^^^^^^ usage of unsafe attribute
@@ -47,7 +47,7 @@
    |       +++++++         +
 
 error: unsafe attribute used without unsafe
-  --> $DIR/unsafe-attributes-fix.rs:51:8
+  --> $DIR/unsafe-attributes-fix.rs:50:8
    |
 LL | meta2!(export_name = "baw");
    |        ^^^^^^^^^^^ usage of unsafe attribute
@@ -60,7 +60,7 @@
    |        +++++++                   +
 
 error: unsafe attribute used without unsafe
-  --> $DIR/unsafe-attributes-fix.rs:23:11
+  --> $DIR/unsafe-attributes-fix.rs:22:11
    |
 LL |         #[$e = $l]
    |           ^^ usage of unsafe attribute
@@ -77,7 +77,7 @@
    |           +++++++       +
 
 error: unsafe attribute used without unsafe
-  --> $DIR/unsafe-attributes-fix.rs:56:3
+  --> $DIR/unsafe-attributes-fix.rs:55:3
    |
 LL | #[no_mangle]
    |   ^^^^^^^^^ usage of unsafe attribute
diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.edition2024.stderr b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.edition2024.stderr
index 35475d6..fb697e1 100644
--- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.edition2024.stderr
+++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.edition2024.stderr
@@ -1,5 +1,5 @@
 error: unsafe attribute used without unsafe
-  --> $DIR/unsafe-attributes.rs:9:3
+  --> $DIR/unsafe-attributes.rs:8:3
    |
 LL | #[no_mangle]
    |   ^^^^^^^^^ usage of unsafe attribute
diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.rs
index 3a6af9d..f6f2994 100644
--- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.rs
+++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.rs
@@ -4,7 +4,6 @@
 //@[edition2024] edition:2024
 //@[edition2024] compile-flags: -Zunstable-options
 
-#![feature(unsafe_attributes)]
 
 #[no_mangle] //[edition2024]~ ERROR: unsafe attribute used without unsafe
 extern "C" fn foo() {}
diff --git a/tests/ui/rust-2024/unsafe-before_exec.e2024.stderr b/tests/ui/rust-2024/unsafe-before_exec.e2024.stderr
new file mode 100644
index 0000000..2798ccd
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-before_exec.e2024.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function `before_exec` is unsafe and requires unsafe block
+  --> $DIR/unsafe-before_exec.rs:14:5
+   |
+LL |     cmd.before_exec(|| Ok(()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/rust-2024/unsafe-before_exec.rs b/tests/ui/rust-2024/unsafe-before_exec.rs
new file mode 100644
index 0000000..540394d
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-before_exec.rs
@@ -0,0 +1,17 @@
+//@ revisions: e2021 e2024
+//@ only-unix
+//@[e2021] edition: 2021
+//@[e2021] check-pass
+//@[e2024] edition: 2024
+//@[e2024] compile-flags: -Zunstable-options
+
+use std::process::Command;
+use std::os::unix::process::CommandExt;
+
+#[allow(deprecated)]
+fn main() {
+    let mut cmd = Command::new("sleep");
+    cmd.before_exec(|| Ok(()));
+    //[e2024]~^ ERROR call to unsafe function `before_exec` is unsafe
+    drop(cmd); // we don't actually run the command.
+}
diff --git a/tests/ui/typeck/const-in-fn-call-generics.rs b/tests/ui/typeck/const-in-fn-call-generics.rs
new file mode 100644
index 0000000..675dbcc
--- /dev/null
+++ b/tests/ui/typeck/const-in-fn-call-generics.rs
@@ -0,0 +1,16 @@
+fn generic<const N: u32>() {}
+
+trait Collate<const A: u32> {
+    type Pass;
+    fn collate(self) -> Self::Pass;
+}
+
+impl<const B: u32> Collate<B> for i32 {
+    type Pass = ();
+    fn collate(self) -> Self::Pass {
+        generic::<{ true }>()
+        //~^ ERROR: mismatched types
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/typeck/const-in-fn-call-generics.stderr b/tests/ui/typeck/const-in-fn-call-generics.stderr
new file mode 100644
index 0000000..12dd454
--- /dev/null
+++ b/tests/ui/typeck/const-in-fn-call-generics.stderr
@@ -0,0 +1,9 @@
+error[E0308]: mismatched types
+  --> $DIR/const-in-fn-call-generics.rs:11:21
+   |
+LL |         generic::<{ true }>()
+   |                     ^^^^ expected `u32`, found `bool`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.