about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorb-naber <bn263@gmx.de>2022-11-08 16:27:01 +0100
committerb-naber <b_naber@gmx.de>2023-02-19 22:11:27 +0000
commit2f79f73821db1f1f169abe526fc89c8b376636ea (patch)
tree4667b7d366487daa5f886efcb9c5c4bff7ae1c44 /compiler
parentcb35a7b4815774f7d8c8b9770df5a9719532755f (diff)
downloadrust-2f79f73821db1f1f169abe526fc89c8b376636ea.tar.gz
rust-2f79f73821db1f1f169abe526fc89c8b376636ea.zip
collect region contexts during mir renumbering
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_borrowck/src/lib.rs90
-rw-r--r--compiler/rustc_borrowck/src/nll.rs16
-rw-r--r--compiler/rustc_borrowck/src/renumber.rs115
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs34
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs117
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs2
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs2
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs7
8 files changed, 347 insertions, 36 deletions
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index fef5f4bf466..70f6a323e88 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -25,7 +25,9 @@ use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_index::bit_set::ChunkedBitSet;
 use rustc_index::vec::IndexVec;
-use rustc_infer::infer::{DefiningAnchor, InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt};
+use rustc_infer::infer::{
+    DefiningAnchor, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
+};
 use rustc_middle::mir::{
     traversal, Body, ClearCrossCrate, Local, Location, Mutability, NonDivergingIntrinsic, Operand,
     Place, PlaceElem, PlaceRef, VarDebugInfoContents,
@@ -95,6 +97,7 @@ use nll::{PoloniusOutput, ToRegionVid};
 use place_ext::PlaceExt;
 use places_conflict::{places_conflict, PlaceConflictBias};
 use region_infer::RegionInferenceContext;
+use renumber::RegionCtxt;
 
 // FIXME(eddyb) perhaps move this somewhere more centrally.
 #[derive(Debug)]
@@ -168,10 +171,10 @@ fn do_mir_borrowck<'tcx>(
     return_body_with_facts: bool,
 ) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
     let def = input_body.source.with_opt_param().as_local().unwrap();
-
     debug!(?def);
 
     let tcx = infcx.tcx;
+    let infcx = BorrowckInferCtxt::new(infcx);
     let param_env = tcx.param_env(def.did);
 
     let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
@@ -219,7 +222,7 @@ fn do_mir_borrowck<'tcx>(
     let mut body_owned = input_body.clone();
     let mut promoted = input_promoted.clone();
     let free_regions =
-        nll::replace_regions_in_mir(infcx, param_env, &mut body_owned, &mut promoted);
+        nll::replace_regions_in_mir(&infcx, param_env, &mut body_owned, &mut promoted);
     let body = &body_owned; // no further changes
 
     let location_table_owned = LocationTable::new(body);
@@ -257,7 +260,7 @@ fn do_mir_borrowck<'tcx>(
         opt_closure_req,
         nll_errors,
     } = nll::compute_regions(
-        infcx,
+        &infcx,
         free_regions,
         body,
         &promoted,
@@ -272,12 +275,12 @@ fn do_mir_borrowck<'tcx>(
 
     // Dump MIR results into a file, if that is enabled. This let us
     // write unit-tests, as well as helping with debugging.
-    nll::dump_mir_results(infcx, &body, &regioncx, &opt_closure_req);
+    nll::dump_mir_results(&infcx, &body, &regioncx, &opt_closure_req);
 
     // We also have a `#[rustc_regions]` annotation that causes us to dump
     // information.
     nll::dump_annotation(
-        infcx,
+        &infcx,
         &body,
         &regioncx,
         &opt_closure_req,
@@ -321,7 +324,7 @@ fn do_mir_borrowck<'tcx>(
 
         if let Err((move_data, move_errors)) = move_data_results {
             let mut promoted_mbcx = MirBorrowckCtxt {
-                infcx,
+                infcx: &infcx,
                 param_env,
                 body: promoted_body,
                 move_data: &move_data,
@@ -350,7 +353,7 @@ fn do_mir_borrowck<'tcx>(
     }
 
     let mut mbcx = MirBorrowckCtxt {
-        infcx,
+        infcx: &infcx,
         param_env,
         body,
         move_data: &mdpe.move_data,
@@ -482,22 +485,81 @@ pub struct BodyWithBorrowckFacts<'tcx> {
     pub location_table: LocationTable,
 }
 
-struct BorrowckInferCtxt<'cx, 'tcx> {
+pub struct BorrowckInferCtxt<'cx, 'tcx> {
     pub(crate) infcx: &'cx InferCtxt<'tcx>,
 
     #[cfg(debug_assertions)]
-    pub(crate) _reg_var_to_origin: RefCell<FxHashMap<ty::Region<'tcx>, NllRegionVariableOrigin>>,
+    pub(crate) reg_var_to_origin: RefCell<FxHashMap<ty::RegionVid, RegionCtxt>>,
 }
 
 impl<'cx, 'tcx> BorrowckInferCtxt<'cx, 'tcx> {
     #[cfg(not(debug_assertions))]
-    pub(crate) fn _new(infcx: &'cx InferCtxt<'tcx>) -> Self {
+    pub(crate) fn new(infcx: &'cx InferCtxt<'tcx>) -> Self {
         BorrowckInferCtxt { infcx }
     }
 
     #[cfg(debug_assertions)]
-    pub(crate) fn _new(infcx: &'cx InferCtxt<'tcx>) -> Self {
-        BorrowckInferCtxt { infcx, _reg_var_to_origin: RefCell::new(Default::default()) }
+    pub(crate) fn new(infcx: &'cx InferCtxt<'tcx>) -> Self {
+        BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()) }
+    }
+
+    #[cfg(not(debug_assertions))]
+    pub(crate) fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> {
+        self.infcx.next_region_var(origin)
+    }
+
+    #[cfg(debug_assertions)]
+    pub(crate) fn next_region_var(
+        &self,
+        origin: RegionVariableOrigin,
+        ctxt: RegionCtxt,
+    ) -> ty::Region<'tcx> {
+        let next_region = self.infcx.next_region_var(origin);
+        let vid = next_region
+            .try_get_var()
+            .unwrap_or_else(|| bug!("expected RegionKind::RegionVar on {:?}", next_region));
+
+        debug!("inserting vid {:?} with origin {:?} into var_to_origin", vid, origin);
+        let mut var_to_origin = self.reg_var_to_origin.borrow_mut();
+        let prev = var_to_origin.insert(vid, ctxt);
+        debug!("var_to_origin after insertion: {:?}", var_to_origin);
+
+        // This only makes sense if not called in a canonicalization context. If this
+        // ever changes we either want to get rid of `BorrowckInferContext::reg_var_to_origin`
+        // or modify how we track nll region vars for that map.
+        assert!(matches!(prev, None));
+
+        next_region
+    }
+
+    #[cfg(not(debug_assertions))]
+    pub(crate) fn next_nll_region_var(&self, origin: NllRegionVariableOrigin) -> ty::Region<'tcx> {
+        self.infcx.next_nll_region_var(origin)
+    }
+
+    #[cfg(debug_assertions)]
+    #[instrument(skip(self), level = "debug")]
+    pub(crate) fn next_nll_region_var(
+        &self,
+        origin: NllRegionVariableOrigin,
+        ctxt: RegionCtxt,
+    ) -> ty::Region<'tcx> {
+        let next_region = self.infcx.next_nll_region_var(origin.clone());
+        let vid = next_region
+            .try_get_var()
+            .unwrap_or_else(|| bug!("expected RegionKind::RegionVar on {:?}", next_region));
+
+        debug!("inserting vid {:?} with origin {:?} into var_to_origin", vid, origin);
+        let mut var_to_origin = self.reg_var_to_origin.borrow_mut();
+        let prev = var_to_origin.insert(vid, ctxt);
+        debug!("var_to_origin after insertion: {:?}", var_to_origin);
+
+        // This only makes sense if not called in a canonicalization context. If this
+        // ever changes we either want to get rid of `BorrowckInferContext::reg_var_to_origin`
+        // or modify how we track nll region vars for that map.
+        assert!(matches!(prev, None));
+
+        next_region
     }
 }
 
@@ -510,7 +572,7 @@ impl<'cx, 'tcx> Deref for BorrowckInferCtxt<'cx, 'tcx> {
 }
 
 struct MirBorrowckCtxt<'cx, 'tcx> {
-    infcx: &'cx InferCtxt<'tcx>,
+    infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
     param_env: ParamEnv<'tcx>,
     body: &'cx Body<'tcx>,
     move_data: &'cx MoveData<'tcx>,
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index c71413e8e7c..c3301ac1442 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -5,7 +5,6 @@
 use rustc_data_structures::vec_map::VecMap;
 use rustc_hir::def_id::LocalDefId;
 use rustc_index::vec::IndexVec;
-use rustc_infer::infer::InferCtxt;
 use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere};
 use rustc_middle::mir::{
     BasicBlock, Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location,
@@ -37,7 +36,7 @@ use crate::{
     renumber,
     type_check::{self, MirTypeckRegionConstraints, MirTypeckResults},
     universal_regions::UniversalRegions,
-    Upvar,
+    BorrowckInferCtxt, Upvar,
 };
 
 pub type PoloniusOutput = Output<RustcFacts>;
@@ -58,7 +57,7 @@ pub(crate) struct NllOutput<'tcx> {
 /// `compute_regions`.
 #[instrument(skip(infcx, param_env, body, promoted), level = "debug")]
 pub(crate) fn replace_regions_in_mir<'tcx>(
-    infcx: &InferCtxt<'tcx>,
+    infcx: &BorrowckInferCtxt<'_, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     body: &mut Body<'tcx>,
     promoted: &mut IndexVec<Promoted, Body<'tcx>>,
@@ -157,7 +156,7 @@ fn populate_polonius_move_facts(
 ///
 /// This may result in errors being reported.
 pub(crate) fn compute_regions<'cx, 'tcx>(
-    infcx: &InferCtxt<'tcx>,
+    infcx: &BorrowckInferCtxt<'_, 'tcx>,
     universal_regions: UniversalRegions<'tcx>,
     body: &Body<'tcx>,
     promoted: &IndexVec<Promoted, Body<'tcx>>,
@@ -258,6 +257,11 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
         borrow_set,
     );
 
+    if cfg!(debug_assertions) {
+        let var_to_origin = infcx.reg_var_to_origin.borrow();
+        debug!("var_to_origin: {:#?}", var_to_origin);
+    }
+
     let mut regioncx = RegionInferenceContext::new(
         var_origins,
         universal_regions,
@@ -322,7 +326,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
 }
 
 pub(super) fn dump_mir_results<'tcx>(
-    infcx: &InferCtxt<'tcx>,
+    infcx: &BorrowckInferCtxt<'_, 'tcx>,
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     closure_region_requirements: &Option<ClosureRegionRequirements<'_>>,
@@ -372,7 +376,7 @@ pub(super) fn dump_mir_results<'tcx>(
 #[allow(rustc::diagnostic_outside_of_impl)]
 #[allow(rustc::untranslatable_diagnostic)]
 pub(super) fn dump_annotation<'tcx>(
-    infcx: &InferCtxt<'tcx>,
+    infcx: &BorrowckInferCtxt<'_, 'tcx>,
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     closure_region_requirements: &Option<ClosureRegionRequirements<'_>>,
diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs
index 084754830bd..adf9869c34b 100644
--- a/compiler/rustc_borrowck/src/renumber.rs
+++ b/compiler/rustc_borrowck/src/renumber.rs
@@ -1,18 +1,23 @@
+<<<<<<< HEAD
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
+=======
+use crate::BorrowckInferCtxt;
+>>>>>>> 2464f768a17 (collect region contexts during mir renumbering)
 use rustc_index::vec::IndexVec;
-use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
+use rustc_infer::infer::NllRegionVariableOrigin;
 use rustc_middle::mir::visit::{MutVisitor, TyContext};
 use rustc_middle::mir::Constant;
 use rustc_middle::mir::{Body, Location, Promoted};
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
+use rustc_span::Symbol;
 
 /// Replaces all free regions appearing in the MIR with fresh
 /// inference variables, returning the number of variables created.
 #[instrument(skip(infcx, body, promoted), level = "debug")]
 pub fn renumber_mir<'tcx>(
-    infcx: &InferCtxt<'tcx>,
+    infcx: &BorrowckInferCtxt<'_, 'tcx>,
     body: &mut Body<'tcx>,
     promoted: &mut IndexVec<Promoted, Body<'tcx>>,
 ) {
@@ -29,8 +34,9 @@ pub fn renumber_mir<'tcx>(
 
 /// Replaces all regions appearing in `value` with fresh inference
 /// variables.
+#[cfg(not(debug_assertions))]
 #[instrument(skip(infcx), level = "debug")]
-pub fn renumber_regions<'tcx, T>(infcx: &InferCtxt<'tcx>, value: T) -> T
+pub(crate) fn renumber_regions<'tcx, T>(infcx: &BorrowckInferCtxt<'_, 'tcx>, value: T) -> T
 where
     T: TypeFoldable<'tcx>,
 {
@@ -40,11 +46,73 @@ where
     })
 }
 
+/// Replaces all regions appearing in `value` with fresh inference
+/// variables.
+#[cfg(debug_assertions)]
+#[instrument(skip(infcx), level = "debug")]
+pub(crate) fn renumber_regions<'tcx, T>(
+    infcx: &BorrowckInferCtxt<'_, 'tcx>,
+    value: T,
+    ctxt: RegionCtxt,
+) -> T
+where
+    T: TypeFoldable<'tcx>,
+{
+    infcx.tcx.fold_regions(value, |_region, _depth| {
+        let origin = NllRegionVariableOrigin::Existential { from_forall: false };
+        infcx.next_nll_region_var(origin, ctxt)
+    })
+}
+
+#[cfg(debug_assertions)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
+pub(crate) enum RegionCtxt {
+    Location(Location),
+    TyContext(TyContext),
+    Free(Symbol),
+    Bound(Symbol),
+    LateBound(Symbol),
+    Existential(Option<Symbol>),
+    Placeholder(Symbol),
+    Unknown,
+}
+
+#[cfg(debug_assertions)]
+impl RegionCtxt {
+    /// Used to determine the representative of a component in the strongly connected
+    /// constraint graph
+    /// FIXME: don't use underscore here. Got a 'not used' error for some reason
+    pub(crate) fn _preference_value(self) -> usize {
+        let _anon = Symbol::intern("anon");
+
+        match self {
+            RegionCtxt::Unknown => 1,
+            RegionCtxt::Existential(None) => 2,
+            RegionCtxt::Existential(Some(_anon))
+            | RegionCtxt::Free(_anon)
+            | RegionCtxt::Bound(_anon)
+            | RegionCtxt::LateBound(_anon) => 2,
+            RegionCtxt::Location(_) => 3,
+            RegionCtxt::TyContext(_) => 4,
+            _ => 5,
+        }
+    }
+}
+
 struct NllVisitor<'a, 'tcx> {
-    infcx: &'a InferCtxt<'tcx>,
+    infcx: &'a BorrowckInferCtxt<'a, 'tcx>,
 }
 
 impl<'a, 'tcx> NllVisitor<'a, 'tcx> {
+    #[cfg(debug_assertions)]
+    fn renumber_regions<T>(&mut self, value: T, ctxt: RegionCtxt) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        renumber_regions(self.infcx, value, ctxt)
+    }
+
+    #[cfg(not(debug_assertions))]
     fn renumber_regions<T>(&mut self, value: T) -> T
     where
         T: TypeFoldable<'tcx>,
@@ -58,13 +126,23 @@ impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> {
         self.infcx.tcx
     }
 
+    #[cfg(not(debug_assertions))]
     #[instrument(skip(self), level = "debug")]
-    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
+    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _ty_context: TyContext) {
         *ty = self.renumber_regions(*ty);
 
         debug!(?ty);
     }
 
+    #[cfg(debug_assertions)]
+    #[instrument(skip(self), level = "debug")]
+    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _ty_context: TyContext) {
+        *ty = self.renumber_regions(*ty, RegionCtxt::TyContext(_ty_context));
+
+        debug!(?ty);
+    }
+
+    #[cfg(not(debug_assertions))]
     #[instrument(skip(self), level = "debug")]
     fn visit_substs(&mut self, substs: &mut SubstsRef<'tcx>, location: Location) {
         *substs = self.renumber_regions(*substs);
@@ -72,6 +150,15 @@ impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> {
         debug!(?substs);
     }
 
+    #[cfg(debug_assertions)]
+    #[instrument(skip(self), level = "debug")]
+    fn visit_substs(&mut self, substs: &mut SubstsRef<'tcx>, location: Location) {
+        *substs = self.renumber_regions(*substs, RegionCtxt::Location(location));
+
+        debug!(?substs);
+    }
+
+    #[cfg(not(debug_assertions))]
     #[instrument(skip(self), level = "debug")]
     fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) {
         let old_region = *region;
@@ -80,10 +167,28 @@ impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> {
         debug!(?region);
     }
 
+    #[cfg(debug_assertions)]
+    #[instrument(skip(self), level = "debug")]
+    fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) {
+        let old_region = *region;
+        *region = self.renumber_regions(old_region, RegionCtxt::Location(location));
+
+        debug!(?region);
+    }
+
+    #[cfg(not(debug_assertions))]
     #[instrument(skip(self), level = "debug")]
     fn visit_constant(&mut self, constant: &mut Constant<'tcx>, _location: Location) {
         let literal = constant.literal;
         constant.literal = self.renumber_regions(literal);
         debug!("constant: {:#?}", constant);
     }
+
+    #[cfg(debug_assertions)]
+    #[instrument(skip(self), level = "debug")]
+    fn visit_constant(&mut self, constant: &mut Constant<'tcx>, _location: Location) {
+        let literal = constant.literal;
+        constant.literal = self.renumber_regions(literal, RegionCtxt::Location(_location));
+        debug!("constant: {:#?}", constant);
+    }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 004b945eada..8eb42ade65b 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1335,11 +1335,35 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
                 };
                 let (sig, map) = tcx.replace_late_bound_regions(sig, |br| {
-                    self.infcx.next_region_var(LateBoundRegion(
-                        term.source_info.span,
-                        br.kind,
-                        LateBoundRegionConversionTime::FnCall,
-                    ))
+                    #[cfg(not(debug_assertions))]
+                    {
+                        self.infcx.next_region_var(LateBoundRegion(
+                            term.source_info.span,
+                            br.kind,
+                            LateBoundRegionConversionTime::FnCall,
+                        ))
+                    }
+
+                    #[cfg(debug_assertions)]
+                    {
+                        use crate::renumber::RegionCtxt;
+                        use rustc_span::Symbol;
+
+                        let name = match br.kind {
+                            ty::BoundRegionKind::BrAnon(_) => Symbol::intern("anon"),
+                            ty::BoundRegionKind::BrNamed(_, name) => name,
+                            ty::BoundRegionKind::BrEnv => Symbol::intern("env"),
+                        };
+
+                        self.infcx.next_region_var(
+                            LateBoundRegion(
+                                term.source_info.span,
+                                br.kind,
+                                LateBoundRegionConversionTime::FnCall,
+                            ),
+                            RegionCtxt::LateBound(name),
+                        )
+                    }
                 });
                 debug!(?sig);
                 // IMPORTANT: We have to prove well formed for the function signature before
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index efa5a29c5dd..919c772a203 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -20,15 +20,18 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{BodyOwnerKind, HirId};
 use rustc_index::vec::{Idx, IndexVec};
-use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
+use rustc_infer::infer::NllRegionVariableOrigin;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{
     self, DefIdTree, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt,
 };
 use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_span::Symbol;
 use std::iter;
 
 use crate::nll::ToRegionVid;
+use crate::renumber::RegionCtxt;
+use crate::BorrowckInferCtxt;
 
 #[derive(Debug)]
 pub struct UniversalRegions<'tcx> {
@@ -224,7 +227,7 @@ impl<'tcx> UniversalRegions<'tcx> {
     /// signature. This will also compute the relationships that are
     /// known between those regions.
     pub fn new(
-        infcx: &InferCtxt<'tcx>,
+        infcx: &BorrowckInferCtxt<'_, 'tcx>,
         mir_def: ty::WithOptConstParam<LocalDefId>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Self {
@@ -385,7 +388,7 @@ impl<'tcx> UniversalRegions<'tcx> {
 }
 
 struct UniversalRegionsBuilder<'cx, 'tcx> {
-    infcx: &'cx InferCtxt<'tcx>,
+    infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
     mir_def: ty::WithOptConstParam<LocalDefId>,
     mir_hir_id: HirId,
     param_env: ty::ParamEnv<'tcx>,
@@ -403,7 +406,13 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
         assert_eq!(FIRST_GLOBAL_INDEX, self.infcx.num_region_vars());
 
         // Create the "global" region that is always free in all contexts: 'static.
+        #[cfg(not(debug_assertions))]
         let fr_static = self.infcx.next_nll_region_var(FR).to_region_vid();
+        #[cfg(debug_assertions)]
+        let fr_static = self
+            .infcx
+            .next_nll_region_var(FR, RegionCtxt::Free(Symbol::intern("static")))
+            .to_region_vid();
 
         // We've now added all the global regions. The next ones we
         // add will be external.
@@ -480,6 +489,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
                     LangItem::VaList,
                     Some(self.infcx.tcx.def_span(self.mir_def.did)),
                 );
+                let reg_vid = self.infcx.next_nll_region_var(FR, RegionCtxt::Free(Symbol::intern("c-variadic")).to_region_vid();
                 let region =
                     self.infcx.tcx.mk_re_var(self.infcx.next_nll_region_var(FR).to_region_vid());
                 let va_list_ty =
@@ -491,7 +501,14 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
             }
         }
 
+        #[cfg(not(debug_assertions))]
         let fr_fn_body = self.infcx.next_nll_region_var(FR).to_region_vid();
+        #[cfg(debug_assertions)]
+        let fr_fn_body = self
+            .infcx
+            .next_nll_region_var(FR, RegionCtxt::Free(Symbol::intern("fn_body")))
+            .to_region_vid();
+
         let num_universals = self.infcx.num_region_vars();
 
         debug!("build: global regions = {}..{}", FIRST_GLOBAL_INDEX, first_extern_index);
@@ -718,7 +735,8 @@ trait InferCtxtExt<'tcx> {
     );
 }
 
-impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
+impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> {
+    #[cfg(not(debug_assertions))]
     fn replace_free_regions_with_nll_infer_vars<T>(
         &self,
         origin: NllRegionVariableOrigin,
@@ -727,9 +745,33 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
     where
         T: TypeFoldable<'tcx>,
     {
-        self.tcx.fold_regions(value, |_region, _depth| self.next_nll_region_var(origin))
+        self.tcx.fold_regions(value, |_region, _depth| self.infcx.next_nll_region_var(origin))
     }
 
+    #[cfg(debug_assertions)]
+    #[instrument(skip(self), level = "debug")]
+    fn replace_free_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NllRegionVariableOrigin,
+        value: T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.infcx.tcx.fold_regions(value, |region, _depth| {
+            let name = match region.get_name() {
+                Some(name) => name,
+                _ => Symbol::intern("anon"),
+            };
+            debug!(?region, ?name);
+
+            let reg_var = self.next_nll_region_var(origin, RegionCtxt::Free(name));
+
+            reg_var
+        })
+    }
+
+    #[cfg(not(debug_assertions))]
     #[instrument(level = "debug", skip(self, indices))]
     fn replace_bound_regions_with_nll_infer_vars<T>(
         &self,
@@ -752,6 +794,38 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         value
     }
 
+    #[cfg(debug_assertions)]
+    #[instrument(level = "debug", skip(self, indices))]
+    fn replace_bound_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NllRegionVariableOrigin,
+        all_outlive_scope: LocalDefId,
+        value: ty::Binder<'tcx, T>,
+        indices: &mut UniversalRegionIndices<'tcx>,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        let (value, _map) = self.tcx.replace_late_bound_regions(value, |br| {
+            debug!(?br);
+            let liberated_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
+                scope: all_outlive_scope.to_def_id(),
+                bound_region: br.kind,
+            }));
+
+            let name = match br.kind.get_name() {
+                Some(name) => name,
+                _ => Symbol::intern("anon"),
+            };
+
+            let region_vid = self.next_nll_region_var(origin, RegionCtxt::Bound(name));
+            indices.insert_late_bound_region(liberated_region, region_vid.to_region_vid());
+            debug!(?liberated_region, ?region_vid);
+            region_vid
+        });
+        value
+    }
+
     /// Finds late-bound regions that do not appear in the parameter listing and adds them to the
     /// indices vector. Typically, we identify late-bound regions as we process the inputs and
     /// outputs of the closure/function. However, sometimes there are late-bound regions which do
@@ -761,6 +835,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
     /// entries for them and store them in the indices map. This code iterates over the complete
     /// set of late-bound regions and checks for any that we have not yet seen, adding them to the
     /// inputs vector.
+    #[cfg(not(debug_assertions))]
     #[instrument(skip(self, indices))]
     fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope(
         &self,
@@ -792,6 +867,38 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
             }
         });
     }
+
+    /// Finds late-bound regions that do not appear in the parameter listing and adds them to the
+    /// indices vector. Typically, we identify late-bound regions as we process the inputs and
+    /// outputs of the closure/function. However, sometimes there are late-bound regions which do
+    /// not appear in the fn parameters but which are nonetheless in scope. The simplest case of
+    /// this are unused functions, like fn foo<'a>() { } (see e.g., #51351). Despite not being used,
+    /// users can still reference these regions (e.g., let x: &'a u32 = &22;), so we need to create
+    /// entries for them and store them in the indices map. This code iterates over the complete
+    /// set of late-bound regions and checks for any that we have not yet seen, adding them to the
+    /// inputs vector.
+    #[cfg(debug_assertions)]
+    #[instrument(skip(self, indices))]
+    fn replace_late_bound_regions_with_nll_infer_vars(
+        &self,
+        mir_def_id: LocalDefId,
+        indices: &mut UniversalRegionIndices<'tcx>,
+    ) {
+        let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id());
+        for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| {
+            debug!(?r);
+            if !indices.indices.contains_key(&r) {
+                let name = match r.get_name() {
+                    Some(name) => name,
+                    _ => Symbol::intern("anon"),
+                };
+
+                let region_vid = self.next_nll_region_var(FR, RegionCtxt::LateBound(name));
+                debug!(?region_vid);
+                indices.insert_late_bound_region(r, region_vid.to_region_vid());
+            }
+        });
+    }
 }
 
 impl<'tcx> UniversalRegionIndices<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index aa316b2dadb..64384fadc63 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1111,11 +1111,13 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 
     /// Just a convenient wrapper of `next_region_var` for using during NLL.
+    #[instrument(skip(self), level = "debug")]
     pub fn next_nll_region_var(&self, origin: NllRegionVariableOrigin) -> ty::Region<'tcx> {
         self.next_region_var(RegionVariableOrigin::Nll(origin))
     }
 
     /// Just a convenient wrapper of `next_region_var` for using during NLL.
+    #[instrument(skip(self), level = "debug")]
     pub fn next_nll_region_var_in_universe(
         &self,
         origin: NllRegionVariableOrigin,
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 443c1b2d261..7f0935fb149 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -1214,7 +1214,7 @@ impl<'tcx> MirVisitable<'tcx> for Option<Terminator<'tcx>> {
 
 /// Extra information passed to `visit_ty` and friends to give context
 /// about where the type etc appears.
-#[derive(Debug)]
+#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
 pub enum TyContext {
     LocalDecl {
         /// The index of the local variable we are visiting.
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index f8d17433cf7..c5d6df5b1ad 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1751,6 +1751,13 @@ impl<'tcx> Region<'tcx> {
     pub fn is_var(self) -> bool {
         matches!(self.kind(), ty::ReVar(_))
     }
+
+    pub fn try_get_var(self) -> Option<RegionVid> {
+        match self.kind() {
+            ty::ReVar(vid) => Some(vid),
+            _ => None,
+        }
+    }
 }
 
 /// Type utilities