about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml1
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs25
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs46
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/declare.rs39
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs11
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs9
-rw-r--r--compiler/rustc_driver_impl/messages.ftl5
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs38
-rw-r--r--compiler/rustc_driver_impl/src/pretty.rs6
-rw-r--r--compiler/rustc_driver_impl/src/session_diagnostics.rs15
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0699.md4
-rw-r--r--compiler/rustc_error_codes/src/lib.rs2
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs11
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl3
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs43
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs15
-rw-r--r--compiler/rustc_interface/src/passes.rs25
-rw-r--r--compiler/rustc_lint/src/context/diagnostics.rs5
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp12
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs52
-rw-r--r--compiler/rustc_middle/src/mir/traversal.rs96
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coroutine/by_move_body.rs36
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs20
-rw-r--r--compiler/rustc_sanitizers/Cargo.toml15
-rw-r--r--compiler/rustc_sanitizers/README.md2
-rw-r--r--compiler/rustc_sanitizers/src/cfi/mod.rs6
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs (renamed from compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs)792
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs123
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs450
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/mod.rs54
-rw-r--r--compiler/rustc_sanitizers/src/kcfi/mod.rs7
-rw-r--r--compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs55
-rw-r--r--compiler/rustc_sanitizers/src/lib.rs7
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs34
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs11
-rw-r--r--compiler/rustc_span/src/source_map.rs15
-rw-r--r--compiler/rustc_symbol_mangling/Cargo.toml3
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs1
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid.rs100
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs8
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs59
-rw-r--r--compiler/stable_mir/src/compiler_interface.rs5
-rw-r--r--compiler/stable_mir/src/mir/body.rs38
56 files changed, 1274 insertions, 1089 deletions
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 093a985495c..01addc8127e 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -346,7 +346,7 @@ impl<'a> AstValidator<'a> {
             in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }),
             const_context_label: parent_constness,
             remove_const_sugg: (
-                self.session.source_map().span_extend_while(span, |c| c == ' ').unwrap_or(span),
+                self.session.source_map().span_extend_while_whitespace(span),
                 match parent_constness {
                     Some(_) => rustc_errors::Applicability::MachineApplicable,
                     None => rustc_errors::Applicability::MaybeIncorrect,
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 1307a62a60d..771e5b21958 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -267,7 +267,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
         .generic_activity("codegen prelude")
         .run(|| crate::abi::codegen_fn_prelude(fx, start_block));
 
-    for (bb, bb_data) in fx.mir.basic_blocks.iter_enumerated() {
+    for (bb, bb_data) in traversal::mono_reachable(fx.mir, fx.tcx, fx.instance) {
         let block = fx.get_block(bb);
         fx.bcx.switch_to_block(block);
 
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index 3fda59e8b52..bb5045ec872 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -28,6 +28,7 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_metadata = { path = "../rustc_metadata" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_query_system = { path = "../rustc_query_system" }
+rustc_sanitizers = { path = "../rustc_sanitizers" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 06a681c24e6..e61af863dc0 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -608,7 +608,7 @@ pub(crate) fn run_pass_manager(
             "LTOPostLink".as_ptr().cast(),
             11,
         ) {
-            llvm::LLVMRustAddModuleFlag(
+            llvm::LLVMRustAddModuleFlagU32(
                 module.module_llvm.llmod(),
                 llvm::LLVMModFlagBehavior::Error,
                 c"LTOPostLink".as_ptr().cast(),
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 1a32958d362..b7235972204 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -20,12 +20,9 @@ use rustc_middle::ty::layout::{
     FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
 };
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
+use rustc_sanitizers::{cfi, kcfi};
 use rustc_session::config::OptLevel;
 use rustc_span::Span;
-use rustc_symbol_mangling::typeid::{
-    kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance,
-    TypeIdOptions,
-};
 use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
 use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
 use smallvec::SmallVec;
@@ -1632,18 +1629,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
                 return;
             }
 
-            let mut options = TypeIdOptions::empty();
+            let mut options = cfi::TypeIdOptions::empty();
             if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
-                options.insert(TypeIdOptions::GENERALIZE_POINTERS);
+                options.insert(cfi::TypeIdOptions::GENERALIZE_POINTERS);
             }
             if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
-                options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
+                options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS);
             }
 
             let typeid = if let Some(instance) = instance {
-                typeid_for_instance(self.tcx, instance, options)
+                cfi::typeid_for_instance(self.tcx, instance, options)
             } else {
-                typeid_for_fnabi(self.tcx, fn_abi, options)
+                cfi::typeid_for_fnabi(self.tcx, fn_abi, options)
             };
             let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap();
 
@@ -1680,18 +1677,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
                 return None;
             }
 
-            let mut options = TypeIdOptions::empty();
+            let mut options = kcfi::TypeIdOptions::empty();
             if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
-                options.insert(TypeIdOptions::GENERALIZE_POINTERS);
+                options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS);
             }
             if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
-                options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
+                options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS);
             }
 
             let kcfi_typeid = if let Some(instance) = instance {
-                kcfi_typeid_for_instance(self.tcx, instance, options)
+                kcfi::typeid_for_instance(self.tcx, instance, options)
             } else {
-                kcfi_typeid_for_fnabi(self.tcx, fn_abi, options)
+                kcfi::typeid_for_fnabi(self.tcx, fn_abi, options)
             };
 
             Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index df9f066e58a..d32baa6dc02 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -180,13 +180,13 @@ pub unsafe fn create_module<'ll>(
     // to ensure intrinsic calls don't use it.
     if !sess.needs_plt() {
         let avoid_plt = c"RtLibUseGOT".as_ptr().cast();
-        llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
+        llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
     }
 
     // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.)
     if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() {
         let canonical_jump_tables = c"CFI Canonical Jump Tables".as_ptr().cast();
-        llvm::LLVMRustAddModuleFlag(
+        llvm::LLVMRustAddModuleFlagU32(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
             canonical_jump_tables,
@@ -197,7 +197,7 @@ pub unsafe fn create_module<'ll>(
     // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.)
     if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() {
         let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr().cast();
-        llvm::LLVMRustAddModuleFlag(
+        llvm::LLVMRustAddModuleFlagU32(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
             enable_split_lto_unit,
@@ -208,7 +208,7 @@ pub unsafe fn create_module<'ll>(
     // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.)
     if sess.is_sanitizer_kcfi_enabled() {
         let kcfi = c"kcfi".as_ptr().cast();
-        llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
+        llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
     }
 
     // Control Flow Guard is currently only supported by the MSVC linker on Windows.
@@ -217,7 +217,7 @@ pub unsafe fn create_module<'ll>(
             CFGuard::Disabled => {}
             CFGuard::NoChecks => {
                 // Set `cfguard=1` module flag to emit metadata only.
-                llvm::LLVMRustAddModuleFlag(
+                llvm::LLVMRustAddModuleFlagU32(
                     llmod,
                     llvm::LLVMModFlagBehavior::Warning,
                     c"cfguard".as_ptr() as *const _,
@@ -226,7 +226,7 @@ pub unsafe fn create_module<'ll>(
             }
             CFGuard::Checks => {
                 // Set `cfguard=2` module flag to emit metadata and checks.
-                llvm::LLVMRustAddModuleFlag(
+                llvm::LLVMRustAddModuleFlagU32(
                     llmod,
                     llvm::LLVMModFlagBehavior::Warning,
                     c"cfguard".as_ptr() as *const _,
@@ -238,26 +238,26 @@ pub unsafe fn create_module<'ll>(
 
     if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection {
         if sess.target.arch == "aarch64" {
-            llvm::LLVMRustAddModuleFlag(
+            llvm::LLVMRustAddModuleFlagU32(
                 llmod,
                 llvm::LLVMModFlagBehavior::Min,
                 c"branch-target-enforcement".as_ptr().cast(),
                 bti.into(),
             );
-            llvm::LLVMRustAddModuleFlag(
+            llvm::LLVMRustAddModuleFlagU32(
                 llmod,
                 llvm::LLVMModFlagBehavior::Min,
                 c"sign-return-address".as_ptr().cast(),
                 pac_ret.is_some().into(),
             );
             let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A });
-            llvm::LLVMRustAddModuleFlag(
+            llvm::LLVMRustAddModuleFlagU32(
                 llmod,
                 llvm::LLVMModFlagBehavior::Min,
                 c"sign-return-address-all".as_ptr().cast(),
                 pac_opts.leaf.into(),
             );
-            llvm::LLVMRustAddModuleFlag(
+            llvm::LLVMRustAddModuleFlagU32(
                 llmod,
                 llvm::LLVMModFlagBehavior::Min,
                 c"sign-return-address-with-bkey".as_ptr().cast(),
@@ -273,7 +273,7 @@ pub unsafe fn create_module<'ll>(
 
     // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang).
     if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
-        llvm::LLVMRustAddModuleFlag(
+        llvm::LLVMRustAddModuleFlagU32(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
             c"cf-protection-branch".as_ptr().cast(),
@@ -281,7 +281,7 @@ pub unsafe fn create_module<'ll>(
         )
     }
     if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
-        llvm::LLVMRustAddModuleFlag(
+        llvm::LLVMRustAddModuleFlagU32(
             llmod,
             llvm::LLVMModFlagBehavior::Override,
             c"cf-protection-return".as_ptr().cast(),
@@ -290,7 +290,7 @@ pub unsafe fn create_module<'ll>(
     }
 
     if sess.opts.unstable_opts.virtual_function_elimination {
-        llvm::LLVMRustAddModuleFlag(
+        llvm::LLVMRustAddModuleFlagU32(
             llmod,
             llvm::LLVMModFlagBehavior::Error,
             c"Virtual Function Elim".as_ptr().cast(),
@@ -300,7 +300,7 @@ pub unsafe fn create_module<'ll>(
 
     // Set module flag to enable Windows EHCont Guard (/guard:ehcont).
     if sess.opts.unstable_opts.ehcont_guard {
-        llvm::LLVMRustAddModuleFlag(
+        llvm::LLVMRustAddModuleFlagU32(
             llmod,
             llvm::LLVMModFlagBehavior::Warning,
             c"ehcontguard".as_ptr() as *const _,
@@ -326,6 +326,22 @@ pub unsafe fn create_module<'ll>(
         llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1),
     );
 
+    // Emit RISC-V specific target-abi metadata
+    // to workaround lld as the LTO plugin not
+    // correctly setting target-abi for the LTO object
+    // FIXME: https://github.com/llvm/llvm-project/issues/50591
+    // If llvm_abiname is empty, emit nothing.
+    let llvm_abiname = &sess.target.options.llvm_abiname;
+    if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() {
+        llvm::LLVMRustAddModuleFlagString(
+            llmod,
+            llvm::LLVMModFlagBehavior::Error,
+            c"target-abi".as_ptr(),
+            llvm_abiname.as_ptr().cast(),
+            llvm_abiname.len(),
+        );
+    }
+
     // Add module flags specified via -Z llvm_module_flag
     for (key, value, behavior) in &sess.opts.unstable_opts.llvm_module_flag {
         let key = format!("{key}\0");
@@ -341,7 +357,7 @@ pub unsafe fn create_module<'ll>(
             // We already checked this during option parsing
             _ => unreachable!(),
         };
-        llvm::LLVMRustAddModuleFlag(llmod, behavior, key.as_ptr().cast(), *value)
+        llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_ptr().cast(), *value)
     }
 
     llmod
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index d3a851b40c0..4fdaa59e0e5 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -110,7 +110,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
                     .unstable_opts
                     .dwarf_version
                     .unwrap_or(sess.target.default_dwarf_version);
-                llvm::LLVMRustAddModuleFlag(
+                llvm::LLVMRustAddModuleFlagU32(
                     self.llmod,
                     llvm::LLVMModFlagBehavior::Warning,
                     c"Dwarf Version".as_ptr().cast(),
@@ -118,7 +118,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
                 );
             } else {
                 // Indicate that we want CodeView debug information on MSVC
-                llvm::LLVMRustAddModuleFlag(
+                llvm::LLVMRustAddModuleFlagU32(
                     self.llmod,
                     llvm::LLVMModFlagBehavior::Warning,
                     c"CodeView".as_ptr().cast(),
@@ -127,7 +127,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
             }
 
             // Prevent bitcode readers from deleting the debug info.
-            llvm::LLVMRustAddModuleFlag(
+            llvm::LLVMRustAddModuleFlagU32(
                 self.llmod,
                 llvm::LLVMModFlagBehavior::Warning,
                 c"Debug Info Version".as_ptr().cast(),
diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs
index f86cdcaa6f7..7117c4a0ed9 100644
--- a/compiler/rustc_codegen_llvm/src/declare.rs
+++ b/compiler/rustc_codegen_llvm/src/declare.rs
@@ -22,10 +22,7 @@ use itertools::Itertools;
 use rustc_codegen_ssa::traits::TypeMembershipMethods;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_middle::ty::{Instance, Ty};
-use rustc_symbol_mangling::typeid::{
-    kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance,
-    TypeIdOptions,
-};
+use rustc_sanitizers::{cfi, kcfi};
 use smallvec::SmallVec;
 
 /// Declare a function.
@@ -145,27 +142,29 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
             if let Some(instance) = instance {
                 let mut typeids = FxIndexSet::default();
                 for options in [
-                    TypeIdOptions::GENERALIZE_POINTERS,
-                    TypeIdOptions::NORMALIZE_INTEGERS,
-                    TypeIdOptions::USE_CONCRETE_SELF,
+                    cfi::TypeIdOptions::GENERALIZE_POINTERS,
+                    cfi::TypeIdOptions::NORMALIZE_INTEGERS,
+                    cfi::TypeIdOptions::USE_CONCRETE_SELF,
                 ]
                 .into_iter()
                 .powerset()
-                .map(TypeIdOptions::from_iter)
+                .map(cfi::TypeIdOptions::from_iter)
                 {
-                    let typeid = typeid_for_instance(self.tcx, instance, options);
+                    let typeid = cfi::typeid_for_instance(self.tcx, instance, options);
                     if typeids.insert(typeid.clone()) {
                         self.add_type_metadata(llfn, typeid);
                     }
                 }
             } else {
-                for options in
-                    [TypeIdOptions::GENERALIZE_POINTERS, TypeIdOptions::NORMALIZE_INTEGERS]
-                        .into_iter()
-                        .powerset()
-                        .map(TypeIdOptions::from_iter)
+                for options in [
+                    cfi::TypeIdOptions::GENERALIZE_POINTERS,
+                    cfi::TypeIdOptions::NORMALIZE_INTEGERS,
+                ]
+                .into_iter()
+                .powerset()
+                .map(cfi::TypeIdOptions::from_iter)
                 {
-                    let typeid = typeid_for_fnabi(self.tcx, fn_abi, options);
+                    let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options);
                     self.add_type_metadata(llfn, typeid);
                 }
             }
@@ -173,19 +172,19 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
 
         if self.tcx.sess.is_sanitizer_kcfi_enabled() {
             // LLVM KCFI does not support multiple !kcfi_type attachments
-            let mut options = TypeIdOptions::empty();
+            let mut options = kcfi::TypeIdOptions::empty();
             if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
-                options.insert(TypeIdOptions::GENERALIZE_POINTERS);
+                options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS);
             }
             if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
-                options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
+                options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS);
             }
 
             if let Some(instance) = instance {
-                let kcfi_typeid = kcfi_typeid_for_instance(self.tcx, instance, options);
+                let kcfi_typeid = kcfi::typeid_for_instance(self.tcx, instance, options);
                 self.set_kcfi_type_metadata(llfn, kcfi_typeid);
             } else {
-                let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options);
+                let kcfi_typeid = kcfi::typeid_for_fnabi(self.tcx, fn_abi, options);
                 self.set_kcfi_type_metadata(llfn, kcfi_typeid);
             }
         }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 284bc74d5c4..5509baaa3e9 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1801,12 +1801,21 @@ extern "C" {
     ///
     /// In order for Rust-C LTO to work, module flags must be compatible with Clang. What
     /// "compatible" means depends on the merge behaviors involved.
-    pub fn LLVMRustAddModuleFlag(
+    pub fn LLVMRustAddModuleFlagU32(
         M: &Module,
         merge_behavior: LLVMModFlagBehavior,
         name: *const c_char,
         value: u32,
     );
+
+    pub fn LLVMRustAddModuleFlagString(
+        M: &Module,
+        merge_behavior: LLVMModFlagBehavior,
+        name: *const c_char,
+        value: *const c_char,
+        value_len: size_t,
+    );
+
     pub fn LLVMRustHasModuleFlag(M: &Module, name: *const c_char, len: size_t) -> bool;
 
     pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>;
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 387a5366b20..1f4473d2ec4 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -257,20 +257,19 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     // Apply debuginfo to the newly allocated locals.
     fx.debug_introduce_locals(&mut start_bx);
 
-    let reachable_blocks = mir.reachable_blocks_in_mono(cx.tcx(), instance);
-
     // The builders will be created separately for each basic block at `codegen_block`.
     // So drop the builder of `start_llbb` to avoid having two at the same time.
     drop(start_bx);
 
+    let reachable_blocks = traversal::mono_reachable_as_bitset(mir, cx.tcx(), instance);
+
     // Codegen the body of each block using reverse postorder
     for (bb, _) in traversal::reverse_postorder(mir) {
         if reachable_blocks.contains(bb) {
             fx.codegen_block(bb);
         } else {
-            // This may have references to things we didn't monomorphize, so we
-            // don't actually codegen the body. We still create the block so
-            // terminators in other blocks can reference it without worry.
+            // We want to skip this block, because it's not reachable. But we still create
+            // the block so terminators in other blocks can reference it.
             fx.codegen_block_as_unreachable(bb);
         }
     }
diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl
index 62391daecd0..5b39248302e 100644
--- a/compiler/rustc_driver_impl/messages.ftl
+++ b/compiler/rustc_driver_impl/messages.ftl
@@ -1,10 +1,7 @@
 driver_impl_ice = the compiler unexpectedly panicked. this is a bug.
 driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
 driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly
-driver_impl_ice_bug_report_outdated =
-    it seems that this compiler `{$version}` is outdated, a newer nightly should have been released in the meantime
-    .update = please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists
-    .url = if the problem still persists, we would appreciate a bug report: {$bug_report_url}
+driver_impl_ice_bug_report_update_note = please make sure that you have updated to the latest nightly
 driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
 
 driver_impl_ice_flags = compiler flags: {$flags}
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index b4007aeb8d7..1f44621736c 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -61,7 +61,7 @@ use std::str;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::{Arc, OnceLock};
 use std::time::{Instant, SystemTime};
-use time::{Date, OffsetDateTime, Time};
+use time::OffsetDateTime;
 
 #[allow(unused_macros)]
 macro do_not_use_print($($t:tt)*) {
@@ -1385,9 +1385,6 @@ pub fn install_ice_hook(
     using_internal_features
 }
 
-const DATE_FORMAT: &[time::format_description::FormatItem<'static>] =
-    &time::macros::format_description!("[year]-[month]-[day]");
-
 /// Prints the ICE message, including query stack, but without backtrace.
 ///
 /// The message will point the user at `bug_report_url` to report the ICE.
@@ -1416,33 +1413,14 @@ fn report_ice(
         dcx.emit_err(session_diagnostics::Ice);
     }
 
-    use time::ext::NumericalDuration;
-
-    // Try to hint user to update nightly if applicable when reporting an ICE.
-    // Attempt to calculate when current version was released, and add 12 hours
-    // as buffer. If the current version's release timestamp is older than
-    // the system's current time + 24 hours + 12 hours buffer if we're on
-    // nightly.
-    if let Some("nightly") = option_env!("CFG_RELEASE_CHANNEL")
-        && let Some(version) = option_env!("CFG_VERSION")
-        && let Some(ver_date_str) = option_env!("CFG_VER_DATE")
-        && let Ok(ver_date) = Date::parse(&ver_date_str, DATE_FORMAT)
-        && let ver_datetime = OffsetDateTime::new_utc(ver_date, Time::MIDNIGHT)
-        && let system_datetime = OffsetDateTime::from(SystemTime::now())
-        && system_datetime.checked_sub(36.hours()).is_some_and(|d| d > ver_datetime)
-        && !using_internal_features.load(std::sync::atomic::Ordering::Relaxed)
-    {
-        dcx.emit_note(session_diagnostics::IceBugReportOutdated {
-            version,
-            bug_report_url,
-            note_update: (),
-            note_url: (),
-        });
+    if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) {
+        dcx.emit_note(session_diagnostics::IceBugReportInternalFeature);
     } else {
-        if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) {
-            dcx.emit_note(session_diagnostics::IceBugReportInternalFeature);
-        } else {
-            dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url });
+        dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url });
+
+        // Only emit update nightly hint for users on nightly builds.
+        if rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
+            dcx.emit_note(session_diagnostics::UpdateNightlyNote);
         }
     }
 
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index c9bbe45b212..c0c6201f73d 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -336,7 +336,8 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
         ThirTree => {
             let tcx = ex.tcx();
             let mut out = String::new();
-            if rustc_hir_analysis::check_crate(tcx).is_err() {
+            rustc_hir_analysis::check_crate(tcx);
+            if tcx.dcx().has_errors().is_some() {
                 FatalError.raise();
             }
             debug!("pretty printing THIR tree");
@@ -348,7 +349,8 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
         ThirFlat => {
             let tcx = ex.tcx();
             let mut out = String::new();
-            if rustc_hir_analysis::check_crate(tcx).is_err() {
+            rustc_hir_analysis::check_crate(tcx);
+            if tcx.dcx().has_errors().is_some() {
                 FatalError.raise();
             }
             debug!("pretty printing THIR flat");
diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs
index 62d0da62d2a..1a9683e840a 100644
--- a/compiler/rustc_driver_impl/src/session_diagnostics.rs
+++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs
@@ -43,19 +43,12 @@ pub(crate) struct IceBugReport<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(driver_impl_ice_bug_report_internal_feature)]
-pub(crate) struct IceBugReportInternalFeature;
+#[diag(driver_impl_ice_bug_report_update_note)]
+pub(crate) struct UpdateNightlyNote;
 
 #[derive(Diagnostic)]
-#[diag(driver_impl_ice_bug_report_outdated)]
-pub(crate) struct IceBugReportOutdated<'a> {
-    pub version: &'a str,
-    pub bug_report_url: &'a str,
-    #[note(driver_impl_update)]
-    pub note_update: (),
-    #[note(driver_impl_url)]
-    pub note_url: (),
-}
+#[diag(driver_impl_ice_bug_report_internal_feature)]
+pub(crate) struct IceBugReportInternalFeature;
 
 #[derive(Diagnostic)]
 #[diag(driver_impl_ice_version)]
diff --git a/compiler/rustc_error_codes/src/error_codes/E0699.md b/compiler/rustc_error_codes/src/error_codes/E0699.md
index 454d2507e5e..1094ebf4b8f 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0699.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0699.md
@@ -1,8 +1,10 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 A method was called on a raw pointer whose inner type wasn't completely known.
 
 Erroneous code example:
 
-```compile_fail,edition2018,E0699
+```compile_fail,edition2018
 # #![deny(warnings)]
 # fn main() {
 let foo = &1;
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index da688e385aa..f4a33a05c1b 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -441,7 +441,7 @@ E0695: 0695,
 E0696: 0696,
 E0697: 0697,
 E0698: 0698,
-E0699: 0699,
+E0699: 0699, // REMOVED: merged into generic inference var error
 E0700: 0700,
 E0701: 0701,
 E0703: 0703,
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 129ce1d3109..e6b19817de3 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -216,7 +216,7 @@ declare_features! (
     /// Set the maximum pattern complexity allowed (not limited by default).
     (internal, pattern_complexity, "1.78.0", None),
     /// Allows using pattern types.
-    (internal, pattern_types, "CURRENT_RUSTC_VERSION", Some(54882)),
+    (internal, pattern_types, "CURRENT_RUSTC_VERSION", Some(123646)),
     /// Allows using `#[prelude_import]` on glob `use` items.
     (internal, prelude_import, "1.2.0", None),
     /// Used to identify crates that contain the profiler runtime.
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index ebfccd27d17..59f0fac5aa7 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -2249,7 +2249,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         Ty::new_pat(tcx, ty, pat)
                     }
                     hir::PatKind::Err(e) => Ty::new_error(tcx, e),
-                    _ => span_bug!(pat.span, "unsupported pattern for pattern type: {pat:#?}"),
+                    _ => Ty::new_error_with_message(
+                        tcx,
+                        pat.span,
+                        format!("unsupported pattern for pattern type: {pat:#?}"),
+                    ),
                 };
                 self.record_ty(pat.hir_id, ty, pat.span);
                 pat_ty
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index b7786ec219c..5a374fa5e04 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -97,7 +97,6 @@ mod outlives;
 pub mod structured_errors;
 mod variance;
 
-use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_middle::middle;
@@ -153,11 +152,11 @@ pub fn provide(providers: &mut Providers) {
     hir_wf_check::provide(providers);
 }
 
-pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
+pub fn check_crate(tcx: TyCtxt<'_>) {
     let _prof_timer = tcx.sess.timer("type_check_crate");
 
     if tcx.features().rustc_attrs {
-        tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx))?;
+        let _ = tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx));
     }
 
     tcx.sess.time("coherence_checking", || {
@@ -174,11 +173,11 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
     });
 
     if tcx.features().rustc_attrs {
-        tcx.sess.time("variance_testing", || variance::test::test_variance(tcx))?;
+        let _ = tcx.sess.time("variance_testing", || variance::test::test_variance(tcx));
     }
 
     if tcx.features().rustc_attrs {
-        collect::test_opaque_hidden_types(tcx)?;
+        let _ = collect::test_opaque_hidden_types(tcx);
     }
 
     // Make sure we evaluate all static and (non-associated) const items, even if unused.
@@ -213,8 +212,6 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
     });
 
     tcx.ensure().check_unused_traits(());
-
-    Ok(())
 }
 
 /// Lower a [`hir::Ty`] to a [`Ty`].
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 1d51101c940..18d9d739dd6 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -93,9 +93,6 @@ hir_typeck_lossy_provenance_ptr2int =
     .suggestion = use `.addr()` to obtain the address of a pointer
     .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead
 
-hir_typeck_method_call_on_unknown_raw_pointee =
-    cannot call a method on a raw pointer with an unknown pointee type
-
 hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}`
 
 hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method ->
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index f9b2ec69730..d399730bf3d 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -77,13 +77,6 @@ pub struct StructExprNonExhaustive {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_typeck_method_call_on_unknown_raw_pointee, code = E0699)]
-pub struct MethodCallOnUnknownRawPointee {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(hir_typeck_functional_record_update_on_non_struct, code = E0436)]
 pub struct FunctionalRecordUpdateOnNonStruct {
     #[primary_span]
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index d3df3dd3885..7a1a2c498aa 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -2023,8 +2023,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .tcx
                 .sess
                 .source_map()
-                .span_extend_while(range_start.span, |c| c.is_whitespace())
-                .unwrap_or(range_start.span)
+                .span_extend_while_whitespace(range_start.span)
                 .shrink_to_hi()
                 .to(range_end.span);
 
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 4e63600dbdf..28e17e1de36 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -3,7 +3,6 @@ use super::CandidateSource;
 use super::MethodError;
 use super::NoMatchData;
 
-use crate::errors::MethodCallOnUnknownRawPointee;
 use crate::FnCtxt;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -430,21 +429,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if is_suggestion.0 {
                 // Ambiguity was encountered during a suggestion. Just keep going.
                 debug!("ProbeContext: encountered ambiguity in suggestion");
-            } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types {
+            } else if bad_ty.reached_raw_pointer
+                && !self.tcx.features().arbitrary_self_types
+                && !self.tcx.sess.at_least_rust_2018()
+            {
                 // this case used to be allowed by the compiler,
                 // so we do a future-compat lint here for the 2015 edition
                 // (see https://github.com/rust-lang/rust/issues/46906)
-                if self.tcx.sess.at_least_rust_2018() {
-                    self.dcx().emit_err(MethodCallOnUnknownRawPointee { span });
-                } else {
-                    self.tcx.node_span_lint(
-                        lint::builtin::TYVAR_BEHIND_RAW_POINTER,
-                        scope_expr_id,
-                        span,
-                        "type annotations needed",
-                        |_| {},
-                    );
-                }
+                self.tcx.node_span_lint(
+                    lint::builtin::TYVAR_BEHIND_RAW_POINTER,
+                    scope_expr_id,
+                    span,
+                    "type annotations needed",
+                    |_| {},
+                );
             } else {
                 // Ended up encountering a type variable when doing autoderef,
                 // but it may not be a type variable after processing obligations
@@ -455,10 +453,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
                 let ty = self.resolve_vars_if_possible(ty.value);
                 let guar = match *ty.kind() {
-                    ty::Infer(ty::TyVar(_)) => self
-                        .err_ctxt()
-                        .emit_inference_failure_err(self.body_id, span, ty.into(), E0282, true)
-                        .emit(),
+                    ty::Infer(ty::TyVar(_)) => {
+                        let raw_ptr_call =
+                            bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types;
+                        let mut err = self.err_ctxt().emit_inference_failure_err(
+                            self.body_id,
+                            span,
+                            ty.into(),
+                            E0282,
+                            !raw_ptr_call,
+                        );
+                        if raw_ptr_call {
+                            err.span_label(span, "cannot call a method on a raw pointer with an unknown pointee type");
+                        }
+                        err.emit()
+                    }
                     ty::Error(guar) => guar,
                     _ => bug!("unexpected bad final type in method autoderef"),
                 };
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index 9a05fb1c30f..7855031e705 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -15,7 +15,7 @@ use rustc_middle::traits::{
 };
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{sym, Span};
 
 use crate::errors::{
     ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
@@ -763,8 +763,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
             self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
         } else {
-            last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
+            self.tcx
+                .sess
+                .source_map()
+                .span_extend_while_whitespace(last_expr.span)
+                .shrink_to_hi()
+                .with_hi(last_stmt.span.hi())
         };
+
         Some((span, needs_box))
     }
 
@@ -867,10 +873,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         format!(" {ident} ")
                     };
                     let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
-                    (
-                        sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
-                        sugg,
-                    )
+                    (sm.span_extend_while_whitespace(left_span), sugg)
                 };
                 Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
             }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 1f92cc4d763..d04d30b3cb0 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -686,18 +686,15 @@ pub fn create_global_ctxt<'tcx>(
     })
 }
 
-/// Runs the type-checking, region checking and other miscellaneous analysis
-/// passes on the crate.
-fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
+/// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses.
+/// This function never fails.
+fn run_required_analyses(tcx: TyCtxt<'_>) {
     if tcx.sess.opts.unstable_opts.hir_stats {
         rustc_passes::hir_stats::print_hir_stats(tcx);
     }
-
     #[cfg(debug_assertions)]
     rustc_passes::hir_id_validator::check_crate(tcx);
-
     let sess = tcx.sess;
-
     sess.time("misc_checking_1", || {
         parallel!(
             {
@@ -733,10 +730,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
             }
         );
     });
-
-    // passes are timed inside typeck
-    rustc_hir_analysis::check_crate(tcx)?;
-
+    rustc_hir_analysis::check_crate(tcx);
     sess.time("MIR_borrow_checking", || {
         tcx.hir().par_body_owners(|def_id| {
             // Run unsafety check because it's responsible for stealing and
@@ -745,7 +739,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
             tcx.ensure().mir_borrowck(def_id)
         });
     });
-
     sess.time("MIR_effect_checking", || {
         for def_id in tcx.hir().body_owners() {
             tcx.ensure().has_ffi_unwind_calls(def_id);
@@ -761,16 +754,22 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
             }
         }
     });
-
     tcx.hir().par_body_owners(|def_id| {
         if tcx.is_coroutine(def_id.to_def_id()) {
             tcx.ensure().mir_coroutine_witnesses(def_id);
             tcx.ensure().check_coroutine_obligations(def_id);
         }
     });
-
     sess.time("layout_testing", || layout_test::test_layout(tcx));
     sess.time("abi_testing", || abi_test::test_abi(tcx));
+}
+
+/// Runs the type-checking, region checking and other miscellaneous analysis
+/// passes on the crate.
+fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
+    run_required_analyses(tcx);
+
+    let sess = tcx.sess;
 
     // Avoid overwhelming user with errors if borrow checking failed.
     // I'm not sure how helpful this is, to be honest, but it avoids a
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs
index e2010ab3830..16e3b8c11c1 100644
--- a/compiler/rustc_lint/src/context/diagnostics.rs
+++ b/compiler/rustc_lint/src/context/diagnostics.rs
@@ -215,10 +215,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
             if let Some(deletion_span) = deletion_span {
                 let msg = "elide the single-use lifetime";
                 let (use_span, replace_lt) = if elide {
-                    let use_span = sess
-                        .source_map()
-                        .span_extend_while(use_span, char::is_whitespace)
-                        .unwrap_or(use_span);
+                    let use_span = sess.source_map().span_extend_while_whitespace(use_span);
                     (use_span, String::new())
                 } else {
                     (use_span, "'_".to_owned())
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index a6894a7e089..37c2da4c23a 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -817,7 +817,7 @@ extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; }
 
 extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; }
 
-extern "C" void LLVMRustAddModuleFlag(
+extern "C" void LLVMRustAddModuleFlagU32(
     LLVMModuleRef M,
     Module::ModFlagBehavior MergeBehavior,
     const char *Name,
@@ -825,6 +825,16 @@ extern "C" void LLVMRustAddModuleFlag(
   unwrap(M)->addModuleFlag(MergeBehavior, Name, Value);
 }
 
+extern "C" void LLVMRustAddModuleFlagString(
+    LLVMModuleRef M,
+    Module::ModFlagBehavior MergeBehavior,
+    const char *Name,
+    const char *Value,
+    size_t ValueLen) {
+  unwrap(M)->addModuleFlag(MergeBehavior, Name,
+    MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen)));
+}
+
 extern "C" bool LLVMRustHasModuleFlag(LLVMModuleRef M, const char *Name,
                                       size_t Len) {
   return unwrap(M)->getModuleFlag(StringRef(Name, Len)) != nullptr;
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 7ecac0c0e78..601bfc770f4 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -30,7 +30,6 @@ pub use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::graph::dominators::Dominators;
-use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_index::bit_set::BitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_serialize::{Decodable, Encodable};
@@ -687,57 +686,6 @@ impl<'tcx> Body<'tcx> {
         self.injection_phase.is_some()
     }
 
-    /// Finds which basic blocks are actually reachable for a specific
-    /// monomorphization of this body.
-    ///
-    /// This is allowed to have false positives; just because this says a block
-    /// is reachable doesn't mean that's necessarily true. It's thus always
-    /// legal for this to return a filled set.
-    ///
-    /// Regardless, the [`BitSet::domain_size`] of the returned set will always
-    /// exactly match the number of blocks in the body so that `contains`
-    /// checks can be done without worrying about panicking.
-    ///
-    /// This is mostly useful because it lets us skip lowering the `false` side
-    /// of `if <T as Trait>::CONST`, as well as `intrinsics::debug_assertions`.
-    pub fn reachable_blocks_in_mono(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        instance: Instance<'tcx>,
-    ) -> BitSet<BasicBlock> {
-        let mut set = BitSet::new_empty(self.basic_blocks.len());
-        self.reachable_blocks_in_mono_from(tcx, instance, &mut set, START_BLOCK);
-        set
-    }
-
-    fn reachable_blocks_in_mono_from(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        instance: Instance<'tcx>,
-        set: &mut BitSet<BasicBlock>,
-        bb: BasicBlock,
-    ) {
-        if !set.insert(bb) {
-            return;
-        }
-
-        let data = &self.basic_blocks[bb];
-
-        if let Some((bits, targets)) = Self::try_const_mono_switchint(tcx, instance, data) {
-            let target = targets.target_for_value(bits);
-            ensure_sufficient_stack(|| {
-                self.reachable_blocks_in_mono_from(tcx, instance, set, target)
-            });
-            return;
-        }
-
-        for target in data.terminator().successors() {
-            ensure_sufficient_stack(|| {
-                self.reachable_blocks_in_mono_from(tcx, instance, set, target)
-            });
-        }
-    }
-
     /// If this basic block ends with a [`TerminatorKind::SwitchInt`] for which we can evaluate the
     /// dimscriminant in monomorphization, we return the discriminant bits and the
     /// [`SwitchTargets`], just so the caller doesn't also have to match on the terminator.
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index 0a938bcd315..245e9096bad 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -245,7 +245,7 @@ pub fn reachable<'a, 'tcx>(
 /// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
 pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
     let mut iter = preorder(body);
-    iter.by_ref().for_each(drop);
+    while let Some(_) = iter.next() {}
     iter.visited
 }
 
@@ -279,3 +279,97 @@ pub fn reverse_postorder<'a, 'tcx>(
 {
     body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb]))
 }
+
+/// Traversal of a [`Body`] that tries to avoid unreachable blocks in a monomorphized [`Instance`].
+///
+/// This is allowed to have false positives; blocks may be visited even if they are not actually
+/// reachable.
+///
+/// Such a traversal is mostly useful because it lets us skip lowering the `false` side
+/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
+///
+/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
+pub fn mono_reachable<'a, 'tcx>(
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+) -> MonoReachable<'a, 'tcx> {
+    MonoReachable::new(body, tcx, instance)
+}
+
+/// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a
+/// convenience function to run that traversal then extract its set of reached blocks.
+pub fn mono_reachable_as_bitset<'a, 'tcx>(
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+) -> BitSet<BasicBlock> {
+    let mut iter = mono_reachable(body, tcx, instance);
+    while let Some(_) = iter.next() {}
+    iter.visited
+}
+
+pub struct MonoReachable<'a, 'tcx> {
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+    visited: BitSet<BasicBlock>,
+    // Other traversers track their worklist in a Vec. But we don't care about order, so we can
+    // store ours in a BitSet and thus save allocations because BitSet has a small size
+    // optimization.
+    worklist: BitSet<BasicBlock>,
+}
+
+impl<'a, 'tcx> MonoReachable<'a, 'tcx> {
+    pub fn new(
+        body: &'a Body<'tcx>,
+        tcx: TyCtxt<'tcx>,
+        instance: Instance<'tcx>,
+    ) -> MonoReachable<'a, 'tcx> {
+        let mut worklist = BitSet::new_empty(body.basic_blocks.len());
+        worklist.insert(START_BLOCK);
+        MonoReachable {
+            body,
+            tcx,
+            instance,
+            visited: BitSet::new_empty(body.basic_blocks.len()),
+            worklist,
+        }
+    }
+
+    fn add_work(&mut self, blocks: impl IntoIterator<Item = BasicBlock>) {
+        for block in blocks.into_iter() {
+            if !self.visited.contains(block) {
+                self.worklist.insert(block);
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> {
+    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+
+    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
+        while let Some(idx) = self.worklist.iter().next() {
+            self.worklist.remove(idx);
+            if !self.visited.insert(idx) {
+                continue;
+            }
+
+            let data = &self.body[idx];
+
+            if let Some((bits, targets)) =
+                Body::try_const_mono_switchint(self.tcx, self.instance, data)
+            {
+                let target = targets.target_for_value(bits);
+                self.add_work([target]);
+            } else {
+                self.add_work(data.terminator().successors());
+            }
+
+            return Some((idx, data));
+        }
+
+        None
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index d9e99bf07af..dd73f0f4a35 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2231,7 +2231,7 @@ impl<'tcx> Ty<'tcx> {
     pub fn tuple_fields(self) -> &'tcx List<Ty<'tcx>> {
         match self.kind() {
             Tuple(args) => args,
-            _ => bug!("tuple_fields called on non-tuple"),
+            _ => bug!("tuple_fields called on non-tuple: {self:?}"),
         }
     }
 
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 1d022d159a1..ccb229616e8 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -273,14 +273,14 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
         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, additional_projections)) =
+            && let Some(&(remapped_idx, remapped_ty, needs_deref, 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 reffing has now become redundant.
-            let final_deref = if needs_deref {
+            // since a layer of ref'ing has now become redundant.
+            let final_projections = if needs_deref {
                 let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first()
                 else {
                     bug!(
@@ -294,20 +294,18 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
                 projection
             };
 
-            // The only thing that should be left is a deref, if the parent captured
-            // an upvar by-ref.
-            std::assert_matches::assert_matches!(final_deref, [] | [mir::ProjectionElem::Deref]);
-
-            // For all of the additional projections that come out of precise capturing,
-            // re-apply these projections.
-            let additional_projections =
-                additional_projections.iter().map(|elem| match elem.kind {
-                    ProjectionKind::Deref => mir::ProjectionElem::Deref,
-                    ProjectionKind::Field(idx, VariantIdx::ZERO) => {
-                        mir::ProjectionElem::Field(idx, elem.ty)
-                    }
-                    _ => unreachable!("precise captures only through fields and derefs"),
-                });
+            // These projections are applied in order to "bridge" the local that we are
+            // currently transforming *from* the old upvar that the by-ref coroutine used
+            // to capture *to* the upvar of the parent coroutine-closure. For example, if
+            // the parent captures `&s` but the child captures `&(s.field)`, then we will
+            // apply a field projection.
+            let bridging_projections = bridging_projections.iter().map(|elem| match elem.kind {
+                ProjectionKind::Deref => mir::ProjectionElem::Deref,
+                ProjectionKind::Field(idx, VariantIdx::ZERO) => {
+                    mir::ProjectionElem::Field(idx, elem.ty)
+                }
+                _ => unreachable!("precise captures only through fields and derefs"),
+            });
 
             // We start out with an adjusted field index (and ty), representing the
             // upvar that we get from our parent closure. We apply any of the additional
@@ -318,8 +316,8 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
                 projection: self.tcx.mk_place_elems_from_iter(
                     [mir::ProjectionElem::Field(remapped_idx, remapped_ty)]
                         .into_iter()
-                        .chain(additional_projections)
-                        .chain(final_deref.iter().copied()),
+                        .chain(bridging_projections)
+                        .chain(final_projections.iter().copied()),
                 ),
             };
         }
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 0c35f9838ed..ee6c154e6e8 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -214,6 +214,7 @@ use rustc_hir::lang_items::LangItem;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar};
 use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
+use rustc_middle::mir::traversal;
 use rustc_middle::mir::visit::Visitor as MirVisitor;
 use rustc_middle::mir::{self, Location, MentionedItem};
 use rustc_middle::query::TyCtxtAt;
@@ -1414,15 +1415,16 @@ fn collect_items_of_instance<'tcx>(
     };
 
     if mode == CollectionMode::UsedItems {
-        // Visit everything. Here we rely on the visitor also visiting `required_consts`, so that we
-        // evaluate them and abort compilation if any of them errors.
-        collector.visit_body(body);
-    } else {
-        // We only need to evaluate all constants, but can ignore the rest of the MIR.
-        for const_op in &body.required_consts {
-            if let Some(val) = collector.eval_constant(const_op) {
-                collect_const_value(tcx, val, mentioned_items);
-            }
+        for (bb, data) in traversal::mono_reachable(body, tcx, instance) {
+            collector.visit_basic_block_data(bb, data)
+        }
+    }
+
+    // Always visit all `required_consts`, so that we evaluate them and abort compilation if any of
+    // them errors.
+    for const_op in &body.required_consts {
+        if let Some(val) = collector.eval_constant(const_op) {
+            collect_const_value(tcx, val, mentioned_items);
         }
     }
 
diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml
new file mode 100644
index 00000000000..aea2f7dda7f
--- /dev/null
+++ b/compiler/rustc_sanitizers/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "rustc_sanitizers"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
+bitflags = "2.5.0"
+tracing = "0.1"
+twox-hash = "1.6.3"
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_middle = { path = "../rustc_middle" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
+rustc_trait_selection = { path = "../rustc_trait_selection" }
diff --git a/compiler/rustc_sanitizers/README.md b/compiler/rustc_sanitizers/README.md
new file mode 100644
index 00000000000..d2e8f5d3a97
--- /dev/null
+++ b/compiler/rustc_sanitizers/README.md
@@ -0,0 +1,2 @@
+The `rustc_sanitizers` crate contains the source code for providing support for
+the [sanitizers](https://github.com/google/sanitizers) to the Rust compiler.
diff --git a/compiler/rustc_sanitizers/src/cfi/mod.rs b/compiler/rustc_sanitizers/src/cfi/mod.rs
new file mode 100644
index 00000000000..90dab5e0333
--- /dev/null
+++ b/compiler/rustc_sanitizers/src/cfi/mod.rs
@@ -0,0 +1,6 @@
+//! LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI support for the Rust compiler.
+//!
+//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
+//! see design document in the tracking issue #89653.
+pub mod typeid;
+pub use crate::cfi::typeid::{typeid_for_fnabi, typeid_for_instance, TypeIdOptions};
diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
index 6078d901711..ed7cd8c2da7 100644
--- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
@@ -1,76 +1,46 @@
-/// Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow
-/// Integrity (CFI) and cross-language LLVM CFI support.
-///
-/// Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium
-/// C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that
-/// are not used across the FFI boundary.
-///
-/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
-/// see design document in the tracking issue #89653.
+//! Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium
+//! C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that
+//! are not used across the FFI boundary.
+//!
+//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
+//! see design document in the tracking issue #89653.
 use rustc_data_structures::base_n;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
-use rustc_hir::lang_items::LangItem;
-use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
+use rustc_middle::bug;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{
-    self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
-    TermKind, Ty, TyCtxt, UintTy,
+    self, Const, ExistentialPredicate, FloatTy, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
+    IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, TypeFoldable, UintTy,
 };
-use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
-use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
 use rustc_span::def_id::DefId;
 use rustc_span::sym;
-use rustc_target::abi::call::{Conv, FnAbi, PassMode};
 use rustc_target::abi::Integer;
 use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits;
 use std::fmt::Write as _;
-use std::iter;
+use tracing::instrument;
 
-use crate::typeid::TypeIdOptions;
+use crate::cfi::typeid::itanium_cxx_abi::transform::{TransformTy, TransformTyOptions};
+use crate::cfi::typeid::TypeIdOptions;
 
-/// Type and extended type qualifiers.
-#[derive(Eq, Hash, PartialEq)]
-enum TyQ {
-    None,
-    Const,
-    Mut,
-}
+/// Options for encode_ty.
+pub type EncodeTyOptions = TypeIdOptions;
 
 /// Substitution dictionary key.
 #[derive(Eq, Hash, PartialEq)]
-enum DictKey<'tcx> {
+pub enum DictKey<'tcx> {
     Ty(Ty<'tcx>, TyQ),
     Region(Region<'tcx>),
     Const(Const<'tcx>),
     Predicate(ExistentialPredicate<'tcx>),
 }
 
-/// Options for encode_ty.
-type EncodeTyOptions = TypeIdOptions;
-
-/// Options for transform_ty.
-type TransformTyOptions = TypeIdOptions;
-
-/// Converts a number to a disambiguator (see
-/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
-fn to_disambiguator(num: u64) -> String {
-    if let Some(num) = num.checked_sub(1) {
-        format!("s{}_", base_n::encode(num as u128, 62))
-    } else {
-        "s_".to_string()
-    }
-}
-
-/// Converts a number to a sequence number (see
-/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
-fn to_seq_id(num: usize) -> String {
-    if let Some(num) = num.checked_sub(1) {
-        base_n::encode(num as u128, 36).to_uppercase()
-    } else {
-        "".to_string()
-    }
+/// Type and extended type qualifiers.
+#[derive(Eq, Hash, PartialEq)]
+pub enum TyQ {
+    None,
+    Const,
+    Mut,
 }
 
 /// Substitutes a component if found in the substitution dictionary (see
@@ -91,6 +61,37 @@ fn compress<'tcx>(
     }
 }
 
+/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust
+/// types that are not used at the FFI boundary.
+fn encode_args<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    args: GenericArgsRef<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // [I<subst1..substN>E] as part of vendor extended type
+    let mut s = String::new();
+    let args: Vec<GenericArg<'_>> = args.iter().collect();
+    if !args.is_empty() {
+        s.push('I');
+        for arg in args {
+            match arg.unpack() {
+                GenericArgKind::Lifetime(region) => {
+                    s.push_str(&encode_region(region, dict));
+                }
+                GenericArgKind::Type(ty) => {
+                    s.push_str(&encode_ty(tcx, ty, dict, options));
+                }
+                GenericArgKind::Const(c) => {
+                    s.push_str(&encode_const(tcx, c, dict, options));
+                }
+            }
+        }
+        s.push('E');
+    }
+    s
+}
+
 /// Encodes a const using the Itanium C++ ABI as a literal argument (see
 /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
 fn encode_const<'tcx>(
@@ -159,7 +160,6 @@ fn encode_const<'tcx>(
 
 /// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
 /// Rust types that are not used at the FFI boundary.
-#[instrument(level = "trace", skip(tcx, dict))]
 fn encode_fnsig<'tcx>(
     tcx: TyCtxt<'tcx>,
     fn_sig: &FnSig<'tcx>,
@@ -299,137 +299,10 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap<DictKey<'tcx>,
     s
 }
 
-/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust
-/// types that are not used at the FFI boundary.
-fn encode_args<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    args: GenericArgsRef<'tcx>,
-    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
-    options: EncodeTyOptions,
-) -> String {
-    // [I<subst1..substN>E] as part of vendor extended type
-    let mut s = String::new();
-    let args: Vec<GenericArg<'_>> = args.iter().collect();
-    if !args.is_empty() {
-        s.push('I');
-        for arg in args {
-            match arg.unpack() {
-                GenericArgKind::Lifetime(region) => {
-                    s.push_str(&encode_region(region, dict));
-                }
-                GenericArgKind::Type(ty) => {
-                    s.push_str(&encode_ty(tcx, ty, dict, options));
-                }
-                GenericArgKind::Const(c) => {
-                    s.push_str(&encode_const(tcx, c, dict, options));
-                }
-            }
-        }
-        s.push('E');
-    }
-    s
-}
-
-/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
-fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
-    // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
-    // <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
-    //
-    // N<namespace-tagN>..N<namespace-tag1>
-    // C<crate-disambiguator><crate-name>
-    // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
-    //
-    // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
-    //
-    //     pub type Type1 = impl Send;
-    //     let _: Type1 = <Struct1<i32>>::foo;
-    //     fn foo1(_: Type1) { }
-    //
-    //     pub type Type2 = impl Send;
-    //     let _: Type2 = <Trait1<i32>>::foo;
-    //     fn foo2(_: Type2) { }
-    //
-    //     pub type Type3 = impl Send;
-    //     let _: Type3 = <i32 as Trait1<i32>>::foo;
-    //     fn foo3(_: Type3) { }
-    //
-    //     pub type Type4 = impl Send;
-    //     let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
-    //     fn foo3(_: Type4) { }
-    //
-    // Are encoded as:
-    //
-    //     _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
-    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
-    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
-    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
-    //
-    // The reason for not using v0's extended form of paths is to use a consistent and simpler
-    // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e.,
-    // keep symbol names close to how methods are represented in error messages). See
-    // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods.
-    let mut s = String::new();
-
-    // Start and namespace tags
-    let mut def_path = tcx.def_path(def_id);
-    def_path.data.reverse();
-    for disambiguated_data in &def_path.data {
-        s.push('N');
-        s.push_str(match disambiguated_data.data {
-            hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace>
-            hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
-            hir::definitions::DefPathData::TypeNs(..) => "t",
-            hir::definitions::DefPathData::ValueNs(..) => "v",
-            hir::definitions::DefPathData::Closure => "C",
-            hir::definitions::DefPathData::Ctor => "c",
-            hir::definitions::DefPathData::AnonConst => "k",
-            hir::definitions::DefPathData::OpaqueTy => "i",
-            hir::definitions::DefPathData::CrateRoot
-            | hir::definitions::DefPathData::Use
-            | hir::definitions::DefPathData::GlobalAsm
-            | hir::definitions::DefPathData::MacroNs(..)
-            | hir::definitions::DefPathData::LifetimeNs(..)
-            | hir::definitions::DefPathData::AnonAdt => {
-                bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
-            }
-        });
-    }
-
-    // Crate disambiguator and name
-    s.push('C');
-    s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64()));
-    let crate_name = tcx.crate_name(def_path.krate).to_string();
-    let _ = write!(s, "{}{}", crate_name.len(), &crate_name);
-
-    // Disambiguators and names
-    def_path.data.reverse();
-    for disambiguated_data in &def_path.data {
-        let num = disambiguated_data.disambiguator as u64;
-        if num > 0 {
-            s.push_str(&to_disambiguator(num));
-        }
-
-        let name = disambiguated_data.data.to_string();
-        let _ = write!(s, "{}", name.len());
-
-        // Prepend a '_' if name starts with a digit or '_'
-        if let Some(first) = name.as_bytes().first() {
-            if first.is_ascii_digit() || *first == b'_' {
-                s.push('_');
-            }
-        } else {
-            bug!("encode_ty_name: invalid name `{:?}`", name);
-        }
-
-        s.push_str(&name);
-    }
-
-    s
-}
-
 /// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for
 /// Rust types that are not used at the FFI boundary.
-fn encode_ty<'tcx>(
+#[instrument(level = "trace", skip(tcx, dict))]
+pub fn encode_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
@@ -762,486 +635,119 @@ fn encode_ty<'tcx>(
     typeid
 }
 
-struct TransformTy<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    options: TransformTyOptions,
-    parents: Vec<Ty<'tcx>>,
-}
-
-impl<'tcx> TransformTy<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self {
-        TransformTy { tcx, options, parents: Vec::new() }
-    }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
-    // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms
-    // all c_void types into unit types unconditionally, generalizes pointers if
-    // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if
-    // TransformTyOptions::NORMALIZE_INTEGERS option is set.
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match t.kind() {
-            ty::Array(..)
-            | ty::Closure(..)
-            | ty::Coroutine(..)
-            | ty::CoroutineClosure(..)
-            | ty::CoroutineWitness(..)
-            | ty::Dynamic(..)
-            | ty::Float(..)
-            | ty::FnDef(..)
-            | ty::Foreign(..)
-            | ty::Never
-            | ty::Slice(..)
-            | ty::Pat(..)
-            | ty::Str
-            | ty::Tuple(..) => t.super_fold_with(self),
-
-            ty::Bool => {
-                if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
-                    // Note: on all platforms that Rust's currently supports, its size and alignment
-                    // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs.
-                    //
-                    // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
-                    //
-                    // Clang represents bool as an 8-bit unsigned integer.
-                    self.tcx.types.u8
-                } else {
-                    t
-                }
-            }
-
-            ty::Char => {
-                if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
-                    // Since #118032, char is guaranteed to have the same size, alignment, and
-                    // function call ABI as u32 on all platforms.
-                    self.tcx.types.u32
-                } else {
-                    t
-                }
-            }
-
-            ty::Int(..) | ty::Uint(..) => {
-                if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
-                    // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit
-                    // wide. All platforms we currently support have a C platform, and as a
-                    // consequence, isize/usize are at least 16-bit wide for all of them.
-                    //
-                    // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.)
-                    match t.kind() {
-                        ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width {
-                            16 => self.tcx.types.i16,
-                            32 => self.tcx.types.i32,
-                            64 => self.tcx.types.i64,
-                            128 => self.tcx.types.i128,
-                            _ => bug!(
-                                "fold_ty: unexpected pointer width `{}`",
-                                self.tcx.sess.target.pointer_width
-                            ),
-                        },
-                        ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width {
-                            16 => self.tcx.types.u16,
-                            32 => self.tcx.types.u32,
-                            64 => self.tcx.types.u64,
-                            128 => self.tcx.types.u128,
-                            _ => bug!(
-                                "fold_ty: unexpected pointer width `{}`",
-                                self.tcx.sess.target.pointer_width
-                            ),
-                        },
-                        _ => t,
-                    }
-                } else {
-                    t
-                }
-            }
-
-            ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit,
-
-            ty::Adt(adt_def, args) => {
-                if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t)
-                {
-                    // Don't transform repr(transparent) types with an user-defined CFI encoding to
-                    // preserve the user-defined CFI encoding.
-                    if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
-                        return t;
-                    }
-                    let variant = adt_def.non_enum_variant();
-                    let param_env = self.tcx.param_env(variant.def_id);
-                    let field = variant.fields.iter().find(|field| {
-                        let ty = self.tcx.type_of(field.did).instantiate_identity();
-                        let is_zst = self
-                            .tcx
-                            .layout_of(param_env.and(ty))
-                            .is_ok_and(|layout| layout.is_zst());
-                        !is_zst
-                    });
-                    if let Some(field) = field {
-                        let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args);
-                        // Generalize any repr(transparent) user-defined type that is either a
-                        // pointer or reference, and either references itself or any other type that
-                        // contains or references itself, to avoid a reference cycle.
-
-                        // If the self reference is not through a pointer, for example, due
-                        // to using `PhantomData`, need to skip normalizing it if we hit it again.
-                        self.parents.push(t);
-                        let ty = if ty0.is_any_ptr() && ty0.contains(t) {
-                            let options = self.options;
-                            self.options |= TransformTyOptions::GENERALIZE_POINTERS;
-                            let ty = ty0.fold_with(self);
-                            self.options = options;
-                            ty
-                        } else {
-                            ty0.fold_with(self)
-                        };
-                        self.parents.pop();
-                        ty
-                    } else {
-                        // Transform repr(transparent) types without non-ZST field into ()
-                        self.tcx.types.unit
-                    }
-                } else {
-                    t.super_fold_with(self)
-                }
-            }
-
-            ty::Ref(..) => {
-                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
-                    if t.is_mutable_ptr() {
-                        Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
-                    } else {
-                        Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
-                    }
-                } else {
-                    t.super_fold_with(self)
-                }
-            }
-
-            ty::RawPtr(..) => {
-                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
-                    if t.is_mutable_ptr() {
-                        Ty::new_mut_ptr(self.tcx, self.tcx.types.unit)
-                    } else {
-                        Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
-                    }
-                } else {
-                    t.super_fold_with(self)
-                }
-            }
-
-            ty::FnPtr(..) => {
-                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
-                    Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
-                } else {
-                    t.super_fold_with(self)
-                }
-            }
-
-            ty::Alias(..) => {
-                self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t))
-            }
+/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
+fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
+    // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
+    // <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
+    //
+    // N<namespace-tagN>..N<namespace-tag1>
+    // C<crate-disambiguator><crate-name>
+    // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
+    //
+    // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
+    //
+    //     pub type Type1 = impl Send;
+    //     let _: Type1 = <Struct1<i32>>::foo;
+    //     fn foo1(_: Type1) { }
+    //
+    //     pub type Type2 = impl Send;
+    //     let _: Type2 = <Trait1<i32>>::foo;
+    //     fn foo2(_: Type2) { }
+    //
+    //     pub type Type3 = impl Send;
+    //     let _: Type3 = <i32 as Trait1<i32>>::foo;
+    //     fn foo3(_: Type3) { }
+    //
+    //     pub type Type4 = impl Send;
+    //     let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
+    //     fn foo3(_: Type4) { }
+    //
+    // Are encoded as:
+    //
+    //     _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
+    //
+    // The reason for not using v0's extended form of paths is to use a consistent and simpler
+    // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e.,
+    // keep symbol names close to how methods are represented in error messages). See
+    // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods.
+    let mut s = String::new();
 
-            ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => {
-                bug!("fold_ty: unexpected `{:?}`", t.kind());
+    // Start and namespace tags
+    let mut def_path = tcx.def_path(def_id);
+    def_path.data.reverse();
+    for disambiguated_data in &def_path.data {
+        s.push('N');
+        s.push_str(match disambiguated_data.data {
+            hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace>
+            hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
+            hir::definitions::DefPathData::TypeNs(..) => "t",
+            hir::definitions::DefPathData::ValueNs(..) => "v",
+            hir::definitions::DefPathData::Closure => "C",
+            hir::definitions::DefPathData::Ctor => "c",
+            hir::definitions::DefPathData::AnonConst => "k",
+            hir::definitions::DefPathData::OpaqueTy => "i",
+            hir::definitions::DefPathData::CrateRoot
+            | hir::definitions::DefPathData::Use
+            | hir::definitions::DefPathData::GlobalAsm
+            | hir::definitions::DefPathData::MacroNs(..)
+            | hir::definitions::DefPathData::LifetimeNs(..)
+            | hir::definitions::DefPathData::AnonAdt => {
+                bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
             }
-        }
-    }
-
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
+        });
     }
-}
-
-/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
-/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
-#[instrument(level = "trace", skip(tcx))]
-pub fn typeid_for_fnabi<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
-    options: TypeIdOptions,
-) -> String {
-    // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
-    // its type.
-    let mut typeid = String::from("_Z");
 
-    // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
-    // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
-    // code (i.e., 'TS') prefixed to the type encoding for the function.
-    typeid.push_str("TS");
-
-    // Function types are delimited by an "F..E" pair
-    typeid.push('F');
-
-    // A dictionary of substitution candidates used for compression (see
-    // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
-    let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
+    // Crate disambiguator and name
+    s.push('C');
+    s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64()));
+    let crate_name = tcx.crate_name(def_path.krate).to_string();
+    let _ = write!(s, "{}{}", crate_name.len(), &crate_name);
 
-    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
-        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
-    match fn_abi.conv {
-        Conv::C => {
-            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
-        }
-        _ => {
-            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
+    // Disambiguators and names
+    def_path.data.reverse();
+    for disambiguated_data in &def_path.data {
+        let num = disambiguated_data.disambiguator as u64;
+        if num > 0 {
+            s.push_str(&to_disambiguator(num));
         }
-    }
-
-    // Encode the return type
-    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
-        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
-    let mut type_folder = TransformTy::new(tcx, transform_ty_options);
-    let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder);
-    typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
 
-    // Encode the parameter types
+        let name = disambiguated_data.data.to_string();
+        let _ = write!(s, "{}", name.len());
 
-    // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how
-    // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR
-    // interpretation today will allow skipped arguments to simply not be passed at a call-site.
-    if !fn_abi.c_variadic {
-        let mut pushed_arg = false;
-        for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) {
-            pushed_arg = true;
-            let ty = arg.layout.ty.fold_with(&mut type_folder);
-            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
-        }
-        if !pushed_arg {
-            // Empty parameter lists, whether declared as () or conventionally as (void), are
-            // encoded with a void parameter specifier "v".
-            typeid.push('v');
-        }
-    } else {
-        for n in 0..fn_abi.fixed_count as usize {
-            if fn_abi.args[n].mode == PassMode::Ignore {
-                continue;
+        // Prepend a '_' if name starts with a digit or '_'
+        if let Some(first) = name.as_bytes().first() {
+            if first.is_ascii_digit() || *first == b'_' {
+                s.push('_');
             }
-            let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder);
-            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+        } else {
+            bug!("encode_ty_name: invalid name `{:?}`", name);
         }
 
-        typeid.push('z');
-    }
-
-    // Close the "F..E" pair
-    typeid.push('E');
-
-    // Add encoding suffixes
-    if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
-        typeid.push_str(".normalized");
-    }
-
-    if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
-        typeid.push_str(".generalized");
+        s.push_str(&name);
     }
 
-    typeid
+    s
 }
 
-/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with
-/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary.
-pub fn typeid_for_instance<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    mut instance: Instance<'tcx>,
-    options: TypeIdOptions,
-) -> String {
-    if (matches!(instance.def, ty::InstanceDef::Virtual(..))
-        && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn())
-        || matches!(instance.def, ty::InstanceDef::DropGlue(..))
-    {
-        // Adjust the type ids of DropGlues
-        //
-        // DropGlues may have indirect calls to one or more given types drop function. Rust allows
-        // for types to be erased to any trait object and retains the drop function for the original
-        // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is
-        // called a second time, it only has information after type erasure and it could be a call
-        // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on
-        // declaration/definition, and during code generation at call sites so they have the same
-        // type id and match.
-        //
-        // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of
-        //   any other type.
-        //
-        let def_id = tcx
-            .lang_items()
-            .drop_trait()
-            .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item"));
-        let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef {
-            def_id: def_id,
-            args: List::empty(),
-        });
-        let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
-        let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
-        instance.args = tcx.mk_args_trait(self_ty, List::empty());
-    } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
-        let upcast_ty = match tcx.trait_of_item(def_id) {
-            Some(trait_id) => trait_object_ty(
-                tcx,
-                ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)),
-            ),
-            // drop_in_place won't have a defining trait, skip the upcast
-            None => instance.args.type_at(0),
-        };
-        let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
-        instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
-    } else if let ty::InstanceDef::VTableShim(def_id) = instance.def
-        && let Some(trait_id) = tcx.trait_of_item(def_id)
-    {
-        // VTableShims may have a trait method, but a concrete Self. This is not suitable for a vtable,
-        // as the caller will not know the concrete Self.
-        let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args);
-        let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
-        instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
-    }
-
-    if !options.contains(EncodeTyOptions::USE_CONCRETE_SELF) {
-        if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
-            && let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
-        {
-            let impl_method = tcx.associated_item(instance.def_id());
-            let method_id = impl_method
-                .trait_item_def_id
-                .expect("Part of a trait implementation, but not linked to the def_id?");
-            let trait_method = tcx.associated_item(method_id);
-            let trait_id = trait_ref.skip_binder().def_id;
-            if traits::is_vtable_safe_method(tcx, trait_id, trait_method)
-                && tcx.object_safety_violations(trait_id).is_empty()
-            {
-                // Trait methods will have a Self polymorphic parameter, where the concreteized
-                // implementatation will not. We need to walk back to the more general trait method
-                let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
-                    instance.args,
-                    ty::ParamEnv::reveal_all(),
-                    trait_ref,
-                );
-                let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
-
-                // At the call site, any call to this concrete function through a vtable will be
-                // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
-                // original method id, and we've recovered the trait arguments, we can make the callee
-                // instance we're computing the alias set for match the caller instance.
-                //
-                // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
-                // If we ever *do* start encoding the vtable index, we will need to generate an alias set
-                // based on which vtables we are putting this method into, as there will be more than one
-                // index value when supertraits are involved.
-                instance.def = ty::InstanceDef::Virtual(method_id, 0);
-                let abstract_trait_args =
-                    tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
-                instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
-            }
-        } else if tcx.is_closure_like(instance.def_id()) {
-            // We're either a closure or a coroutine. Our goal is to find the trait we're defined on,
-            // instantiate it, and take the type of its only method as our own.
-            let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
-            let (trait_id, inputs) = match closure_ty.kind() {
-                ty::Closure(..) => {
-                    let closure_args = instance.args.as_closure();
-                    let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
-                    let tuple_args =
-                        tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0];
-                    (trait_id, Some(tuple_args))
-                }
-                ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() {
-                    hir::CoroutineKind::Coroutine(..) => (
-                        tcx.require_lang_item(LangItem::Coroutine, None),
-                        Some(instance.args.as_coroutine().resume_ty()),
-                    ),
-                    hir::CoroutineKind::Desugared(desugaring, _) => {
-                        let lang_item = match desugaring {
-                            hir::CoroutineDesugaring::Async => LangItem::Future,
-                            hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator,
-                            hir::CoroutineDesugaring::Gen => LangItem::Iterator,
-                        };
-                        (tcx.require_lang_item(lang_item, None), None)
-                    }
-                },
-                ty::CoroutineClosure(..) => (
-                    tcx.require_lang_item(LangItem::FnOnce, None),
-                    Some(
-                        tcx.instantiate_bound_regions_with_erased(
-                            instance.args.as_coroutine_closure().coroutine_closure_sig(),
-                        )
-                        .tupled_inputs_ty,
-                    ),
-                ),
-                x => bug!("Unexpected type kind for closure-like: {x:?}"),
-            };
-            let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into));
-            let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args);
-            let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
-            let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
-            // There should be exactly one method on this trait, and it should be the one we're
-            // defining.
-            let call = tcx
-                .associated_items(trait_id)
-                .in_definition_order()
-                .find(|it| it.kind == ty::AssocKind::Fn)
-                .expect("No call-family function on closure-like Fn trait?")
-                .def_id;
-
-            instance.def = ty::InstanceDef::Virtual(call, 0);
-            instance.args = abstract_args;
-        }
+/// Converts a number to a disambiguator (see
+/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
+fn to_disambiguator(num: u64) -> String {
+    if let Some(num) = num.checked_sub(1) {
+        format!("s{}_", base_n::encode(num as u128, 62))
+    } else {
+        "s_".to_string()
     }
-
-    let fn_abi = tcx
-        .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty())))
-        .unwrap_or_else(|error| {
-            bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}")
-        });
-
-    typeid_for_fnabi(tcx, fn_abi, options)
 }
 
-fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
-    let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
-        bug!("Tried to strip auto traits from non-dynamic type {ty}");
-    };
-    if preds.principal().is_some() {
-        let filtered_preds =
-            tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
-                !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
-            }));
-        Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind)
+/// Converts a number to a sequence number (see
+/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
+fn to_seq_id(num: usize) -> String {
+    if let Some(num) = num.checked_sub(1) {
+        base_n::encode(num as u128, 36).to_uppercase()
     } else {
-        // If there's no principal type, re-encode it as a unit, since we don't know anything
-        // about it. This technically discards the knowledge that it was a type that was made
-        // into a trait object at some point, but that's not a lot.
-        tcx.types.unit
+        "".to_string()
     }
 }
-
-#[instrument(skip(tcx), ret)]
-fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
-    assert!(!poly_trait_ref.has_non_region_param());
-    let principal_pred = poly_trait_ref.map_bound(|trait_ref| {
-        ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
-    });
-    let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref)
-        .flat_map(|super_poly_trait_ref| {
-            tcx.associated_items(super_poly_trait_ref.def_id())
-                .in_definition_order()
-                .filter(|item| item.kind == ty::AssocKind::Type)
-                .map(move |assoc_ty| {
-                    super_poly_trait_ref.map_bound(|super_trait_ref| {
-                        let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args);
-                        let resolved = tcx.normalize_erasing_regions(
-                            ty::ParamEnv::reveal_all(),
-                            alias_ty.to_ty(tcx),
-                        );
-                        debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx));
-                        ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
-                            def_id: assoc_ty.def_id,
-                            args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args,
-                            term: resolved.into(),
-                        })
-                    })
-                })
-        })
-        .collect();
-    assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
-    let preds = tcx.mk_poly_existential_predicates_from_iter(
-        iter::once(principal_pred).chain(assoc_preds.into_iter()),
-    );
-    Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn)
-}
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs
new file mode 100644
index 00000000000..b6182dc4e63
--- /dev/null
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs
@@ -0,0 +1,123 @@
+//! Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow
+//! Integrity (CFI) and cross-language LLVM CFI support.
+//!
+//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
+//! see design document in the tracking issue #89653.
+use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::bug;
+use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable};
+use rustc_target::abi::call::{Conv, FnAbi, PassMode};
+use tracing::instrument;
+
+mod encode;
+mod transform;
+use crate::cfi::typeid::itanium_cxx_abi::encode::{encode_ty, DictKey, EncodeTyOptions};
+use crate::cfi::typeid::itanium_cxx_abi::transform::{
+    transform_instance, TransformTy, TransformTyOptions,
+};
+use crate::cfi::typeid::TypeIdOptions;
+
+/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
+/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
+#[instrument(level = "trace", skip(tcx))]
+pub fn typeid_for_fnabi<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+    options: TypeIdOptions,
+) -> String {
+    // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
+    // its type.
+    let mut typeid = String::from("_Z");
+
+    // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
+    // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
+    // code (i.e., 'TS') prefixed to the type encoding for the function.
+    typeid.push_str("TS");
+
+    // Function types are delimited by an "F..E" pair
+    typeid.push('F');
+
+    // A dictionary of substitution candidates used for compression (see
+    // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
+    let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
+
+    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
+    match fn_abi.conv {
+        Conv::C => {
+            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+        _ => {
+            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+    }
+
+    // Encode the return type
+    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
+    let mut type_folder = TransformTy::new(tcx, transform_ty_options);
+    let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder);
+    typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+
+    // Encode the parameter types
+
+    // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how
+    // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR
+    // interpretation today will allow skipped arguments to simply not be passed at a call-site.
+    if !fn_abi.c_variadic {
+        let mut pushed_arg = false;
+        for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) {
+            pushed_arg = true;
+            let ty = arg.layout.ty.fold_with(&mut type_folder);
+            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+        }
+        if !pushed_arg {
+            // Empty parameter lists, whether declared as () or conventionally as (void), are
+            // encoded with a void parameter specifier "v".
+            typeid.push('v');
+        }
+    } else {
+        for n in 0..fn_abi.fixed_count as usize {
+            if fn_abi.args[n].mode == PassMode::Ignore {
+                continue;
+            }
+            let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder);
+            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+        }
+
+        typeid.push('z');
+    }
+
+    // Close the "F..E" pair
+    typeid.push('E');
+
+    // Add encoding suffixes
+    if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
+        typeid.push_str(".normalized");
+    }
+
+    if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
+        typeid.push_str(".generalized");
+    }
+
+    typeid
+}
+
+/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with
+/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary.
+#[instrument(level = "trace", skip(tcx))]
+pub fn typeid_for_instance<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+    options: TypeIdOptions,
+) -> String {
+    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("typeid_for_instance: invalid option(s) `{:?}`", options.bits()));
+    let instance = transform_instance(tcx, instance, transform_ty_options);
+    let fn_abi = tcx
+        .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty())))
+        .unwrap_or_else(|error| {
+            bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}")
+        });
+    typeid_for_fnabi(tcx, fn_abi, options)
+}
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
new file mode 100644
index 00000000000..7141c6c9bb0
--- /dev/null
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -0,0 +1,450 @@
+//! Transforms instances and types for LLVM CFI and cross-language LLVM CFI support using Itanium
+//! C++ ABI mangling.
+//!
+//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
+//! see design document in the tracking issue #89653.
+use rustc_hir as hir;
+use rustc_hir::LangItem;
+use rustc_middle::bug;
+use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::{
+    self, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, UintTy,
+};
+use rustc_span::sym;
+use rustc_trait_selection::traits;
+use std::iter;
+use tracing::{debug, instrument};
+
+use crate::cfi::typeid::itanium_cxx_abi::encode::EncodeTyOptions;
+use crate::cfi::typeid::TypeIdOptions;
+
+/// Options for transform_ty.
+pub type TransformTyOptions = TypeIdOptions;
+
+pub struct TransformTy<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    options: TransformTyOptions,
+    parents: Vec<Ty<'tcx>>,
+}
+
+impl<'tcx> TransformTy<'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self {
+        TransformTy { tcx, options, parents: Vec::new() }
+    }
+}
+
+/// Transforms a ty:Ty for being encoded and used in the substitution dictionary.
+///
+/// * Transforms all c_void types into unit types.
+/// * Generalizes pointers if TransformTyOptions::GENERALIZE_POINTERS option is set.
+/// * Normalizes integers if TransformTyOptions::NORMALIZE_INTEGERS option is set.
+/// * Generalizes any repr(transparent) user-defined type that is either a pointer or reference, and
+///   either references itself or any other type that contains or references itself, to avoid a
+///   reference cycle.
+/// * Transforms repr(transparent) types without non-ZST field into ().
+///
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
+    // Transforms a ty:Ty for being encoded and used in the substitution dictionary.
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match t.kind() {
+            ty::Array(..)
+            | ty::Closure(..)
+            | ty::Coroutine(..)
+            | ty::CoroutineClosure(..)
+            | ty::CoroutineWitness(..)
+            | ty::Dynamic(..)
+            | ty::Float(..)
+            | ty::FnDef(..)
+            | ty::Foreign(..)
+            | ty::Never
+            | ty::Pat(..)
+            | ty::Slice(..)
+            | ty::Str
+            | ty::Tuple(..) => t.super_fold_with(self),
+
+            ty::Bool => {
+                if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
+                    // Note: on all platforms that Rust's currently supports, its size and alignment
+                    // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs.
+                    //
+                    // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
+                    //
+                    // Clang represents bool as an 8-bit unsigned integer.
+                    self.tcx.types.u8
+                } else {
+                    t
+                }
+            }
+
+            ty::Char => {
+                if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
+                    // Since #118032, char is guaranteed to have the same size, alignment, and
+                    // function call ABI as u32 on all platforms.
+                    self.tcx.types.u32
+                } else {
+                    t
+                }
+            }
+
+            ty::Int(..) | ty::Uint(..) => {
+                if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
+                    // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit
+                    // wide. All platforms we currently support have a C platform, and as a
+                    // consequence, isize/usize are at least 16-bit wide for all of them.
+                    //
+                    // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.)
+                    match t.kind() {
+                        ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width {
+                            16 => self.tcx.types.i16,
+                            32 => self.tcx.types.i32,
+                            64 => self.tcx.types.i64,
+                            128 => self.tcx.types.i128,
+                            _ => bug!(
+                                "fold_ty: unexpected pointer width `{}`",
+                                self.tcx.sess.target.pointer_width
+                            ),
+                        },
+                        ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width {
+                            16 => self.tcx.types.u16,
+                            32 => self.tcx.types.u32,
+                            64 => self.tcx.types.u64,
+                            128 => self.tcx.types.u128,
+                            _ => bug!(
+                                "fold_ty: unexpected pointer width `{}`",
+                                self.tcx.sess.target.pointer_width
+                            ),
+                        },
+                        _ => t,
+                    }
+                } else {
+                    t
+                }
+            }
+
+            ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit,
+
+            ty::Adt(adt_def, args) => {
+                if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t)
+                {
+                    // Don't transform repr(transparent) types with an user-defined CFI encoding to
+                    // preserve the user-defined CFI encoding.
+                    if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
+                        return t;
+                    }
+                    let variant = adt_def.non_enum_variant();
+                    let param_env = self.tcx.param_env(variant.def_id);
+                    let field = variant.fields.iter().find(|field| {
+                        let ty = self.tcx.type_of(field.did).instantiate_identity();
+                        let is_zst = self
+                            .tcx
+                            .layout_of(param_env.and(ty))
+                            .is_ok_and(|layout| layout.is_zst());
+                        !is_zst
+                    });
+                    if let Some(field) = field {
+                        let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args);
+                        // Generalize any repr(transparent) user-defined type that is either a
+                        // pointer or reference, and either references itself or any other type that
+                        // contains or references itself, to avoid a reference cycle.
+
+                        // If the self reference is not through a pointer, for example, due
+                        // to using `PhantomData`, need to skip normalizing it if we hit it again.
+                        self.parents.push(t);
+                        let ty = if ty0.is_any_ptr() && ty0.contains(t) {
+                            let options = self.options;
+                            self.options |= TransformTyOptions::GENERALIZE_POINTERS;
+                            let ty = ty0.fold_with(self);
+                            self.options = options;
+                            ty
+                        } else {
+                            ty0.fold_with(self)
+                        };
+                        self.parents.pop();
+                        ty
+                    } else {
+                        // Transform repr(transparent) types without non-ZST field into ()
+                        self.tcx.types.unit
+                    }
+                } else {
+                    t.super_fold_with(self)
+                }
+            }
+
+            ty::Ref(..) => {
+                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                    if t.is_mutable_ptr() {
+                        Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
+                    } else {
+                        Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
+                    }
+                } else {
+                    t.super_fold_with(self)
+                }
+            }
+
+            ty::RawPtr(..) => {
+                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                    if t.is_mutable_ptr() {
+                        Ty::new_mut_ptr(self.tcx, self.tcx.types.unit)
+                    } else {
+                        Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
+                    }
+                } else {
+                    t.super_fold_with(self)
+                }
+            }
+
+            ty::FnPtr(..) => {
+                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                    Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
+                } else {
+                    t.super_fold_with(self)
+                }
+            }
+
+            ty::Alias(..) => {
+                self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t))
+            }
+
+            ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => {
+                bug!("fold_ty: unexpected `{:?}`", t.kind());
+            }
+        }
+    }
+
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+}
+
+#[instrument(skip(tcx), ret)]
+fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
+    assert!(!poly_trait_ref.has_non_region_param());
+    let principal_pred = poly_trait_ref.map_bound(|trait_ref| {
+        ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
+    });
+    let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref)
+        .flat_map(|super_poly_trait_ref| {
+            tcx.associated_items(super_poly_trait_ref.def_id())
+                .in_definition_order()
+                .filter(|item| item.kind == ty::AssocKind::Type)
+                .map(move |assoc_ty| {
+                    super_poly_trait_ref.map_bound(|super_trait_ref| {
+                        let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args);
+                        let resolved = tcx.normalize_erasing_regions(
+                            ty::ParamEnv::reveal_all(),
+                            alias_ty.to_ty(tcx),
+                        );
+                        debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx));
+                        ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
+                            def_id: assoc_ty.def_id,
+                            args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args,
+                            term: resolved.into(),
+                        })
+                    })
+                })
+        })
+        .collect();
+    assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
+    let preds = tcx.mk_poly_existential_predicates_from_iter(
+        iter::once(principal_pred).chain(assoc_preds.into_iter()),
+    );
+    Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn)
+}
+
+/// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI
+/// mangling.
+///
+/// typeid_for_instance is called at two locations, initially when declaring/defining functions and
+/// methods, and later during code generation at call sites, after type erasure might have ocurred.
+///
+/// In the first call (i.e., when declaring/defining functions and methods), it encodes type ids for
+/// an FnAbi or Instance, and these type ids are attached to functions and methods. (These type ids
+/// are used later by the LowerTypeTests LLVM pass to aggregate functions in groups derived from
+/// these type ids.)
+///
+/// In the second call (i.e., during code generation at call sites), it encodes a type id for an
+/// FnAbi or Instance, after type erasure might have occured, and this type id is used for testing
+/// if a function is member of the group derived from this type id. Therefore, in the first call to
+/// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at
+/// most as much information that would be available in the second call (i.e., during code
+/// generation at call sites); otherwise, the type ids would not not match.
+///
+/// For this, it:
+///
+/// * Adjust the type ids of DropGlues (see below).
+/// * Adjusts the type ids of VTableShims to the type id expected in the call sites for the
+///   entry in the vtable (i.e., by using the signature of the closure passed as an argument to the
+///   shim, or by just removing self).
+/// * Performs type erasure for calls on trait objects by transforming self into a trait object of
+///   the trait that defines the method.
+/// * Performs type erasure for closures call methods by transforming self into a trait object of
+///   the Fn trait that defines the method (for being attached as a secondary type id).
+///
+#[instrument(level = "trace", skip(tcx))]
+pub fn transform_instance<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mut instance: Instance<'tcx>,
+    options: TransformTyOptions,
+) -> Instance<'tcx> {
+    if (matches!(instance.def, ty::InstanceDef::Virtual(..))
+        && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn())
+        || matches!(instance.def, ty::InstanceDef::DropGlue(..))
+    {
+        // Adjust the type ids of DropGlues
+        //
+        // DropGlues may have indirect calls to one or more given types drop function. Rust allows
+        // for types to be erased to any trait object and retains the drop function for the original
+        // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is
+        // called a second time, it only has information after type erasure and it could be a call
+        // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on
+        // declaration/definition, and during code generation at call sites so they have the same
+        // type id and match.
+        //
+        // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of
+        //   any other type.
+        //
+        let def_id = tcx
+            .lang_items()
+            .drop_trait()
+            .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item"));
+        let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef {
+            def_id: def_id,
+            args: List::empty(),
+        });
+        let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
+        let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
+        instance.args = tcx.mk_args_trait(self_ty, List::empty());
+    } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
+        // Transform self into a trait object of the trait that defines the method for virtual
+        // functions to match the type erasure done below.
+        let upcast_ty = match tcx.trait_of_item(def_id) {
+            Some(trait_id) => trait_object_ty(
+                tcx,
+                ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)),
+            ),
+            // drop_in_place won't have a defining trait, skip the upcast
+            None => instance.args.type_at(0),
+        };
+        let ty::Dynamic(preds, lifetime, kind) = upcast_ty.kind() else {
+            bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}");
+        };
+        let self_ty = if preds.principal().is_some() {
+            let filtered_preds =
+                tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
+                    !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
+                }));
+            Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind)
+        } else {
+            // If there's no principal type, re-encode it as a unit, since we don't know anything
+            // about it. This technically discards the knowledge that it was a type that was made
+            // into a trait object at some point, but that's not a lot.
+            tcx.types.unit
+        };
+        instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1));
+    } else if let ty::InstanceDef::VTableShim(def_id) = instance.def
+        && let Some(trait_id) = tcx.trait_of_item(def_id)
+    {
+        // Adjust the type ids of VTableShims to the type id expected in the call sites for the
+        // entry in the vtable (i.e., by using the signature of the closure passed as an argument
+        // to the shim, or by just removing self).
+        let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args);
+        let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
+        instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
+    }
+
+    if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) {
+        // Perform type erasure for calls on trait objects by transforming self into a trait object
+        // of the trait that defines the method.
+        if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
+            && let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
+        {
+            let impl_method = tcx.associated_item(instance.def_id());
+            let method_id = impl_method
+                .trait_item_def_id
+                .expect("Part of a trait implementation, but not linked to the def_id?");
+            let trait_method = tcx.associated_item(method_id);
+            let trait_id = trait_ref.skip_binder().def_id;
+            if traits::is_vtable_safe_method(tcx, trait_id, trait_method)
+                && tcx.object_safety_violations(trait_id).is_empty()
+            {
+                // Trait methods will have a Self polymorphic parameter, where the concreteized
+                // implementatation will not. We need to walk back to the more general trait method
+                let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
+                    instance.args,
+                    ty::ParamEnv::reveal_all(),
+                    trait_ref,
+                );
+                let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
+
+                // At the call site, any call to this concrete function through a vtable will be
+                // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
+                // original method id, and we've recovered the trait arguments, we can make the callee
+                // instance we're computing the alias set for match the caller instance.
+                //
+                // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
+                // If we ever *do* start encoding the vtable index, we will need to generate an alias set
+                // based on which vtables we are putting this method into, as there will be more than one
+                // index value when supertraits are involved.
+                instance.def = ty::InstanceDef::Virtual(method_id, 0);
+                let abstract_trait_args =
+                    tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
+                instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
+            }
+        } else if tcx.is_closure_like(instance.def_id()) {
+            // We're either a closure or a coroutine. Our goal is to find the trait we're defined on,
+            // instantiate it, and take the type of its only method as our own.
+            let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
+            let (trait_id, inputs) = match closure_ty.kind() {
+                ty::Closure(..) => {
+                    let closure_args = instance.args.as_closure();
+                    let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
+                    let tuple_args =
+                        tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0];
+                    (trait_id, Some(tuple_args))
+                }
+                ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() {
+                    hir::CoroutineKind::Coroutine(..) => (
+                        tcx.require_lang_item(LangItem::Coroutine, None),
+                        Some(instance.args.as_coroutine().resume_ty()),
+                    ),
+                    hir::CoroutineKind::Desugared(desugaring, _) => {
+                        let lang_item = match desugaring {
+                            hir::CoroutineDesugaring::Async => LangItem::Future,
+                            hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator,
+                            hir::CoroutineDesugaring::Gen => LangItem::Iterator,
+                        };
+                        (tcx.require_lang_item(lang_item, None), None)
+                    }
+                },
+                ty::CoroutineClosure(..) => (
+                    tcx.require_lang_item(LangItem::FnOnce, None),
+                    Some(
+                        tcx.instantiate_bound_regions_with_erased(
+                            instance.args.as_coroutine_closure().coroutine_closure_sig(),
+                        )
+                        .tupled_inputs_ty,
+                    ),
+                ),
+                x => bug!("Unexpected type kind for closure-like: {x:?}"),
+            };
+            let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into));
+            let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args);
+            let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
+            let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
+            // There should be exactly one method on this trait, and it should be the one we're
+            // defining.
+            let call = tcx
+                .associated_items(trait_id)
+                .in_definition_order()
+                .find(|it| it.kind == ty::AssocKind::Fn)
+                .expect("No call-family function on closure-like Fn trait?")
+                .def_id;
+
+            instance.def = ty::InstanceDef::Virtual(call, 0);
+            instance.args = abstract_args;
+        }
+    }
+
+    instance
+}
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs
new file mode 100644
index 00000000000..ad8b1804439
--- /dev/null
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs
@@ -0,0 +1,54 @@
+//! Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI
+//! support for the Rust compiler.
+//!
+//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
+//! see design document in the tracking issue #89653.
+use bitflags::bitflags;
+use rustc_middle::ty::{Instance, Ty, TyCtxt};
+use rustc_target::abi::call::FnAbi;
+
+bitflags! {
+    /// Options for typeid_for_fnabi.
+    #[derive(Clone, Copy, Debug)]
+    pub struct TypeIdOptions: u32 {
+        /// Generalizes pointers for compatibility with Clang
+        /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI
+        /// support.
+        const GENERALIZE_POINTERS = 1;
+        /// Generalizes repr(C) user-defined type for extern function types with the "C" calling
+        /// convention (or extern types) for cross-language LLVM CFI and  KCFI support.
+        const GENERALIZE_REPR_C = 2;
+        /// Normalizes integers for compatibility with Clang
+        /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM
+        /// CFI and  KCFI support.
+        const NORMALIZE_INTEGERS = 4;
+        /// Do not perform self type erasure for attaching a secondary type id to methods with their
+        /// concrete self so they can be used as function pointers.
+        ///
+        /// (This applies to typeid_for_instance only and should be used to attach a secondary type
+        /// id to methods during their declaration/definition so they match the type ids returned by
+        /// either typeid_for_instance or typeid_for_fnabi at call sites during code generation for
+        /// type membership tests when methods are used as function pointers.)
+        const USE_CONCRETE_SELF = 8;
+    }
+}
+
+pub mod itanium_cxx_abi;
+
+/// Returns a type metadata identifier for the specified FnAbi.
+pub fn typeid_for_fnabi<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+    options: TypeIdOptions,
+) -> String {
+    itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options)
+}
+
+/// Returns a type metadata identifier for the specified Instance.
+pub fn typeid_for_instance<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+    options: TypeIdOptions,
+) -> String {
+    itanium_cxx_abi::typeid_for_instance(tcx, instance, options)
+}
diff --git a/compiler/rustc_sanitizers/src/kcfi/mod.rs b/compiler/rustc_sanitizers/src/kcfi/mod.rs
new file mode 100644
index 00000000000..a8b632940b0
--- /dev/null
+++ b/compiler/rustc_sanitizers/src/kcfi/mod.rs
@@ -0,0 +1,7 @@
+//! LLVM Kernel Control Flow Integrity (KCFI) and cross-language LLVM KCFI support for the Rust
+//! compiler.
+//!
+//! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler,
+//! see the tracking issue #123479.
+pub mod typeid;
+pub use crate::kcfi::typeid::{typeid_for_fnabi, typeid_for_instance, TypeIdOptions};
diff --git a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs
new file mode 100644
index 00000000000..436c398e39b
--- /dev/null
+++ b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs
@@ -0,0 +1,55 @@
+//! Type metadata identifiers for LLVM Kernel Control Flow Integrity (KCFI) and cross-language LLVM
+//! KCFI support for the Rust compiler.
+//!
+//! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler,
+//! see the tracking issue #123479.
+use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt};
+use rustc_target::abi::call::FnAbi;
+use std::hash::Hasher;
+use twox_hash::XxHash64;
+
+pub use crate::cfi::typeid::{itanium_cxx_abi, TypeIdOptions};
+
+/// Returns a KCFI type metadata identifier for the specified FnAbi.
+pub fn typeid_for_fnabi<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+    options: TypeIdOptions,
+) -> u32 {
+    // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the
+    // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
+    let mut hash: XxHash64 = Default::default();
+    hash.write(itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes());
+    hash.finish() as u32
+}
+
+/// Returns a KCFI type metadata identifier for the specified Instance.
+pub fn typeid_for_instance<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    instance: Instance<'tcx>,
+    mut options: TypeIdOptions,
+) -> u32 {
+    // KCFI support for Rust shares most of its implementation with the CFI support, with some key
+    // differences:
+    //
+    // 1. KCFI performs type tests differently and are implemented as different LLVM passes than CFI
+    //    to not require LTO.
+    // 2. KCFI has the limitation that a function or method may have one type id assigned only.
+    //
+    // Because of the limitation listed above (2), the current KCFI implementation (not CFI) does
+    // reifying of types (i.e., adds shims/trampolines for indirect calls in these cases) for:
+    //
+    // * Supporting casting between function items, closures, and Fn trait objects.
+    // * Supporting methods being cast as function pointers.
+    //
+    // This was implemented for KCFI support in #123106 and #123052 (which introduced the
+    // ReifyReason). The tracking issue for KCFI support for Rust is #123479.
+    if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) {
+        options.insert(TypeIdOptions::USE_CONCRETE_SELF);
+    }
+    // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the
+    // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
+    let mut hash: XxHash64 = Default::default();
+    hash.write(itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes());
+    hash.finish() as u32
+}
diff --git a/compiler/rustc_sanitizers/src/lib.rs b/compiler/rustc_sanitizers/src/lib.rs
new file mode 100644
index 00000000000..1f73e255490
--- /dev/null
+++ b/compiler/rustc_sanitizers/src/lib.rs
@@ -0,0 +1,7 @@
+#![feature(let_chains)]
+//! Sanitizers support for the Rust compiler.
+//!
+//! This crate contains the source code for providing support for the sanitizers to the Rust
+//! compiler.
+pub mod cfi;
+pub mod kcfi;
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index a904cd10041..0893bc31bfa 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -10,7 +10,7 @@ use rustc_span::Symbol;
 use stable_mir::abi::Layout;
 use stable_mir::mir::alloc::AllocId;
 use stable_mir::mir::mono::{Instance, MonoItem, StaticDef};
-use stable_mir::mir::{Mutability, Place, ProjectionElem, Safety};
+use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety};
 use stable_mir::ty::{
     Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const,
     DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig,
@@ -551,6 +551,38 @@ impl RustcInternal for ProjectionElem {
     }
 }
 
+impl RustcInternal for BinOp {
+    type T<'tcx> = rustc_middle::mir::BinOp;
+
+    fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
+        match self {
+            BinOp::Add => rustc_middle::mir::BinOp::Add,
+            BinOp::AddUnchecked => rustc_middle::mir::BinOp::AddUnchecked,
+            BinOp::Sub => rustc_middle::mir::BinOp::Sub,
+            BinOp::SubUnchecked => rustc_middle::mir::BinOp::SubUnchecked,
+            BinOp::Mul => rustc_middle::mir::BinOp::Mul,
+            BinOp::MulUnchecked => rustc_middle::mir::BinOp::MulUnchecked,
+            BinOp::Div => rustc_middle::mir::BinOp::Div,
+            BinOp::Rem => rustc_middle::mir::BinOp::Rem,
+            BinOp::BitXor => rustc_middle::mir::BinOp::BitXor,
+            BinOp::BitAnd => rustc_middle::mir::BinOp::BitAnd,
+            BinOp::BitOr => rustc_middle::mir::BinOp::BitOr,
+            BinOp::Shl => rustc_middle::mir::BinOp::Shl,
+            BinOp::ShlUnchecked => rustc_middle::mir::BinOp::ShlUnchecked,
+            BinOp::Shr => rustc_middle::mir::BinOp::Shr,
+            BinOp::ShrUnchecked => rustc_middle::mir::BinOp::ShrUnchecked,
+            BinOp::Eq => rustc_middle::mir::BinOp::Eq,
+            BinOp::Lt => rustc_middle::mir::BinOp::Lt,
+            BinOp::Le => rustc_middle::mir::BinOp::Le,
+            BinOp::Ne => rustc_middle::mir::BinOp::Ne,
+            BinOp::Ge => rustc_middle::mir::BinOp::Ge,
+            BinOp::Gt => rustc_middle::mir::BinOp::Gt,
+            BinOp::Cmp => rustc_middle::mir::BinOp::Cmp,
+            BinOp::Offset => rustc_middle::mir::BinOp::Offset,
+        }
+    }
+}
+
 impl<T> RustcInternal for &T
 where
     T: RustcInternal,
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 7c12168b809..61bbedf9eec 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -19,7 +19,7 @@ use stable_mir::abi::{FnAbi, Layout, LayoutShape};
 use stable_mir::compiler_interface::Context;
 use stable_mir::mir::alloc::GlobalAlloc;
 use stable_mir::mir::mono::{InstanceDef, StaticDef};
-use stable_mir::mir::{Body, Place};
+use stable_mir::mir::{BinOp, Body, Place};
 use stable_mir::target::{MachineInfo, MachineSize};
 use stable_mir::ty::{
     AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
@@ -668,6 +668,15 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let tcx = tables.tcx;
         format!("{:?}", place.internal(&mut *tables, tcx))
     }
+
+    fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty {
+        let mut tables = self.0.borrow_mut();
+        let tcx = tables.tcx;
+        let rhs_internal = rhs.internal(&mut *tables, tcx);
+        let lhs_internal = lhs.internal(&mut *tables, tcx);
+        let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal);
+        ty.stable(&mut *tables)
+    }
 }
 
 pub struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>);
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 770624d331d..f721a04d6b9 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -654,6 +654,12 @@ impl SourceMap {
         })
     }
 
+    /// Extends the span to include any trailing whitespace, or returns the original
+    /// span if a `SpanSnippetError` was encountered.
+    pub fn span_extend_while_whitespace(&self, span: Span) -> Span {
+        self.span_extend_while(span, char::is_whitespace).unwrap_or(span)
+    }
+
     /// Extends the given `Span` to previous character while the previous character matches the predicate
     pub fn span_extend_prev_while(
         &self,
@@ -1034,12 +1040,9 @@ impl SourceMap {
     /// // ^^^^^^ input
     /// ```
     pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option<Span> {
-        let span = self.span_extend_while(mac_call, char::is_whitespace).ok()?;
-        let span = span.shrink_to_hi().with_hi(BytePos(span.hi().0.checked_add(1)?));
-        if self.span_to_snippet(span).as_deref() != Ok(";") {
-            return None;
-        }
-        Some(span)
+        let span = self.span_extend_while_whitespace(mac_call);
+        let span = self.next_point(span);
+        if self.span_to_snippet(span).as_deref() == Ok(";") { Some(span) } else { None }
     }
 }
 
diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml
index 1c8f1d03670..65aa9e40c8b 100644
--- a/compiler/rustc_symbol_mangling/Cargo.toml
+++ b/compiler/rustc_symbol_mangling/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags = "2.4.1"
 punycode = "0.4.0"
 rustc-demangle = "0.1.21"
 rustc_data_structures = { path = "../rustc_data_structures" }
@@ -15,7 +14,5 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-rustc_trait_selection = { path = "../rustc_trait_selection" }
 tracing = "0.1"
-twox-hash = "1.6.3"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index 0588af9bda7..b9509478702 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -114,7 +114,6 @@ mod v0;
 
 pub mod errors;
 pub mod test;
-pub mod typeid;
 
 /// This function computes the symbol name for the given `instance` and the
 /// given instantiating crate. That is, if you know that instance X is
diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs
deleted file mode 100644
index 7bd998294dd..00000000000
--- a/compiler/rustc_symbol_mangling/src/typeid.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-/// Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI
-/// support.
-///
-/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
-/// see design document in the tracking issue #89653.
-use bitflags::bitflags;
-use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt};
-use rustc_target::abi::call::FnAbi;
-use std::hash::Hasher;
-use twox_hash::XxHash64;
-
-bitflags! {
-    /// Options for typeid_for_fnabi.
-    #[derive(Clone, Copy, Debug)]
-    pub struct TypeIdOptions: u32 {
-        /// Generalizes pointers for compatibility with Clang
-        /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI
-        /// support.
-        const GENERALIZE_POINTERS = 1;
-        /// Generalizes repr(C) user-defined type for extern function types with the "C" calling
-        /// convention (or extern types) for cross-language LLVM CFI and  KCFI support.
-        const GENERALIZE_REPR_C = 2;
-        /// Normalizes integers for compatibility with Clang
-        /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM
-        /// CFI and  KCFI support.
-        const NORMALIZE_INTEGERS = 4;
-        /// Do not perform self type erasure for attaching a secondary type id to methods with their
-        /// concrete self so they can be used as function pointers.
-        ///
-        /// (This applies to typeid_for_instance only and should be used to attach a secondary type
-        /// id to methods during their declaration/definition so they match the type ids returned by
-        /// either typeid_for_instance or typeid_for_fnabi at call sites during code generation for
-        /// type membership tests when methods are used as function pointers.)
-        const USE_CONCRETE_SELF = 8;
-    }
-}
-
-mod typeid_itanium_cxx_abi;
-
-/// Returns a type metadata identifier for the specified FnAbi.
-pub fn typeid_for_fnabi<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
-    options: TypeIdOptions,
-) -> String {
-    typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options)
-}
-
-/// Returns a type metadata identifier for the specified Instance.
-pub fn typeid_for_instance<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    instance: Instance<'tcx>,
-    options: TypeIdOptions,
-) -> String {
-    typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options)
-}
-
-/// Returns a KCFI type metadata identifier for the specified FnAbi.
-pub fn kcfi_typeid_for_fnabi<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
-    options: TypeIdOptions,
-) -> u32 {
-    // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the
-    // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
-    let mut hash: XxHash64 = Default::default();
-    hash.write(typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes());
-    hash.finish() as u32
-}
-
-/// Returns a KCFI type metadata identifier for the specified Instance.
-pub fn kcfi_typeid_for_instance<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    instance: Instance<'tcx>,
-    mut options: TypeIdOptions,
-) -> u32 {
-    // KCFI support for Rust shares most of its implementation with the CFI support, with some key
-    // differences:
-    //
-    // 1. KCFI performs type tests differently and are implemented as different LLVM passes than CFI
-    //    to not require LTO.
-    // 2. KCFI has the limitation that a function or method may have one type id assigned only.
-    //
-    // Because of the limitation listed above (2), the current KCFI implementation (not CFI) does
-    // reifying of types (i.e., adds shims/trampolines for indirect calls in these cases) for:
-    //
-    // * Supporting casting between function items, closures, and Fn trait objects.
-    // * Supporting methods being cast as function pointers.
-    //
-    // This was implemented for KCFI support in #123106 and #123052 (which introduced the
-    // ReifyReason). The tracking issue for KCFI support for Rust is #123479.
-    if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) {
-        options.insert(TypeIdOptions::USE_CONCRETE_SELF);
-    }
-    // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the
-    // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
-    let mut hash: XxHash64 = Default::default();
-    hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes());
-    hash.finish() as u32
-}
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 0199e225c5f..cb255fabfe2 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -45,8 +45,8 @@ pub(super) fn mangle<'tcx>(
         ty::InstanceDef::ThreadLocalShim(_) => Some("tls"),
         ty::InstanceDef::VTableShim(_) => Some("vtable"),
         ty::InstanceDef::ReifyShim(_, None) => Some("reify"),
-        ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify-fnptr"),
-        ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify-vtable"),
+        ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"),
+        ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"),
 
         ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
         | ty::InstanceDef::CoroutineKindShim { .. } => Some("fn_once"),
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 8a96d810134..a778414d9d1 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -292,7 +292,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
             let kind_ty = args.kind_ty();
             let sig = args.coroutine_closure_sig().skip_binder();
 
-            let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
+            let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
+                && !args.tupled_upvars_ty().is_ty_var()
+            {
                 if !closure_kind.extends(goal_kind) {
                     return Err(NoSolution);
                 }
@@ -401,7 +403,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
             let kind_ty = args.kind_ty();
             let sig = args.coroutine_closure_sig().skip_binder();
             let mut nested = vec![];
-            let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
+            let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
+                && !args.tupled_upvars_ty().is_ty_var()
+            {
                 if !closure_kind.extends(goal_kind) {
                     return Err(NoSolution);
                 }
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index befde8f768a..ebf2a0d9621 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -487,6 +487,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             bug!();
         };
 
+        // Bail if the upvars haven't been constrained.
+        if tupled_upvars_ty.expect_ty().is_ty_var() {
+            return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+        }
+
         let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else {
             // We don't need to worry about the self type being an infer var.
             return Err(NoSolution);
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index af90372b97c..2067956d0f5 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1592,8 +1592,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     .tcx
                     .sess
                     .source_map()
-                    .span_extend_while(expr_span, char::is_whitespace)
-                    .unwrap_or(expr_span)
+                    .span_extend_while_whitespace(expr_span)
                     .shrink_to_hi()
                     .to(await_expr.span.shrink_to_hi());
                 err.span_suggestion(
@@ -4851,10 +4850,7 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
     let hir::IsAsync::Async(async_span) = sig.header.asyncness else {
         return None;
     };
-    let Ok(async_span) = tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace())
-    else {
-        return None;
-    };
+    let async_span = tcx.sess.source_map().span_extend_while_whitespace(async_span);
 
     let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
     let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else {
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 8d04fd45940..a5483c5bbc0 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1601,7 +1601,10 @@ fn confirm_closure_candidate<'cx, 'tcx>(
                 // If we know the kind and upvars, use that directly.
                 // Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
                 // the projection, like the `AsyncFn*` traits do.
-                let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() {
+                let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind()
+                    // Fall back to projection if upvars aren't constrained
+                    && !args.tupled_upvars_ty().is_ty_var()
+                {
                     sig.to_coroutine_given_kind_and_upvars(
                         tcx,
                         args.parent_args(),
@@ -1731,7 +1734,10 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
 
             let term = match item_name {
                 sym::CallOnceFuture | sym::CallRefFuture => {
-                    if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
+                    if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
+                        // Fall back to projection if upvars aren't constrained
+                        && !args.tupled_upvars_ty().is_ty_var()
+                    {
                         if !closure_kind.extends(goal_kind) {
                             bug!("we should not be confirming if the closure kind is not met");
                         }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index c1f340dfc7c..974e5ef0e16 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -400,39 +400,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 }
             }
             ty::CoroutineClosure(def_id, args) => {
+                let args = args.as_coroutine_closure();
                 let is_const = self.tcx().is_const_fn_raw(def_id);
-                match self.infcx.closure_kind(self_ty) {
-                    Some(closure_kind) => {
-                        let no_borrows = match self
-                            .infcx
-                            .shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
-                            .kind()
-                        {
-                            ty::Tuple(tys) => tys.is_empty(),
-                            ty::Error(_) => false,
-                            _ => bug!("tuple_fields called on non-tuple"),
-                        };
-                        // A coroutine-closure implements `FnOnce` *always*, since it may
-                        // always be called once. It additionally implements `Fn`/`FnMut`
-                        // only if it has no upvars (therefore no borrows from the closure
-                        // that would need to be represented with a lifetime) and if the
-                        // closure kind permits it.
-                        // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
-                        // if it takes all of its upvars by copy, and none by ref. This would
-                        // require us to record a bit more information during upvar analysis.
-                        if no_borrows && closure_kind.extends(kind) {
-                            candidates.vec.push(ClosureCandidate { is_const });
-                        } else if kind == ty::ClosureKind::FnOnce {
-                            candidates.vec.push(ClosureCandidate { is_const });
-                        }
+                if let Some(closure_kind) = self.infcx.closure_kind(self_ty)
+                    // Ambiguity if upvars haven't been constrained yet
+                    && !args.tupled_upvars_ty().is_ty_var()
+                {
+                    let no_borrows = match args.tupled_upvars_ty().kind() {
+                        ty::Tuple(tys) => tys.is_empty(),
+                        ty::Error(_) => false,
+                        _ => bug!("tuple_fields called on non-tuple"),
+                    };
+                    // A coroutine-closure implements `FnOnce` *always*, since it may
+                    // always be called once. It additionally implements `Fn`/`FnMut`
+                    // only if it has no upvars (therefore no borrows from the closure
+                    // that would need to be represented with a lifetime) and if the
+                    // closure kind permits it.
+                    // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
+                    // if it takes all of its upvars by copy, and none by ref. This would
+                    // require us to record a bit more information during upvar analysis.
+                    if no_borrows && closure_kind.extends(kind) {
+                        candidates.vec.push(ClosureCandidate { is_const });
+                    } else if kind == ty::ClosureKind::FnOnce {
+                        candidates.vec.push(ClosureCandidate { is_const });
                     }
-                    None => {
-                        if kind == ty::ClosureKind::FnOnce {
-                            candidates.vec.push(ClosureCandidate { is_const });
-                        } else {
-                            // This stays ambiguous until kind+upvars are determined.
-                            candidates.ambiguous = true;
-                        }
+                } else {
+                    if kind == ty::ClosureKind::FnOnce {
+                        candidates.vec.push(ClosureCandidate { is_const });
+                    } else {
+                        // This stays ambiguous until kind+upvars are determined.
+                        candidates.ambiguous = true;
                     }
                 }
             }
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs
index 8ed34fab54d..94c552199bc 100644
--- a/compiler/stable_mir/src/compiler_interface.rs
+++ b/compiler/stable_mir/src/compiler_interface.rs
@@ -8,7 +8,7 @@ use std::cell::Cell;
 use crate::abi::{FnAbi, Layout, LayoutShape};
 use crate::mir::alloc::{AllocId, GlobalAlloc};
 use crate::mir::mono::{Instance, InstanceDef, StaticDef};
-use crate::mir::{Body, Place};
+use crate::mir::{BinOp, Body, Place};
 use crate::target::MachineInfo;
 use crate::ty::{
     AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
@@ -211,6 +211,9 @@ pub trait Context {
 
     /// Get a debug string representation of a place.
     fn place_pretty(&self, place: &Place) -> String;
+
+    /// Get the resulting type of binary operation.
+    fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty;
 }
 
 // A thread local variable that stores a pointer to the tables mapping between TyCtxt
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 593b1868f26..1ad05633d62 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -1,3 +1,4 @@
+use crate::compiler_interface::with;
 use crate::mir::pretty::function_body;
 use crate::ty::{
     AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind,
@@ -337,42 +338,7 @@ impl BinOp {
     /// Return the type of this operation for the given input Ty.
     /// This function does not perform type checking, and it currently doesn't handle SIMD.
     pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty {
-        match self {
-            BinOp::Add
-            | BinOp::AddUnchecked
-            | BinOp::Sub
-            | BinOp::SubUnchecked
-            | BinOp::Mul
-            | BinOp::MulUnchecked
-            | BinOp::Div
-            | BinOp::Rem
-            | BinOp::BitXor
-            | BinOp::BitAnd
-            | BinOp::BitOr => {
-                assert_eq!(lhs_ty, rhs_ty);
-                assert!(lhs_ty.kind().is_primitive());
-                lhs_ty
-            }
-            BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => {
-                assert!(lhs_ty.kind().is_primitive());
-                assert!(rhs_ty.kind().is_primitive());
-                lhs_ty
-            }
-            BinOp::Offset => {
-                assert!(lhs_ty.kind().is_raw_ptr());
-                assert!(rhs_ty.kind().is_integral());
-                lhs_ty
-            }
-            BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
-                assert_eq!(lhs_ty, rhs_ty);
-                let lhs_kind = lhs_ty.kind();
-                assert!(lhs_kind.is_primitive() || lhs_kind.is_raw_ptr() || lhs_kind.is_fn_ptr());
-                Ty::bool_ty()
-            }
-            BinOp::Cmp => {
-                unimplemented!("Should cmp::Ordering be a RigidTy?");
-            }
-        }
+        with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty))
     }
 }