about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/inline.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/validate_attr.rs12
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs1
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs7
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs1
-rw-r--r--compiler/rustc_borrowck/src/lib.rs6
-rw-r--r--compiler/rustc_borrowck/src/places_conflict.rs2
-rw-r--r--compiler/rustc_borrowck/src/prefixes.rs3
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs9
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/value_and_place.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/debuginfo.rs13
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs54
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs71
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs53
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs9
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs48
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs33
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs53
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs22
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/statement.rs48
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/debuginfo.rs14
-rw-r--r--compiler/rustc_const_eval/src/check_consts/qualifs.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0608.md15
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0719.md14
-rw-r--r--compiler/rustc_expand/src/base.rs17
-rw-r--r--compiler/rustc_expand/src/expand.rs9
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs58
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs38
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs27
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs9
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs53
-rw-r--r--compiler/rustc_infer/src/infer/canonical/instantiate.rs74
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs18
-rw-r--r--compiler/rustc_infer/src/infer/outlives/test_type_match.rs6
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp1
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs45
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs26
-rw-r--r--compiler/rustc_middle/src/mir/statement.rs120
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs24
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs8
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs53
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs14
-rw-r--r--compiler/rustc_middle/src/ty/context.rs47
-rw-r--r--compiler/rustc_middle/src/ty/fold.rs14
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs4
-rw-r--r--compiler/rustc_middle/src/ty/region.rs36
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs22
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs12
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs6
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_place.rs3
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs77
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs5
-rw-r--r--compiler/rustc_mir_transform/src/add_subtyping_projections.rs3
-rw-r--r--compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs4
-rw-r--r--compiler/rustc_mir_transform/src/copy_prop.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs2
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs2
-rw-r--r--compiler/rustc_mir_transform/src/dead_store_elimination.rs71
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs4
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs5
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs8
-rw-r--r--compiler/rustc_mir_transform/src/known_panics_lint.rs2
-rw-r--r--compiler/rustc_mir_transform/src/large_enums.rs2
-rw-r--r--compiler/rustc_mir_transform/src/patch.rs2
-rw-r--r--compiler/rustc_mir_transform/src/promote_consts.rs3
-rw-r--r--compiler/rustc_mir_transform/src/ref_prop.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_place_mention.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_storage_markers.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_zsts.rs2
-rw-r--r--compiler/rustc_mir_transform/src/simplify.rs155
-rw-r--r--compiler/rustc_mir_transform/src/simplify_comparison_integral.rs4
-rw-r--r--compiler/rustc_mir_transform/src/sroa.rs10
-rw-r--r--compiler/rustc_mir_transform/src/strip_debuginfo.rs17
-rw-r--r--compiler/rustc_mir_transform/src/validate.rs24
-rw-r--r--compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs34
-rw-r--r--compiler/rustc_next_trait_solver/src/canonical/mod.rs12
-rw-r--r--compiler/rustc_next_trait_solver/src/placeholder.rs18
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs40
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs58
-rw-r--r--compiler/rustc_public/src/mir/body.rs11
-rw-r--r--compiler/rustc_public/src/mir/visit.rs2
-rw-r--r--compiler/rustc_public/src/unstable/convert/internal.rs3
-rw-r--r--compiler/rustc_public/src/unstable/convert/stable/mir.rs2
-rw-r--r--compiler/rustc_public/src/unstable/convert/stable/ty.rs7
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs5
-rw-r--r--compiler/rustc_session/src/session.rs3
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs5
-rw-r--r--compiler/rustc_target/src/spec/base/android.rs4
-rw-r--r--compiler/rustc_target/src/spec/base/linux.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs5
-rw-r--r--compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs13
-rw-r--r--compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs31
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs2
-rw-r--r--compiler/rustc_type_ir/src/binder.rs43
-rw-r--r--compiler/rustc_type_ir/src/canonical.rs23
-rw-r--r--compiler/rustc_type_ir/src/const_kind.rs4
-rw-r--r--compiler/rustc_type_ir/src/flags.rs19
-rw-r--r--compiler/rustc_type_ir/src/fold.rs24
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs6
-rw-r--r--compiler/rustc_type_ir/src/lib.rs17
-rw-r--r--compiler/rustc_type_ir/src/region_kind.rs4
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs4
-rw-r--r--compiler/rustc_type_ir/src/ty_kind/closure.rs5
124 files changed, 1461 insertions, 787 deletions
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
index a73430c9d00..a10ad27fcc6 100644
--- a/compiler/rustc_attr_parsing/src/attributes/inline.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -72,7 +72,11 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
     const PATH: &'static [Symbol] = &[sym::rustc_force_inline];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
+    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
+        Allow(Target::Fn),
+        Allow(Target::Method(MethodKind::Inherent)),
+    ]);
+
     const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason");
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs
index 7a7624893bd..927417f89f8 100644
--- a/compiler/rustc_attr_parsing/src/validate_attr.rs
+++ b/compiler/rustc_attr_parsing/src/validate_attr.rs
@@ -207,10 +207,9 @@ pub fn check_attribute_safety(
             }
         }
 
-        // - Normal builtin attribute, or any non-builtin attribute
-        // - All non-builtin attributes are currently considered safe; writing `#[unsafe(..)]` is
-        //   not permitted on non-builtin attributes or normal builtin attributes
-        (Some(AttributeSafety::Normal) | None, Safety::Unsafe(unsafe_span)) => {
+        // - Normal builtin attribute
+        // - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
+        (Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
             psess.dcx().emit_err(errors::InvalidAttrUnsafe {
                 span: unsafe_span,
                 name: attr_item.path.clone(),
@@ -224,9 +223,8 @@ pub fn check_attribute_safety(
         }
 
         // - Non-builtin attribute
-        // - No explicit `#[unsafe(..)]` written.
-        (None, Safety::Default) => {
-            // OK
+        (None, Safety::Unsafe(_) | Safety::Default) => {
+            // OK (not checked here)
         }
 
         (
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index efb622e2155..fa1be4cec1e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -3968,7 +3968,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                         }
                         ProjectionElem::ConstantIndex { .. }
                         | ProjectionElem::Subslice { .. }
-                        | ProjectionElem::Subtype(_)
                         | ProjectionElem::Index(_)
                         | ProjectionElem::UnwrapUnsafeBinder(_) => kind,
                     },
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 5642cdf87fd..e13c1c712d8 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -402,7 +402,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 ProjectionElem::Downcast(..) if opt.including_downcast => return None,
                 ProjectionElem::Downcast(..) => (),
                 ProjectionElem::OpaqueCast(..) => (),
-                ProjectionElem::Subtype(..) => (),
                 ProjectionElem::UnwrapUnsafeBinder(_) => (),
                 ProjectionElem::Field(field, _ty) => {
                     // FIXME(project-rfc_2229#36): print capture precisely here.
@@ -484,9 +483,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
                 }
                 ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
-                ProjectionElem::Subtype(ty)
-                | ProjectionElem::OpaqueCast(ty)
-                | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty),
+                ProjectionElem::OpaqueCast(ty) | ProjectionElem::UnwrapUnsafeBinder(ty) => {
+                    PlaceTy::from_ty(*ty)
+                }
                 ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
             },
         };
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 6d69040c711..727cf19cd8b 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -192,7 +192,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     [
                         ..,
                         ProjectionElem::Index(_)
-                        | ProjectionElem::Subtype(_)
                         | ProjectionElem::ConstantIndex { .. }
                         | ProjectionElem::OpaqueCast { .. }
                         | ProjectionElem::Subslice { .. }
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 268cb47fd12..a85dcf64d8d 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -278,7 +278,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
         mut map: impl FnMut(ty::RegionVid) -> ty::Region<'tcx>,
     ) -> Ty<'tcx> {
         fold_regions(tcx, self.inner, |r, depth| match r.kind() {
-            ty::ReBound(debruijn, br) => {
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) => {
                 debug_assert_eq!(debruijn, depth);
                 map(ty::RegionVid::from_usize(br.var.index()))
             }
@@ -1989,10 +1989,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                 },
                 // `OpaqueCast`: only transmutes the type, so no moves there.
                 // `Downcast`  : only changes information about a `Place` without moving.
-                // `Subtype`   : only transmutes the type, so no moves.
                 // So it's safe to skip these.
                 ProjectionElem::OpaqueCast(_)
-                | ProjectionElem::Subtype(_)
                 | ProjectionElem::Downcast(_, _)
                 | ProjectionElem::UnwrapUnsafeBinder(_) => (),
             }
@@ -2218,7 +2216,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
         for (place_base, elem) in place.iter_projections().rev() {
             match elem {
                 ProjectionElem::Index(_/*operand*/) |
-                ProjectionElem::Subtype(_) |
                 ProjectionElem::OpaqueCast(_) |
                 ProjectionElem::ConstantIndex { .. } |
                 // assigning to P[i] requires P to be valid.
@@ -2610,7 +2607,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                     | ProjectionElem::Index(..)
                     | ProjectionElem::ConstantIndex { .. }
                     | ProjectionElem::Subslice { .. }
-                    | ProjectionElem::Subtype(..)
                     | ProjectionElem::OpaqueCast { .. }
                     | ProjectionElem::Downcast(..)
                     | ProjectionElem::UnwrapUnsafeBinder(_) => {
diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs
index cf3e82426e8..60676ac6b86 100644
--- a/compiler/rustc_borrowck/src/places_conflict.rs
+++ b/compiler/rustc_borrowck/src/places_conflict.rs
@@ -249,7 +249,6 @@ fn place_components_conflict<'tcx>(
                 | (ProjectionElem::ConstantIndex { .. }, _, _)
                 | (ProjectionElem::Subslice { .. }, _, _)
                 | (ProjectionElem::OpaqueCast { .. }, _, _)
-                | (ProjectionElem::Subtype(_), _, _)
                 | (ProjectionElem::Downcast { .. }, _, _)
                 | (ProjectionElem::UnwrapUnsafeBinder(_), _, _) => {
                     // Recursive case. This can still be disjoint on a
@@ -510,7 +509,6 @@ fn place_projection_conflict<'tcx>(
             | ProjectionElem::Field(..)
             | ProjectionElem::Index(..)
             | ProjectionElem::ConstantIndex { .. }
-            | ProjectionElem::Subtype(_)
             | ProjectionElem::OpaqueCast { .. }
             | ProjectionElem::Subslice { .. }
             | ProjectionElem::Downcast(..),
diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs
index 83cca38a5c0..9e51264d8ed 100644
--- a/compiler/rustc_borrowck/src/prefixes.rs
+++ b/compiler/rustc_borrowck/src/prefixes.rs
@@ -77,9 +77,6 @@ impl<'tcx> Iterator for Prefixes<'tcx> {
                         | ProjectionElem::Index(_) => {
                             cursor = cursor_base;
                         }
-                        ProjectionElem::Subtype(..) => {
-                            panic!("Subtype projection is not allowed before borrow check")
-                        }
                         ProjectionElem::Deref => {
                             match self.kind {
                                 PrefixSet::Shallow => {
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 606d3d95d9e..781fb5ba113 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1558,6 +1558,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             ),
                         }
                     }
+                    CastKind::Subtype => {
+                        bug!("CastKind::Subtype shouldn't exist in borrowck")
+                    }
                 }
             }
 
@@ -1882,9 +1885,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 )
                 .unwrap();
             }
-            ProjectionElem::Subtype(_) => {
-                bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
-            }
         }
     }
 }
@@ -2412,9 +2412,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 | ProjectionElem::UnwrapUnsafeBinder(_) => {
                     // other field access
                 }
-                ProjectionElem::Subtype(_) => {
-                    bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
-                }
             }
         }
     }
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 2cc5b82ddd3..ebf2ccf74de 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -789,7 +789,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
                     let operand = codegen_operand(fx, operand);
                     crate::unsize::coerce_unsized_into(fx, operand, lval);
                 }
-                Rvalue::Cast(CastKind::Transmute, ref operand, _to_ty) => {
+                Rvalue::Cast(CastKind::Transmute | CastKind::Subtype, ref operand, _to_ty) => {
                     let operand = codegen_operand(fx, operand);
                     lval.write_cvalue_transmute(fx, operand);
                 }
@@ -996,7 +996,7 @@ pub(crate) fn codegen_place<'tcx>(
                 cplace = cplace.place_deref(fx);
             }
             PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"),
-            PlaceElem::Subtype(ty) | PlaceElem::UnwrapUnsafeBinder(ty) => {
+            PlaceElem::UnwrapUnsafeBinder(ty) => {
                 cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty));
             }
             PlaceElem::Field(field, _ty) => {
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index 04e10cf1708..db9b80c0f6a 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -660,7 +660,7 @@ impl<'tcx> CPlace<'tcx> {
         }
     }
 
-    /// Used for `ProjectionElem::Subtype`, `ty` has to be monomorphized before
+    /// Used for `ProjectionElem::UnwrapUnsafeBinder`, `ty` has to be monomorphized before
     /// passed on.
     pub(crate) fn place_transmute_type(
         self,
diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs
index 4c8585192a1..0f015cc23f5 100644
--- a/compiler/rustc_codegen_gcc/src/debuginfo.rs
+++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs
@@ -29,13 +29,24 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
         _variable_alloca: Self::Value,
         _direct_offset: Size,
         _indirect_offsets: &[Size],
-        _fragment: Option<Range<Size>>,
+        _fragment: &Option<Range<Size>>,
     ) {
         // FIXME(tempdragon): Not sure if this is correct, probably wrong but still keep it here.
         #[cfg(feature = "master")]
         _variable_alloca.set_location(_dbg_loc);
     }
 
+    fn dbg_var_value(
+        &mut self,
+        _dbg_var: Self::DIVariable,
+        _dbg_loc: Self::DILocation,
+        _value: Self::Value,
+        _direct_offset: Size,
+        _indirect_offsets: &[Size],
+        _fragment: &Option<Range<Size>>,
+    ) {
+    }
+
     fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
         // TODO(antoyo): insert reference to gdb debug scripts section global.
     }
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index cc09fa5b69b..93b1cf272ab 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -538,9 +538,7 @@ pub(crate) fn inline_asm_call<'ll>(
             bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)),
         )
     }));
-    let md = unsafe { llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()) };
-    let md = bx.get_metadata_value(md);
-    llvm::LLVMSetMetadata(call, kind, md);
+    bx.cx.set_metadata_node(call, kind, &srcloc);
 
     Some(call)
 }
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 5271d0b4bb8..e7cb18ab22d 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -1,12 +1,12 @@
 use std::borrow::{Borrow, Cow};
+use std::iter;
 use std::ops::Deref;
-use std::{iter, ptr};
 
 use rustc_ast::expand::typetree::FncTree;
 pub(crate) mod autodiff;
 pub(crate) mod gpu_offload;
 
-use libc::{c_char, c_uint, size_t};
+use libc::{c_char, c_uint};
 use rustc_abi as abi;
 use rustc_abi::{Align, Size, WrappingRange};
 use rustc_codegen_ssa::MemFlags;
@@ -396,10 +396,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             md.push(weight(is_cold));
         }
 
-        unsafe {
-            let md_node = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len() as size_t);
-            self.cx.set_metadata(switch, llvm::MD_prof, md_node);
-        }
+        self.cx.set_metadata_node(switch, llvm::MD_prof, &md);
     }
 
     fn invoke(
@@ -801,22 +798,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             return;
         }
 
-        unsafe {
-            let llty = self.cx.val_ty(load);
-            let md = [
-                llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)),
-                llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))),
-            ];
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len());
-            self.set_metadata(load, llvm::MD_range, md);
-        }
+        let llty = self.cx.val_ty(load);
+        let md = [
+            llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)),
+            llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))),
+        ];
+        self.set_metadata_node(load, llvm::MD_range, &md);
     }
 
     fn nonnull_metadata(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_nonnull, md);
-        }
+        self.set_metadata_node(load, llvm::MD_nonnull, &[]);
     }
 
     fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value {
@@ -865,8 +856,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
                     //
                     // [1]: https://llvm.org/docs/LangRef.html#store-instruction
                     let one = llvm::LLVMValueAsMetadata(self.cx.const_i32(1));
-                    let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, &one, 1);
-                    self.set_metadata(store, llvm::MD_nontemporal, md);
+                    self.set_metadata_node(store, llvm::MD_nontemporal, &[one]);
                 }
             }
             store
@@ -1381,10 +1371,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn set_invariant_load(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_invariant_load, md);
-        }
+        self.set_metadata_node(load, llvm::MD_invariant_load, &[]);
     }
 
     fn lifetime_start(&mut self, ptr: &'ll Value, size: Size) {
@@ -1528,25 +1515,16 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
 }
 impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
     fn align_metadata(&mut self, load: &'ll Value, align: Align) {
-        unsafe {
-            let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len());
-            self.set_metadata(load, llvm::MD_align, md);
-        }
+        let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
+        self.set_metadata_node(load, llvm::MD_align, &md);
     }
 
     fn noundef_metadata(&mut self, load: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(load, llvm::MD_noundef, md);
-        }
+        self.set_metadata_node(load, llvm::MD_noundef, &[]);
     }
 
     pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) {
-        unsafe {
-            let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0);
-            self.set_metadata(inst, llvm::MD_unpredictable, md);
-        }
+        self.set_metadata_node(inst, llvm::MD_unpredictable, &[]);
     }
 }
 impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 40375ef6510..b4ca85a26c8 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -494,16 +494,7 @@ impl<'ll> CodegenCx<'ll, '_> {
                 let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len());
                 let alloc = self.create_metadata(bytes);
                 let data = [section, alloc];
-                let meta =
-                    unsafe { llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()) };
-                let val = self.get_metadata_value(meta);
-                unsafe {
-                    llvm::LLVMAddNamedMetadataOperand(
-                        self.llmod,
-                        c"wasm.custom_sections".as_ptr(),
-                        val,
-                    )
-                };
+                self.module_add_named_metadata_node(self.llmod(), c"wasm.custom_sections", &data);
             }
         } else {
             base::set_link_section(g, attrs);
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index b1da6f7c740..922575dd63c 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -34,7 +34,7 @@ use smallvec::SmallVec;
 use crate::back::write::to_llvm_code_model;
 use crate::callee::get_fn;
 use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
-use crate::llvm::{Metadata, MetadataKindId};
+use crate::llvm::{Metadata, MetadataKindId, Module};
 use crate::type_::Type;
 use crate::value::Value;
 use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util};
@@ -495,14 +495,7 @@ pub(crate) unsafe fn create_module<'ll>(
         format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"));
 
     let name_metadata = cx.create_metadata(rustc_producer.as_bytes());
-
-    unsafe {
-        llvm::LLVMAddNamedMetadataOperand(
-            llmod,
-            c"llvm.ident".as_ptr(),
-            &cx.get_metadata_value(llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)),
-        );
-    }
+    cx.module_add_named_metadata_node(llmod, c"llvm.ident", &[name_metadata]);
 
     // Emit RISC-V specific target-abi metadata
     // to workaround lld as the LTO plugin not
@@ -1002,6 +995,11 @@ impl CodegenCx<'_, '_> {
 }
 
 impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
+    /// Wrapper for `LLVMMDNodeInContext2`, i.e. `llvm::MDNode::get`.
+    pub(crate) fn md_node_in_context(&self, md_list: &[&'ll Metadata]) -> &'ll Metadata {
+        unsafe { llvm::LLVMMDNodeInContext2(self.llcx(), md_list.as_ptr(), md_list.len()) }
+    }
+
     /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`.
     pub(crate) fn set_metadata<'a>(
         &self,
@@ -1012,6 +1010,61 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
         let node = self.get_metadata_value(md);
         llvm::LLVMSetMetadata(val, kind_id, node);
     }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`)
+    /// - `LLVMSetMetadata` (to set that node as metadata of `kind_id` for `instruction`)
+    pub(crate) fn set_metadata_node(
+        &self,
+        instruction: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        self.set_metadata(instruction, kind_id, md);
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMMetadataAsValue` (to adapt that node to an `llvm::Value`)
+    /// - `LLVMAddNamedMetadataOperand` (to set that node as metadata of `kind_name` for `module`)
+    pub(crate) fn module_add_named_metadata_node(
+        &self,
+        module: &'ll Module,
+        kind_name: &CStr,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        let md_as_val = self.get_metadata_value(md);
+        unsafe { llvm::LLVMAddNamedMetadataOperand(module, kind_name.as_ptr(), md_as_val) };
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMRustGlobalAddMetadata` (to set that node as metadata of `kind_id` for `global`)
+    pub(crate) fn global_add_metadata_node(
+        &self,
+        global: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        unsafe { llvm::LLVMRustGlobalAddMetadata(global, kind_id, md) };
+    }
+
+    /// Helper method for the sequence of calls:
+    /// - `LLVMMDNodeInContext2` (to create an `llvm::MDNode` from a list of metadata)
+    /// - `LLVMGlobalSetMetadata` (to set that node as metadata of `kind_id` for `global`)
+    pub(crate) fn global_set_metadata_node(
+        &self,
+        global: &'ll Value,
+        kind_id: MetadataKindId,
+        md_list: &[&'ll Metadata],
+    ) {
+        let md = self.md_node_in_context(md_list);
+        unsafe { llvm::LLVMGlobalSetMetadata(global, kind_id, md) };
+    }
 }
 
 impl HasDataLayout for CodegenCx<'_, '_> {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs
index 40842915222..52d04625749 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs
@@ -35,3 +35,6 @@ declare_constant!(DW_OP_plus_uconst: u64);
 /// Double-checked by a static assertion in `RustWrapper.cpp`.
 #[allow(non_upper_case_globals)]
 pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000;
+// It describes the actual value of a source variable which might not exist in registers or in memory.
+#[allow(non_upper_case_globals)]
+pub(crate) const DW_OP_stack_value: u64 = 0x9f;
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index bc20c759413..2f9e7cae54f 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -1607,17 +1607,11 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
     let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref);
     let typeid = cx.create_metadata(trait_ref_typeid.as_bytes());
 
-    unsafe {
-        let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
-        llvm::LLVMRustGlobalAddMetadata(
-            vtable,
-            llvm::MD_type,
-            llvm::LLVMMDNodeInContext2(cx.llcx, v.as_ptr(), v.len()),
-        );
-        let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64));
-        let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1);
-        llvm::LLVMGlobalSetMetadata(vtable, llvm::MD_vcall_visibility, vcall_visibility_metadata);
-    }
+    let type_ = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
+    cx.global_add_metadata_node(vtable, llvm::MD_type, &type_);
+
+    let vcall_visibility = [llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64))];
+    cx.global_set_metadata_node(vtable, llvm::MD_vcall_visibility, &vcall_visibility);
 }
 
 /// Creates debug information for the given vtable, which is for the
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index af64e4ebed0..c6ad1c2e18e 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -156,7 +156,7 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
         variable_alloca: Self::Value,
         direct_offset: Size,
         indirect_offsets: &[Size],
-        fragment: Option<Range<Size>>,
+        fragment: &Option<Range<Size>>,
     ) {
         use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst};
 
@@ -187,7 +187,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
             llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
         };
         unsafe {
-            // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
             llvm::LLVMDIBuilderInsertDeclareRecordAtEnd(
                 di_builder,
                 variable_alloca,
@@ -199,6 +198,56 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
         };
     }
 
+    fn dbg_var_value(
+        &mut self,
+        dbg_var: &'ll DIVariable,
+        dbg_loc: &'ll DILocation,
+        value: Self::Value,
+        direct_offset: Size,
+        indirect_offsets: &[Size],
+        fragment: &Option<Range<Size>>,
+    ) {
+        use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value};
+
+        // Convert the direct and indirect offsets and fragment byte range to address ops.
+        let mut addr_ops = SmallVec::<[u64; 8]>::new();
+
+        if direct_offset.bytes() > 0 {
+            addr_ops.push(DW_OP_plus_uconst);
+            addr_ops.push(direct_offset.bytes() as u64);
+            addr_ops.push(DW_OP_stack_value);
+        }
+        for &offset in indirect_offsets {
+            addr_ops.push(DW_OP_deref);
+            if offset.bytes() > 0 {
+                addr_ops.push(DW_OP_plus_uconst);
+                addr_ops.push(offset.bytes() as u64);
+            }
+        }
+        if let Some(fragment) = fragment {
+            // `DW_OP_LLVM_fragment` takes as arguments the fragment's
+            // offset and size, both of them in bits.
+            addr_ops.push(DW_OP_LLVM_fragment);
+            addr_ops.push(fragment.start.bits() as u64);
+            addr_ops.push((fragment.end - fragment.start).bits() as u64);
+        }
+
+        let di_builder = DIB(self.cx());
+        let addr_expr = unsafe {
+            llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
+        };
+        unsafe {
+            llvm::LLVMDIBuilderInsertDbgValueRecordAtEnd(
+                di_builder,
+                value,
+                dbg_var,
+                addr_expr,
+                dbg_loc,
+                self.llbb(),
+            );
+        }
+    }
+
     fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) {
         unsafe {
             llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc);
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index e9f92267a7d..7fbba029407 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1991,6 +1991,15 @@ unsafe extern "C" {
         Block: &'ll BasicBlock,
     ) -> &'ll DbgRecord;
 
+    pub(crate) fn LLVMDIBuilderInsertDbgValueRecordAtEnd<'ll>(
+        Builder: &DIBuilder<'ll>,
+        Val: &'ll Value,
+        VarInfo: &'ll Metadata,
+        Expr: &'ll Metadata,
+        DebugLoc: &'ll Metadata,
+        Block: &'ll BasicBlock,
+    ) -> &'ll DbgRecord;
+
     pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>(
         Builder: &DIBuilder<'ll>,
         Scope: &'ll Metadata,
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index 5b97898a4b8..1e5cf8374e3 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -302,26 +302,14 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
 impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn add_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
         let typeid_metadata = self.create_metadata(typeid);
-        unsafe {
-            let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
-            llvm::LLVMRustGlobalAddMetadata(
-                function,
-                llvm::MD_type,
-                llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
-            )
-        }
+        let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
+        self.global_add_metadata_node(function, llvm::MD_type, &v);
     }
 
     fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
         let typeid_metadata = self.create_metadata(typeid);
-        unsafe {
-            let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
-            llvm::LLVMGlobalSetMetadata(
-                function,
-                llvm::MD_type,
-                llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()),
-            )
-        }
+        let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
+        self.global_set_metadata_node(function, llvm::MD_type, &v);
     }
 
     fn typeid_metadata(&self, typeid: &[u8]) -> Option<&'ll Metadata> {
@@ -329,32 +317,12 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     }
 
     fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
-        let kcfi_type_metadata = self.const_u32(kcfi_typeid);
-        unsafe {
-            llvm::LLVMRustGlobalAddMetadata(
-                function,
-                llvm::MD_kcfi_type,
-                llvm::LLVMMDNodeInContext2(
-                    self.llcx,
-                    &llvm::LLVMValueAsMetadata(kcfi_type_metadata),
-                    1,
-                ),
-            )
-        }
+        let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))];
+        self.global_add_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata);
     }
 
     fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
-        let kcfi_type_metadata = self.const_u32(kcfi_typeid);
-        unsafe {
-            llvm::LLVMGlobalSetMetadata(
-                function,
-                llvm::MD_kcfi_type,
-                llvm::LLVMMDNodeInContext2(
-                    self.llcx,
-                    &llvm::LLVMValueAsMetadata(kcfi_type_metadata),
-                    1,
-                ),
-            )
-        }
+        let kcfi_type_metadata = [llvm::LLVMValueAsMetadata(self.const_u32(kcfi_typeid))];
+        self.global_set_metadata_node(function, llvm::MD_kcfi_type, &kcfi_type_metadata);
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index e644a43f883..ac123143738 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -17,7 +17,6 @@ use rustc_middle::middle::exported_symbols::{
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
-use rustc_span::sym;
 use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld};
 use tracing::{debug, warn};
 
@@ -1324,37 +1323,7 @@ struct WasmLd<'a> {
 
 impl<'a> WasmLd<'a> {
     fn new(cmd: Command, sess: &'a Session) -> WasmLd<'a> {
-        // If the atomics feature is enabled for wasm then we need a whole bunch
-        // of flags:
-        //
-        // * `--shared-memory` - the link won't even succeed without this, flags
-        //   the one linear memory as `shared`
-        //
-        // * `--max-memory=1G` - when specifying a shared memory this must also
-        //   be specified. We conservatively choose 1GB but users should be able
-        //   to override this with `-C link-arg`.
-        //
-        // * `--import-memory` - it doesn't make much sense for memory to be
-        //   exported in a threaded module because typically you're
-        //   sharing memory and instantiating the module multiple times. As a
-        //   result if it were exported then we'd just have no sharing.
-        //
-        // On wasm32-unknown-unknown, we also export symbols for glue code to use:
-        //    * `--export=*tls*` - when `#[thread_local]` symbols are used these
-        //      symbols are how the TLS segments are initialized and configured.
-        let mut wasm_ld = WasmLd { cmd, sess };
-        if sess.target_features.contains(&sym::atomics) {
-            wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]);
-            if sess.target.os == "unknown" || sess.target.os == "none" {
-                wasm_ld.link_args(&[
-                    "--export=__wasm_init_tls",
-                    "--export=__tls_size",
-                    "--export=__tls_align",
-                    "--export=__tls_base",
-                ]);
-            }
-        }
-        wasm_ld
+        WasmLd { cmd, sess }
     }
 }
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index c2c023af090..0a37a904193 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -150,10 +150,6 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx>
                     {
                         layout.for_variant(self.fx.cx, vidx)
                     }
-                    mir::PlaceElem::Subtype(subtype_ty) => {
-                        let subtype_ty = self.fx.monomorphize(subtype_ty);
-                        self.fx.cx.layout_of(subtype_ty)
-                    }
                     _ => {
                         self.locals[place_ref.local] = LocalKind::Memory;
                         return;
@@ -264,6 +260,10 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer
             PlaceContext::MutatingUse(MutatingUseContext::Yield) => bug!(),
         }
     }
+
+    fn visit_statement_debuginfo(&mut self, _: &mir::StmtDebugInfo<'tcx>, _: Location) {
+        // Debuginfo does not generate actual code.
+    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index b2dc4fe32b0..e371f1a7623 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1320,6 +1320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             for statement in &data.statements {
                 self.codegen_statement(bx, statement);
             }
+            self.codegen_stmt_debuginfos(bx, &data.after_last_stmt_debuginfos);
 
             let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
             if let MergingSucc::False = merging_succ {
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index b8f635ab781..0c9acf087f9 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -253,6 +253,53 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         spill_slot
     }
 
+    // Indicates that local is set to a new value. The `layout` and `projection` are used to
+    // calculate the offset.
+    pub(crate) fn debug_new_val_to_local(
+        &self,
+        bx: &mut Bx,
+        local: mir::Local,
+        base: PlaceRef<'tcx, Bx::Value>,
+        projection: &[mir::PlaceElem<'tcx>],
+    ) {
+        let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
+        if !full_debug_info {
+            return;
+        }
+
+        let vars = match &self.per_local_var_debug_info {
+            Some(per_local) => &per_local[local],
+            None => return,
+        };
+
+        let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
+            calculate_debuginfo_offset(bx, projection, base.layout);
+        for var in vars.iter() {
+            let Some(dbg_var) = var.dbg_var else {
+                continue;
+            };
+            let Some(dbg_loc) = self.dbg_loc(var.source_info) else {
+                continue;
+            };
+            bx.dbg_var_value(
+                dbg_var,
+                dbg_loc,
+                base.val.llval,
+                direct_offset,
+                &indirect_offsets,
+                &var.fragment,
+            );
+        }
+    }
+
+    pub(crate) fn debug_poison_to_local(&self, bx: &mut Bx, local: mir::Local) {
+        let ty = self.monomorphize(self.mir.local_decls[local].ty);
+        let layout = bx.cx().layout_of(ty);
+        let to_backend_ty = bx.cx().immediate_backend_type(layout);
+        let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
+        self.debug_new_val_to_local(bx, local, place_ref, &[]);
+    }
+
     /// Apply debuginfo and/or name, after creating the `alloca` for a local,
     /// or initializing the local with an operand (whichever applies).
     pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
@@ -424,7 +471,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 alloca.val.llval,
                 Size::ZERO,
                 &[Size::ZERO],
-                var.fragment,
+                &var.fragment,
             );
         } else {
             bx.dbg_var_addr(
@@ -433,7 +480,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 base.val.llval,
                 direct_offset,
                 &indirect_offsets,
-                var.fragment,
+                &var.fragment,
             );
         }
     }
@@ -455,7 +502,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
                 bx.clear_dbg_loc();
 
-                bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment);
+                bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], &fragment);
             }
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index d851c332980..88a8e2a844c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -71,16 +71,23 @@ pub enum OperandValue<V> {
 }
 
 impl<V: CodegenObject> OperandValue<V> {
+    /// Return the data pointer and optional metadata as backend values
+    /// if this value can be treat as a pointer.
+    pub(crate) fn try_pointer_parts(self) -> Option<(V, Option<V>)> {
+        match self {
+            OperandValue::Immediate(llptr) => Some((llptr, None)),
+            OperandValue::Pair(llptr, llextra) => Some((llptr, Some(llextra))),
+            OperandValue::Ref(_) | OperandValue::ZeroSized => None,
+        }
+    }
+
     /// Treat this value as a pointer and return the data pointer and
     /// optional metadata as backend values.
     ///
     /// If you're making a place, use [`Self::deref`] instead.
     pub(crate) fn pointer_parts(self) -> (V, Option<V>) {
-        match self {
-            OperandValue::Immediate(llptr) => (llptr, None),
-            OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
-            _ => bug!("OperandValue cannot be a pointer: {self:?}"),
-        }
+        self.try_pointer_parts()
+            .unwrap_or_else(|| bug!("OperandValue cannot be a pointer: {self:?}"))
     }
 
     /// Treat this value as a pointer and return the place to which it points.
@@ -956,11 +963,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             let layout = o.layout.for_variant(bx.cx(), vidx);
                             o = OperandRef { val: o.val, layout }
                         }
-                        mir::PlaceElem::Subtype(subtype_ty) => {
-                            let subtype_ty = self.monomorphize(subtype_ty);
-                            let layout = self.cx.layout_of(subtype_ty);
-                            o = OperandRef { val: o.val, layout }
-                        }
                         _ => return None,
                     }
                 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 0090be9fdef..50f56f913a5 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -347,7 +347,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 mir::ProjectionElem::OpaqueCast(ty) => {
                     bug!("encountered OpaqueCast({ty}) in codegen")
                 }
-                mir::ProjectionElem::Subtype(ty) => cg_base.project_type(bx, self.monomorphize(ty)),
                 mir::ProjectionElem::UnwrapUnsafeBinder(ty) => {
                     cg_base.project_type(bx, self.monomorphize(ty))
                 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 0a4b0f8d494..d629003bff5 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -86,7 +86,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
             }
 
-            mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, _ty) => {
+            mir::Rvalue::Cast(
+                mir::CastKind::Transmute | mir::CastKind::Subtype,
+                ref operand,
+                _ty,
+            ) => {
                 let src = self.codegen_operand(bx, operand);
                 self.codegen_transmute(bx, src, dest);
             }
@@ -486,7 +490,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                 bug!("Unsupported cast of {operand:?} to {cast:?}");
                             })
                     }
-                    mir::CastKind::Transmute => {
+                    mir::CastKind::Transmute | mir::CastKind::Subtype => {
                         self.codegen_transmute_operand(bx, operand, cast)
                     }
                 };
diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs
index 0a50d7f18db..88590b6271b 100644
--- a/compiler/rustc_codegen_ssa/src/mir/statement.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs
@@ -1,4 +1,4 @@
-use rustc_middle::mir::{self, NonDivergingIntrinsic};
+use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
 use rustc_middle::span_bug;
 use tracing::instrument;
 
@@ -8,6 +8,7 @@ use crate::traits::*;
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     #[instrument(level = "debug", skip(self, bx))]
     pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
+        self.codegen_stmt_debuginfos(bx, &statement.debuginfos);
         self.set_debug_loc(bx, statement.source_info);
         match statement.kind {
             mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
@@ -101,4 +102,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             | mir::StatementKind::Nop => {}
         }
     }
+
+    pub(crate) fn codegen_stmt_debuginfo(&mut self, bx: &mut Bx, debuginfo: &StmtDebugInfo<'tcx>) {
+        match debuginfo {
+            StmtDebugInfo::AssignRef(dest, place) => {
+                let local_ref = match self.locals[place.local] {
+                    // For an rvalue like `&(_1.1)`, when `BackendRepr` is `BackendRepr::Memory`, we allocate a block of memory to this place.
+                    // The place is an indirect pointer, we can refer to it directly.
+                    LocalRef::Place(place_ref) => Some((place_ref, place.projection.as_slice())),
+                    // For an rvalue like `&((*_1).1)`, we are calculating the address of `_1.1`.
+                    // The deref projection is no-op here.
+                    LocalRef::Operand(operand_ref) if place.is_indirect_first_projection() => {
+                        Some((operand_ref.deref(bx.cx()), &place.projection[1..]))
+                    }
+                    // For an rvalue like `&1`, when `BackendRepr` is `BackendRepr::Scalar`,
+                    // we cannot get the address.
+                    // N.B. `non_ssa_locals` returns that this is an SSA local.
+                    LocalRef::Operand(_) => None,
+                    LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => None,
+                }
+                .filter(|(_, projection)| {
+                    // Drop unsupported projections.
+                    projection.iter().all(|p| p.can_use_in_debuginfo())
+                });
+                if let Some((base, projection)) = local_ref {
+                    self.debug_new_val_to_local(bx, *dest, base, projection);
+                } else {
+                    // If the address cannot be calculated, use poison to indicate that the value has been optimized out.
+                    self.debug_poison_to_local(bx, *dest);
+                }
+            }
+            StmtDebugInfo::InvalidAssign(local) => {
+                self.debug_poison_to_local(bx, *local);
+            }
+        }
+    }
+
+    pub(crate) fn codegen_stmt_debuginfos(
+        &mut self,
+        bx: &mut Bx,
+        debuginfos: &[StmtDebugInfo<'tcx>],
+    ) {
+        for debuginfo in debuginfos {
+            self.codegen_stmt_debuginfo(bx, debuginfo);
+        }
+    }
 }
diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
index b9d4950e0ad..a4da6c915de 100644
--- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
@@ -77,7 +77,19 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
         indirect_offsets: &[Size],
         // Byte range in the `dbg_var` covered by this fragment,
         // if this is a fragment of a composite `DIVariable`.
-        fragment: Option<Range<Size>>,
+        fragment: &Option<Range<Size>>,
+    );
+    fn dbg_var_value(
+        &mut self,
+        dbg_var: Self::DIVariable,
+        dbg_loc: Self::DILocation,
+        value: Self::Value,
+        direct_offset: Size,
+        // NB: each offset implies a deref (i.e. they're steps in a pointer chain).
+        indirect_offsets: &[Size],
+        // Byte range in the `dbg_var` covered by this fragment,
+        // if this is a fragment of a composite `DIVariable`.
+        fragment: &Option<Range<Size>>,
     );
     fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
     fn clear_dbg_loc(&mut self);
diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
index 34d1fdd8c86..8a6827bca2b 100644
--- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
@@ -293,7 +293,6 @@ where
             ProjectionElem::Index(index) if in_local(index) => return true,
 
             ProjectionElem::Deref
-            | ProjectionElem::Subtype(_)
             | ProjectionElem::Field(_, _)
             | ProjectionElem::OpaqueCast(_)
             | ProjectionElem::ConstantIndex { .. }
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 0075740e031..b058d4b8ad4 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -133,7 +133,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 }
             }
 
-            CastKind::Transmute => {
+            CastKind::Transmute | CastKind::Subtype => {
                 assert!(src.layout.is_sized());
                 assert!(dest.layout.is_sized());
                 assert_eq!(cast_ty, dest.layout.ty); // we otherwise ignore `cast_ty` enirely...
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index d05871bfc77..2fd1657f6ba 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -395,8 +395,6 @@ where
                 span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
             }
             UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?,
-            // We don't want anything happening here, this is here as a dummy.
-            Subtype(_) => base.transmute(base.layout(), self)?,
             Field(field, _) => self.project_field(base, field)?,
             Downcast(_, variant) => self.project_downcast(base, variant)?,
             Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
diff --git a/compiler/rustc_error_codes/src/error_codes/E0608.md b/compiler/rustc_error_codes/src/error_codes/E0608.md
index d0ebc3a26f0..3c29484f575 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0608.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0608.md
@@ -1,5 +1,5 @@
-An attempt to use index on a type which doesn't implement the `std::ops::Index`
-trait was performed.
+Attempted to index a value whose type doesn't implement the
+`std::ops::Index` trait.
 
 Erroneous code example:
 
@@ -7,8 +7,8 @@ Erroneous code example:
 0u8[2]; // error: cannot index into a value of type `u8`
 ```
 
-To be able to index into a type it needs to implement the `std::ops::Index`
-trait. Example:
+Only values with types that implement the `std::ops::Index` trait
+can be indexed with square brackets. Example:
 
 ```
 let v: Vec<u8> = vec![0, 1, 2, 3];
@@ -16,3 +16,10 @@ let v: Vec<u8> = vec![0, 1, 2, 3];
 // The `Vec` type implements the `Index` trait so you can do:
 println!("{}", v[2]);
 ```
+
+Tuples and structs are indexed with dot (`.`), not with brackets (`[]`),
+and tuple element names are their positions:
+```ignore(pseudo code)
+// this (pseudo code) expression is true for any tuple:
+tuple == (tuple.0, tuple.1, ...)
+```
diff --git a/compiler/rustc_error_codes/src/error_codes/E0719.md b/compiler/rustc_error_codes/src/error_codes/E0719.md
index cd981db1058..6aec38b42a3 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0719.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0719.md
@@ -1,4 +1,4 @@
-An associated type value was specified more than once.
+An associated item was specified more than once in a trait object.
 
 Erroneous code example:
 
@@ -7,21 +7,15 @@ trait FooTrait {}
 trait BarTrait {}
 
 // error: associated type `Item` in trait `Iterator` is specified twice
-struct Foo<T: Iterator<Item: FooTrait, Item: BarTrait>> { f: T }
+type Foo = dyn Iterator<Item = u32, Item = u32>;
 ```
 
-`Item` in trait `Iterator` cannot be specified multiple times for struct `Foo`.
-To fix this, create a new trait that is a combination of the desired traits and
-specify the associated type with the new trait.
+To fix this, remove the duplicate specifier:
 
 Corrected example:
 
 ```
-trait FooTrait {}
-trait BarTrait {}
-trait FooBarTrait: FooTrait + BarTrait {}
-
-struct Foo<T: Iterator<Item: FooBarTrait>> { f: T } // ok!
+type Foo = dyn Iterator<Item = u32>; // ok!
 ```
 
 For more information about associated types, see [the book][bk-at]. For more
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 33b712e3aed..810a5a21a05 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -10,7 +10,7 @@ use rustc_ast::attr::{AttributeExt, MarkedAttrs};
 use rustc_ast::token::MetaVarKind;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{AssocCtxt, Visitor};
-use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
+use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind, Safety};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_data_structures::sync;
 use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult};
@@ -345,6 +345,21 @@ pub trait AttrProcMacro {
         annotation: TokenStream,
         annotated: TokenStream,
     ) -> Result<TokenStream, ErrorGuaranteed>;
+
+    // Default implementation for safe attributes; override if the attribute can be unsafe.
+    fn expand_with_safety<'cx>(
+        &self,
+        ecx: &'cx mut ExtCtxt<'_>,
+        safety: Safety,
+        span: Span,
+        annotation: TokenStream,
+        annotated: TokenStream,
+    ) -> Result<TokenStream, ErrorGuaranteed> {
+        if let Safety::Unsafe(span) = safety {
+            ecx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
+        }
+        self.expand(ecx, span, annotation, annotated)
+    }
 }
 
 impl<F> AttrProcMacro for F
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 172bc3d1d9f..3dfa3cdcc35 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -812,11 +812,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         _ => item.to_tokens(),
                     };
                     let attr_item = attr.get_normal_item();
+                    let safety = attr_item.unsafety;
                     if let AttrArgs::Eq { .. } = attr_item.args {
                         self.cx.dcx().emit_err(UnsupportedKeyValue { span });
                     }
                     let inner_tokens = attr_item.args.inner_tokens();
-                    match expander.expand(self.cx, span, inner_tokens, tokens) {
+                    match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) {
                         Ok(tok_result) => {
                             let fragment = self.parse_ast_fragment(
                                 tok_result,
@@ -840,6 +841,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
                     }
                 } else if let SyntaxExtensionKind::LegacyAttr(expander) = ext {
+                    // `LegacyAttr` is only used for builtin attribute macros, which have their
+                    // safety checked by `check_builtin_meta_item`, so we don't need to check
+                    // `unsafety` here.
                     match validate_attr::parse_meta(&self.cx.sess.psess, &attr) {
                         Ok(meta) => {
                             let item_clone = macro_stats.then(|| item.clone());
@@ -882,6 +886,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         }
                     }
                 } else if let SyntaxExtensionKind::NonMacroAttr = ext {
+                    if let ast::Safety::Unsafe(span) = attr.get_normal_item().unsafety {
+                        self.cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
+                    }
                     // `-Zmacro-stats` ignores these because they don't do any real expansion.
                     self.cx.expanded_inert_attrs.mark(&attr);
                     item.visit_attrs(|attrs| attrs.insert(pos, attr));
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index d4504ba720e..c548cea537f 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -8,7 +8,7 @@ use rustc_ast::token::NtPatKind::*;
 use rustc_ast::token::TokenKind::*;
 use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
 use rustc_ast::tokenstream::{self, DelimSpan, TokenStream};
-use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
+use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId, Safety};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
@@ -131,6 +131,7 @@ pub(super) enum MacroRule {
     Func { lhs: Vec<MatcherLoc>, lhs_span: Span, rhs: mbe::TokenTree },
     /// An attr rule, for use with `#[m]`
     Attr {
+        unsafe_rule: bool,
         args: Vec<MatcherLoc>,
         args_span: Span,
         body: Vec<MatcherLoc>,
@@ -248,7 +249,18 @@ impl TTMacroExpander for MacroRulesMacroExpander {
 impl AttrProcMacro for MacroRulesMacroExpander {
     fn expand(
         &self,
+        _cx: &mut ExtCtxt<'_>,
+        _sp: Span,
+        _args: TokenStream,
+        _body: TokenStream,
+    ) -> Result<TokenStream, ErrorGuaranteed> {
+        unreachable!("`expand` called on `MacroRulesMacroExpander`, expected `expand_with_safety`")
+    }
+
+    fn expand_with_safety(
+        &self,
         cx: &mut ExtCtxt<'_>,
+        safety: Safety,
         sp: Span,
         args: TokenStream,
         body: TokenStream,
@@ -260,6 +272,7 @@ impl AttrProcMacro for MacroRulesMacroExpander {
             self.node_id,
             self.name,
             self.transparency,
+            safety,
             args,
             body,
             &self.rules,
@@ -408,6 +421,7 @@ fn expand_macro_attr(
     node_id: NodeId,
     name: Ident,
     transparency: Transparency,
+    safety: Safety,
     args: TokenStream,
     body: TokenStream,
     rules: &[MacroRule],
@@ -429,13 +443,26 @@ fn expand_macro_attr(
     // Track nothing for the best performance.
     match try_match_macro_attr(psess, name, &args, &body, rules, &mut NoopTracker) {
         Ok((i, rule, named_matches)) => {
-            let MacroRule::Attr { rhs, .. } = rule else {
+            let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else {
                 panic!("try_macro_match_attr returned non-attr rule");
             };
             let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
                 cx.dcx().span_bug(sp, "malformed macro rhs");
             };
 
+            match (safety, unsafe_rule) {
+                (Safety::Default, false) | (Safety::Unsafe(_), true) => {}
+                (Safety::Default, true) => {
+                    cx.dcx().span_err(sp, "unsafe attribute invocation requires `unsafe`");
+                }
+                (Safety::Unsafe(span), false) => {
+                    cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute invocation");
+                }
+                (Safety::Safe(span), _) => {
+                    cx.dcx().span_bug(span, "unexpected `safe` keyword");
+                }
+            }
+
             let id = cx.current_expansion.id;
             let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id)
                 .map_err(|e| e.emit())?;
@@ -681,6 +708,11 @@ pub fn compile_declarative_macro(
     let mut rules = Vec::new();
 
     while p.token != token::Eof {
+        let unsafe_rule = p.eat_keyword_noexpect(kw::Unsafe);
+        let unsafe_keyword_span = p.prev_token.span;
+        if unsafe_rule && let Some(guar) = check_no_eof(sess, &p, "expected `attr`") {
+            return dummy_syn_ext(guar);
+        }
         let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) {
             kinds |= MacroKinds::ATTR;
             if !features.macro_attr() {
@@ -705,6 +737,10 @@ pub fn compile_declarative_macro(
                 feature_err(sess, sym::macro_derive, span, "`macro_rules!` derives are unstable")
                     .emit();
             }
+            if unsafe_rule {
+                sess.dcx()
+                    .span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules");
+            }
             if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") {
                 return dummy_syn_ext(guar);
             }
@@ -730,6 +766,10 @@ pub fn compile_declarative_macro(
             (None, true)
         } else {
             kinds |= MacroKinds::BANG;
+            if unsafe_rule {
+                sess.dcx()
+                    .span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules");
+            }
             (None, false)
         };
         let lhs_tt = p.parse_token_tree();
@@ -741,10 +781,10 @@ pub fn compile_declarative_macro(
         if let Some(guar) = check_no_eof(sess, &p, "expected right-hand side of macro rule") {
             return dummy_syn_ext(guar);
         }
-        let rhs_tt = p.parse_token_tree();
-        let rhs_tt = parse_one_tt(rhs_tt, RulePart::Body, sess, node_id, features, edition);
-        check_emission(check_rhs(sess, &rhs_tt));
-        check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs_tt));
+        let rhs = p.parse_token_tree();
+        let rhs = parse_one_tt(rhs, RulePart::Body, sess, node_id, features, edition);
+        check_emission(check_rhs(sess, &rhs));
+        check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs));
         let lhs_span = lhs_tt.span();
         // Convert the lhs into `MatcherLoc` form, which is better for doing the
         // actual matching.
@@ -760,11 +800,11 @@ pub fn compile_declarative_macro(
             };
             let args = mbe::macro_parser::compute_locs(&delimited.tts);
             let body_span = lhs_span;
-            rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
+            rules.push(MacroRule::Attr { unsafe_rule, args, args_span, body: lhs, body_span, rhs });
         } else if is_derive {
-            rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt });
+            rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs });
         } else {
-            rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
+            rules.push(MacroRule::Func { lhs, lhs_span, rhs });
         }
         if p.token == token::Eof {
             break;
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index 38ae7852ca9..b069a74bf5a 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -195,11 +195,10 @@ impl<'tcx> InherentCollect<'tcx> {
             | ty::Closure(..)
             | ty::CoroutineClosure(..)
             | ty::Coroutine(..)
-            | ty::CoroutineWitness(..)
-            | ty::Alias(ty::Free, _)
-            | ty::Bound(..)
-            | ty::Placeholder(_)
-            | ty::Infer(_) => {
+            | ty::CoroutineWitness(..) => {
+                Err(self.tcx.dcx().delayed_bug("cannot define inherent `impl` for closure types"))
+            }
+            ty::Alias(ty::Free, _) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) => {
                 bug!("unexpected impl self type of impl: {:?} {:?}", id, self_ty);
             }
             // We could bail out here, but that will silence other useful errors.
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 621431ae234..5a61248cab8 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -230,10 +230,12 @@ pub(crate) fn orphan_check_impl(
             ty::Closure(..)
             | ty::CoroutineClosure(..)
             | ty::Coroutine(..)
-            | ty::CoroutineWitness(..)
-            | ty::Bound(..)
-            | ty::Placeholder(..)
-            | ty::Infer(..) => {
+            | ty::CoroutineWitness(..) => {
+                return Err(tcx
+                    .dcx()
+                    .delayed_bug("cannot define inherent `impl` for closure types"));
+            }
+            ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => {
                 let sp = tcx.def_span(impl_def_id);
                 span_bug!(sp, "weird self type for autotrait impl")
             }
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index ba54fa8cc0d..129b26d8ff0 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -12,7 +12,7 @@ use tracing::{debug, instrument};
 
 use super::ItemCtxt;
 use super::predicates_of::assert_only_contains_predicates_from;
-use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter};
+use crate::hir_ty_lowering::{HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter};
 
 /// For associated types we include both bounds written on the type
 /// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`.
@@ -37,7 +37,14 @@ fn associated_type_bounds<'tcx>(
 
         let icx = ItemCtxt::new(tcx, assoc_item_def_id);
         let mut bounds = Vec::new();
-        icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
+        icx.lowerer().lower_bounds(
+            item_ty,
+            hir_bounds,
+            &mut bounds,
+            ty::List::empty(),
+            filter,
+            OverlappingAsssocItemConstraints::Allowed,
+        );
 
         match filter {
             PredicateFilter::All
@@ -174,21 +181,25 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>(
     for (param, var) in std::iter::zip(&generics.own_params, gat_vars) {
         let existing = match var.kind() {
             ty::GenericArgKind::Lifetime(re) => {
-                if let ty::RegionKind::ReBound(ty::INNERMOST, bv) = re.kind() {
+                if let ty::RegionKind::ReBound(ty::BoundVarIndexKind::Bound(ty::INNERMOST), bv) =
+                    re.kind()
+                {
                     mapping.insert(bv.var, tcx.mk_param_from_def(param))
                 } else {
                     return None;
                 }
             }
             ty::GenericArgKind::Type(ty) => {
-                if let ty::Bound(ty::INNERMOST, bv) = *ty.kind() {
+                if let ty::Bound(ty::BoundVarIndexKind::Bound(ty::INNERMOST), bv) = *ty.kind() {
                     mapping.insert(bv.var, tcx.mk_param_from_def(param))
                 } else {
                     return None;
                 }
             }
             ty::GenericArgKind::Const(ct) => {
-                if let ty::ConstKind::Bound(ty::INNERMOST, bv) = ct.kind() {
+                if let ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(ty::INNERMOST), bv) =
+                    ct.kind()
+                {
                     mapping.insert(bv.var, tcx.mk_param_from_def(param))
                 } else {
                     return None;
@@ -253,7 +264,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for MapAndCompressBoundVars<'tcx> {
             return ty;
         }
 
-        if let ty::Bound(binder, old_bound) = *ty.kind()
+        if let ty::Bound(ty::BoundVarIndexKind::Bound(binder), old_bound) = *ty.kind()
             && self.binder == binder
         {
             let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) {
@@ -279,7 +290,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for MapAndCompressBoundVars<'tcx> {
     }
 
     fn fold_region(&mut self, re: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        if let ty::ReBound(binder, old_bound) = re.kind()
+        if let ty::ReBound(ty::BoundVarIndexKind::Bound(binder), old_bound) = re.kind()
             && self.binder == binder
         {
             let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) {
@@ -307,7 +318,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for MapAndCompressBoundVars<'tcx> {
             return ct;
         }
 
-        if let ty::ConstKind::Bound(binder, old_bound) = ct.kind()
+        if let ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(binder), old_bound) = ct.kind()
             && self.binder == binder
         {
             let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) {
@@ -347,7 +358,14 @@ fn opaque_type_bounds<'tcx>(
     ty::print::with_reduced_queries!({
         let icx = ItemCtxt::new(tcx, opaque_def_id);
         let mut bounds = Vec::new();
-        icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter);
+        icx.lowerer().lower_bounds(
+            item_ty,
+            hir_bounds,
+            &mut bounds,
+            ty::List::empty(),
+            filter,
+            OverlappingAsssocItemConstraints::Allowed,
+        );
         // Implicit bounds are added to opaque types unless a `?Trait` bound is found
         match filter {
             PredicateFilter::All
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index dd3590f9ac5..ffdf2a2f4c0 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -18,7 +18,9 @@ use super::item_bounds::explicit_item_bounds_with_filter;
 use crate::collect::ItemCtxt;
 use crate::constrained_generic_params as cgp;
 use crate::delegation::inherit_predicates_for_delegation_item;
-use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter, RegionInferReason};
+use crate::hir_ty_lowering::{
+    HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
+};
 
 /// Returns a list of all type predicates (explicit and implicit) for the definition with
 /// ID `def_id`. This includes all predicates returned by `explicit_predicates_of`, plus
@@ -187,6 +189,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             &mut bounds,
             ty::List::empty(),
             PredicateFilter::All,
+            OverlappingAsssocItemConstraints::Allowed,
         );
         icx.lowerer().add_sizedness_bounds(
             &mut bounds,
@@ -289,6 +292,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                     &mut bounds,
                     bound_vars,
                     PredicateFilter::All,
+                    OverlappingAsssocItemConstraints::Allowed,
                 );
                 predicates.extend(bounds);
             }
@@ -659,7 +663,14 @@ pub(super) fn implied_predicates_with_filter<'tcx>(
 
     let self_param_ty = tcx.types.self_param;
     let mut bounds = Vec::new();
-    icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter);
+    icx.lowerer().lower_bounds(
+        self_param_ty,
+        superbounds,
+        &mut bounds,
+        ty::List::empty(),
+        filter,
+        OverlappingAsssocItemConstraints::Allowed,
+    );
     match filter {
         PredicateFilter::All
         | PredicateFilter::SelfOnly
@@ -984,6 +995,7 @@ impl<'tcx> ItemCtxt<'tcx> {
                 &mut bounds,
                 bound_vars,
                 filter,
+                OverlappingAsssocItemConstraints::Allowed,
             );
         }
 
@@ -1063,6 +1075,7 @@ pub(super) fn const_conditions<'tcx>(
                     &mut bounds,
                     bound_vars,
                     PredicateFilter::ConstIfConst,
+                    OverlappingAsssocItemConstraints::Allowed,
                 );
             }
             _ => {}
@@ -1083,6 +1096,7 @@ pub(super) fn const_conditions<'tcx>(
             &mut bounds,
             ty::List::empty(),
             PredicateFilter::ConstIfConst,
+            OverlappingAsssocItemConstraints::Allowed,
         );
     }
 
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 99dc8e6e522..7accab8df87 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -21,7 +21,8 @@ use tracing::{debug, instrument};
 use super::errors::GenericsArgsErrExtend;
 use crate::errors;
 use crate::hir_ty_lowering::{
-    AssocItemQSelf, FeedConstTy, HirTyLowerer, PredicateFilter, RegionInferReason,
+    AssocItemQSelf, FeedConstTy, HirTyLowerer, OverlappingAsssocItemConstraints, PredicateFilter,
+    RegionInferReason,
 };
 
 #[derive(Debug, Default)]
@@ -338,6 +339,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
         predicate_filter: PredicateFilter,
+        overlapping_assoc_constraints: OverlappingAsssocItemConstraints,
     ) where
         'tcx: 'hir,
     {
@@ -362,6 +364,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         param_ty,
                         bounds,
                         predicate_filter,
+                        overlapping_assoc_constraints,
                     );
                 }
                 hir::GenericBound::Outlives(lifetime) => {
@@ -402,7 +405,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_ref: ty::PolyTraitRef<'tcx>,
         constraint: &hir::AssocItemConstraint<'tcx>,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
-        duplicates: &mut FxIndexMap<DefId, Span>,
+        duplicates: Option<&mut FxIndexMap<DefId, Span>>,
         path_span: Span,
         predicate_filter: PredicateFilter,
     ) -> Result<(), ErrorGuaranteed> {
@@ -458,17 +461,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             )
             .expect("failed to find associated item");
 
-        duplicates
-            .entry(assoc_item.def_id)
-            .and_modify(|prev_span| {
-                self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
-                    span: constraint.span,
-                    prev_span: *prev_span,
-                    item_name: constraint.ident,
-                    def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
-                });
-            })
-            .or_insert(constraint.span);
+        if let Some(duplicates) = duplicates {
+            duplicates
+                .entry(assoc_item.def_id)
+                .and_modify(|prev_span| {
+                    self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
+                        span: constraint.span,
+                        prev_span: *prev_span,
+                        item_name: constraint.ident,
+                        def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
+                    });
+                })
+                .or_insert(constraint.span);
+        }
 
         let projection_term = if let ty::AssocTag::Fn = assoc_tag {
             let bound_vars = tcx.late_bound_vars(constraint.hir_id);
@@ -600,6 +605,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             bounds,
                             projection_ty.bound_vars(),
                             predicate_filter,
+                            OverlappingAsssocItemConstraints::Allowed,
                         );
                     }
                     PredicateFilter::SelfOnly
@@ -915,7 +921,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 't
             ty::Param(param) => {
                 self.params.insert(param.index);
             }
-            ty::Bound(db, bt) if *db >= self.depth => {
+            ty::Bound(ty::BoundVarIndexKind::Bound(db), bt) if *db >= self.depth => {
                 self.vars.insert(match bt.kind {
                     ty::BoundTyKind::Param(def_id) => def_id,
                     ty::BoundTyKind::Anon => {
@@ -938,7 +944,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 't
             ty::ReEarlyParam(param) => {
                 self.params.insert(param.index);
             }
-            ty::ReBound(db, br) if db >= self.depth => {
+            ty::ReBound(ty::BoundVarIndexKind::Bound(db), br) if db >= self.depth => {
                 self.vars.insert(match br.kind {
                     ty::BoundRegionKind::Named(def_id) => def_id,
                     ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => {
@@ -961,7 +967,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 't
             ty::ConstKind::Param(param) => {
                 self.params.insert(param.index);
             }
-            ty::ConstKind::Bound(db, _) if db >= self.depth => {
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(db), _) if db >= self.depth => {
                 let guar = self.cx.dcx().delayed_bug("unexpected escaping late-bound const var");
                 return ControlFlow::Break(guar);
             }
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
index c248cd7fec2..c0b13773089 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
@@ -23,7 +23,9 @@ use tracing::{debug, instrument};
 
 use super::HirTyLowerer;
 use crate::errors::SelfInTypeAlias;
-use crate::hir_ty_lowering::{GenericArgCountMismatch, PredicateFilter, RegionInferReason};
+use crate::hir_ty_lowering::{
+    GenericArgCountMismatch, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason,
+};
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     /// Lower a trait object type from the HIR to our internal notion of a type.
@@ -60,6 +62,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 dummy_self,
                 &mut user_written_bounds,
                 PredicateFilter::SelfOnly,
+                OverlappingAsssocItemConstraints::Forbidden,
             );
             if let Err(GenericArgCountMismatch { invalid_args, .. }) = result.correct {
                 potential_assoc_types.extend(invalid_args);
@@ -157,10 +160,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 self.dcx()
                     .struct_span_err(
                         span,
-                        format!(
-                            "conflicting associated type bounds for `{item}` when \
-                            expanding trait alias"
-                        ),
+                        format!("conflicting associated type bounds for `{item}`"),
                     )
                     .with_span_label(
                         old_proj_span,
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 9b198d04454..eb660804c2b 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -332,6 +332,15 @@ pub(crate) enum GenericArgPosition {
     MethodCall,
 }
 
+/// Whether to allow duplicate associated iten constraints in a trait ref, e.g.
+/// `Trait<Assoc = Ty, Assoc = Ty>`. This is forbidden in `dyn Trait<...>`
+/// but allowed everywhere else.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub(crate) enum OverlappingAsssocItemConstraints {
+    Allowed,
+    Forbidden,
+}
+
 /// A marker denoting that the generic arguments that were
 /// provided did not match the respective generic parameters.
 #[derive(Clone, Debug)]
@@ -752,6 +761,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         self_ty: Ty<'tcx>,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         predicate_filter: PredicateFilter,
+        overlapping_assoc_item_constraints: OverlappingAsssocItemConstraints,
     ) -> GenericArgCountResult {
         let tcx = self.tcx();
 
@@ -908,7 +918,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
-        let mut dup_constraints = FxIndexMap::default();
+        let mut dup_constraints = (overlapping_assoc_item_constraints
+            == OverlappingAsssocItemConstraints::Forbidden)
+            .then_some(FxIndexMap::default());
+
         for constraint in trait_segment.args().constraints {
             // Don't register any associated item constraints for negative bounds,
             // since we should have emitted an error for them earlier, and they
@@ -927,7 +940,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 poly_trait_ref,
                 constraint,
                 bounds,
-                &mut dup_constraints,
+                dup_constraints.as_mut(),
                 constraint.span,
                 predicate_filter,
             );
@@ -2484,6 +2497,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     &mut bounds,
                     ty::List::empty(),
                     PredicateFilter::All,
+                    OverlappingAsssocItemConstraints::Allowed,
                 );
                 self.add_sizedness_bounds(
                     &mut bounds,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index d1ce0afddf9..f9cdc923670 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -3551,35 +3551,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                     // Try to give some advice about indexing tuples.
                     if let ty::Tuple(types) = base_t.kind() {
-                        let mut needs_note = true;
-                        // If the index is an integer, we can show the actual
-                        // fixed expression:
+                        err.help(
+                            "tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.",
+                        );
+                        // If index is an unsuffixed integer, show the fixed expression:
                         if let ExprKind::Lit(lit) = idx.kind
                             && let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node
-                            && i.get()
-                                < types
-                                    .len()
-                                    .try_into()
-                                    .expect("expected tuple index to be < usize length")
+                            && i.get() < types.len().try_into().expect("tuple length fits in u128")
                         {
                             err.span_suggestion(
                                 brackets_span,
-                                "to access tuple elements, use",
+                                format!("to access tuple element `{i}`, use"),
                                 format!(".{i}"),
                                 Applicability::MachineApplicable,
                             );
-                            needs_note = false;
-                        } else if let ExprKind::Path(..) = idx.peel_borrows().kind {
-                            err.span_label(
-                                idx.span,
-                                "cannot access tuple elements at a variable index",
-                            );
-                        }
-                        if needs_note {
-                            err.help(
-                                "to access tuple elements, use tuple indexing \
-                                        syntax (e.g., `tuple.0`)",
-                            );
                         }
                     }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 833ce433d56..35253e4c291 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -611,19 +611,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         typeck_results.rvalue_scopes = rvalue_scopes;
     }
 
-    /// Unify the inference variables corresponding to coroutine witnesses, and save all the
-    /// predicates that were stalled on those inference variables.
-    ///
-    /// This process allows to conservatively save all predicates that do depend on the coroutine
-    /// interior types, for later processing by `check_coroutine_obligations`.
-    ///
-    /// We must not attempt to select obligations after this method has run, or risk query cycle
-    /// ICE.
+    /// Drain all obligations that are stalled on coroutines defined in this body.
     #[instrument(level = "debug", skip(self))]
-    pub(crate) fn resolve_coroutine_interiors(&self) {
-        // Try selecting all obligations that are not blocked on inference variables.
-        // Once we start unifying coroutine witnesses, trying to select obligations on them will
-        // trigger query cycle ICEs, as doing so requires MIR.
+    pub(crate) fn drain_stalled_coroutine_obligations(&self) {
+        // Make as much inference progress as possible before
+        // draining the stalled coroutine obligations as this may
+        // change obligations from being stalled on infer vars to
+        // being stalled on a coroutine.
         self.select_obligations_where_possible(|_| {});
 
         let ty::TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode()
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index acc0481e457..9f5a85b6926 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -243,18 +243,15 @@ fn typeck_with_inspect<'tcx>(
 
     debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
 
-    // This must be the last thing before `report_ambiguity_errors`.
-    fcx.resolve_coroutine_interiors();
-
-    debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
-
     // We need to handle opaque types before emitting ambiguity errors as applying
     // defining uses may guide type inference.
     if fcx.next_trait_solver() {
         fcx.handle_opaque_type_uses_next();
     }
 
-    fcx.select_obligations_where_possible(|_| {});
+    // This must be the last thing before `report_ambiguity_errors` below except `select_obligations_where_possible`.
+    // So don't put anything after this.
+    fcx.drain_stalled_coroutine_obligations();
     if fcx.infcx.tainted_by_errors().is_none() {
         fcx.report_ambiguity_errors();
     }
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index 3c5e4a91c98..e445def4faa 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -303,8 +303,6 @@ struct Canonicalizer<'cx, 'tcx> {
     sub_root_lookup_table: SsoHashMap<ty::TyVid, usize>,
     canonicalize_mode: &'cx dyn CanonicalizeMode,
     needs_canonical_flags: TypeFlags,
-
-    binder_index: ty::DebruijnIndex,
 }
 
 impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
@@ -312,24 +310,12 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
         self.tcx
     }
 
-    fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
-    where
-        T: TypeFoldable<TyCtxt<'tcx>>,
-    {
-        self.binder_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.binder_index.shift_out(1);
-        t
-    }
-
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match r.kind() {
-            ty::ReBound(index, ..) => {
-                if index >= self.binder_index {
-                    bug!("escaping late-bound region during canonicalization");
-                } else {
-                    r
-                }
+            ty::ReBound(ty::BoundVarIndexKind::Bound(_), ..) => r,
+
+            ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => {
+                bug!("canonicalized bound var found during canonicalization");
             }
 
             ty::ReStatic
@@ -403,12 +389,10 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
                 self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t)
             }
 
-            ty::Bound(debruijn, _) => {
-                if debruijn >= self.binder_index {
-                    bug!("escaping bound type during canonicalization")
-                } else {
-                    t
-                }
+            ty::Bound(ty::BoundVarIndexKind::Bound(_), _) => t,
+
+            ty::Bound(ty::BoundVarIndexKind::Canonical, _) => {
+                bug!("canonicalized bound var found during canonicalization");
             }
 
             ty::Closure(..)
@@ -479,12 +463,11 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
             ty::ConstKind::Infer(InferConst::Fresh(_)) => {
                 bug!("encountered a fresh const during canonicalization")
             }
-            ty::ConstKind::Bound(debruijn, _) => {
-                if debruijn >= self.binder_index {
-                    bug!("escaping bound const during canonicalization")
-                } else {
-                    return ct;
-                }
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(_), _) => {
+                return ct;
+            }
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, _) => {
+                bug!("canonicalized bound var found during canonicalization");
             }
             ty::ConstKind::Placeholder(placeholder) => {
                 return self
@@ -569,7 +552,6 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
             query_state,
             indices: FxHashMap::default(),
             sub_root_lookup_table: Default::default(),
-            binder_index: ty::INNERMOST,
         };
         if canonicalizer.query_state.var_values.spilled() {
             canonicalizer.indices = canonicalizer
@@ -751,8 +733,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
         r: ty::Region<'tcx>,
     ) -> ty::Region<'tcx> {
         let var = self.canonical_var(var_kind, r.into());
-        let br = ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon };
-        ty::Region::new_bound(self.cx(), self.binder_index, br)
+        ty::Region::new_canonical_bound(self.cx(), var)
     }
 
     /// Given a type variable `ty_var` of the given kind, first check
@@ -766,8 +747,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
     ) -> Ty<'tcx> {
         debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var)));
         let var = self.canonical_var(var_kind, ty_var.into());
-        let bt = ty::BoundTy { var, kind: ty::BoundTyKind::Anon };
-        Ty::new_bound(self.tcx, self.binder_index, bt)
+        Ty::new_canonical_bound(self.tcx, var)
     }
 
     /// Given a type variable `const_var` of the given kind, first check
@@ -783,7 +763,6 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
             !self.infcx.is_some_and(|infcx| ct_var != infcx.shallow_resolve_const(ct_var))
         );
         let var = self.canonical_var(var_kind, ct_var.into());
-        let bc = ty::BoundConst { var };
-        ty::Const::new_bound(self.tcx, self.binder_index, bc)
+        ty::Const::new_canonical_bound(self.tcx, var)
     }
 }
diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs
index cc052fbd85c..c215a9db2a0 100644
--- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs
+++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::{
     self, DelayedMap, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
     TypeVisitableExt, TypeVisitor,
 };
-use rustc_type_ir::TypeVisitable;
+use rustc_type_ir::{TypeFlags, TypeVisitable};
 
 use crate::infer::canonical::{Canonical, CanonicalVarValues};
 
@@ -66,7 +66,6 @@ where
 
     value.fold_with(&mut CanonicalInstantiator {
         tcx,
-        current_index: ty::INNERMOST,
         var_values: var_values.var_values,
         cache: Default::default(),
     })
@@ -79,12 +78,9 @@ struct CanonicalInstantiator<'tcx> {
     // The values that the bound vars are are being instantiated with.
     var_values: ty::GenericArgsRef<'tcx>,
 
-    /// As with `BoundVarReplacer`, represents the index of a binder *just outside*
-    /// the ones we have visited.
-    current_index: ty::DebruijnIndex,
-
-    // Instantiation is a pure function of `DebruijnIndex` and `Ty`.
-    cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>,
+    // Because we use `ty::BoundVarIndexKind::Canonical`, we can cache
+    // based only on the entire ty, not worrying about a `DebruijnIndex`
+    cache: DelayedMap<Ty<'tcx>, Ty<'tcx>>,
 }
 
 impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
@@ -92,29 +88,19 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
         self.tcx
     }
 
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        self.current_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.current_index.shift_out(1);
-        t
-    }
-
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
         match *t.kind() {
-            ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
+            ty::Bound(ty::BoundVarIndexKind::Canonical, bound_ty) => {
                 self.var_values[bound_ty.var.as_usize()].expect_ty()
             }
             _ => {
-                if !t.has_vars_bound_at_or_above(self.current_index) {
+                if !t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
                     t
-                } else if let Some(&t) = self.cache.get(&(self.current_index, t)) {
+                } else if let Some(&t) = self.cache.get(&t) {
                     t
                 } else {
                     let res = t.super_fold_with(self);
-                    assert!(self.cache.insert((self.current_index, t), res));
+                    assert!(self.cache.insert(t, res));
                     res
                 }
             }
@@ -123,7 +109,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
 
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match r.kind() {
-            ty::ReBound(debruijn, br) if debruijn == self.current_index => {
+            ty::ReBound(ty::BoundVarIndexKind::Canonical, br) => {
                 self.var_values[br.var.as_usize()].expect_region()
             }
             _ => r,
@@ -132,7 +118,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
 
     fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
         match ct.kind() {
-            ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bound_const) => {
                 self.var_values[bound_const.var.as_usize()].expect_const()
             }
             _ => ct.super_fold_with(self),
@@ -140,22 +126,14 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
     }
 
     fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
-        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+        if p.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { p.super_fold_with(self) } else { p }
     }
 
     fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
-        if !c.has_vars_bound_at_or_above(self.current_index) {
+        if !c.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
             return c;
         }
 
-        // Since instantiation is a function of `DebruijnIndex`, we don't want
-        // to have to cache more copies of clauses when we're inside of binders.
-        // Since we currently expect to only have clauses in the outermost
-        // debruijn index, we just fold if we're inside of a binder.
-        if self.current_index > ty::INNERMOST {
-            return c.super_fold_with(self);
-        }
-
         // Our cache key is `(clauses, var_values)`, but we also don't care about
         // var values that aren't named in the clauses, since they can change without
         // affecting the output. Since `ParamEnv`s are cached first, we compute the
@@ -185,45 +163,29 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
 fn highest_var_in_clauses<'tcx>(c: ty::Clauses<'tcx>) -> usize {
     struct HighestVarInClauses {
         max_var: usize,
-        current_index: ty::DebruijnIndex,
     }
     impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HighestVarInClauses {
-        fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-            &mut self,
-            t: &ty::Binder<'tcx, T>,
-        ) -> Self::Result {
-            self.current_index.shift_in(1);
-            let t = t.super_visit_with(self);
-            self.current_index.shift_out(1);
-            t
-        }
         fn visit_ty(&mut self, t: Ty<'tcx>) {
-            if let ty::Bound(debruijn, bound_ty) = *t.kind()
-                && debruijn == self.current_index
-            {
+            if let ty::Bound(ty::BoundVarIndexKind::Canonical, bound_ty) = *t.kind() {
                 self.max_var = self.max_var.max(bound_ty.var.as_usize());
-            } else if t.has_vars_bound_at_or_above(self.current_index) {
+            } else if t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
                 t.super_visit_with(self);
             }
         }
         fn visit_region(&mut self, r: ty::Region<'tcx>) {
-            if let ty::ReBound(debruijn, bound_region) = r.kind()
-                && debruijn == self.current_index
-            {
+            if let ty::ReBound(ty::BoundVarIndexKind::Canonical, bound_region) = r.kind() {
                 self.max_var = self.max_var.max(bound_region.var.as_usize());
             }
         }
         fn visit_const(&mut self, ct: ty::Const<'tcx>) {
-            if let ty::ConstKind::Bound(debruijn, bound_const) = ct.kind()
-                && debruijn == self.current_index
-            {
+            if let ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bound_const) = ct.kind() {
                 self.max_var = self.max_var.max(bound_const.var.as_usize());
-            } else if ct.has_vars_bound_at_or_above(self.current_index) {
+            } else if ct.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) {
                 ct.super_visit_with(self);
             }
         }
     }
-    let mut visitor = HighestVarInClauses { max_var: 0, current_index: ty::INNERMOST };
+    let mut visitor = HighestVarInClauses { max_var: 0 };
     c.visit_with(&mut visitor);
     visitor.max_var
 }
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index b3959113d5d..37a19605206 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -440,28 +440,28 @@ impl<'tcx> InferCtxt<'tcx> {
                     // and only use it for placeholders. We need to handle the
                     // `sub_root` of type inference variables which would make this
                     // more involved. They are also a lot rarer than region variables.
-                    if let ty::Bound(debruijn, b) = *result_value.kind()
+                    if let ty::Bound(index_kind, b) = *result_value.kind()
                         && !matches!(
                             query_response.variables[b.var.as_usize()],
                             CanonicalVarKind::Ty { .. }
                         )
                     {
-                        // We only allow a `ty::INNERMOST` index in generic parameters.
-                        assert_eq!(debruijn, ty::INNERMOST);
+                        // We only allow a `Canonical` index in generic parameters.
+                        assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical));
                         opt_values[b.var] = Some(*original_value);
                     }
                 }
                 GenericArgKind::Lifetime(result_value) => {
-                    if let ty::ReBound(debruijn, b) = result_value.kind() {
-                        // We only allow a `ty::INNERMOST` index in generic parameters.
-                        assert_eq!(debruijn, ty::INNERMOST);
+                    if let ty::ReBound(index_kind, b) = result_value.kind() {
+                        // We only allow a `Canonical` index in generic parameters.
+                        assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical));
                         opt_values[b.var] = Some(*original_value);
                     }
                 }
                 GenericArgKind::Const(result_value) => {
-                    if let ty::ConstKind::Bound(debruijn, b) = result_value.kind() {
-                        // We only allow a `ty::INNERMOST` index in generic parameters.
-                        assert_eq!(debruijn, ty::INNERMOST);
+                    if let ty::ConstKind::Bound(index_kind, b) = result_value.kind() {
+                        // We only allow a `Canonical` index in generic parameters.
+                        assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical));
                         opt_values[b.var] = Some(*original_value);
                     }
                 }
diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
index f06eb58a371..6592360cf0a 100644
--- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
+++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
@@ -44,8 +44,8 @@ pub fn extract_verify_if_eq<'tcx>(
     let verify_if_eq = verify_if_eq_b.skip_binder();
     m.relate(verify_if_eq.ty, test_ty).ok()?;
 
-    if let ty::RegionKind::ReBound(depth, br) = verify_if_eq.bound.kind() {
-        assert!(depth == ty::INNERMOST);
+    if let ty::RegionKind::ReBound(index_kind, br) = verify_if_eq.bound.kind() {
+        assert!(matches!(index_kind, ty::BoundVarIndexKind::Bound(ty::INNERMOST)));
         match m.map.get(&br) {
             Some(&r) => Some(r),
             None => {
@@ -156,7 +156,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstHigherRankedOutlives<'tcx>
         pattern: ty::Region<'tcx>,
         value: ty::Region<'tcx>,
     ) -> RelateResult<'tcx, ty::Region<'tcx>> {
-        if let ty::RegionKind::ReBound(depth, br) = pattern.kind()
+        if let ty::RegionKind::ReBound(ty::BoundVarIndexKind::Bound(depth), br) = pattern.kind()
             && depth == self.pattern_depth
         {
             self.bind(br, value)
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 2b83ea24ac6..e38474f09ff 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -58,6 +58,7 @@ using namespace llvm::object;
 // This opcode is an LLVM detail that could hypothetically change (?), so
 // verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM.
 static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000);
+static_assert(dwarf::DW_OP_stack_value == 0x9f);
 
 // LLVMAtomicOrdering is already an enum - don't create another
 // one.
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 28142382b13..8eb7aa71fcd 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1298,6 +1298,10 @@ pub struct BasicBlockData<'tcx> {
     /// List of statements in this block.
     pub statements: Vec<Statement<'tcx>>,
 
+    /// All debuginfos happen before the statement.
+    /// Put debuginfos here when the last statement is eliminated.
+    pub after_last_stmt_debuginfos: StmtDebugInfos<'tcx>,
+
     /// Terminator for this block.
     ///
     /// N.B., this should generally ONLY be `None` during construction.
@@ -1325,7 +1329,12 @@ impl<'tcx> BasicBlockData<'tcx> {
         terminator: Option<Terminator<'tcx>>,
         is_cleanup: bool,
     ) -> BasicBlockData<'tcx> {
-        BasicBlockData { statements, terminator, is_cleanup }
+        BasicBlockData {
+            statements,
+            after_last_stmt_debuginfos: StmtDebugInfos::default(),
+            terminator,
+            is_cleanup,
+        }
     }
 
     /// Accessor for terminator.
@@ -1360,6 +1369,36 @@ impl<'tcx> BasicBlockData<'tcx> {
             self.terminator().successors()
         }
     }
+
+    pub fn retain_statements<F>(&mut self, mut f: F)
+    where
+        F: FnMut(&Statement<'tcx>) -> bool,
+    {
+        // Place debuginfos into the next retained statement,
+        // this `debuginfos` variable is used to cache debuginfos between two retained statements.
+        let mut debuginfos = StmtDebugInfos::default();
+        self.statements.retain_mut(|stmt| {
+            let retain = f(stmt);
+            if retain {
+                stmt.debuginfos.prepend(&mut debuginfos);
+            } else {
+                debuginfos.append(&mut stmt.debuginfos);
+            }
+            retain
+        });
+        self.after_last_stmt_debuginfos.prepend(&mut debuginfos);
+    }
+
+    pub fn strip_nops(&mut self) {
+        self.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop))
+    }
+
+    pub fn drop_debuginfo(&mut self) {
+        self.after_last_stmt_debuginfos.drop_debuginfo();
+        for stmt in self.statements.iter_mut() {
+            stmt.debuginfos.drop_debuginfo();
+        }
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -1664,10 +1703,10 @@ mod size_asserts {
 
     use super::*;
     // tidy-alphabetical-start
-    static_assert_size!(BasicBlockData<'_>, 128);
+    static_assert_size!(BasicBlockData<'_>, 152);
     static_assert_size!(LocalDecl<'_>, 40);
     static_assert_size!(SourceScopeData<'_>, 64);
-    static_assert_size!(Statement<'_>, 32);
+    static_assert_size!(Statement<'_>, 56);
     static_assert_size!(Terminator<'_>, 96);
     static_assert_size!(VarDebugInfo<'_>, 88);
     // tidy-alphabetical-end
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 96148fd5b92..d87e3abe3b2 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -719,6 +719,11 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> {
         let mut current_location = Location { block, statement_index: 0 };
         for statement in &data.statements {
             (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
+
+            for debuginfo in statement.debuginfos.iter() {
+                writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?;
+            }
+
             let indented_body = format!("{INDENT}{INDENT}{statement:?};");
             if self.options.include_extra_comments {
                 writeln!(
@@ -749,6 +754,10 @@ impl<'de, 'tcx> MirWriter<'de, 'tcx> {
             current_location.statement_index += 1;
         }
 
+        for debuginfo in data.after_last_stmt_debuginfos.iter() {
+            writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?;
+        }
+
         // Terminator at the bottom.
         (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
         if data.terminator.is_some() {
@@ -829,6 +838,19 @@ impl Debug for Statement<'_> {
     }
 }
 
+impl Debug for StmtDebugInfo<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            StmtDebugInfo::AssignRef(local, place) => {
+                write!(fmt, "{local:?} = &{place:?}")
+            }
+            StmtDebugInfo::InvalidAssign(local) => {
+                write!(fmt, "{local:?} = &?")
+            }
+        }
+    }
+}
+
 impl Display for NonDivergingIntrinsic<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
@@ -1274,7 +1296,6 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
     for &elem in projection.iter().rev() {
         match elem {
             ProjectionElem::OpaqueCast(_)
-            | ProjectionElem::Subtype(_)
             | ProjectionElem::Downcast(_, _)
             | ProjectionElem::Field(_, _) => {
                 write!(fmt, "(")?;
@@ -1300,9 +1321,6 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
             ProjectionElem::OpaqueCast(ty) => {
                 write!(fmt, " as {ty})")?;
             }
-            ProjectionElem::Subtype(ty) => {
-                write!(fmt, " as subtype {ty})")?;
-            }
             ProjectionElem::Downcast(Some(name), _index) => {
                 write!(fmt, " as {name})")?;
             }
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
index 28294b47e90..f310e1e5762 100644
--- a/compiler/rustc_middle/src/mir/statement.rs
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -1,5 +1,7 @@
 //! Functionality for statements, operands, places, and things that appear in them.
 
+use std::ops;
+
 use tracing::{debug, instrument};
 
 use super::interpret::GlobalAlloc;
@@ -15,17 +17,28 @@ use crate::ty::CoroutineArgsExt;
 pub struct Statement<'tcx> {
     pub source_info: SourceInfo,
     pub kind: StatementKind<'tcx>,
+    /// Some debuginfos appearing before the primary statement.
+    pub debuginfos: StmtDebugInfos<'tcx>,
 }
 
 impl<'tcx> Statement<'tcx> {
     /// Changes a statement to a nop. This is both faster than deleting instructions and avoids
     /// invalidating statement indices in `Location`s.
-    pub fn make_nop(&mut self) {
-        self.kind = StatementKind::Nop
+    pub fn make_nop(&mut self, drop_debuginfo: bool) {
+        if matches!(self.kind, StatementKind::Nop) {
+            return;
+        }
+        let replaced_stmt = std::mem::replace(&mut self.kind, StatementKind::Nop);
+        if !drop_debuginfo {
+            let Some(debuginfo) = replaced_stmt.as_debuginfo() else {
+                bug!("debuginfo is not yet supported.")
+            };
+            self.debuginfos.push(debuginfo);
+        }
     }
 
     pub fn new(source_info: SourceInfo, kind: StatementKind<'tcx>) -> Self {
-        Statement { source_info, kind }
+        Statement { source_info, kind, debuginfos: StmtDebugInfos::default() }
     }
 }
 
@@ -63,6 +76,17 @@ impl<'tcx> StatementKind<'tcx> {
             _ => None,
         }
     }
+
+    pub fn as_debuginfo(&self) -> Option<StmtDebugInfo<'tcx>> {
+        match self {
+            StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place)))
+                if let Some(local) = place.as_local() =>
+            {
+                Some(StmtDebugInfo::AssignRef(local, *ref_place))
+            }
+            _ => None,
+        }
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -222,7 +246,6 @@ impl<'tcx> PlaceTy<'tcx> {
                 fty,
             )),
             ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)),
-            ProjectionElem::Subtype(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)),
 
             // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general.
             ProjectionElem::UnwrapUnsafeBinder(ty) => {
@@ -244,7 +267,6 @@ impl<V, T> ProjectionElem<V, T> {
             Self::Field(_, _)
             | Self::Index(_)
             | Self::OpaqueCast(_)
-            | Self::Subtype(_)
             | Self::ConstantIndex { .. }
             | Self::Subslice { .. }
             | Self::Downcast(_, _)
@@ -259,7 +281,6 @@ impl<V, T> ProjectionElem<V, T> {
             Self::Deref | Self::Index(_) => false,
             Self::Field(_, _)
             | Self::OpaqueCast(_)
-            | Self::Subtype(_)
             | Self::ConstantIndex { .. }
             | Self::Subslice { .. }
             | Self::Downcast(_, _)
@@ -286,7 +307,6 @@ impl<V, T> ProjectionElem<V, T> {
             | Self::Field(_, _) => true,
             Self::ConstantIndex { from_end: true, .. }
             | Self::Index(_)
-            | Self::Subtype(_)
             | Self::OpaqueCast(_)
             | Self::Subslice { .. } => false,
 
@@ -319,7 +339,6 @@ impl<V, T> ProjectionElem<V, T> {
                 ProjectionElem::Subslice { from, to, from_end }
             }
             ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(t(ty)),
-            ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(t(ty)),
             ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(t(ty)),
             ProjectionElem::Index(val) => ProjectionElem::Index(v(val)?),
         })
@@ -508,6 +527,20 @@ impl<'tcx> PlaceRef<'tcx> {
         })
     }
 
+    /// Return the place accessed locals that include the base local.
+    pub fn accessed_locals(self) -> impl Iterator<Item = Local> {
+        std::iter::once(self.local).chain(self.projection.iter().filter_map(|proj| match proj {
+            ProjectionElem::Index(local) => Some(*local),
+            ProjectionElem::Deref
+            | ProjectionElem::Field(_, _)
+            | ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Subslice { .. }
+            | ProjectionElem::Downcast(_, _)
+            | ProjectionElem::OpaqueCast(_)
+            | ProjectionElem::UnwrapUnsafeBinder(_) => None,
+        }))
+    }
+
     /// Generates a new place by appending `more_projections` to the existing ones
     /// and interning the result.
     pub fn project_deeper(
@@ -706,7 +739,8 @@ impl<'tcx> Rvalue<'tcx> {
                 | CastKind::PtrToPtr
                 | CastKind::PointerCoercion(_, _)
                 | CastKind::PointerWithExposedProvenance
-                | CastKind::Transmute,
+                | CastKind::Transmute
+                | CastKind::Subtype,
                 _,
                 _,
             )
@@ -971,3 +1005,71 @@ impl RawPtrKind {
         }
     }
 }
+
+#[derive(Default, Debug, Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+pub struct StmtDebugInfos<'tcx>(Vec<StmtDebugInfo<'tcx>>);
+
+impl<'tcx> StmtDebugInfos<'tcx> {
+    pub fn push(&mut self, debuginfo: StmtDebugInfo<'tcx>) {
+        self.0.push(debuginfo);
+    }
+
+    pub fn drop_debuginfo(&mut self) {
+        self.0.clear();
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+
+    pub fn prepend(&mut self, debuginfos: &mut Self) {
+        if debuginfos.is_empty() {
+            return;
+        };
+        debuginfos.0.append(self);
+        std::mem::swap(debuginfos, self);
+    }
+
+    pub fn append(&mut self, debuginfos: &mut Self) {
+        if debuginfos.is_empty() {
+            return;
+        };
+        self.0.append(debuginfos);
+    }
+
+    pub fn extend(&mut self, debuginfos: &Self) {
+        if debuginfos.is_empty() {
+            return;
+        };
+        self.0.extend_from_slice(debuginfos);
+    }
+
+    pub fn retain<F>(&mut self, f: F)
+    where
+        F: FnMut(&StmtDebugInfo<'tcx>) -> bool,
+    {
+        self.0.retain(f);
+    }
+}
+
+impl<'tcx> ops::Deref for StmtDebugInfos<'tcx> {
+    type Target = Vec<StmtDebugInfo<'tcx>>;
+
+    #[inline]
+    fn deref(&self) -> &Vec<StmtDebugInfo<'tcx>> {
+        &self.0
+    }
+}
+
+impl<'tcx> ops::DerefMut for StmtDebugInfos<'tcx> {
+    #[inline]
+    fn deref_mut(&mut self) -> &mut Vec<StmtDebugInfo<'tcx>> {
+        &mut self.0
+    }
+}
+
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+pub enum StmtDebugInfo<'tcx> {
+    AssignRef(Local, Place<'tcx>),
+    InvalidAssign(Local),
+}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index e6c8512564e..a823c365394 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1275,18 +1275,6 @@ pub enum ProjectionElem<V, T> {
     /// A transmute from an unsafe binder to the type that it wraps. This is a projection
     /// of a place, so it doesn't necessarily constitute a move out of the binder.
     UnwrapUnsafeBinder(T),
-
-    /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where
-    /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
-    /// explicit during optimizations and codegen.
-    ///
-    /// This projection doesn't impact the runtime behavior of the program except for potentially changing
-    /// some type metadata of the interpreter or codegen backend.
-    ///
-    /// This goal is achieved with mir_transform pass `Subtyper`, which runs right after
-    /// borrowchecker, as we only care about subtyping that can affect trait selection and
-    /// `TypeId`.
-    Subtype(T),
 }
 
 /// Alias for projections as they appear in places, where the base is a place
@@ -1513,6 +1501,18 @@ pub enum CastKind {
     /// MIR is well-formed if the input and output types have different sizes,
     /// but running a transmute between differently-sized types is UB.
     Transmute,
+
+    /// A `Subtype` cast is applied to any `StatementKind::Assign` where
+    /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
+    /// explicit during optimizations and codegen.
+    ///
+    /// This cast doesn't impact the runtime behavior of the program except for potentially changing
+    /// some type metadata of the interpreter or codegen backend.
+    ///
+    /// This goal is achieved with mir_transform pass `Subtyper`, which runs right after
+    /// borrowchecker, as we only care about subtyping that can affect trait selection and
+    /// `TypeId`.
+    Subtype,
 }
 
 /// Represents how a [`CastKind::PointerCoercion`] was constructed.
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 4034a3a06e9..4249914346c 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -444,6 +444,14 @@ impl<'tcx> Terminator<'tcx> {
         self.kind.successors()
     }
 
+    /// Return `Some` if all successors are identical.
+    #[inline]
+    pub fn identical_successor(&self) -> Option<BasicBlock> {
+        let mut successors = self.successors();
+        let first_succ = successors.next()?;
+        if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None }
+    }
+
     #[inline]
     pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
         self.kind.successors_mut(f)
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 81df239dee4..9654e189f2e 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -95,6 +95,14 @@ macro_rules! make_mir_visitor {
                 self.super_source_scope_data(scope_data);
             }
 
+            fn visit_statement_debuginfo(
+                &mut self,
+                stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>,
+                location: Location
+            ) {
+                self.super_statement_debuginfo(stmt_debuginfo, location);
+            }
+
             fn visit_statement(
                 &mut self,
                 statement: & $($mutability)? Statement<'tcx>,
@@ -301,6 +309,7 @@ macro_rules! make_mir_visitor {
             {
                 let BasicBlockData {
                     statements,
+                    after_last_stmt_debuginfos,
                     terminator,
                     is_cleanup: _
                 } = data;
@@ -312,8 +321,11 @@ macro_rules! make_mir_visitor {
                     index += 1;
                 }
 
+                let location = Location { block, statement_index: index };
+                for debuginfo in after_last_stmt_debuginfos as & $($mutability)? [_] {
+                    self.visit_statement_debuginfo(debuginfo, location);
+                }
                 if let Some(terminator) = terminator {
-                    let location = Location { block, statement_index: index };
                     self.visit_terminator(terminator, location);
                 }
             }
@@ -376,14 +388,45 @@ macro_rules! make_mir_visitor {
                 }
             }
 
+            fn super_statement_debuginfo(
+                &mut self,
+                stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>,
+                location: Location
+            ) {
+                match stmt_debuginfo {
+                    StmtDebugInfo::AssignRef(local, place) => {
+                        self.visit_local(
+                            $(& $mutability)? *local,
+                            PlaceContext::NonUse(NonUseContext::VarDebugInfo),
+                            location
+                        );
+                        self.visit_place(
+                            place,
+                            PlaceContext::NonUse(NonUseContext::VarDebugInfo),
+                            location
+                        );
+                    },
+                    StmtDebugInfo::InvalidAssign(local) => {
+                        self.visit_local(
+                            $(& $mutability)? *local,
+                            PlaceContext::NonUse(NonUseContext::VarDebugInfo),
+                            location
+                        );
+                    }
+                }
+            }
+
             fn super_statement(
                 &mut self,
                 statement: & $($mutability)? Statement<'tcx>,
                 location: Location
             ) {
-                let Statement { source_info, kind } = statement;
+                let Statement { source_info, kind, debuginfos } = statement;
 
                 self.visit_source_info(source_info);
+                for debuginfo in debuginfos as & $($mutability)? [_] {
+                    self.visit_statement_debuginfo(debuginfo, location);
+                }
                 match kind {
                     StatementKind::Assign(box (place, rvalue)) => {
                         self.visit_assign(place, rvalue, location);
@@ -1166,11 +1209,6 @@ macro_rules! visit_place_fns {
                     self.visit_ty(&mut new_ty, TyContext::Location(location));
                     if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None }
                 }
-                PlaceElem::Subtype(ty) => {
-                    let mut new_ty = ty;
-                    self.visit_ty(&mut new_ty, TyContext::Location(location));
-                    if ty != new_ty { Some(PlaceElem::Subtype(new_ty)) } else { None }
-                }
                 PlaceElem::UnwrapUnsafeBinder(ty) => {
                     let mut new_ty = ty;
                     self.visit_ty(&mut new_ty, TyContext::Location(location));
@@ -1244,7 +1282,6 @@ macro_rules! visit_place_fns {
         ) {
             match elem {
                 ProjectionElem::OpaqueCast(ty)
-                | ProjectionElem::Subtype(ty)
                 | ProjectionElem::Field(_, ty)
                 | ProjectionElem::UnwrapUnsafeBinder(ty) => {
                     self.visit_ty(ty, TyContext::Location(location));
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 614b6471f18..95adb561c70 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -95,7 +95,15 @@ impl<'tcx> Const<'tcx> {
         debruijn: ty::DebruijnIndex,
         bound_const: ty::BoundConst,
     ) -> Const<'tcx> {
-        Const::new(tcx, ty::ConstKind::Bound(debruijn, bound_const))
+        Const::new(tcx, ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const))
+    }
+
+    #[inline]
+    pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Const<'tcx> {
+        Const::new(
+            tcx,
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst { var }),
+        )
     }
 
     #[inline]
@@ -180,6 +188,10 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
         Const::new_bound(tcx, debruijn, ty::BoundConst { var })
     }
 
+    fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: rustc_type_ir::BoundVar) -> Self {
+        Const::new_canonical_bound(tcx, var)
+    }
+
     fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderConst) -> Self {
         Const::new_placeholder(tcx, placeholder)
     }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index fe3fa024cd5..1b89a49cf98 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1110,6 +1110,15 @@ const NUM_PREINTERNED_FRESH_TYS: u32 = 20;
 const NUM_PREINTERNED_FRESH_INT_TYS: u32 = 3;
 const NUM_PREINTERNED_FRESH_FLOAT_TYS: u32 = 3;
 const NUM_PREINTERNED_ANON_BOUND_TYS_I: u32 = 3;
+
+// From general profiling of the *max vars during canonicalization* of a value:
+// - about 90% of the time, there are no canonical vars
+// - about 9% of the time, there is only one canonical var
+// - there are rarely more than 3-5 canonical vars (with exceptions in particularly pathological cases)
+// This may not match the number of bound vars found in `for`s.
+// Given that this is all heap interned, it seems likely that interning fewer
+// vars here won't make an appreciable difference. Though, if we were to inline the data (in an array),
+// we may want to consider reducing the number for canonicalized vars down to 4 or so.
 const NUM_PREINTERNED_ANON_BOUND_TYS_V: u32 = 20;
 
 // This number may seem high, but it is reached in all but the smallest crates.
@@ -1160,9 +1169,14 @@ pub struct CommonTypes<'tcx> {
     pub fresh_float_tys: Vec<Ty<'tcx>>,
 
     /// Pre-interned values of the form:
-    /// `Bound(DebruijnIndex(i), BoundTy { var: v, kind: BoundTyKind::Anon})`
+    /// `Bound(BoundVarIndexKind::Bound(DebruijnIndex(i)), BoundTy { var: v, kind: BoundTyKind::Anon})`
     /// for small values of `i` and `v`.
     pub anon_bound_tys: Vec<Vec<Ty<'tcx>>>,
+
+    // Pre-interned values of the form:
+    // `Bound(BoundVarIndexKind::Canonical, BoundTy { var: v, kind: BoundTyKind::Anon })`
+    // for small values of `v`.
+    pub anon_canonical_bound_tys: Vec<Ty<'tcx>>,
 }
 
 pub struct CommonLifetimes<'tcx> {
@@ -1176,9 +1190,14 @@ pub struct CommonLifetimes<'tcx> {
     pub re_vars: Vec<Region<'tcx>>,
 
     /// Pre-interned values of the form:
-    /// `ReBound(DebruijnIndex(i), BoundRegion { var: v, kind: BoundRegionKind::Anon })`
+    /// `ReBound(BoundVarIndexKind::Bound(DebruijnIndex(i)), BoundRegion { var: v, kind: BoundRegionKind::Anon })`
     /// for small values of `i` and `v`.
     pub anon_re_bounds: Vec<Vec<Region<'tcx>>>,
+
+    // Pre-interned values of the form:
+    // `ReBound(BoundVarIndexKind::Canonical, BoundRegion { var: v, kind: BoundRegionKind::Anon })`
+    // for small values of `v`.
+    pub anon_re_canonical_bounds: Vec<Region<'tcx>>,
 }
 
 pub struct CommonConsts<'tcx> {
@@ -1211,7 +1230,7 @@ impl<'tcx> CommonTypes<'tcx> {
                 (0..NUM_PREINTERNED_ANON_BOUND_TYS_V)
                     .map(|v| {
                         mk(ty::Bound(
-                            ty::DebruijnIndex::from(i),
+                            ty::BoundVarIndexKind::Bound(ty::DebruijnIndex::from(i)),
                             ty::BoundTy { var: ty::BoundVar::from(v), kind: ty::BoundTyKind::Anon },
                         ))
                     })
@@ -1219,6 +1238,15 @@ impl<'tcx> CommonTypes<'tcx> {
             })
             .collect();
 
+        let anon_canonical_bound_tys = (0..NUM_PREINTERNED_ANON_BOUND_TYS_V)
+            .map(|v| {
+                mk(ty::Bound(
+                    ty::BoundVarIndexKind::Canonical,
+                    ty::BoundTy { var: ty::BoundVar::from(v), kind: ty::BoundTyKind::Anon },
+                ))
+            })
+            .collect();
+
         CommonTypes {
             unit: mk(Tuple(List::empty())),
             bool: mk(Bool),
@@ -1250,6 +1278,7 @@ impl<'tcx> CommonTypes<'tcx> {
             fresh_int_tys,
             fresh_float_tys,
             anon_bound_tys,
+            anon_canonical_bound_tys,
         }
     }
 }
@@ -1270,7 +1299,7 @@ impl<'tcx> CommonLifetimes<'tcx> {
                 (0..NUM_PREINTERNED_ANON_RE_BOUNDS_V)
                     .map(|v| {
                         mk(ty::ReBound(
-                            ty::DebruijnIndex::from(i),
+                            ty::BoundVarIndexKind::Bound(ty::DebruijnIndex::from(i)),
                             ty::BoundRegion {
                                 var: ty::BoundVar::from(v),
                                 kind: ty::BoundRegionKind::Anon,
@@ -1281,11 +1310,21 @@ impl<'tcx> CommonLifetimes<'tcx> {
             })
             .collect();
 
+        let anon_re_canonical_bounds = (0..NUM_PREINTERNED_ANON_RE_BOUNDS_V)
+            .map(|v| {
+                mk(ty::ReBound(
+                    ty::BoundVarIndexKind::Canonical,
+                    ty::BoundRegion { var: ty::BoundVar::from(v), kind: ty::BoundRegionKind::Anon },
+                ))
+            })
+            .collect();
+
         CommonLifetimes {
             re_static: mk(ty::ReStatic),
             re_erased: mk(ty::ReErased),
             re_vars,
             anon_re_bounds,
+            anon_re_canonical_bounds,
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index 7d56ec1635f..ee29afcff63 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -125,7 +125,9 @@ where
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
         match *t.kind() {
-            ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
+            ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty)
+                if debruijn == self.current_index =>
+            {
                 let ty = self.delegate.replace_ty(bound_ty);
                 debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST));
                 ty::shift_vars(self.tcx, ty, self.current_index.as_u32())
@@ -146,9 +148,11 @@ where
 
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match r.kind() {
-            ty::ReBound(debruijn, br) if debruijn == self.current_index => {
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br)
+                if debruijn == self.current_index =>
+            {
                 let region = self.delegate.replace_region(br);
-                if let ty::ReBound(debruijn1, br) = region.kind() {
+                if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn1), br) = region.kind() {
                     // If the callback returns a bound region,
                     // that region should always use the INNERMOST
                     // debruijn index. Then we adjust it to the
@@ -165,7 +169,9 @@ where
 
     fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
         match ct.kind() {
-            ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const)
+                if debruijn == self.current_index =>
+            {
                 let ct = self.delegate.replace_const(bound_const);
                 debug_assert!(!ct.has_vars_bound_above(ty::INNERMOST));
                 ty::shift_vars(self.tcx, ct, self.current_index.as_u32())
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 8f7c8170f7a..4d1fcaeda5e 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -2664,7 +2664,7 @@ impl<'a, 'tcx> ty::TypeFolder<TyCtxt<'tcx>> for RegionFolder<'a, 'tcx> {
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         let name = &mut self.name;
         let region = match r.kind() {
-            ty::ReBound(db, br) if db >= self.current_index => {
+            ty::ReBound(ty::BoundVarIndexKind::Bound(db), br) if db >= self.current_index => {
                 *self.region_map.entry(br).or_insert_with(|| name(Some(db), self.current_index, br))
             }
             ty::RePlaceholder(ty::PlaceholderRegion {
@@ -2687,7 +2687,7 @@ impl<'a, 'tcx> ty::TypeFolder<TyCtxt<'tcx>> for RegionFolder<'a, 'tcx> {
             }
             _ => return r,
         };
-        if let ty::ReBound(debruijn1, br) = region.kind() {
+        if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn1), br) = region.kind() {
             assert_eq!(debruijn1, ty::INNERMOST);
             ty::Region::new_bound(self.tcx, self.current_index, br)
         } else {
diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs
index 3a7852dea06..f0687f2bc72 100644
--- a/compiler/rustc_middle/src/ty/region.rs
+++ b/compiler/rustc_middle/src/ty/region.rs
@@ -31,7 +31,7 @@ impl<'tcx> rustc_type_ir::Flags for Region<'tcx> {
 
     fn outer_exclusive_binder(&self) -> ty::DebruijnIndex {
         match self.kind() {
-            ty::ReBound(debruijn, _) => debruijn.shifted_in(1),
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) => debruijn.shifted_in(1),
             _ => ty::INNERMOST,
         }
     }
@@ -59,7 +59,20 @@ impl<'tcx> Region<'tcx> {
         {
             re
         } else {
-            tcx.intern_region(ty::ReBound(debruijn, bound_region))
+            tcx.intern_region(ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), bound_region))
+        }
+    }
+
+    #[inline]
+    pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Region<'tcx> {
+        // Use a pre-interned one when possible.
+        if let Some(re) = tcx.lifetimes.anon_re_canonical_bounds.get(var.as_usize()).copied() {
+            re
+        } else {
+            tcx.intern_region(ty::ReBound(
+                ty::BoundVarIndexKind::Canonical,
+                ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
+            ))
         }
     }
 
@@ -122,7 +135,12 @@ impl<'tcx> Region<'tcx> {
     pub fn new_from_kind(tcx: TyCtxt<'tcx>, kind: RegionKind<'tcx>) -> Region<'tcx> {
         match kind {
             ty::ReEarlyParam(region) => Region::new_early_param(tcx, region),
-            ty::ReBound(debruijn, region) => Region::new_bound(tcx, debruijn, region),
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), region) => {
+                Region::new_bound(tcx, debruijn, region)
+            }
+            ty::ReBound(ty::BoundVarIndexKind::Canonical, region) => {
+                Region::new_canonical_bound(tcx, region.var)
+            }
             ty::ReLateParam(ty::LateParamRegion { scope, kind }) => {
                 Region::new_late_param(tcx, scope, kind)
             }
@@ -148,6 +166,10 @@ impl<'tcx> rustc_type_ir::inherent::Region<TyCtxt<'tcx>> for Region<'tcx> {
         Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon })
     }
 
+    fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: rustc_type_ir::BoundVar) -> Self {
+        Region::new_canonical_bound(tcx, var)
+    }
+
     fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderRegion) -> Self {
         Region::new_placeholder(tcx, placeholder)
     }
@@ -223,7 +245,7 @@ impl<'tcx> Region<'tcx> {
     #[inline]
     pub fn bound_at_or_above_binder(self, index: ty::DebruijnIndex) -> bool {
         match self.kind() {
-            ty::ReBound(debruijn, _) => debruijn >= index,
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) => debruijn >= index,
             _ => false,
         }
     }
@@ -254,7 +276,11 @@ impl<'tcx> Region<'tcx> {
             ty::ReStatic => {
                 flags = flags | TypeFlags::HAS_FREE_REGIONS;
             }
-            ty::ReBound(..) => {
+            ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => {
+                flags = flags | TypeFlags::HAS_RE_BOUND;
+                flags = flags | TypeFlags::HAS_CANONICAL_BOUND;
+            }
+            ty::ReBound(ty::BoundVarIndexKind::Bound(..), _) => {
                 flags = flags | TypeFlags::HAS_RE_BOUND;
             }
             ty::ReErased => {
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index de35e5e847c..a3fdd4e35b6 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -487,7 +487,23 @@ impl<'tcx> Ty<'tcx> {
         {
             ty
         } else {
-            Ty::new(tcx, Bound(index, bound_ty))
+            Ty::new(tcx, Bound(ty::BoundVarIndexKind::Bound(index), bound_ty))
+        }
+    }
+
+    #[inline]
+    pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: BoundVar) -> Ty<'tcx> {
+        // Use a pre-interned one when possible.
+        if let Some(ty) = tcx.types.anon_canonical_bound_tys.get(var.as_usize()).copied() {
+            ty
+        } else {
+            Ty::new(
+                tcx,
+                Bound(
+                    ty::BoundVarIndexKind::Canonical,
+                    ty::BoundTy { var, kind: ty::BoundTyKind::Anon },
+                ),
+            )
         }
     }
 
@@ -952,6 +968,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
         Ty::new_bound(tcx, debruijn, ty::BoundTy { var, kind: ty::BoundTyKind::Anon })
     }
 
+    fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Self {
+        Ty::new_canonical_bound(tcx, var)
+    }
+
     fn new_alias(
         interner: TyCtxt<'tcx>,
         kind: ty::AliasTyKind,
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 944bd9756a9..b276b993ec9 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -798,8 +798,8 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
                     match arg.kind() {
                         GenericArgKind::Type(ty) => match ty.kind() {
                             ty::Bound(debruijn, b) => {
-                                // We only allow a `ty::INNERMOST` index in generic parameters.
-                                assert_eq!(*debruijn, ty::INNERMOST);
+                                // We only allow a `ty::BoundVarIndexKind::Canonical` index in generic parameters.
+                                assert_eq!(*debruijn, ty::BoundVarIndexKind::Canonical);
                                 cvar == b.var
                             }
                             _ => false,
@@ -807,8 +807,8 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
 
                         GenericArgKind::Lifetime(r) => match r.kind() {
                             ty::ReBound(debruijn, b) => {
-                                // We only allow a `ty::INNERMOST` index in generic parameters.
-                                assert_eq!(debruijn, ty::INNERMOST);
+                                // We only allow a `ty::BoundVarIndexKind::Canonical` index in generic parameters.
+                                assert_eq!(debruijn, ty::BoundVarIndexKind::Canonical);
                                 cvar == b.var
                             }
                             _ => false,
@@ -816,8 +816,8 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
 
                         GenericArgKind::Const(ct) => match ct.kind() {
                             ty::ConstKind::Bound(debruijn, b) => {
-                                // We only allow a `ty::INNERMOST` index in generic parameters.
-                                assert_eq!(debruijn, ty::INNERMOST);
+                                // We only allow a `ty::BoundVarIndexKind::Canonical` index in generic parameters.
+                                assert_eq!(debruijn, ty::BoundVarIndexKind::Canonical);
                                 cvar == b.var
                             }
                             _ => false,
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index f0c47f257cc..e84ac56b31d 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -78,7 +78,9 @@ impl<'tcx> TyCtxt<'tcx> {
 
             fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
                 match r.kind() {
-                    ty::ReBound(debruijn, _) if debruijn < self.outer_index => {
+                    ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _)
+                        if debruijn < self.outer_index =>
+                    {
                         ControlFlow::Continue(())
                     }
                     _ => {
@@ -205,7 +207,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
     }
 
     fn visit_region(&mut self, r: ty::Region<'tcx>) {
-        if let ty::ReBound(debruijn, br) = r.kind() {
+        if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) = r.kind() {
             if debruijn == self.current_index {
                 self.regions.insert(br.kind);
             }
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
index 5a6bd2f413c..5e7a57d51a9 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
@@ -103,7 +103,7 @@ fn convert_to_hir_projections_and_truncate_for_capture(
             }
             ProjectionElem::UnwrapUnsafeBinder(_) => HirProjectionKind::UnwrapUnsafeBinder,
             // These do not affect anything, they just make sure we know the right type.
-            ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue,
+            ProjectionElem::OpaqueCast(_) => continue,
             ProjectionElem::Index(..)
             | ProjectionElem::ConstantIndex { .. }
             | ProjectionElem::Subslice { .. } => {
@@ -802,7 +802,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     ProjectionElem::Field(..)
                     | ProjectionElem::Downcast(..)
                     | ProjectionElem::OpaqueCast(..)
-                    | ProjectionElem::Subtype(..)
                     | ProjectionElem::ConstantIndex { .. }
                     | ProjectionElem::Subslice { .. }
                     | ProjectionElem::UnwrapUnsafeBinder(_) => (),
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 5eba474a60c..f6aaa65ad9f 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -210,6 +210,7 @@ impl DefUse {
 /// All of the caveats of `MaybeLiveLocals` apply.
 pub struct MaybeTransitiveLiveLocals<'a> {
     always_live: &'a DenseBitSet<Local>,
+    debuginfo_locals: &'a DenseBitSet<Local>,
 }
 
 impl<'a> MaybeTransitiveLiveLocals<'a> {
@@ -217,8 +218,48 @@ impl<'a> MaybeTransitiveLiveLocals<'a> {
     /// considered live.
     ///
     /// This should include at least all locals that are ever borrowed.
-    pub fn new(always_live: &'a DenseBitSet<Local>) -> Self {
-        MaybeTransitiveLiveLocals { always_live }
+    pub fn new(
+        always_live: &'a DenseBitSet<Local>,
+        debuginfo_locals: &'a DenseBitSet<Local>,
+    ) -> Self {
+        MaybeTransitiveLiveLocals { always_live, debuginfo_locals }
+    }
+
+    pub fn can_be_removed_if_dead<'tcx>(
+        stmt_kind: &StatementKind<'tcx>,
+        always_live: &DenseBitSet<Local>,
+        debuginfo_locals: &'a DenseBitSet<Local>,
+    ) -> Option<Place<'tcx>> {
+        // Compute the place that we are storing to, if any
+        let destination = match stmt_kind {
+            StatementKind::Assign(box (place, rvalue)) => (rvalue.is_safe_to_remove()
+                // FIXME: We are not sure how we should represent this debugging information for some statements,
+                // keep it for now.
+                && (!debuginfo_locals.contains(place.local)
+                    || (place.as_local().is_some() && stmt_kind.as_debuginfo().is_some())))
+            .then_some(*place),
+            StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
+                (!debuginfo_locals.contains(place.local)).then_some(**place)
+            }
+            StatementKind::FakeRead(_)
+            | StatementKind::StorageLive(_)
+            | StatementKind::StorageDead(_)
+            | StatementKind::Retag(..)
+            | StatementKind::AscribeUserType(..)
+            | StatementKind::PlaceMention(..)
+            | StatementKind::Coverage(..)
+            | StatementKind::Intrinsic(..)
+            | StatementKind::ConstEvalCounter
+            | StatementKind::BackwardIncompatibleDropHint { .. }
+            | StatementKind::Nop => None,
+        };
+        if let Some(destination) = destination
+            && !destination.is_indirect()
+            && !always_live.contains(destination.local)
+        {
+            return Some(destination);
+        }
+        None
     }
 }
 
@@ -243,32 +284,12 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
-        // Compute the place that we are storing to, if any
-        let destination = match &statement.kind {
-            StatementKind::Assign(assign) => assign.1.is_safe_to_remove().then_some(assign.0),
-            StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
-                Some(**place)
-            }
-            StatementKind::FakeRead(_)
-            | StatementKind::StorageLive(_)
-            | StatementKind::StorageDead(_)
-            | StatementKind::Retag(..)
-            | StatementKind::AscribeUserType(..)
-            | StatementKind::PlaceMention(..)
-            | StatementKind::Coverage(..)
-            | StatementKind::Intrinsic(..)
-            | StatementKind::ConstEvalCounter
-            | StatementKind::BackwardIncompatibleDropHint { .. }
-            | StatementKind::Nop => None,
-        };
-        if let Some(destination) = destination {
-            if !destination.is_indirect()
-                && !state.contains(destination.local)
-                && !self.always_live.contains(destination.local)
-            {
-                // This store is dead
-                return;
-            }
+        if let Some(destination) =
+            Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals)
+            && !state.contains(destination.local)
+        {
+            // This store is dead
+            return;
         }
         TransferFunction(state).visit_statement(statement, location);
     }
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 72d4cd72c2b..434f106302f 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -227,11 +227,8 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
                 ProjectionElem::UnwrapUnsafeBinder(_) => {}
                 // `OpaqueCast`:Only transmutes the type, so no moves there.
                 // `Downcast`  :Only changes information about a `Place` without moving.
-                // `Subtype`   :Only transmutes the type, so moves.
                 // So it's safe to skip these.
-                ProjectionElem::OpaqueCast(_)
-                | ProjectionElem::Subtype(_)
-                | ProjectionElem::Downcast(_, _) => (),
+                ProjectionElem::OpaqueCast(_) | ProjectionElem::Downcast(_, _) => (),
             }
             let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty;
             if !(self.filter)(elem_ty) {
diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs
index be4f84d64d0..a6a60fddf90 100644
--- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs
+++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs
@@ -40,8 +40,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> {
                 .new_temp(rval_ty, self.local_decls[place.as_ref().local].source_info.span);
             let new_place = Place::from(temp);
             self.patcher.add_assign(location, new_place, rvalue.clone());
-            let subtyped = new_place.project_deeper(&[ProjectionElem::Subtype(place_ty)], self.tcx);
-            *rvalue = Rvalue::Use(Operand::Move(subtyped));
+            *rvalue = Rvalue::Cast(CastKind::Subtype, Operand::Move(new_place), place_ty);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
index 4be67b873f7..b0bf7f484be 100644
--- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
+++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
@@ -36,7 +36,9 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck {
                         CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. },
                     )
                     | StatementKind::FakeRead(..)
-                    | StatementKind::BackwardIncompatibleDropHint { .. } => statement.make_nop(),
+                    | StatementKind::BackwardIncompatibleDropHint { .. } => {
+                        statement.make_nop(true)
+                    }
                     StatementKind::Assign(box (
                         _,
                         Rvalue::Cast(
diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs
index cddeefca681..f0bc286a940 100644
--- a/compiler/rustc_mir_transform/src/copy_prop.rs
+++ b/compiler/rustc_mir_transform/src/copy_prop.rs
@@ -138,7 +138,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
         if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind
             && self.storage_to_remove.contains(l)
         {
-            stmt.make_nop();
+            stmt.make_nop(true);
             return;
         }
 
@@ -150,7 +150,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
                 *rhs
             && lhs == rhs
         {
-            stmt.make_nop();
+            stmt.make_nop(true);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index c5cd06f170c..814eded910d 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -411,7 +411,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
             if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = s.kind
                 && self.remap.contains(l)
             {
-                s.make_nop();
+                s.make_nop(true);
             }
         }
 
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 491e910ff6f..e970f7ff81a 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -440,7 +440,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
                     FlatSet::Top => FlatSet::Top,
                 }
             }
-            Rvalue::Cast(CastKind::Transmute, operand, _) => {
+            Rvalue::Cast(CastKind::Transmute | CastKind::Subtype, operand, _) => {
                 match self.eval_operand(operand, state) {
                     FlatSet::Elem(op) => self.wrap_immediate(*op),
                     FlatSet::Bottom => FlatSet::Bottom,
diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
index eea2b0990d7..732c3dcd44a 100644
--- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs
+++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
@@ -22,21 +22,22 @@ use rustc_mir_dataflow::impls::{
     LivenessTransferFunction, MaybeTransitiveLiveLocals, borrowed_locals,
 };
 
+use crate::simplify::UsedInStmtLocals;
 use crate::util::is_within_packed;
 
 /// Performs the optimization on the body
 ///
 /// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this
 /// body. It can be generated via the [`borrowed_locals`] function.
-fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+/// Returns true if any instruction is eliminated.
+fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
     let borrowed_locals = borrowed_locals(body);
 
     // If the user requests complete debuginfo, mark the locals that appear in it as live, so
     // we don't remove assignments to them.
-    let mut always_live = debuginfo_locals(body);
-    always_live.union(&borrowed_locals);
+    let debuginfo_locals = debuginfo_locals(body);
 
-    let mut live = MaybeTransitiveLiveLocals::new(&always_live)
+    let mut live = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals)
         .iterate_to_fixpoint(tcx, body, None)
         .into_results_cursor(body);
 
@@ -75,47 +76,36 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         }
 
         for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
-            let loc = Location { block: bb, statement_index };
-            if let StatementKind::Assign(assign) = &statement.kind {
-                if !assign.1.is_safe_to_remove() {
-                    continue;
-                }
-            }
-            match &statement.kind {
-                StatementKind::Assign(box (place, _))
-                | StatementKind::SetDiscriminant { place: box place, .. }
-                | StatementKind::Deinit(box place) => {
-                    if !place.is_indirect() && !always_live.contains(place.local) {
-                        live.seek_before_primary_effect(loc);
-                        if !live.get().contains(place.local) {
-                            patch.push(loc);
-                        }
-                    }
-                }
-                StatementKind::Retag(_, _)
-                | StatementKind::StorageLive(_)
-                | StatementKind::StorageDead(_)
-                | StatementKind::Coverage(_)
-                | StatementKind::Intrinsic(_)
-                | StatementKind::ConstEvalCounter
-                | StatementKind::PlaceMention(_)
-                | StatementKind::BackwardIncompatibleDropHint { .. }
-                | StatementKind::Nop => {}
-
-                StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
-                    bug!("{:?} not found in this MIR phase!", statement.kind)
+            if let Some(destination) = MaybeTransitiveLiveLocals::can_be_removed_if_dead(
+                &statement.kind,
+                &borrowed_locals,
+                &debuginfo_locals,
+            ) {
+                let loc = Location { block: bb, statement_index };
+                live.seek_before_primary_effect(loc);
+                if !live.get().contains(destination.local) {
+                    let drop_debuginfo = !debuginfo_locals.contains(destination.local);
+                    // When eliminating a dead statement, we need to address
+                    // the debug information for that statement.
+                    assert!(
+                        drop_debuginfo || statement.kind.as_debuginfo().is_some(),
+                        "don't know how to retain the debug information for {:?}",
+                        statement.kind
+                    );
+                    patch.push((loc, drop_debuginfo));
                 }
             }
         }
     }
 
     if patch.is_empty() && call_operands_to_move.is_empty() {
-        return;
+        return false;
     }
+    let eliminated = !patch.is_empty();
 
     let bbs = body.basic_blocks.as_mut_preserves_cfg();
-    for Location { block, statement_index } in patch {
-        bbs[block].statements[statement_index].make_nop();
+    for (Location { block, statement_index }, drop_debuginfo) in patch {
+        bbs[block].statements[statement_index].make_nop(drop_debuginfo);
     }
     for (block, argument_index) in call_operands_to_move {
         let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else {
@@ -125,6 +115,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         let Operand::Copy(place) = *arg else { bug!() };
         *arg = Operand::Move(place);
     }
+
+    eliminated
 }
 
 pub(super) enum DeadStoreElimination {
@@ -145,7 +137,12 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination {
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        eliminate(tcx, body);
+        if eliminate(tcx, body) {
+            UsedInStmtLocals::new(body).remove_unused_storage_annotations(body);
+            for data in body.basic_blocks.as_mut_preserves_cfg() {
+                data.strip_nops();
+            }
+        }
     }
 
     fn is_required(&self) -> bool {
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 74c22ff10c1..1f38433fa5a 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -276,7 +276,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> {
             StatementKind::StorageDead(local) | StatementKind::StorageLive(local)
                 if self.merged_locals.contains(*local) =>
             {
-                statement.make_nop();
+                statement.make_nop(true);
                 return;
             }
             _ => (),
@@ -291,7 +291,7 @@ impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> {
                         // (this includes the original statement we wanted to eliminate).
                         if dest == place {
                             debug!("{:?} turned into self-assignment, deleting", location);
-                            statement.make_nop();
+                            statement.make_nop(true);
                         }
                     }
                     _ => {}
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 29f6879aacd..3ff8dc6dbb3 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -656,7 +656,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?;
                     res.into()
                 }
-                CastKind::Transmute => {
+                CastKind::Transmute | CastKind::Subtype => {
                     let value = self.evaluated[value].as_ref()?;
                     // `offset` for immediates generally only supports projections that match the
                     // type of the immediate. However, as a HACK, we exploit that it can also do
@@ -788,7 +788,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 ProjectionElem::Subslice { from, to, from_end }
             }
             ProjectionElem::OpaqueCast(_) => ProjectionElem::OpaqueCast(()),
-            ProjectionElem::Subtype(_) => ProjectionElem::Subtype(()),
             ProjectionElem::UnwrapUnsafeBinder(_) => ProjectionElem::UnwrapUnsafeBinder(()),
         };
 
@@ -1878,7 +1877,7 @@ impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> {
             StatementKind::StorageLive(l) | StatementKind::StorageDead(l)
                 if self.reused_locals.contains(l) =>
             {
-                stmt.make_nop()
+                stmt.make_nop(true)
             }
             _ => self.super_statement(stmt, loc),
         }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 3d49eb4e8ef..8593e25d6aa 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -21,7 +21,7 @@ use tracing::{debug, instrument, trace, trace_span};
 
 use crate::cost_checker::{CostChecker, is_call_like};
 use crate::deref_separator::deref_finder;
-use crate::simplify::simplify_cfg;
+use crate::simplify::{UsedInStmtLocals, simplify_cfg};
 use crate::validate::validate_types;
 use crate::{check_inline, util};
 
@@ -935,7 +935,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
         in_cleanup_block: false,
         return_block,
         tcx,
-        always_live_locals: DenseBitSet::new_filled(callee_body.local_decls.len()),
+        always_live_locals: UsedInStmtLocals::new(&callee_body).locals,
     };
 
     // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones
@@ -995,6 +995,10 @@ fn inline_call<'tcx, I: Inliner<'tcx>>(
         // people working on rust can build with or without debuginfo while
         // still getting consistent results from the mir-opt tests.
         caller_body.var_debug_info.append(&mut callee_body.var_debug_info);
+    } else {
+        for bb in callee_body.basic_blocks_mut() {
+            bb.drop_debuginfo();
+        }
     }
     caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut());
 
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 5fffba55f17..93abc0f8860 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -637,7 +637,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                     let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?;
                     res.into()
                 }
-                CastKind::Transmute => {
+                CastKind::Transmute | CastKind::Subtype => {
                     let value = self.eval_operand(value)?;
                     let to = self.ecx.layout_of(to).ok()?;
                     // `offset` for immediates only supports scalar/scalar-pair ABIs,
diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs
index 1a91d6bd7da..1b90e9158f6 100644
--- a/compiler/rustc_mir_transform/src/large_enums.rs
+++ b/compiler/rustc_mir_transform/src/large_enums.rs
@@ -156,7 +156,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt {
                     patch.add_statement(location, stmt);
                 }
 
-                st.make_nop();
+                st.make_nop(true);
             }
         }
 
diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs
index cc8ea76011b..2c535d011a0 100644
--- a/compiler/rustc_mir_transform/src/patch.rs
+++ b/compiler/rustc_mir_transform/src/patch.rs
@@ -270,7 +270,7 @@ impl<'tcx> MirPatch<'tcx> {
         body.local_decls.extend(self.new_locals);
 
         for loc in self.nop_statements {
-            bbs[loc.block].statements[loc.statement_index].make_nop();
+            bbs[loc.block].statements[loc.statement_index].make_nop(true);
         }
 
         let mut new_statements = self.new_statements;
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index a0b0c8c990f..c7dc18a4a13 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -292,7 +292,6 @@ impl<'tcx> Validator<'_, 'tcx> {
         match elem {
             // Recurse directly.
             ProjectionElem::ConstantIndex { .. }
-            | ProjectionElem::Subtype(_)
             | ProjectionElem::Subslice { .. }
             | ProjectionElem::UnwrapUnsafeBinder(_) => {}
 
@@ -1050,7 +1049,7 @@ fn promote_candidates<'tcx>(
     // Eliminate assignments to, and drops of promoted temps.
     let promoted = |index: Local| temps[index] == TempState::PromotedOut;
     for block in body.basic_blocks_mut() {
-        block.statements.retain(|statement| match &statement.kind {
+        block.retain_statements(|statement| match &statement.kind {
             StatementKind::Assign(box (place, _)) => {
                 if let Some(index) = place.as_local() {
                     !promoted(index)
diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs
index b9d6e74ecae..deb0a146476 100644
--- a/compiler/rustc_mir_transform/src/ref_prop.rs
+++ b/compiler/rustc_mir_transform/src/ref_prop.rs
@@ -435,7 +435,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
             StatementKind::StorageLive(l) | StatementKind::StorageDead(l)
                 if self.storage_to_remove.contains(l) =>
             {
-                stmt.make_nop();
+                stmt.make_nop(true);
             }
             // Do not remove assignments as they may still be useful for debuginfo.
             _ => self.super_statement(stmt, loc),
diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs
index cb598ceb4df..d56b51bb496 100644
--- a/compiler/rustc_mir_transform/src/remove_place_mention.rs
+++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs
@@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention {
     fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         trace!("Running RemovePlaceMention on {:?}", body.source);
         for data in body.basic_blocks.as_mut_preserves_cfg() {
-            data.statements.retain(|statement| match statement.kind {
+            data.retain_statements(|statement| match statement.kind {
                 StatementKind::PlaceMention(..) | StatementKind::Nop => false,
                 _ => true,
             })
diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs
index 1ae33c00968..cb97d2c865a 100644
--- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs
+++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs
@@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers {
     fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         trace!("Running RemoveStorageMarkers on {:?}", body.source);
         for data in body.basic_blocks.as_mut_preserves_cfg() {
-            data.statements.retain(|statement| match statement.kind {
+            data.retain_statements(|statement| match statement.kind {
                 StatementKind::StorageLive(..)
                 | StatementKind::StorageDead(..)
                 | StatementKind::Nop => false,
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index c4dc8638b26..90c1b3520b9 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -141,7 +141,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
             && let ty = place_for_ty.ty(self.local_decls, self.tcx).ty
             && self.known_to_be_zst(ty)
         {
-            statement.make_nop();
+            statement.make_nop(true);
         } else {
             self.super_statement(statement, loc);
         }
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index 75917d23883..8b5efb74205 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -35,10 +35,12 @@
 //! pre-"runtime" MIR!
 
 use itertools::Itertools as _;
+use rustc_index::bit_set::DenseBitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
+use rustc_mir_dataflow::debuginfo::debuginfo_locals;
 use rustc_span::DUMMY_SP;
 use smallvec::SmallVec;
 use tracing::{debug, trace};
@@ -142,7 +144,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
         // statements itself to avoid moving the (relatively) large statements twice.
         // We do not push the statements directly into the target block (`bb`) as that is slower
         // due to additional reallocations
-        let mut merged_blocks = Vec::new();
+        let mut merged_blocks: Vec<BasicBlock> = Vec::new();
         let mut outer_changed = false;
         loop {
             let mut changed = false;
@@ -157,8 +159,9 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
                 let mut terminator =
                     self.basic_blocks[bb].terminator.take().expect("invalid terminator state");
 
-                terminator
-                    .successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed));
+                terminator.successors_mut(|successor| {
+                    self.collapse_goto_chain(successor, &mut changed);
+                });
 
                 let mut inner_changed = true;
                 merged_blocks.clear();
@@ -175,10 +178,18 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
                 if statements_to_merge > 0 {
                     let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements);
                     statements.reserve(statements_to_merge);
+                    let mut parent_bb_last_debuginfos =
+                        std::mem::take(&mut self.basic_blocks[bb].after_last_stmt_debuginfos);
                     for &from in &merged_blocks {
+                        if let Some(stmt) = self.basic_blocks[from].statements.first_mut() {
+                            stmt.debuginfos.prepend(&mut parent_bb_last_debuginfos);
+                        }
                         statements.append(&mut self.basic_blocks[from].statements);
+                        parent_bb_last_debuginfos =
+                            std::mem::take(&mut self.basic_blocks[from].after_last_stmt_debuginfos);
                     }
                     self.basic_blocks[bb].statements = statements;
+                    self.basic_blocks[bb].after_last_stmt_debuginfos = parent_bb_last_debuginfos;
                 }
 
                 self.basic_blocks[bb].terminator = Some(terminator);
@@ -218,10 +229,14 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
         // goto chains. We should probably benchmark different sizes.
         let mut terminators: SmallVec<[_; 1]> = Default::default();
         let mut current = *start;
+        // If each successor has only one predecessor, it's a trivial goto chain.
+        // We can move all debuginfos to the last basic block.
+        let mut trivial_goto_chain = true;
         while let Some(terminator) = self.take_terminator_if_simple_goto(current) {
             let Terminator { kind: TerminatorKind::Goto { target }, .. } = terminator else {
                 unreachable!();
             };
+            trivial_goto_chain &= self.pred_count[target] == 1;
             terminators.push((current, terminator));
             current = target;
         }
@@ -233,6 +248,17 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
             else {
                 unreachable!();
             };
+            if trivial_goto_chain {
+                let mut pred_debuginfos =
+                    std::mem::take(&mut self.basic_blocks[current].after_last_stmt_debuginfos);
+                let debuginfos = if let Some(stmt) = self.basic_blocks[last].statements.first_mut()
+                {
+                    &mut stmt.debuginfos
+                } else {
+                    &mut self.basic_blocks[last].after_last_stmt_debuginfos
+                };
+                debuginfos.prepend(&mut pred_debuginfos);
+            }
             *changed |= *target != last;
             *target = last;
             debug!("collapsing goto chain from {:?} to {:?}", current, target);
@@ -303,7 +329,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
 
     fn strip_nops(&mut self) {
         for blk in self.basic_blocks.iter_mut() {
-            blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop))
+            blk.strip_nops();
         }
     }
 }
@@ -476,17 +502,22 @@ fn make_local_map<V>(
 /// Keeps track of used & unused locals.
 struct UsedLocals {
     increment: bool,
-    arg_count: u32,
     use_count: IndexVec<Local, u32>,
+    always_used: DenseBitSet<Local>,
 }
 
 impl UsedLocals {
     /// Determines which locals are used & unused in the given body.
     fn new(body: &Body<'_>) -> Self {
+        let mut always_used = debuginfo_locals(body);
+        always_used.insert(RETURN_PLACE);
+        for arg in body.args_iter() {
+            always_used.insert(arg);
+        }
         let mut this = Self {
             increment: true,
-            arg_count: body.arg_count.try_into().unwrap(),
             use_count: IndexVec::from_elem(0, &body.local_decls),
+            always_used,
         };
         this.visit_body(body);
         this
@@ -494,10 +525,16 @@ impl UsedLocals {
 
     /// Checks if local is used.
     ///
-    /// Return place and arguments are always considered used.
+    /// Return place, arguments, var debuginfo are always considered used.
     fn is_used(&self, local: Local) -> bool {
-        trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]);
-        local.as_u32() <= self.arg_count || self.use_count[local] != 0
+        trace!(
+            "is_used({:?}): use_count: {:?}, always_used: {}",
+            local,
+            self.use_count[local],
+            self.always_used.contains(local)
+        );
+        // To keep things simple, we don't handle debugging information here, these are in DSE.
+        self.always_used.contains(local) || self.use_count[local] != 0
     }
 
     /// Updates the use counts to reflect the removal of given statement.
@@ -539,10 +576,10 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
                 self.super_statement(statement, location);
             }
 
-            StatementKind::ConstEvalCounter | StatementKind::Nop => {}
-
-            StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {}
-
+            StatementKind::ConstEvalCounter
+            | StatementKind::Nop
+            | StatementKind::StorageLive(..)
+            | StatementKind::StorageDead(..) => {}
             StatementKind::Assign(box (ref place, ref rvalue)) => {
                 if rvalue.is_safe_to_remove() {
                     self.visit_lhs(place, location);
@@ -560,7 +597,10 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
         }
     }
 
-    fn visit_local(&mut self, local: Local, _ctx: PlaceContext, _location: Location) {
+    fn visit_local(&mut self, local: Local, ctx: PlaceContext, _location: Location) {
+        if matches!(ctx, PlaceContext::NonUse(_)) {
+            return;
+        }
         if self.increment {
             self.use_count[local] += 1;
         } else {
@@ -583,28 +623,26 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod
 
         for data in body.basic_blocks.as_mut_preserves_cfg() {
             // Remove unnecessary StorageLive and StorageDead annotations.
-            data.statements.retain(|statement| {
-                let keep = match &statement.kind {
+            for statement in data.statements.iter_mut() {
+                let keep_statement = match &statement.kind {
                     StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
                         used_locals.is_used(*local)
                     }
-                    StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local),
-
-                    StatementKind::SetDiscriminant { place, .. }
-                    | StatementKind::BackwardIncompatibleDropHint { place, reason: _ }
-                    | StatementKind::Deinit(place) => used_locals.is_used(place.local),
-                    StatementKind::Nop => false,
-                    _ => true,
+                    StatementKind::Assign(box (place, _))
+                    | StatementKind::SetDiscriminant { box place, .. }
+                    | StatementKind::BackwardIncompatibleDropHint { box place, .. }
+                    | StatementKind::Deinit(box place) => used_locals.is_used(place.local),
+                    _ => continue,
                 };
-
-                if !keep {
-                    trace!("removing statement {:?}", statement);
-                    modified = true;
-                    used_locals.statement_removed(statement);
+                if keep_statement {
+                    continue;
                 }
-
-                keep
-            });
+                trace!("removing statement {:?}", statement);
+                modified = true;
+                used_locals.statement_removed(statement);
+                statement.make_nop(true);
+            }
+            data.strip_nops();
         }
     }
 }
@@ -619,7 +657,62 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
         self.tcx
     }
 
+    fn visit_statement_debuginfo(
+        &mut self,
+        stmt_debuginfo: &mut StmtDebugInfo<'tcx>,
+        location: Location,
+    ) {
+        match stmt_debuginfo {
+            StmtDebugInfo::AssignRef(local, place) => {
+                if place.as_ref().accessed_locals().any(|local| self.map[local].is_none()) {
+                    *stmt_debuginfo = StmtDebugInfo::InvalidAssign(*local);
+                }
+            }
+            StmtDebugInfo::InvalidAssign(_) => {}
+        }
+        self.super_statement_debuginfo(stmt_debuginfo, location);
+    }
+
     fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
         *l = self.map[*l].unwrap();
     }
 }
+
+pub(crate) struct UsedInStmtLocals {
+    pub(crate) locals: DenseBitSet<Local>,
+}
+
+impl UsedInStmtLocals {
+    pub(crate) fn new(body: &Body<'_>) -> Self {
+        let mut this = Self { locals: DenseBitSet::new_empty(body.local_decls.len()) };
+        this.visit_body(body);
+        this
+    }
+
+    pub(crate) fn remove_unused_storage_annotations<'tcx>(&self, body: &mut Body<'tcx>) {
+        for data in body.basic_blocks.as_mut_preserves_cfg() {
+            // Remove unnecessary StorageLive and StorageDead annotations.
+            for statement in data.statements.iter_mut() {
+                let keep_statement = match &statement.kind {
+                    StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
+                        self.locals.contains(*local)
+                    }
+                    _ => continue,
+                };
+                if keep_statement {
+                    continue;
+                }
+                statement.make_nop(true);
+            }
+        }
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for UsedInStmtLocals {
+    fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
+        if matches!(context, PlaceContext::NonUse(_)) {
+            return;
+        }
+        self.locals.insert(local);
+    }
+}
diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
index c60eb566521..4597439e269 100644
--- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
+++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
@@ -76,7 +76,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral {
             // delete comparison statement if it the value being switched on was moved, which means
             // it can not be user later on
             if opt.can_remove_bin_op_stmt {
-                bb.statements[opt.bin_op_stmt_idx].make_nop();
+                bb.statements[opt.bin_op_stmt_idx].make_nop(true);
             } else {
                 // if the integer being compared to a const integral is being moved into the
                 // comparison, e.g `_2 = Eq(move _3, const 'x');`
@@ -136,7 +136,7 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral {
         }
 
         for (idx, bb_idx) in storage_deads_to_remove {
-            body.basic_blocks_mut()[bb_idx].statements[idx].make_nop();
+            body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(true);
         }
 
         for (idx, stmt) in storage_deads_to_insert {
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
index 38769885f36..99f10b8d91d 100644
--- a/compiler/rustc_mir_transform/src/sroa.rs
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -318,7 +318,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
                     for (_, _, fl) in final_locals {
                         self.patch.add_statement(location, StatementKind::StorageLive(fl));
                     }
-                    statement.make_nop();
+                    statement.make_nop(true);
                 }
                 return;
             }
@@ -327,7 +327,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
                     for (_, _, fl) in final_locals {
                         self.patch.add_statement(location, StatementKind::StorageDead(fl));
                     }
-                    statement.make_nop();
+                    statement.make_nop(true);
                 }
                 return;
             }
@@ -337,7 +337,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
                         self.patch
                             .add_statement(location, StatementKind::Deinit(Box::new(fl.into())));
                     }
-                    statement.make_nop();
+                    statement.make_nop(true);
                     return;
                 }
             }
@@ -367,7 +367,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
                             );
                         }
                     }
-                    statement.make_nop();
+                    statement.make_nop(true);
                     return;
                 }
             }
@@ -429,7 +429,7 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
                             StatementKind::Assign(Box::new((new_local.into(), rvalue))),
                         );
                     }
-                    statement.make_nop();
+                    statement.make_nop(true);
                     return;
                 }
             }
diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs
index 9ede8aa79c4..7fec25ccb52 100644
--- a/compiler/rustc_mir_transform/src/strip_debuginfo.rs
+++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs
@@ -1,5 +1,6 @@
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
+use rustc_mir_dataflow::debuginfo::debuginfo_locals;
 use rustc_session::config::MirStripDebugInfo;
 
 /// Conditionally remove some of the VarDebugInfo in MIR.
@@ -30,6 +31,22 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo {
                     if place.local.as_usize() <= body.arg_count && place.local != RETURN_PLACE,
             )
         });
+
+        let debuginfo_locals = debuginfo_locals(body);
+        for data in body.basic_blocks.as_mut_preserves_cfg() {
+            for stmt in data.statements.iter_mut() {
+                stmt.debuginfos.retain(|debuginfo| match debuginfo {
+                    StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => {
+                        debuginfo_locals.contains(*local)
+                    }
+                });
+            }
+            data.after_last_stmt_debuginfos.retain(|debuginfo| match debuginfo {
+                StmtDebugInfo::AssignRef(local, _) | StmtDebugInfo::InvalidAssign(local) => {
+                    debuginfo_locals.contains(*local)
+                }
+            });
+        }
     }
 
     fn is_required(&self) -> bool {
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index c8a9a88dc3f..cbabb982df8 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -814,22 +814,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     }
                 }
             }
-            ProjectionElem::Subtype(ty) => {
-                if !util::sub_types(
-                    self.tcx,
-                    self.typing_env,
-                    ty,
-                    place_ref.ty(&self.body.local_decls, self.tcx).ty,
-                ) {
-                    self.fail(
-                        location,
-                        format!(
-                            "Failed subtyping {ty} and {}",
-                            place_ref.ty(&self.body.local_decls, self.tcx).ty
-                        ),
-                    )
-                }
-            }
             ProjectionElem::UnwrapUnsafeBinder(unwrapped_ty) => {
                 let binder_ty = place_ref.ty(&self.body.local_decls, self.tcx);
                 let ty::UnsafeBinder(binder_ty) = *binder_ty.ty.kind() else {
@@ -1331,6 +1315,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             );
                         }
                     }
+                    CastKind::Subtype => {
+                        if !util::sub_types(self.tcx, self.typing_env, op_ty, *target_type) {
+                            self.fail(
+                                location,
+                                format!("Failed subtyping {op_ty} and {target_type}"),
+                            )
+                        }
+                    }
                 }
             }
             Rvalue::NullaryOp(NullOp::OffsetOf(indices), container) => {
diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs
index b25671d676b..9162284422d 100644
--- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs
@@ -74,12 +74,10 @@ pub(super) struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner
     /// we set the `sub_root` of the second variable to the position of the first.
     /// Otherwise the `sub_root` of each type variable is just its own position.
     sub_root_lookup_table: HashMap<ty::TyVid, usize>,
-    binder_index: ty::DebruijnIndex,
 
-    /// We only use the debruijn index during lookup. We don't need to
-    /// track the `variables` as each generic arg only results in a single
-    /// bound variable regardless of how many times it is encountered.
-    cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>,
+    /// We can simply cache based on the ty itself, because we use
+    /// `ty::BoundVarIndexKind::Canonical`.
+    cache: HashMap<I::Ty, I::Ty>,
 }
 
 impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
@@ -97,7 +95,6 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
             variable_lookup_table: Default::default(),
             sub_root_lookup_table: Default::default(),
             var_kinds: Vec::new(),
-            binder_index: ty::INNERMOST,
 
             cache: Default::default(),
         };
@@ -141,12 +138,10 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
                         variable_lookup_table: Default::default(),
                         sub_root_lookup_table: Default::default(),
                         var_kinds: Vec::new(),
-                        binder_index: ty::INNERMOST,
 
                         cache: Default::default(),
                     };
                     let param_env = param_env.fold_with(&mut env_canonicalizer);
-                    debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
                     debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
                     CanonicalParamEnvCacheEntry {
                         param_env,
@@ -175,12 +170,10 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
                 variable_lookup_table: Default::default(),
                 sub_root_lookup_table: Default::default(),
                 var_kinds: Vec::new(),
-                binder_index: ty::INNERMOST,
 
                 cache: Default::default(),
             };
             let param_env = param_env.fold_with(&mut env_canonicalizer);
-            debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
             debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
             (param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds)
         }
@@ -212,7 +205,6 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
             variable_lookup_table,
             sub_root_lookup_table: Default::default(),
             var_kinds,
-            binder_index: ty::INNERMOST,
 
             // We do not reuse the cache as it may contain entries whose canonicalized
             // value contains `'static`. While we could alternatively handle this by
@@ -409,7 +401,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
 
         let var = self.get_or_insert_bound_var(t, kind);
 
-        Ty::new_anon_bound(self.cx(), self.binder_index, var)
+        Ty::new_canonical_bound(self.cx(), var)
     }
 }
 
@@ -418,16 +410,6 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
         self.delegate.cx()
     }
 
-    fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
-    where
-        T: TypeFoldable<I>,
-    {
-        self.binder_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.binder_index.shift_out(1);
-        t
-    }
-
     fn fold_region(&mut self, r: I::Region) -> I::Region {
         let kind = match r.kind() {
             ty::ReBound(..) => return r,
@@ -491,15 +473,15 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
 
         let var = self.get_or_insert_bound_var(r, kind);
 
-        Region::new_anon_bound(self.cx(), self.binder_index, var)
+        Region::new_canonical_bound(self.cx(), var)
     }
 
     fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
-        if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
+        if let Some(&ty) = self.cache.get(&t) {
             ty
         } else {
             let res = self.inner_fold_ty(t);
-            let old = self.cache.insert((self.binder_index, t), res);
+            let old = self.cache.insert(t, res);
             assert_eq!(old, None);
             res
         }
@@ -552,7 +534,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
 
         let var = self.get_or_insert_bound_var(c, kind);
 
-        Const::new_anon_bound(self.cx(), self.binder_index, var)
+        Const::new_canonical_bound(self.cx(), var)
     }
 
     fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs
index b036ee6df7e..b4cea8701d8 100644
--- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs
@@ -165,25 +165,25 @@ where
                 // and only use it for placeholders. We need to handle the
                 // `sub_root` of type inference variables which would make this
                 // more involved. They are also a lot rarer than region variables.
-                if let ty::Bound(debruijn, b) = t.kind()
+                if let ty::Bound(index_kind, b) = t.kind()
                     && !matches!(
                         response.variables.get(b.var().as_usize()).unwrap(),
                         CanonicalVarKind::Ty { .. }
                     )
                 {
-                    assert_eq!(debruijn, ty::INNERMOST);
+                    assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical));
                     opt_values[b.var()] = Some(*original_value);
                 }
             }
             ty::GenericArgKind::Lifetime(r) => {
-                if let ty::ReBound(debruijn, br) = r.kind() {
-                    assert_eq!(debruijn, ty::INNERMOST);
+                if let ty::ReBound(index_kind, br) = r.kind() {
+                    assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical));
                     opt_values[br.var()] = Some(*original_value);
                 }
             }
             ty::GenericArgKind::Const(c) => {
-                if let ty::ConstKind::Bound(debruijn, bv) = c.kind() {
-                    assert_eq!(debruijn, ty::INNERMOST);
+                if let ty::ConstKind::Bound(index_kind, bv) = c.kind() {
+                    assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical));
                     opt_values[bv.var()] = Some(*original_value);
                 }
             }
diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs
index c88fb8defae..c8016759f23 100644
--- a/compiler/rustc_next_trait_solver/src/placeholder.rs
+++ b/compiler/rustc_next_trait_solver/src/placeholder.rs
@@ -90,7 +90,7 @@ where
 
     fn fold_region(&mut self, r: I::Region) -> I::Region {
         match r.kind() {
-            ty::ReBound(debruijn, _)
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _)
                 if debruijn.as_usize()
                     >= self.current_index.as_usize() + self.universe_indices.len() =>
             {
@@ -99,7 +99,9 @@ where
                     self.universe_indices
                 );
             }
-            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br)
+                if debruijn >= self.current_index =>
+            {
                 let universe = self.universe_for(debruijn);
                 let p = PlaceholderLike::new(universe, br);
                 self.mapped_regions.insert(p, br);
@@ -111,7 +113,7 @@ where
 
     fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
         match t.kind() {
-            ty::Bound(debruijn, _)
+            ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), _)
                 if debruijn.as_usize() + 1
                     > self.current_index.as_usize() + self.universe_indices.len() =>
             {
@@ -120,7 +122,9 @@ where
                     self.universe_indices
                 );
             }
-            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+            ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty)
+                if debruijn >= self.current_index =>
+            {
                 let universe = self.universe_for(debruijn);
                 let p = PlaceholderLike::new(universe, bound_ty);
                 self.mapped_types.insert(p, bound_ty);
@@ -133,7 +137,7 @@ where
 
     fn fold_const(&mut self, ct: I::Const) -> I::Const {
         match ct.kind() {
-            ty::ConstKind::Bound(debruijn, _)
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), _)
                 if debruijn.as_usize() + 1
                     > self.current_index.as_usize() + self.universe_indices.len() =>
             {
@@ -142,7 +146,9 @@ where
                     self.universe_indices
                 );
             }
-            ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const)
+                if debruijn >= self.current_index =>
+            {
                 let universe = self.universe_for(debruijn);
                 let p = PlaceholderLike::new(universe, bound_const);
                 self.mapped_consts.insert(p, bound_const);
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 1cb1618e584..8b87e4d9690 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -2748,28 +2748,7 @@ impl<'a> Parser<'a> {
         if token::Colon != self.token.kind {
             return first_pat;
         }
-        if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
-            || !self.look_ahead(1, |token| token.is_non_reserved_ident())
-        {
-            let mut snapshot_type = self.create_snapshot_for_diagnostic();
-            snapshot_type.bump(); // `:`
-            match snapshot_type.parse_ty() {
-                Err(inner_err) => {
-                    inner_err.cancel();
-                }
-                Ok(ty) => {
-                    let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
-                        return first_pat;
-                    };
-                    err.span_label(ty.span, "specifying the type of a pattern isn't supported");
-                    self.restore_snapshot(snapshot_type);
-                    let span = first_pat.span.to(ty.span);
-                    first_pat = self.mk_pat(span, PatKind::Wild);
-                    err.emit();
-                }
-            }
-            return first_pat;
-        }
+
         // The pattern looks like it might be a path with a `::` -> `:` typo:
         // `match foo { bar:baz => {} }`
         let colon_span = self.token.span;
@@ -2857,7 +2836,13 @@ impl<'a> Parser<'a> {
                                 Applicability::MaybeIncorrect,
                             );
                         } else {
-                            first_pat = self.mk_pat(new_span, PatKind::Wild);
+                            first_pat = self.mk_pat(
+                                new_span,
+                                PatKind::Err(
+                                    self.dcx()
+                                        .span_delayed_bug(colon_span, "recovered bad path pattern"),
+                                ),
+                            );
                         }
                         self.restore_snapshot(snapshot_pat);
                     }
@@ -2870,7 +2855,14 @@ impl<'a> Parser<'a> {
                         err.span_label(ty.span, "specifying the type of a pattern isn't supported");
                         self.restore_snapshot(snapshot_type);
                         let new_span = first_pat.span.to(ty.span);
-                        first_pat = self.mk_pat(new_span, PatKind::Wild);
+                        first_pat =
+                            self.mk_pat(
+                                new_span,
+                                PatKind::Err(self.dcx().span_delayed_bug(
+                                    colon_span,
+                                    "recovered bad pattern with type",
+                                )),
+                            );
                     }
                 }
                 err.emit();
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 81a5d48d94e..8046abcd70b 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -3612,7 +3612,7 @@ impl<'a> Parser<'a> {
         self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1)
     }
 
-    fn is_certainly_not_a_block(&self) -> bool {
+    fn is_likely_struct_lit(&self) -> bool {
         // `{ ident, ` and `{ ident: ` cannot start a block.
         self.look_ahead(1, |t| t.is_ident())
             && self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon)
@@ -3624,24 +3624,50 @@ impl<'a> Parser<'a> {
         path: &ast::Path,
     ) -> Option<PResult<'a, Box<Expr>>> {
         let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
-        if struct_allowed || self.is_certainly_not_a_block() {
-            if let Err(err) = self.expect(exp!(OpenBrace)) {
-                return Some(Err(err));
+        match (struct_allowed, self.is_likely_struct_lit()) {
+            // A struct literal isn't expected and one is pretty much assured not to be present. The
+            // only situation that isn't detected is when a struct with a single field was attempted
+            // in a place where a struct literal wasn't expected, but regular parser errors apply.
+            // Happy path.
+            (false, false) => None,
+            (true, _) => {
+                // A struct is accepted here, try to parse it and rely on `parse_expr_struct` for
+                // any kind of recovery. Happy path.
+                if let Err(err) = self.expect(exp!(OpenBrace)) {
+                    return Some(Err(err));
+                }
+                Some(self.parse_expr_struct(qself.clone(), path.clone(), true))
             }
-            let expr = self.parse_expr_struct(qself.clone(), path.clone(), true);
-            if let (Ok(expr), false) = (&expr, struct_allowed) {
-                // This is a struct literal, but we don't can't accept them here.
-                self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
-                    span: expr.span,
-                    sub: errors::StructLiteralNotAllowedHereSugg {
-                        left: path.span.shrink_to_lo(),
-                        right: expr.span.shrink_to_hi(),
-                    },
-                });
+            (false, true) => {
+                // We have something like `match foo { bar,` or `match foo { bar:`, which means the
+                // user might have meant to write a struct literal as part of the `match`
+                // discriminant. This is done purely for error recovery.
+                let snapshot = self.create_snapshot_for_diagnostic();
+                if let Err(err) = self.expect(exp!(OpenBrace)) {
+                    return Some(Err(err));
+                }
+                match self.parse_expr_struct(qself.clone(), path.clone(), false) {
+                    Ok(expr) => {
+                        // This is a struct literal, but we don't accept them here.
+                        self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
+                            span: expr.span,
+                            sub: errors::StructLiteralNotAllowedHereSugg {
+                                left: path.span.shrink_to_lo(),
+                                right: expr.span.shrink_to_hi(),
+                            },
+                        });
+                        Some(Ok(expr))
+                    }
+                    Err(err) => {
+                        // We couldn't parse a valid struct, rollback and let the parser emit an
+                        // error elsewhere.
+                        err.cancel();
+                        self.restore_snapshot(snapshot);
+                        None
+                    }
+                }
             }
-            return Some(expr);
         }
-        None
     }
 
     pub(super) fn parse_struct_fields(
diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs
index 276adacd99e..7bd06fee721 100644
--- a/compiler/rustc_public/src/mir/body.rs
+++ b/compiler/rustc_public/src/mir/body.rs
@@ -836,14 +836,6 @@ pub enum ProjectionElem {
     /// Like an explicit cast from an opaque type to a concrete type, but without
     /// requiring an intermediate variable.
     OpaqueCast(Ty),
-
-    /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where
-    /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
-    /// explicit during optimizations and codegen.
-    ///
-    /// This projection doesn't impact the runtime behavior of the program except for potentially changing
-    /// some type metadata of the interpreter or codegen backend.
-    Subtype(Ty),
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, Serialize)]
@@ -1028,6 +1020,7 @@ pub enum CastKind {
     PtrToPtr,
     FnPtrToPtr,
     Transmute,
+    Subtype,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
@@ -1089,7 +1082,7 @@ impl ProjectionElem {
                 Self::subslice_ty(ty, *from, *to, *from_end)
             }
             ProjectionElem::Downcast(_) => Ok(ty),
-            ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty),
+            ProjectionElem::OpaqueCast(ty) => Ok(*ty),
         }
     }
 
diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs
index 04c4d4d2a82..7563c9ca008 100644
--- a/compiler/rustc_public/src/mir/visit.rs
+++ b/compiler/rustc_public/src/mir/visit.rs
@@ -471,7 +471,6 @@ macro_rules! visit_place_fns {
                 ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
                 ProjectionElem::Downcast(_idx) => {}
                 ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location),
-                ProjectionElem::Subtype(ty) => self.visit_ty(ty, location),
             }
         }
     };
@@ -512,7 +511,6 @@ macro_rules! visit_place_fns {
                 ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
                 ProjectionElem::Downcast(_idx) => {}
                 ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location),
-                ProjectionElem::Subtype(ty) => self.visit_ty(ty, location),
             }
         }
     };
diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs
index dc9abd88614..064fb6c6803 100644
--- a/compiler/rustc_public/src/unstable/convert/internal.rs
+++ b/compiler/rustc_public/src/unstable/convert/internal.rs
@@ -703,9 +703,6 @@ impl RustcInternal for ProjectionElem {
             ProjectionElem::OpaqueCast(ty) => {
                 rustc_middle::mir::PlaceElem::OpaqueCast(ty.internal(tables, tcx))
             }
-            ProjectionElem::Subtype(ty) => {
-                rustc_middle::mir::PlaceElem::Subtype(ty.internal(tables, tcx))
-            }
         }
     }
 }
diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
index b10af6526ea..62ab91d17ba 100644
--- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs
+++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
@@ -356,6 +356,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind {
             PtrToPtr => crate::mir::CastKind::PtrToPtr,
             FnPtrToPtr => crate::mir::CastKind::FnPtrToPtr,
             Transmute => crate::mir::CastKind::Transmute,
+            Subtype => crate::mir::CastKind::Subtype,
         }
     }
 }
@@ -453,7 +454,6 @@ impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> {
             // found at https://github.com/rust-lang/rust/pull/117517#issuecomment-1811683486
             Downcast(_, idx) => crate::mir::ProjectionElem::Downcast(idx.stable(tables, cx)),
             OpaqueCast(ty) => crate::mir::ProjectionElem::OpaqueCast(ty.stable(tables, cx)),
-            Subtype(ty) => crate::mir::ProjectionElem::Subtype(ty.stable(tables, cx)),
             UnwrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"),
         }
     }
diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs
index 7f14f878d37..59440e5407f 100644
--- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs
+++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs
@@ -453,7 +453,10 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> {
                 TyKind::Alias(alias_kind.stable(tables, cx), alias_ty.stable(tables, cx))
             }
             ty::Param(param_ty) => TyKind::Param(param_ty.stable(tables, cx)),
-            ty::Bound(debruijn_idx, bound_ty) => {
+            ty::Bound(ty::BoundVarIndexKind::Canonical, _) => {
+                unreachable!()
+            }
+            ty::Bound(ty::BoundVarIndexKind::Bound(debruijn_idx), bound_ty) => {
                 TyKind::Bound(debruijn_idx.as_usize(), bound_ty.stable(tables, cx))
             }
             ty::CoroutineWitness(def_id, args) => TyKind::RigidTy(RigidTy::CoroutineWitness(
@@ -907,7 +910,7 @@ impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> {
                 index: early_reg.index,
                 name: early_reg.name.to_string(),
             }),
-            ty::ReBound(db_index, bound_reg) => RegionKind::ReBound(
+            ty::ReBound(ty::BoundVarIndexKind::Bound(db_index), bound_reg) => RegionKind::ReBound(
                 db_index.as_u32(),
                 BoundRegion {
                     var: bound_reg.var.as_u32(),
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
index ec7a4a81a71..621cc0fb3ef 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
@@ -287,7 +287,7 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap<DictKey<'tcx>,
     // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type
     let mut s = String::new();
     match region.kind() {
-        RegionKind::ReBound(debruijn, r) => {
+        RegionKind::ReBound(ty::BoundVarIndexKind::Bound(debruijn), r) => {
             s.push_str("u6regionI");
             // Debruijn index, which identifies the binder, as region disambiguator
             let num = debruijn.index() as u64;
@@ -303,7 +303,8 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap<DictKey<'tcx>,
             s.push_str("u6region");
             compress(dict, DictKey::Region(region), &mut s);
         }
-        RegionKind::ReEarlyParam(..)
+        RegionKind::ReBound(ty::BoundVarIndexKind::Canonical, _)
+        | RegionKind::ReEarlyParam(..)
         | RegionKind::ReLateParam(..)
         | RegionKind::ReStatic
         | RegionKind::ReError(_)
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 25b46241c52..172672a80fb 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -758,7 +758,8 @@ impl Session {
         //     ELF x86-64 abi, but it can be disabled for some compilation units.
         //
         // Typically when we're compiling with `-C panic=abort` we don't need
-        // `uwtable` because we can't generate any exceptions!
+        // `uwtable` because we can't generate any exceptions! But note that
+        // some targets require unwind tables to generate backtraces.
         // Unwind tables are needed when compiling with `-C panic=unwind`, but
         // LLVM won't omit unwind tables unless the function is also marked as
         // `nounwind`, so users are allowed to disable `uwtable` emission.
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index d24924b424a..aeac40a65bd 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -413,7 +413,10 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> {
 
             // Bound lifetimes use indices starting at 1,
             // see `BinderLevel` for more details.
-            ty::ReBound(debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }) => {
+            ty::ReBound(
+                ty::BoundVarIndexKind::Bound(debruijn),
+                ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
+            ) => {
                 let binder = &self.binders[self.binders.len() - 1 - debruijn.index()];
                 let depth = binder.lifetime_depths.start + var.as_u32();
 
diff --git a/compiler/rustc_target/src/spec/base/android.rs b/compiler/rustc_target/src/spec/base/android.rs
index 0426ea44c6a..df2757aaabf 100644
--- a/compiler/rustc_target/src/spec/base/android.rs
+++ b/compiler/rustc_target/src/spec/base/android.rs
@@ -8,10 +8,6 @@ pub(crate) fn opts() -> TargetOptions {
     base.tls_model = TlsModel::Emulated;
     base.has_thread_local = false;
     base.supported_sanitizers = SanitizerSet::ADDRESS;
-    // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
-    // for context. (At that time, there was no `-C force-unwind-tables`, so the only solution
-    // was to always emit `uwtable`).
-    base.default_uwtable = true;
     base.crt_static_respected = true;
     base
 }
diff --git a/compiler/rustc_target/src/spec/base/linux.rs b/compiler/rustc_target/src/spec/base/linux.rs
index 9982c254eca..26e4590cf5e 100644
--- a/compiler/rustc_target/src/spec/base/linux.rs
+++ b/compiler/rustc_target/src/spec/base/linux.rs
@@ -12,6 +12,9 @@ pub(crate) fn opts() -> TargetOptions {
         relro_level: RelroLevel::Full,
         has_thread_local: true,
         crt_static_respected: true,
+        // We want backtraces to work by default and they rely on unwind tables
+        // (regardless of `-C panic` strategy).
+        default_uwtable: true,
         supported_split_debuginfo: Cow::Borrowed(&[
             SplitDebuginfo::Packed,
             SplitDebuginfo::Unpacked,
diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs
index a3f5389f0aa..0c711d5e71a 100644
--- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs
+++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs
@@ -19,6 +19,11 @@ pub(crate) fn target() -> Target {
             max_atomic_width: Some(64),
             mcount: "\u{1}__gnu_mcount_nc".into(),
             llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()),
+            // The default on linux is to have `default_uwtable=true`, but on
+            // this target we get an "`__aeabi_unwind_cpp_pr0` not defined"
+            // linker error, so set it to `true` here.
+            // FIXME(#146996): Remove this override once #146996 has been fixed.
+            default_uwtable: false,
             ..base::linux_gnu::opts()
         },
     }
diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs
index a0eb4a254fc..06e5cfaed92 100644
--- a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs
@@ -6,11 +6,18 @@ use crate::spec::{Cc, LinkerFlavor, Target, TargetMetadata, base};
 pub(crate) fn target() -> Target {
     let mut options = base::linux_wasm::opts();
 
-    options
-        .add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &["--export-memory", "--shared-memory"]);
+    options.add_pre_link_args(
+        LinkerFlavor::WasmLld(Cc::No),
+        &["--export-memory", "--shared-memory", "--max-memory=1073741824"],
+    );
     options.add_pre_link_args(
         LinkerFlavor::WasmLld(Cc::Yes),
-        &["--target=wasm32-wasi-threads", "-Wl,--export-memory,", "-Wl,--shared-memory"],
+        &[
+            "--target=wasm32-wasi-threads",
+            "-Wl,--export-memory,",
+            "-Wl,--shared-memory",
+            "-Wl,--max-memory=1073741824",
+        ],
     );
 
     Target {
diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
index 44d906a507d..c735c72cb1c 100644
--- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
+++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
@@ -19,7 +19,7 @@ pub(crate) fn target() -> Target {
 
     options.add_pre_link_args(
         LinkerFlavor::WasmLld(Cc::No),
-        &["--import-memory", "--export-memory", "--shared-memory"],
+        &["--import-memory", "--export-memory", "--shared-memory", "--max-memory=1073741824"],
     );
     options.add_pre_link_args(
         LinkerFlavor::WasmLld(Cc::Yes),
@@ -28,6 +28,7 @@ pub(crate) fn target() -> Target {
             "-Wl,--import-memory",
             "-Wl,--export-memory,",
             "-Wl,--shared-memory",
+            "-Wl,--max-memory=1073741824",
         ],
     );
 
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 3260dd712b9..6ab92531e4e 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -830,7 +830,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseEscapingBoundRegions<'tcx> {
     }
 
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        if let ty::ReBound(debruijn, _) = r.kind()
+        if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) = r.kind()
             && debruijn < self.binder
         {
             r
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index a54eb80fedc..c6eb0caee1a 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -145,7 +145,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
     #[inline]
     fn visit_region(&mut self, r: ty::Region<'tcx>) {
         match r.kind() {
-            ty::ReBound(debruijn, _) if debruijn > self.outer_index => {
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _)
+                if debruijn > self.outer_index =>
+            {
                 self.escaping =
                     self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
             }
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 7ad65a1df8e..708a53f6c65 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -423,19 +423,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 constituents.types,
             );
 
-            // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types`
-            // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really
-            // matter yet.
-            for assumption in constituents.assumptions {
-                let assumption = normalize_with_depth_to(
-                    self,
-                    obligation.param_env,
-                    cause.clone(),
-                    obligation.recursion_depth + 1,
-                    assumption,
-                    &mut obligations,
-                );
-                self.infcx.register_region_assumption(assumption);
+            // Only normalize these goals if `-Zhigher-ranked-assumptions` is enabled, since
+            // we don't want to cause ourselves to do extra work if we're not even able to
+            // take advantage of these assumption clauses.
+            if self.tcx().sess.opts.unstable_opts.higher_ranked_assumptions {
+                // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types`
+                // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really
+                // matter yet.
+                for assumption in constituents.assumptions {
+                    let assumption = normalize_with_depth_to(
+                        self,
+                        obligation.param_env,
+                        cause.clone(),
+                        obligation.recursion_depth + 1,
+                        assumption,
+                        &mut obligations,
+                    );
+                    self.infcx.register_region_assumption(assumption);
+                }
             }
 
             Ok(obligations)
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index e91e5055e90..bb25a14ef74 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -235,7 +235,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
             // bounds of the RPITIT. Shift these binders back out when
             // constructing the top-level projection predicate.
             let shifted_alias_ty = fold_regions(self.tcx, unshifted_alias_ty, |re, depth| {
-                if let ty::ReBound(index, bv) = re.kind() {
+                if let ty::ReBound(ty::BoundVarIndexKind::Bound(index), bv) = re.kind() {
                     if depth != ty::INNERMOST {
                         return ty::Region::new_error_with_message(
                             self.tcx,
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index 6591d3148cb..94b950357e1 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -4,6 +4,7 @@ use std::ops::{ControlFlow, Deref};
 use derive_where::derive_where;
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
+use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 use tracing::instrument;
 
 use crate::data_structures::SsoHashSet;
@@ -11,7 +12,7 @@ use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldabl
 use crate::inherent::*;
 use crate::lift::Lift;
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
-use crate::{self as ty, Interner};
+use crate::{self as ty, DebruijnIndex, Interner};
 
 /// `Binder` is a binder for higher-ranked lifetimes or types. It is part of the
 /// compiler's representation for things like `for<'a> Fn(&'a isize)`
@@ -299,7 +300,9 @@ impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
             return ControlFlow::Break(());
         }
         match t.kind() {
-            ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
+            ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty)
+                if debruijn == self.binder_index =>
+            {
                 let idx = bound_ty.var().as_usize();
                 if self.bound_vars.len() <= idx {
                     panic!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars);
@@ -317,7 +320,9 @@ impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
             return ControlFlow::Break(());
         }
         match c.kind() {
-            ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.binder_index => {
+            ty::ConstKind::Bound(debruijn, bound_const)
+                if debruijn == ty::BoundVarIndexKind::Bound(self.binder_index) =>
+            {
                 let idx = bound_const.var().as_usize();
                 if self.bound_vars.len() <= idx {
                     panic!("Not enough bound vars: {:?} not found in {:?}", c, self.bound_vars);
@@ -332,7 +337,7 @@ impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
 
     fn visit_region(&mut self, r: I::Region) -> Self::Result {
         match r.kind() {
-            ty::ReBound(index, br) if index == self.binder_index => {
+            ty::ReBound(index, br) if index == ty::BoundVarIndexKind::Bound(self.binder_index) => {
                 let idx = br.var().as_usize();
                 if self.bound_vars.len() <= idx {
                     panic!("Not enough bound vars: {:?} not found in {:?}", r, self.bound_vars);
@@ -913,3 +918,33 @@ impl<'a, I: Interner> ArgFolder<'a, I> {
         }
     }
 }
+
+/// Okay, we do something fun for `Bound` types/regions/consts:
+/// Specifically, we distinguish between *canonically* bound things and
+/// `for<>` bound things. And, really, it comes down to caching during
+/// canonicalization and instantiation.
+///
+/// To understand why we do this, imagine we have a type `(T, for<> fn(T))`.
+/// If we just tracked canonically bound types with a `DebruijnIndex` (as we
+/// used to), then the canonicalized type would be something like
+/// `for<0> (^0.0, for<> fn(^1.0))` and so we can't cache `T -> ^0.0`,
+/// we have to also factor in binder level. (Of course, we don't cache that
+/// exactly, but rather the entire enclosing type, but the point stands.)
+///
+/// Of course, this is okay because we don't ever nest canonicalization, so
+/// `BoundVarIndexKind::Canonical` is unambiguous. We, alternatively, could
+/// have some sentinel `DebruijinIndex`, but that just seems too scary.
+///
+/// This doesn't seem to have a huge perf swing either way, but in the next
+/// solver, canonicalization is hot and there are some pathological cases where
+/// this is needed (`post-mono-higher-ranked-hang`).
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
+pub enum BoundVarIndexKind {
+    Bound(DebruijnIndex),
+    Canonical,
+}
diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs
index ecf3ae4f8b2..7b4b953b2cf 100644
--- a/compiler/rustc_type_ir/src/canonical.rs
+++ b/compiler/rustc_type_ir/src/canonical.rs
@@ -230,13 +230,13 @@ impl<I: Interner> CanonicalVarValues<I> {
     pub fn is_identity(&self) -> bool {
         self.var_values.iter().enumerate().all(|(bv, arg)| match arg.kind() {
             ty::GenericArgKind::Lifetime(r) => {
-                matches!(r.kind(), ty::ReBound(ty::INNERMOST, br) if br.var().as_usize() == bv)
+                matches!(r.kind(), ty::ReBound(ty::BoundVarIndexKind::Canonical, br) if br.var().as_usize() == bv)
             }
             ty::GenericArgKind::Type(ty) => {
-                matches!(ty.kind(), ty::Bound(ty::INNERMOST, bt) if bt.var().as_usize() == bv)
+                matches!(ty.kind(), ty::Bound(ty::BoundVarIndexKind::Canonical, bt) if bt.var().as_usize() == bv)
             }
             ty::GenericArgKind::Const(ct) => {
-                matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if bc.var().as_usize() == bv)
+                matches!(ct.kind(), ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bc) if bc.var().as_usize() == bv)
             }
         })
     }
@@ -246,21 +246,23 @@ impl<I: Interner> CanonicalVarValues<I> {
         for arg in self.var_values.iter() {
             match arg.kind() {
                 ty::GenericArgKind::Lifetime(r) => {
-                    if matches!(r.kind(), ty::ReBound(ty::INNERMOST, br) if var == br.var()) {
+                    if matches!(r.kind(), ty::ReBound(ty::BoundVarIndexKind::Canonical, br) if var == br.var())
+                    {
                         var = var + 1;
                     } else {
                         // It's ok if this region var isn't an identity variable
                     }
                 }
                 ty::GenericArgKind::Type(ty) => {
-                    if matches!(ty.kind(), ty::Bound(ty::INNERMOST, bt) if var == bt.var()) {
+                    if matches!(ty.kind(), ty::Bound(ty::BoundVarIndexKind::Canonical, bt) if var == bt.var())
+                    {
                         var = var + 1;
                     } else {
                         return false;
                     }
                 }
                 ty::GenericArgKind::Const(ct) => {
-                    if matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if var == bc.var())
+                    if matches!(ct.kind(), ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bc) if var == bc.var())
                     {
                         var = var + 1;
                     } else {
@@ -284,16 +286,13 @@ impl<I: Interner> CanonicalVarValues<I> {
                         | CanonicalVarKind::Int
                         | CanonicalVarKind::Float
                         | CanonicalVarKind::PlaceholderTy(_) => {
-                            Ty::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
-                                .into()
+                            Ty::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into()
                         }
                         CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => {
-                            Region::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
-                                .into()
+                            Region::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into()
                         }
                         CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => {
-                            Const::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
-                                .into()
+                            Const::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into()
                         }
                     }
                 },
diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs
index 6de41b47bde..273b6096008 100644
--- a/compiler/rustc_type_ir/src/const_kind.rs
+++ b/compiler/rustc_type_ir/src/const_kind.rs
@@ -7,7 +7,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
 
-use crate::{self as ty, DebruijnIndex, Interner};
+use crate::{self as ty, BoundVarIndexKind, Interner};
 
 /// Represents a constant in Rust.
 #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)]
@@ -23,7 +23,7 @@ pub enum ConstKind<I: Interner> {
     Infer(InferConst),
 
     /// Bound const variable, used only when preparing a trait query.
-    Bound(DebruijnIndex, I::BoundConst),
+    Bound(BoundVarIndexKind, I::BoundConst),
 
     /// A placeholder const - universally quantified higher-ranked const.
     Placeholder(I::PlaceholderConst),
diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs
index 24704c5bb53..34b030ee768 100644
--- a/compiler/rustc_type_ir/src/flags.rs
+++ b/compiler/rustc_type_ir/src/flags.rs
@@ -133,6 +133,9 @@ bitflags::bitflags! {
 
         /// Does this type have any coroutines in it?
         const HAS_TY_CORO                 = 1 << 24;
+
+        /// Does this have have a `Bound(BoundVarIndexKind::Canonical, _)`?
+        const HAS_CANONICAL_BOUND      = 1 << 25;
     }
 }
 
@@ -254,7 +257,12 @@ impl<I: Interner> FlagComputation<I> {
                 self.add_args(args.as_slice());
             }
 
-            ty::Bound(debruijn, _) => {
+            ty::Bound(ty::BoundVarIndexKind::Canonical, _) => {
+                self.add_flags(TypeFlags::HAS_TY_BOUND);
+                self.add_flags(TypeFlags::HAS_CANONICAL_BOUND);
+            }
+
+            ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) => {
                 self.add_bound_var(debruijn);
                 self.add_flags(TypeFlags::HAS_TY_BOUND);
             }
@@ -347,6 +355,7 @@ impl<I: Interner> FlagComputation<I> {
 
     fn add_ty_pat(&mut self, pat: <I as Interner>::Pat) {
         self.add_flags(pat.flags());
+        self.add_exclusive_binder(pat.outer_exclusive_binder());
     }
 
     fn add_predicate(&mut self, binder: ty::Binder<I, ty::PredicateKind<I>>) {
@@ -434,7 +443,7 @@ impl<I: Interner> FlagComputation<I> {
 
     fn add_region(&mut self, r: I::Region) {
         self.add_flags(r.flags());
-        if let ty::ReBound(debruijn, _) = r.kind() {
+        if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) = r.kind() {
             self.add_bound_var(debruijn);
         }
     }
@@ -454,10 +463,14 @@ impl<I: Interner> FlagComputation<I> {
                 ty::InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH),
                 ty::InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER),
             },
-            ty::ConstKind::Bound(debruijn, _) => {
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) => {
                 self.add_bound_var(debruijn);
                 self.add_flags(TypeFlags::HAS_CT_BOUND);
             }
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, _) => {
+                self.add_flags(TypeFlags::HAS_CT_BOUND);
+                self.add_flags(TypeFlags::HAS_CANONICAL_BOUND);
+            }
             ty::ConstKind::Param(_) => {
                 self.add_flags(TypeFlags::HAS_CT_PARAM);
             }
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs
index a5eb8699e5f..d1a50599e8b 100644
--- a/compiler/rustc_type_ir/src/fold.rs
+++ b/compiler/rustc_type_ir/src/fold.rs
@@ -55,7 +55,7 @@ use tracing::{debug, instrument};
 
 use crate::inherent::*;
 use crate::visit::{TypeVisitable, TypeVisitableExt as _};
-use crate::{self as ty, Interner, TypeFlags};
+use crate::{self as ty, BoundVarIndexKind, Interner, TypeFlags};
 
 /// This trait is implemented for every type that can be folded,
 /// providing the skeleton of the traversal.
@@ -398,7 +398,9 @@ impl<I: Interner> TypeFolder<I> for Shifter<I> {
 
     fn fold_region(&mut self, r: I::Region) -> I::Region {
         match r.kind() {
-            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br)
+                if debruijn >= self.current_index =>
+            {
                 let debruijn = debruijn.shifted_in(self.amount);
                 Region::new_bound(self.cx, debruijn, br)
             }
@@ -408,7 +410,9 @@ impl<I: Interner> TypeFolder<I> for Shifter<I> {
 
     fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
         match ty.kind() {
-            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+            ty::Bound(BoundVarIndexKind::Bound(debruijn), bound_ty)
+                if debruijn >= self.current_index =>
+            {
                 let debruijn = debruijn.shifted_in(self.amount);
                 Ty::new_bound(self.cx, debruijn, bound_ty)
             }
@@ -420,7 +424,9 @@ impl<I: Interner> TypeFolder<I> for Shifter<I> {
 
     fn fold_const(&mut self, ct: I::Const) -> I::Const {
         match ct.kind() {
-            ty::ConstKind::Bound(debruijn, bound_ct) if debruijn >= self.current_index => {
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ct)
+                if debruijn >= self.current_index =>
+            {
                 let debruijn = debruijn.shifted_in(self.amount);
                 Const::new_bound(self.cx, debruijn, bound_ct)
             }
@@ -435,7 +441,7 @@ impl<I: Interner> TypeFolder<I> for Shifter<I> {
 
 pub fn shift_region<I: Interner>(cx: I, region: I::Region, amount: u32) -> I::Region {
     match region.kind() {
-        ty::ReBound(debruijn, br) if amount > 0 => {
+        ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), br) if amount > 0 => {
             Region::new_bound(cx, debruijn.shifted_in(amount), br)
         }
         _ => region,
@@ -515,7 +521,13 @@ where
     #[instrument(skip(self), level = "debug", ret)]
     fn fold_region(&mut self, r: I::Region) -> I::Region {
         match r.kind() {
-            ty::ReBound(debruijn, _) if debruijn < self.current_index => {
+            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _)
+                if debruijn < self.current_index =>
+            {
+                debug!(?self.current_index, "skipped bound region");
+                r
+            }
+            ty::ReBound(ty::BoundVarIndexKind::Canonical, _) => {
                 debug!(?self.current_index, "skipped bound region");
                 r
             }
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index b5b552dbaec..75ba0231d98 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -48,6 +48,8 @@ pub trait Ty<I: Interner<Ty = Self>>:
 
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
+    fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self;
+
     fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy<I>) -> Self;
 
     fn new_projection_from_args(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self {
@@ -230,6 +232,8 @@ pub trait Region<I: Interner<Region = Self>>:
 
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
+    fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self;
+
     fn new_static(interner: I) -> Self;
 
     fn new_placeholder(interner: I, var: I::PlaceholderRegion) -> Self;
@@ -260,6 +264,8 @@ pub trait Const<I: Interner<Const = Self>>:
 
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
+    fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self;
+
     fn new_placeholder(interner: I, param: I::PlaceholderConst) -> Self;
 
     fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>) -> Self;
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 61e0b67b163..c1e30196126 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -196,13 +196,20 @@ impl DebruijnIndex {
 
 pub fn debug_bound_var<T: std::fmt::Write>(
     fmt: &mut T,
-    debruijn: DebruijnIndex,
+    bound_index: BoundVarIndexKind,
     var: impl std::fmt::Debug,
 ) -> Result<(), std::fmt::Error> {
-    if debruijn == INNERMOST {
-        write!(fmt, "^{var:?}")
-    } else {
-        write!(fmt, "^{}_{:?}", debruijn.index(), var)
+    match bound_index {
+        BoundVarIndexKind::Bound(debruijn) => {
+            if debruijn == INNERMOST {
+                write!(fmt, "^{var:?}")
+            } else {
+                write!(fmt, "^{}_{:?}", debruijn.index(), var)
+            }
+        }
+        BoundVarIndexKind::Canonical => {
+            write!(fmt, "^c_{:?}", var)
+        }
     }
 }
 
diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs
index 06048af0436..1e8585cf52c 100644
--- a/compiler/rustc_type_ir/src/region_kind.rs
+++ b/compiler/rustc_type_ir/src/region_kind.rs
@@ -7,7 +7,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
 
 use self::RegionKind::*;
-use crate::{DebruijnIndex, Interner};
+use crate::{BoundVarIndexKind, Interner};
 
 rustc_index::newtype_index! {
     /// A **region** **v**ariable **ID**.
@@ -147,7 +147,7 @@ pub enum RegionKind<I: Interner> {
     /// Bound regions inside of types **must not** be erased, as they impact trait
     /// selection and the `TypeId` of that type. `for<'a> fn(&'a ())` and
     /// `fn(&'static ())` are different types and have to be treated as such.
-    ReBound(DebruijnIndex, I::BoundRegion),
+    ReBound(BoundVarIndexKind, I::BoundRegion),
 
     /// Late-bound function parameters are represented using a `ReBound`. When
     /// inside of a function, we convert these bound variables to placeholder
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index dda59283677..bb80e2cf46d 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -15,7 +15,7 @@ pub use self::closure::*;
 use crate::inherent::*;
 #[cfg(feature = "nightly")]
 use crate::visit::TypeVisitable;
-use crate::{self as ty, DebruijnIndex, FloatTy, IntTy, Interner, UintTy};
+use crate::{self as ty, BoundVarIndexKind, FloatTy, IntTy, Interner, UintTy};
 
 mod closure;
 
@@ -229,7 +229,7 @@ pub enum TyKind<I: Interner> {
     ///
     /// [1]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
     /// [2]: https://rustc-dev-guide.rust-lang.org/traits/canonical-queries.html
-    Bound(DebruijnIndex, I::BoundTy),
+    Bound(BoundVarIndexKind, I::BoundTy),
 
     /// A placeholder type, used during higher ranked subtyping to instantiate
     /// bound variables.
diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs
index 3a6d1acfa8d..4d9fd6040d8 100644
--- a/compiler/rustc_type_ir/src/ty_kind/closure.rs
+++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs
@@ -344,7 +344,8 @@ impl<I: Interner> TypeVisitor<I> for HasRegionsBoundAt {
     }
 
     fn visit_region(&mut self, r: I::Region) -> Self::Result {
-        if matches!(r.kind(), ty::ReBound(binder, _) if self.binder == binder) {
+        if matches!(r.kind(), ty::ReBound(ty::BoundVarIndexKind::Bound(binder), _) if self.binder == binder)
+        {
             ControlFlow::Break(())
         } else {
             ControlFlow::Continue(())
@@ -531,7 +532,7 @@ impl<I: Interner> TypeFolder<I> for FoldEscapingRegions<I> {
     }
 
     fn fold_region(&mut self, r: <I as Interner>::Region) -> <I as Interner>::Region {
-        if let ty::ReBound(debruijn, _) = r.kind() {
+        if let ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) = r.kind() {
             assert!(
                 debruijn <= self.debruijn,
                 "cannot instantiate binder with escaping bound vars"