diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 32af537e271..b3d6b891b11 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -864,6 +864,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             };
 
             // Use the underlying local for this (necessarily interior) borrow.
+            debug_assert!(region.is_erased());
             let ty = local_decls[place.local].ty;
             let span = statement.source_info.span;
 
@@ -873,8 +874,6 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                 ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
             );
 
-            *region = tcx.lifetimes.re_erased;
-
             let mut projection = vec![PlaceElem::Deref];
             projection.extend(place.projection);
             place.projection = tcx.mk_place_elems(&projection);
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index d5fb4340e1c..55c01d2084b 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -5,7 +5,7 @@ use rustc_hir::{ForeignItem, ForeignItemKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::{ObligationCause, WellFormedLoc};
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, Region, TyCtxt, TypeFoldable, TypeFolder};
+use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::def_id::LocalDefId;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
 
@@ -68,7 +68,13 @@ fn diagnostic_hir_wf_check<'tcx>(
             let infcx = self.tcx.infer_ctxt().build();
             let ocx = ObligationCtxt::new(&infcx);
 
-            let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
+            let tcx_ty = self.icx.to_ty(ty);
+            // This visitor can walk into binders, resulting in the `tcx_ty` to
+            // potentially reference escaping bound variables. We simply erase
+            // those here.
+            let tcx_ty = self.tcx.fold_regions(tcx_ty, |r, _| {
+                if r.is_bound() { self.tcx.lifetimes.re_erased } else { r }
+            });
             let cause = traits::ObligationCause::new(
                 ty.span,
                 self.def_id,
@@ -178,25 +184,3 @@ fn diagnostic_hir_wf_check<'tcx>(
     }
     visitor.cause
 }
-
-struct EraseAllBoundRegions<'tcx> {
-    tcx: TyCtxt<'tcx>,
-}
-
-// Higher ranked regions are complicated.
-// To make matters worse, the HIR WF check can instantiate them
-// outside of a `Binder`, due to the way we (ab)use
-// `ItemCtxt::to_ty`. To make things simpler, we just erase all
-// of them, regardless of depth. At worse, this will give
-// us an inaccurate span for an error message, but cannot
-// lead to unsoundness (we call `delay_span_bug` at the start
-// of `diagnostic_hir_wf_check`).
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseAllBoundRegions<'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-    fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
-        // FIXME(@lcnr): only erase escaping bound regions!
-        if r.is_bound() { self.tcx.lifetimes.re_erased } else { r }
-    }
-}
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 2dfe7272673..2462ac93632 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -9,7 +9,7 @@ use rustc_hir as hir;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
-use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::symbol::sym;
@@ -768,41 +768,22 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
     }
 }
 
-struct EraseEarlyRegions<'tcx> {
-    tcx: TyCtxt<'tcx>,
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseEarlyRegions<'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if ty.has_type_flags(ty::TypeFlags::HAS_FREE_REGIONS) {
-            ty.super_fold_with(self)
-        } else {
-            ty
-        }
-    }
-    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        if r.is_bound() { r } else { self.tcx.lifetimes.re_erased }
-    }
-}
-
 impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
     fn interner(&self) -> TyCtxt<'tcx> {
         self.fcx.tcx
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        let tcx = self.fcx.tcx;
         match self.fcx.fully_resolve(t) {
             Ok(t) if self.fcx.next_trait_solver() => {
                 // We must normalize erasing regions here, since later lints
                 // expect that types that show up in the typeck are fully
                 // normalized.
-                if let Ok(t) = self.fcx.tcx.try_normalize_erasing_regions(self.fcx.param_env, t) {
+                if let Ok(t) = tcx.try_normalize_erasing_regions(self.fcx.param_env, t) {
                     t
                 } else {
-                    EraseEarlyRegions { tcx: self.fcx.tcx }.fold_ty(t)
+                    tcx.fold_regions(t, |_, _| tcx.lifetimes.re_erased)
                 }
             }
             Ok(t) => {
@@ -810,7 +791,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
                 // (e.g. keep `for<'a>` named `for<'a>`).
                 // This allows NLL to generate error messages that
                 // refer to the higher-ranked lifetime names written by the user.
-                EraseEarlyRegions { tcx: self.fcx.tcx }.fold_ty(t)
+                tcx.fold_regions(t, |_, _| tcx.lifetimes.re_erased)
             }
             Err(_) => {
                 debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs
index 294bd9d1d54..3ee4710bdef 100644
--- a/compiler/rustc_infer/src/errors/note_and_explain.rs
+++ b/compiler/rustc_infer/src/errors/note_and_explain.rs
@@ -75,7 +75,7 @@ impl<'a> DescriptionCtx<'a> {
             // We shouldn't really be having unification failures with ReVar
             // and ReBound though.
             //
-            // FIXME(@lcnr): figure out why we `ReBound` have to handle `ReBound`
+            // FIXME(@lcnr): figure out why we have to handle `ReBound`
             // here, this feels somewhat off.
             ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => {
                 (alt_span, "revar", format!("{region:?}"))