about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-10-02 08:09:33 +0000
committerbors <bors@rust-lang.org>2025-10-02 08:09:33 +0000
commit4b9c62b4da3e17cee99d3d2052f1c576b188e2a8 (patch)
treeb8500d73a15205e55e5cbc3d59f397d2547de007
parent42b384ec0dfcd528d99a4db0a337d9188a9eecaa (diff)
parentd1bbd39c59523d7a5499816a9da200a5910f8b7f (diff)
downloadrust-4b9c62b4da3e17cee99d3d2052f1c576b188e2a8.tar.gz
rust-4b9c62b4da3e17cee99d3d2052f1c576b188e2a8.zip
Auto merge of #147138 - jackh726:split-canonical-bound, r=lcnr
Split Bound index into Canonical and Bound

See [#t-types/trait-system-refactor > perf &#96;async-closures/post-mono-higher-ranked-hang.rs&#96;](https://rust-lang.zulipchat.com/#narrow/channel/364551-t-types.2Ftrait-system-refactor/topic/perf.20.60async-closures.2Fpost-mono-higher-ranked-hang.2Ers.60/with/541535613) for context

Things compile and tests pass, but not sure if this actually solves the perf issue (edit: it does). Opening up this to do a perf (and maybe crater) run.

r? lcnr
-rw-r--r--compiler/rustc_borrowck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs6
-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_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_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_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_symbol_mangling/src/v0.rs5
-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_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.rs18
-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
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/mod.rs4
-rw-r--r--tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir18
-rw-r--r--tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.current.stderr (renamed from tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr)6
-rw-r--r--tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.next.stderr21
-rw-r--r--tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs4
-rw-r--r--tests/ui/higher-ranked/trait-bounds/issue-59311.stderr2
-rw-r--r--tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr2
-rw-r--r--tests/ui/lifetimes/re-empty-in-error.stderr2
-rw-r--r--tests/ui/nll/user-annotations/dump-fn-method.rs4
-rw-r--r--tests/ui/nll/user-annotations/dump-fn-method.stderr4
45 files changed, 391 insertions, 247 deletions
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 865cfd7d2e2..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()))
             }
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 9841fafc82c..129b26d8ff0 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -181,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;
@@ -260,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) {
@@ -286,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) {
@@ -314,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) {
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 8682fdc5494..7accab8df87 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -921,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 => {
@@ -944,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 => {
@@ -967,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_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_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_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_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_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_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_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 ea3903d401e..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);
             }
@@ -435,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);
         }
     }
@@ -455,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"
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index 1b1e77bbea8..6e9142b22e0 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -14,7 +14,7 @@ use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, No
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_middle::ty::{self, RegionKind, TyCtxt};
+use rustc_middle::ty::{self, BoundVarIndexKind, RegionKind, TyCtxt};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{Span, sym};
@@ -151,7 +151,7 @@ impl PassByRefOrValue {
             match *ty.skip_binder().kind() {
                 ty::Ref(lt, ty, Mutability::Not) => {
                     match lt.kind() {
-                        RegionKind::ReBound(index, region)
+                        RegionKind::ReBound(BoundVarIndexKind::Bound(index), region)
                             if index.as_u32() == 0 && output_regions.contains(&region) =>
                         {
                             continue;
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index e4bc3b76829..c03469c2b88 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -21,7 +21,7 @@ use rustc_middle::traits::EvaluationResult;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{
-    self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
+    self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
     GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
     TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
 };
@@ -826,7 +826,7 @@ pub fn for_each_top_level_late_bound_region<B>(
     impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<TyCtxt<'tcx>> for V<F> {
         type Result = ControlFlow<B>;
         fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
-            if let RegionKind::ReBound(idx, bound) = r.kind()
+            if let RegionKind::ReBound(BoundVarIndexKind::Bound(idx), bound) = r.kind()
                 && idx.as_u32() == self.index
             {
                 (self.f)(bound)
diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir
index a18d7e7478f..a1fe278c652 100644
--- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir
+++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir
@@ -1,30 +1,30 @@
 // MIR for `address_of_reborrow` after SimplifyCfg-initial
 
 | User Type Annotations
-| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10]
+| 0: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10]
 | 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send
-| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10]
-| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10]
+| 2: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10]
+| 3: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10]
 | 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10]
 | 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10]
 | 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send
 | 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send
 | 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32]
 | 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32]
-| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10]
+| 10: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10]
 | 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send
-| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10]
-| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10]
+| 12: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10]
+| 13: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10]
 | 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10]
 | 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10]
 | 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send
 | 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send
 | 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32]
 | 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32]
-| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10]
+| 20: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10]
 | 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send
-| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10]
-| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10]
+| 22: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10]
+| 23: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10]
 | 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10]
 | 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10]
 | 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send
diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.current.stderr
index 486e5f94165..f3938ff606f 100644
--- a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr
+++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.current.stderr
@@ -1,5 +1,5 @@
-error: reached the recursion limit while instantiating `ToChain::<'_, '_>::perm_pairs::<{async closure@$DIR/post-mono-higher-ranked-hang.rs:43:45: 43:67}>`
-  --> $DIR/post-mono-higher-ranked-hang.rs:43:21
+error: reached the recursion limit while instantiating `ToChain::<'_, '_>::perm_pairs::<{async closure@$DIR/post-mono-higher-ranked-hang.rs:47:45: 47:67}>`
+  --> $DIR/post-mono-higher-ranked-hang.rs:47:21
    |
 LL | /                     self.perm_pairs(l, &mut async move |left_pair| {
 LL | |
@@ -8,7 +8,7 @@ LL | |                     })
    | |______________________^
    |
 note: `ToChain::<'env, 'db>::perm_pairs` defined here
-  --> $DIR/post-mono-higher-ranked-hang.rs:34:5
+  --> $DIR/post-mono-higher-ranked-hang.rs:38:5
    |
 LL | /     fn perm_pairs<'l>(
 LL | |         &'l self,
diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.next.stderr b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.next.stderr
new file mode 100644
index 00000000000..f3938ff606f
--- /dev/null
+++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.next.stderr
@@ -0,0 +1,21 @@
+error: reached the recursion limit while instantiating `ToChain::<'_, '_>::perm_pairs::<{async closure@$DIR/post-mono-higher-ranked-hang.rs:47:45: 47:67}>`
+  --> $DIR/post-mono-higher-ranked-hang.rs:47:21
+   |
+LL | /                     self.perm_pairs(l, &mut async move |left_pair| {
+LL | |
+LL | |                         self.perm_pairs(r, yield_chain).await
+LL | |                     })
+   | |______________________^
+   |
+note: `ToChain::<'env, 'db>::perm_pairs` defined here
+  --> $DIR/post-mono-higher-ranked-hang.rs:38:5
+   |
+LL | /     fn perm_pairs<'l>(
+LL | |         &'l self,
+LL | |         perm: &'l SymPerm<'db>,
+LL | |         yield_chain: &'l mut impl AsyncFnMut(&SymPerm<'db>),
+LL | |     ) -> Pin<Box<dyn std::future::Future<Output = ()> + 'l>> {
+   | |____________________________________________________________^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs
index f6ebf787f81..55d7cc30ec9 100644
--- a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs
+++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs
@@ -2,6 +2,10 @@
 //@ aux-build:block-on.rs
 //@ edition:2021
 
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
 // Regression test for <https://github.com/rust-lang/rust/issues/135780>.
 
 extern crate block_on;
diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr
index a26c617dc93..f8f64dcc545 100644
--- a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr
@@ -37,7 +37,7 @@ error: higher-ranked lifetime error
 LL |     v.t(|| {});
    |         ^^^^^
    |
-   = note: could not prove `for<'a> &'a V: 'b`
+   = note: could not prove `for<'a> &'a V: '_`
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr b/tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr
index 9e0d7e4b7be..a04c6d770d7 100644
--- a/tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/trivial-does-not-hold.stderr
@@ -4,7 +4,7 @@ error: higher-ranked lifetime error
 LL |     || {};
    |     ^^^^^
    |
-   = note: could not prove `for<'a> &'a (): 'b`
+   = note: could not prove `for<'a> &'a (): '_`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lifetimes/re-empty-in-error.stderr b/tests/ui/lifetimes/re-empty-in-error.stderr
index 554bcb5451f..b3b6d3d269c 100644
--- a/tests/ui/lifetimes/re-empty-in-error.stderr
+++ b/tests/ui/lifetimes/re-empty-in-error.stderr
@@ -4,7 +4,7 @@ error: higher-ranked lifetime error
 LL |     foo(&10);
    |     ^^^^^^^^
    |
-   = note: could not prove `for<'b> &'b (): 'a`
+   = note: could not prove `for<'b> &'b (): '_`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/nll/user-annotations/dump-fn-method.rs b/tests/ui/nll/user-annotations/dump-fn-method.rs
index 26714b6ffe3..ec349e36839 100644
--- a/tests/ui/nll/user-annotations/dump-fn-method.rs
+++ b/tests/ui/nll/user-annotations/dump-fn-method.rs
@@ -31,7 +31,7 @@ fn main() {
     // Here: we only want the `T` to be given, the rest should be variables.
     //
     // (`T` refers to the declaration of `Bazoom`)
-    let x = <_ as Bazoom<u32>>::method::<_>; //~ ERROR [^0, u32, ^1]
+    let x = <_ as Bazoom<u32>>::method::<_>; //~ ERROR [^c_0, u32, ^c_1]
     x(&22, 44, 66);
 
     // Here: all are given and definitely contain no lifetimes, so we
@@ -48,7 +48,7 @@ fn main() {
     //
     // (`U` refers to the declaration of `Bazoom`)
     let y = 22_u32;
-    y.method::<u32>(44, 66); //~ ERROR [^0, ^1, u32]
+    y.method::<u32>(44, 66); //~ ERROR [^c_0, ^c_1, u32]
 
     // Here: nothing is given, so we don't have any annotation.
     let y = 22_u32;
diff --git a/tests/ui/nll/user-annotations/dump-fn-method.stderr b/tests/ui/nll/user-annotations/dump-fn-method.stderr
index 8e847b464e1..f00fb0013df 100644
--- a/tests/ui/nll/user-annotations/dump-fn-method.stderr
+++ b/tests/ui/nll/user-annotations/dump-fn-method.stderr
@@ -4,7 +4,7 @@ error: user args: UserArgs { args: [&'static u32], user_self_ty: None }
 LL |     let x = foo::<&'static u32>;
    |             ^^^^^^^^^^^^^^^^^^^
 
-error: user args: UserArgs { args: [^0, u32, ^1], user_self_ty: None }
+error: user args: UserArgs { args: [^c_0, u32, ^c_1], user_self_ty: None }
   --> $DIR/dump-fn-method.rs:34:13
    |
 LL |     let x = <_ as Bazoom<u32>>::method::<_>;
@@ -16,7 +16,7 @@ error: user args: UserArgs { args: [u8, &'static u16, u32], user_self_ty: None }
 LL |     let x = <u8 as Bazoom<&'static u16>>::method::<u32>;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: user args: UserArgs { args: [^0, ^1, u32], user_self_ty: None }
+error: user args: UserArgs { args: [^c_0, ^c_1, u32], user_self_ty: None }
   --> $DIR/dump-fn-method.rs:51:5
    |
 LL |     y.method::<u32>(44, 66);