about summary refs log tree commit diff
path: root/compiler/rustc_borrowck/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_borrowck/src')
-rw-r--r--compiler/rustc_borrowck/src/consumers.rs11
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs44
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs43
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs398
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs45
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs145
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs250
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs782
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/opaque_types.rs (renamed from compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs)76
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs89
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs10
-rw-r--r--compiler/rustc_borrowck/src/handle_placeholders.rs231
-rw-r--r--compiler/rustc_borrowck/src/lib.rs174
-rw-r--r--compiler/rustc_borrowck/src/member_constraints.rs226
-rw-r--r--compiler/rustc_borrowck/src/nll.rs118
-rw-r--r--compiler/rustc_borrowck/src/polonius/constraints.rs4
-rw-r--r--compiler/rustc_borrowck/src/polonius/dump.rs87
-rw-r--r--compiler/rustc_borrowck/src/polonius/legacy/facts.rs16
-rw-r--r--compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs9
-rw-r--r--compiler/rustc_borrowck/src/polonius/liveness_constraints.rs12
-rw-r--r--compiler/rustc_borrowck/src/polonius/loan_liveness.rs191
-rw-r--r--compiler/rustc_borrowck/src/polonius/mod.rs6
-rw-r--r--compiler/rustc_borrowck/src/polonius/typeck_constraints.rs19
-rw-r--r--compiler/rustc_borrowck/src/region_infer/graphviz.rs27
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs615
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs291
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs194
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs716
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs114
-rw-r--r--compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs10
-rw-r--r--compiler/rustc_borrowck/src/region_infer/values.rs2
-rw-r--r--compiler/rustc_borrowck/src/renumber.rs2
-rw-r--r--compiler/rustc_borrowck/src/root_cx.rs299
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs121
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs53
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs33
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs1
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs374
-rw-r--r--compiler/rustc_borrowck/src/type_check/opaque_types.rs333
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs29
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs39
42 files changed, 3293 insertions, 2948 deletions
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index 1c4ff5a6779..54897371410 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -17,7 +17,7 @@ pub use super::polonius::legacy::{
     RichLocation, RustcFacts,
 };
 pub use super::region_infer::RegionInferenceContext;
-use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
+use crate::BorrowCheckRootCtxt;
 
 /// Struct used during mir borrowck to collect bodies with facts for a typeck root and all
 /// its nested bodies.
@@ -127,13 +127,6 @@ pub fn get_bodies_with_borrowck_facts(
 ) -> FxHashMap<LocalDefId, BodyWithBorrowckFacts<'_>> {
     let mut root_cx =
         BorrowCheckRootCtxt::new(tcx, root_def_id, Some(BorrowckConsumer::new(options)));
-
-    // See comment in `rustc_borrowck::mir_borrowck`
-    let nested_bodies = tcx.nested_bodies_within(root_def_id);
-    for def_id in nested_bodies {
-        root_cx.get_or_insert_nested(def_id);
-    }
-
-    do_mir_borrowck(&mut root_cx, root_def_id);
+    root_cx.do_mir_borrowck();
     root_cx.consumer.unwrap().bodies
 }
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 57db2e9fb57..d3f6c01ab8c 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -1,7 +1,6 @@
 use std::fmt;
 
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_data_structures::graph;
 use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
 use rustc_middle::mir::{
     self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
@@ -317,9 +316,8 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
             loans_out_of_scope_at_location: FxIndexMap::default(),
         };
         for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
-            let issuing_region = loan_data.region;
             let loan_issued_at = loan_data.reserve_location;
-            prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at);
+            prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
         }
 
         prec.loans_out_of_scope_at_location
@@ -328,45 +326,7 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
     /// Loans are in scope while they are live: whether they are contained within any live region.
     /// In the location-insensitive analysis, a loan will be contained in a region if the issuing
     /// region can reach it in the subset graph. So this is a reachability problem.
-    fn precompute_loans_out_of_scope(
-        &mut self,
-        loan_idx: BorrowIndex,
-        issuing_region: RegionVid,
-        loan_issued_at: Location,
-    ) {
-        let sccs = self.regioncx.constraint_sccs();
-        let universal_regions = self.regioncx.universal_regions();
-
-        // The loop below was useful for the location-insensitive analysis but shouldn't be
-        // impactful in the location-sensitive case. It seems that it does, however, as without it a
-        // handful of tests fail. That likely means some liveness or outlives data related to choice
-        // regions is missing
-        // FIXME: investigate the impact of loans traversing applied member constraints and why some
-        // tests fail otherwise.
-        //
-        // We first handle the cases where the loan doesn't go out of scope, depending on the
-        // issuing region's successors.
-        for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) {
-            // Via applied member constraints
-            //
-            // The issuing region can flow into the choice regions, and they are either:
-            // - placeholders or free regions themselves,
-            // - or also transitively outlive a free region.
-            //
-            // That is to say, if there are applied member constraints here, the loan escapes the
-            // function and cannot go out of scope. We could early return here.
-            //
-            // For additional insurance via fuzzing and crater, we verify that the constraint's min
-            // choice indeed escapes the function. In the future, we could e.g. turn this check into
-            // a debug assert and early return as an optimization.
-            let scc = sccs.scc(successor);
-            for constraint in self.regioncx.applied_member_constraints(scc) {
-                if universal_regions.is_universal_region(constraint.min_choice) {
-                    return;
-                }
-            }
-        }
-
+    fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
         let first_block = loan_issued_at.block;
         let first_bb_data = &self.body.basic_blocks[first_block];
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index 0de4bd67f0c..76da9dc8832 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -3,7 +3,7 @@ use std::rc::Rc;
 
 use rustc_errors::Diag;
 use rustc_hir::def_id::LocalDefId;
-use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
+use rustc_infer::infer::region_constraints::{Constraint, ConstraintKind, RegionConstraintData};
 use rustc_infer::infer::{
     InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt as _,
 };
@@ -277,7 +277,7 @@ where
         // `QueryNormalizeExt::query_normalize` used in the query and `normalize` called below:
         // the former fails to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs`
         // test. Check after #85499 lands to see if its fixes have erased this difference.
-        let (param_env, value) = key.into_parts();
+        let ty::ParamEnvAnd { param_env, value } = key;
         let _ = ocx.normalize(&cause, param_env, value.value);
 
         let diag = try_extract_error_from_fulfill_cx(
@@ -324,7 +324,7 @@ where
             mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
         let ocx = ObligationCtxt::new(&infcx);
 
-        let (param_env, value) = key.into_parts();
+        let ty::ParamEnvAnd { param_env, value } = key;
         let _ = ocx.deeply_normalize(&cause, param_env, value.value);
 
         let diag = try_extract_error_from_fulfill_cx(
@@ -454,25 +454,24 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>(
             (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound,
             _ => a_region == b_region,
         };
-    let mut check =
-        |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match *constraint {
-            Constraint::RegSubReg(sub, sup)
-                if ((exact && sup == placeholder_region)
-                    || (!exact && regions_the_same(sup, placeholder_region)))
-                    && sup != sub =>
-            {
-                Some((sub, cause.clone()))
-            }
-            Constraint::VarSubReg(vid, sup)
-                if (exact
-                    && sup == placeholder_region
-                    && !universe_of_region(vid).can_name(placeholder_universe))
-                    || (!exact && regions_the_same(sup, placeholder_region)) =>
-            {
-                Some((ty::Region::new_var(infcx.tcx, vid), cause.clone()))
-            }
-            _ => None,
-        };
+    let mut check = |c: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match c.kind {
+        ConstraintKind::RegSubReg
+            if ((exact && c.sup == placeholder_region)
+                || (!exact && regions_the_same(c.sup, placeholder_region)))
+                && c.sup != c.sub =>
+        {
+            Some((c.sub, cause.clone()))
+        }
+        ConstraintKind::VarSubReg
+            if (exact
+                && c.sup == placeholder_region
+                && !universe_of_region(c.sub.as_var()).can_name(placeholder_universe))
+                || (!exact && regions_the_same(c.sup, placeholder_region)) =>
+        {
+            Some((c.sub, cause.clone()))
+        }
+        _ => None,
+    };
 
     let mut find_culprit = |exact_match: bool| {
         region_constraints
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 040a0607db5..7e20a5133e0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -410,18 +410,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 }
                 let typeck = self.infcx.tcx.typeck(self.mir_def_id());
                 let parent = self.infcx.tcx.parent_hir_node(expr.hir_id);
-                let (def_id, call_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
+                let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
                     && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
                 {
                     let def_id = typeck.type_dependent_def_id(parent_expr.hir_id);
-                    (def_id, Some(parent_expr.hir_id), args, 1)
+                    (def_id, args, 1)
                 } else if let hir::Node::Expr(parent_expr) = parent
                     && let hir::ExprKind::Call(call, args) = parent_expr.kind
                     && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
                 {
-                    (Some(*def_id), Some(call.hir_id), args, 0)
+                    (Some(*def_id), args, 0)
                 } else {
-                    (None, None, &[][..], 0)
+                    (None, &[][..], 0)
                 };
                 let ty = place.ty(self.body, self.infcx.tcx).ty;
 
@@ -459,11 +459,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     // If the moved place is used generically by the callee and a reference to it
                     // would still satisfy any bounds on its type, suggest borrowing.
                     if let Some(&param) = arg_param
-                        && let Some(generic_args) = call_id.and_then(|id| typeck.node_args_opt(id))
+                        && let hir::Node::Expr(call_expr) = parent
                         && let Some(ref_mutability) = self.suggest_borrow_generic_arg(
                             err,
+                            typeck,
+                            call_expr,
                             def_id,
-                            generic_args,
                             param,
                             moved_place,
                             pos + offset,
@@ -627,8 +628,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     fn suggest_borrow_generic_arg(
         &self,
         err: &mut Diag<'_>,
+        typeck: &ty::TypeckResults<'tcx>,
+        call_expr: &hir::Expr<'tcx>,
         callee_did: DefId,
-        generic_args: ty::GenericArgsRef<'tcx>,
         param: ty::ParamTy,
         moved_place: PlaceRef<'tcx>,
         moved_arg_pos: usize,
@@ -639,6 +641,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let sig = tcx.fn_sig(callee_did).instantiate_identity().skip_binder();
         let clauses = tcx.predicates_of(callee_did);
 
+        let generic_args = match call_expr.kind {
+            // For method calls, generic arguments are attached to the call node.
+            hir::ExprKind::MethodCall(..) => typeck.node_args_opt(call_expr.hir_id)?,
+            // For normal calls, generic arguments are in the callee's type.
+            // This diagnostic is only run for `FnDef` callees.
+            hir::ExprKind::Call(callee, _)
+                if let &ty::FnDef(_, args) = typeck.node_type(callee.hir_id).kind() =>
+            {
+                args
+            }
+            _ => return None,
+        };
+
         // First, is there at least one method on one of `param`'s trait bounds?
         // This keeps us from suggesting borrowing the argument to `mem::drop`, e.g.
         if !clauses.instantiate_identity(tcx).predicates.iter().any(|clause| {
@@ -1290,6 +1305,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 span,
                 format!("if `{ty}` implemented `Clone`, you could clone the value"),
             );
+        } else if let ty::Adt(_, _) = ty.kind()
+            && let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
+        {
+            // For cases like `Option<NonClone>`, where `Option<T>: Clone` if `T: Clone`, we point
+            // at the types that should be `Clone`.
+            let ocx = ObligationCtxt::new_with_diagnostics(self.infcx);
+            let cause = ObligationCause::misc(expr.span, self.mir_def_id());
+            ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait);
+            let errors = ocx.select_all_or_error();
+            if errors.iter().all(|error| {
+                match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) {
+                    Some(clause) => match clause.self_ty().skip_binder().kind() {
+                        ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait,
+                        _ => false,
+                    },
+                    None => false,
+                }
+            }) {
+                let mut type_spans = vec![];
+                let mut types = FxIndexSet::default();
+                for clause in errors
+                    .iter()
+                    .filter_map(|e| e.obligation.predicate.as_clause())
+                    .filter_map(|c| c.as_trait_clause())
+                {
+                    let ty::Adt(def, _) = clause.self_ty().skip_binder().kind() else { continue };
+                    type_spans.push(self.infcx.tcx.def_span(def.did()));
+                    types.insert(
+                        self.infcx
+                            .tcx
+                            .short_string(clause.self_ty().skip_binder(), &mut err.long_ty_path()),
+                    );
+                }
+                let mut span: MultiSpan = type_spans.clone().into();
+                for sp in type_spans {
+                    span.push_span_label(sp, "consider implementing `Clone` for this type");
+                }
+                span.push_span_label(expr.span, "you could clone this value");
+                let types: Vec<_> = types.into_iter().collect();
+                let msg = match &types[..] {
+                    [only] => format!("`{only}`"),
+                    [head @ .., last] => format!(
+                        "{} and `{last}`",
+                        head.iter().map(|t| format!("`{t}`")).collect::<Vec<_>>().join(", ")
+                    ),
+                    [] => unreachable!(),
+                };
+                err.span_note(
+                    span,
+                    format!("if {msg} implemented `Clone`, you could clone the value"),
+                );
+            }
         }
     }
 
@@ -2332,7 +2399,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         if let Some(body_expr) = finder.body_expr
             && let Some(loop_span) = finder.loop_span
             && let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id)
-            && let Some(trait_did) = tcx.trait_of_item(def_id)
+            && let Some(trait_did) = tcx.trait_of_assoc(def_id)
             && tcx.is_diagnostic_item(sym::Iterator, trait_did)
         {
             if let Some(loop_bind) = finder.loop_bind {
@@ -2481,13 +2548,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         // Check that the parent of the closure is a method call,
         // with receiver matching with local's type (modulo refs)
-        if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id) {
-            if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind {
-                let recv_ty = typeck_results.expr_ty(recv);
+        if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id)
+            && let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind
+        {
+            let recv_ty = typeck_results.expr_ty(recv);
 
-                if recv_ty.peel_refs() != local_ty {
-                    return;
-                }
+            if recv_ty.peel_refs() != local_ty {
+                return;
             }
         }
 
@@ -2753,16 +2820,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 // With the place of a union and a field access into it, we traverse the second
                 // borrowed place and look for an access to a different field of the same union.
                 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
-                    if let ProjectionElem::Field(field, _) = elem {
-                        if let Some(union_ty) = union_ty(place_base) {
-                            if field != target_field && place_base == target_base {
-                                return Some((
-                                    self.describe_any_place(place_base),
-                                    self.describe_any_place(first_borrowed_place.as_ref()),
-                                    self.describe_any_place(second_borrowed_place.as_ref()),
-                                    union_ty.to_string(),
-                                ));
-                            }
+                    if let ProjectionElem::Field(field, _) = elem
+                        && let Some(union_ty) = union_ty(place_base)
+                    {
+                        if field != target_field && place_base == target_base {
+                            return Some((
+                                self.describe_any_place(place_base),
+                                self.describe_any_place(first_borrowed_place.as_ref()),
+                                self.describe_any_place(second_borrowed_place.as_ref()),
+                                union_ty.to_string(),
+                            ));
                         }
                     }
                 }
@@ -2949,16 +3016,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             from_closure: false,
             ..
         } = explanation
-        {
-            if let Err(diag) = self.try_report_cannot_return_reference_to_local(
+            && let Err(diag) = self.try_report_cannot_return_reference_to_local(
                 borrow,
                 borrow_span,
                 span,
                 category,
                 opt_place_desc.as_ref(),
-            ) {
-                return diag;
-            }
+            )
+        {
+            return diag;
         }
 
         let name = format!("`{name}`");
@@ -3720,30 +3786,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let loan_span = loan_spans.args_or_use();
 
         let descr_place = self.describe_any_place(place.as_ref());
-        if let BorrowKind::Fake(_) = loan.kind {
-            if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
-                let mut err = self.cannot_mutate_in_immutable_section(
-                    span,
-                    loan_span,
-                    &descr_place,
-                    section,
-                    "assign",
-                );
+        if let BorrowKind::Fake(_) = loan.kind
+            && let Some(section) = self.classify_immutable_section(loan.assigned_place)
+        {
+            let mut err = self.cannot_mutate_in_immutable_section(
+                span,
+                loan_span,
+                &descr_place,
+                section,
+                "assign",
+            );
 
-                loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
-                    use crate::session_diagnostics::CaptureVarCause::*;
-                    match kind {
-                        hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
-                        hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
-                            BorrowUseInClosure { var_span }
-                        }
+            loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
+                use crate::session_diagnostics::CaptureVarCause::*;
+                match kind {
+                    hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
+                    hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
+                        BorrowUseInClosure { var_span }
                     }
-                });
+                }
+            });
 
-                self.buffer_error(err);
+            self.buffer_error(err);
 
-                return;
-            }
+            return;
         }
 
         let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
@@ -3996,119 +4062,116 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
                     target, stmt
                 );
-                if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
-                    if let Some(assigned_to) = place.as_local() {
-                        debug!(
-                            "annotate_argument_and_return_for_borrow: assigned_to={:?} \
+                if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind
+                    && let Some(assigned_to) = place.as_local()
+                {
+                    debug!(
+                        "annotate_argument_and_return_for_borrow: assigned_to={:?} \
                              rvalue={:?}",
-                            assigned_to, rvalue
-                        );
-                        // Check if our `target` was captured by a closure.
-                        if let Rvalue::Aggregate(
-                            box AggregateKind::Closure(def_id, args),
-                            operands,
-                        ) = rvalue
-                        {
-                            let def_id = def_id.expect_local();
-                            for operand in operands {
-                                let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
-                                    operand
-                                else {
-                                    continue;
-                                };
-                                debug!(
-                                    "annotate_argument_and_return_for_borrow: assigned_from={:?}",
-                                    assigned_from
-                                );
+                        assigned_to, rvalue
+                    );
+                    // Check if our `target` was captured by a closure.
+                    if let Rvalue::Aggregate(box AggregateKind::Closure(def_id, args), operands) =
+                        rvalue
+                    {
+                        let def_id = def_id.expect_local();
+                        for operand in operands {
+                            let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
+                                operand
+                            else {
+                                continue;
+                            };
+                            debug!(
+                                "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+                                assigned_from
+                            );
 
-                                // Find the local from the operand.
-                                let Some(assigned_from_local) =
-                                    assigned_from.local_or_deref_local()
-                                else {
-                                    continue;
-                                };
+                            // Find the local from the operand.
+                            let Some(assigned_from_local) = assigned_from.local_or_deref_local()
+                            else {
+                                continue;
+                            };
 
-                                if assigned_from_local != target {
-                                    continue;
-                                }
+                            if assigned_from_local != target {
+                                continue;
+                            }
 
-                                // If a closure captured our `target` and then assigned
-                                // into a place then we should annotate the closure in
-                                // case it ends up being assigned into the return place.
-                                annotated_closure =
-                                    self.annotate_fn_sig(def_id, args.as_closure().sig());
-                                debug!(
-                                    "annotate_argument_and_return_for_borrow: \
+                            // If a closure captured our `target` and then assigned
+                            // into a place then we should annotate the closure in
+                            // case it ends up being assigned into the return place.
+                            annotated_closure =
+                                self.annotate_fn_sig(def_id, args.as_closure().sig());
+                            debug!(
+                                "annotate_argument_and_return_for_borrow: \
                                      annotated_closure={:?} assigned_from_local={:?} \
                                      assigned_to={:?}",
-                                    annotated_closure, assigned_from_local, assigned_to
-                                );
+                                annotated_closure, assigned_from_local, assigned_to
+                            );
 
-                                if assigned_to == mir::RETURN_PLACE {
-                                    // If it was assigned directly into the return place, then
-                                    // return now.
-                                    return annotated_closure;
-                                } else {
-                                    // Otherwise, update the target.
-                                    target = assigned_to;
-                                }
+                            if assigned_to == mir::RETURN_PLACE {
+                                // If it was assigned directly into the return place, then
+                                // return now.
+                                return annotated_closure;
+                            } else {
+                                // Otherwise, update the target.
+                                target = assigned_to;
                             }
-
-                            // If none of our closure's operands matched, then skip to the next
-                            // statement.
-                            continue;
                         }
 
-                        // Otherwise, look at other types of assignment.
-                        let assigned_from = match rvalue {
-                            Rvalue::Ref(_, _, assigned_from) => assigned_from,
-                            Rvalue::Use(operand) => match operand {
-                                Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
-                                    assigned_from
-                                }
-                                _ => continue,
-                            },
+                        // If none of our closure's operands matched, then skip to the next
+                        // statement.
+                        continue;
+                    }
+
+                    // Otherwise, look at other types of assignment.
+                    let assigned_from = match rvalue {
+                        Rvalue::Ref(_, _, assigned_from) => assigned_from,
+                        Rvalue::Use(operand) => match operand {
+                            Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
+                                assigned_from
+                            }
                             _ => continue,
-                        };
-                        debug!(
-                            "annotate_argument_and_return_for_borrow: \
+                        },
+                        _ => continue,
+                    };
+                    debug!(
+                        "annotate_argument_and_return_for_borrow: \
                              assigned_from={:?}",
-                            assigned_from,
-                        );
+                        assigned_from,
+                    );
 
-                        // Find the local from the rvalue.
-                        let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
-                            continue;
-                        };
-                        debug!(
-                            "annotate_argument_and_return_for_borrow: \
+                    // Find the local from the rvalue.
+                    let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
+                        continue;
+                    };
+                    debug!(
+                        "annotate_argument_and_return_for_borrow: \
                              assigned_from_local={:?}",
-                            assigned_from_local,
-                        );
+                        assigned_from_local,
+                    );
 
-                        // Check if our local matches the target - if so, we've assigned our
-                        // borrow to a new place.
-                        if assigned_from_local != target {
-                            continue;
-                        }
+                    // Check if our local matches the target - if so, we've assigned our
+                    // borrow to a new place.
+                    if assigned_from_local != target {
+                        continue;
+                    }
 
-                        // If we assigned our `target` into a new place, then we should
-                        // check if it was the return place.
-                        debug!(
-                            "annotate_argument_and_return_for_borrow: \
+                    // If we assigned our `target` into a new place, then we should
+                    // check if it was the return place.
+                    debug!(
+                        "annotate_argument_and_return_for_borrow: \
                              assigned_from_local={:?} assigned_to={:?}",
-                            assigned_from_local, assigned_to
-                        );
-                        if assigned_to == mir::RETURN_PLACE {
-                            // If it was then return the annotated closure if there was one,
-                            // else, annotate this function.
-                            return annotated_closure.or_else(fallback);
-                        }
-
-                        // If we didn't assign into the return place, then we just update
-                        // the target.
-                        target = assigned_to;
+                        assigned_from_local, assigned_to
+                    );
+                    if assigned_to == mir::RETURN_PLACE {
+                        // If it was then return the annotated closure if there was one,
+                        // else, annotate this function.
+                        return annotated_closure.or_else(fallback);
                     }
+
+                    // If we didn't assign into the return place, then we just update
+                    // the target.
+                    target = assigned_to;
                 }
             }
 
@@ -4120,32 +4183,31 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             );
             if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
                 &terminator.kind
+                && let Some(assigned_to) = destination.as_local()
             {
-                if let Some(assigned_to) = destination.as_local() {
+                debug!(
+                    "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
+                    assigned_to, args
+                );
+                for operand in args {
+                    let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
+                        &operand.node
+                    else {
+                        continue;
+                    };
                     debug!(
-                        "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
-                        assigned_to, args
+                        "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+                        assigned_from,
                     );
-                    for operand in args {
-                        let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
-                            &operand.node
-                        else {
-                            continue;
-                        };
+
+                    if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
                         debug!(
-                            "annotate_argument_and_return_for_borrow: assigned_from={:?}",
-                            assigned_from,
+                            "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
+                            assigned_from_local,
                         );
 
-                        if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
-                            debug!(
-                                "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
-                                assigned_from_local,
-                            );
-
-                            if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
-                                return annotated_closure.or_else(fallback);
-                            }
+                        if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
+                            return annotated_closure.or_else(fallback);
                         }
                     }
                 }
@@ -4244,10 +4306,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 // as the HIR doesn't have full types for closure arguments.
                 let return_ty = sig.output().skip_binder();
                 let mut return_span = fn_decl.output.span();
-                if let hir::FnRetTy::Return(ty) = &fn_decl.output {
-                    if let hir::TyKind::Ref(lifetime, _) = ty.kind {
-                        return_span = lifetime.ident.span;
-                    }
+                if let hir::FnRetTy::Return(ty) = &fn_decl.output
+                    && let hir::TyKind::Ref(lifetime, _) = ty.kind
+                {
+                    return_span = lifetime.ident.span;
                 }
 
                 Some(AnnotatedBorrowFnSignature::NamedFunction {
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index f9e52239d6f..7ca07bb9b43 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -341,7 +341,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
                                 }
                             }
                         } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() {
-                            let sp = info.span.find_oldest_ancestor_in_same_ctxt();
+                            let sp = info.span.find_ancestor_not_from_macro().unwrap_or(info.span);
                             if info.tail_result_is_ignored {
                                 // #85581: If the first mutable borrow's scope contains
                                 // the second borrow, this suggestion isn't helpful.
@@ -438,7 +438,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
 
             let elaborated_args =
                 std::iter::zip(*args, &generics.own_params).map(|(arg, param)| {
-                    if let Some(ty::Dynamic(obj, _, ty::Dyn)) = arg.as_type().map(Ty::kind) {
+                    if let Some(ty::Dynamic(obj, _)) = arg.as_type().map(Ty::kind) {
                         let default = tcx.object_lifetime_default(param.def_id);
 
                         let re_static = tcx.lifetimes.re_static;
@@ -464,7 +464,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
 
                         has_dyn = true;
 
-                        Ty::new_dynamic(tcx, obj, implied_region, ty::Dyn).into()
+                        Ty::new_dynamic(tcx, obj, implied_region).into()
                     } else {
                         arg
                     }
@@ -565,7 +565,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         let (blame_constraint, path) = self.regioncx.best_blame_constraint(
             borrow_region,
             NllRegionVariableOrigin::FreeRegion,
-            |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
+            outlived_region,
         );
         let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
 
@@ -917,30 +917,29 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
 
                 if let TerminatorKind::Call { destination, target: Some(block), args, .. } =
                     &terminator.kind
+                    && let Some(dest) = destination.as_local()
                 {
-                    if let Some(dest) = destination.as_local() {
-                        debug!(
-                            "was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
-                            target, dest, args
-                        );
-                        // Check if one of the arguments to this function is the target place.
-                        let found_target = args.iter().any(|arg| {
-                            if let Operand::Move(place) = arg.node {
-                                if let Some(potential) = place.as_local() {
-                                    potential == target
-                                } else {
-                                    false
-                                }
+                    debug!(
+                        "was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
+                        target, dest, args
+                    );
+                    // Check if one of the arguments to this function is the target place.
+                    let found_target = args.iter().any(|arg| {
+                        if let Operand::Move(place) = arg.node {
+                            if let Some(potential) = place.as_local() {
+                                potential == target
                             } else {
                                 false
                             }
-                        });
-
-                        // If it is, follow this to the next block and update the target.
-                        if found_target {
-                            target = dest;
-                            queue.push(block.start_location());
+                        } else {
+                            false
                         }
+                    });
+
+                    // If it is, follow this to the next block and update the target.
+                    if found_target {
+                        target = dest;
+                        queue.push(block.start_location());
                     }
                 }
             }
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 9ad91d605a7..5642cdf87fd 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -6,7 +6,9 @@ use rustc_abi::{FieldIdx, VariantIdx};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
 use rustc_hir::def::{CtorKind, Namespace};
-use rustc_hir::{self as hir, CoroutineKind, LangItem};
+use rustc_hir::{
+    self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
+};
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
 use rustc_infer::traits::SelectionError;
@@ -51,7 +53,7 @@ mod conflict_errors;
 mod explain_borrow;
 mod move_errors;
 mod mutability_errors;
-mod opaque_suggestions;
+mod opaque_types;
 mod region_errors;
 
 pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};
@@ -266,48 +268,44 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             args,
             ..
         } = &terminator.kind
+            && let ty::FnDef(id, _) = *const_.ty().kind()
         {
-            if let ty::FnDef(id, _) = *const_.ty().kind() {
-                debug!("add_moved_or_invoked_closure_note: id={:?}", id);
-                if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
-                    let closure = match args.first() {
-                        Some(Spanned {
-                            node: Operand::Copy(place) | Operand::Move(place), ..
-                        }) if target == place.local_or_deref_local() => {
-                            place.local_or_deref_local().unwrap()
-                        }
-                        _ => return false,
-                    };
+            debug!("add_moved_or_invoked_closure_note: id={:?}", id);
+            if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
+                let closure = match args.first() {
+                    Some(Spanned { node: Operand::Copy(place) | Operand::Move(place), .. })
+                        if target == place.local_or_deref_local() =>
+                    {
+                        place.local_or_deref_local().unwrap()
+                    }
+                    _ => return false,
+                };
 
-                    debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
-                    if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
-                        let did = did.expect_local();
-                        if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
-                            diag.subdiagnostic(OnClosureNote::InvokedTwice {
-                                place_name: &ty::place_to_string_for_capture(
-                                    self.infcx.tcx,
-                                    hir_place,
-                                ),
-                                span: *span,
-                            });
-                            return true;
-                        }
+                debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
+                if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
+                    let did = did.expect_local();
+                    if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
+                        diag.subdiagnostic(OnClosureNote::InvokedTwice {
+                            place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
+                            span: *span,
+                        });
+                        return true;
                     }
                 }
             }
         }
 
         // Check if we are just moving a closure after it has been invoked.
-        if let Some(target) = target {
-            if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
-                let did = did.expect_local();
-                if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
-                    diag.subdiagnostic(OnClosureNote::MovedTwice {
-                        place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
-                        span: *span,
-                    });
-                    return true;
-                }
+        if let Some(target) = target
+            && let ty::Closure(did, _) = self.body.local_decls[target].ty.kind()
+        {
+            let did = did.expect_local();
+            if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
+                diag.subdiagnostic(OnClosureNote::MovedTwice {
+                    place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
+                    span: *span,
+                });
+                return true;
             }
         }
         false
@@ -617,7 +615,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
     /// name where required.
     pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
-        let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
+        let mut p = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
 
         // We need to add synthesized lifetimes where appropriate. We do
         // this by hooking into the pretty printer and telling it to label the
@@ -628,19 +626,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 | ty::RePlaceholder(ty::PlaceholderRegion {
                     bound: ty::BoundRegion { kind: br, .. },
                     ..
-                }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
+                }) => p.region_highlight_mode.highlighting_bound_region(br, counter),
                 _ => {}
             }
         }
 
-        ty.print(&mut printer).unwrap();
-        printer.into_buffer()
+        ty.print(&mut p).unwrap();
+        p.into_buffer()
     }
 
     /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
     /// synthesized lifetime name where required.
     pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
-        let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
+        let mut p = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
 
         let region = if let ty::Ref(region, ..) = ty.kind() {
             match region.kind() {
@@ -648,7 +646,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 | ty::RePlaceholder(ty::PlaceholderRegion {
                     bound: ty::BoundRegion { kind: br, .. },
                     ..
-                }) => printer.region_highlight_mode.highlighting_bound_region(br, counter),
+                }) => p.region_highlight_mode.highlighting_bound_region(br, counter),
                 _ => {}
             }
             region
@@ -656,31 +654,72 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             bug!("ty for annotation of borrow region is not a reference");
         };
 
-        region.print(&mut printer).unwrap();
-        printer.into_buffer()
+        region.print(&mut p).unwrap();
+        p.into_buffer()
     }
 
     /// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
     /// implicitly introduce an "outlives `'static`" constraint.
+    ///
+    /// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this
+    /// note for failed type tests instead of outlives errors.
     fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
         &self,
-        err: &mut Diag<'_, G>,
+        diag: &mut Diag<'_, G>,
         path: &[OutlivesConstraint<'tcx>],
     ) {
-        let predicate_span = path.iter().find_map(|constraint| {
+        let tcx = self.infcx.tcx;
+        let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| {
             let outlived = constraint.sub;
             if let Some(origin) = self.regioncx.definitions.get(outlived)
-                && let NllRegionVariableOrigin::Placeholder(_) = origin.origin
-                && let ConstraintCategory::Predicate(span) = constraint.category
+                && let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin
+                && let Some(id) = placeholder.bound.kind.get_id()
+                && let Some(placeholder_id) = id.as_local()
+                && let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
+                && let Some(generics_impl) =
+                    tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
             {
-                Some(span)
+                Some((gat_hir_id, generics_impl))
             } else {
                 None
             }
-        });
+        }) else {
+            return;
+        };
 
-        if let Some(span) = predicate_span {
-            err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
+        // Look for the where-bound which introduces the placeholder.
+        // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
+        // and `T: for<'a> Trait`<'a>.
+        for pred in generics.predicates {
+            let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
+                bound_generic_params,
+                bounds,
+                ..
+            }) = pred.kind
+            else {
+                continue;
+            };
+            if bound_generic_params
+                .iter()
+                .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
+                .is_some()
+            {
+                diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
+                return;
+            }
+            for bound in bounds.iter() {
+                if let GenericBound::Trait(bound) = bound {
+                    if bound
+                        .bound_generic_params
+                        .iter()
+                        .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
+                        .is_some()
+                    {
+                        diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
+                        return;
+                    }
+                }
+            }
         }
     }
 
@@ -942,7 +981,7 @@ impl<'tcx> BorrowedContentSource<'tcx> {
     fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
         match *func.kind() {
             ty::FnDef(def_id, args) => {
-                let trait_id = tcx.trait_of_item(def_id)?;
+                let trait_id = tcx.trait_of_assoc(def_id)?;
 
                 if tcx.is_lang_item(trait_id, LangItem::Deref)
                     || tcx.is_lang_item(trait_id, LangItem::DerefMut)
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 92ca868eb99..0b3151fd8b8 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -9,7 +9,8 @@ use rustc_middle::bug;
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
-use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
+use rustc_span::def_id::DefId;
+use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
 use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
 use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::debug;
@@ -115,10 +116,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     fn append_to_grouped_errors(
         &self,
         grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
-        error: MoveError<'tcx>,
+        MoveError { place: original_path, location, kind }: MoveError<'tcx>,
     ) {
-        let MoveError { place: original_path, location, kind } = error;
-
         // Note: that the only time we assign a place isn't a temporary
         // to a user variable is when initializing it.
         // If that ever stops being the case, then the ever initialized
@@ -128,36 +127,35 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 .statements
                 .get(location.statement_index)
                 .map(|stmt| &stmt.kind)
+            && let Some(local) = place.as_local()
         {
-            if let Some(local) = place.as_local() {
-                let local_decl = &self.body.local_decls[local];
-                // opt_match_place is the
-                // match_span is the span of the expression being matched on
-                // match *x.y { ... }        match_place is Some(*x.y)
-                //       ^^^^                match_span is the span of *x.y
-                //
-                // opt_match_place is None for let [mut] x = ... statements,
-                // whether or not the right-hand side is a place expression
-                if let LocalInfo::User(BindingForm::Var(VarBindingForm {
-                    opt_match_place: Some((opt_match_place, match_span)),
-                    binding_mode: _,
-                    opt_ty_info: _,
-                    pat_span: _,
-                })) = *local_decl.local_info()
-                {
-                    let stmt_source_info = self.body.source_info(location);
-                    self.append_binding_error(
-                        grouped_errors,
-                        kind,
-                        original_path,
-                        *move_from,
-                        local,
-                        opt_match_place,
-                        match_span,
-                        stmt_source_info.span,
-                    );
-                    return;
-                }
+            let local_decl = &self.body.local_decls[local];
+            // opt_match_place is the
+            // match_span is the span of the expression being matched on
+            // match *x.y { ... }        match_place is Some(*x.y)
+            //       ^^^^                match_span is the span of *x.y
+            //
+            // opt_match_place is None for let [mut] x = ... statements,
+            // whether or not the right-hand side is a place expression
+            if let LocalInfo::User(BindingForm::Var(VarBindingForm {
+                opt_match_place: Some((opt_match_place, match_span)),
+                binding_mode: _,
+                opt_ty_info: _,
+                pat_span: _,
+            })) = *local_decl.local_info()
+            {
+                let stmt_source_info = self.body.source_info(location);
+                self.append_binding_error(
+                    grouped_errors,
+                    kind,
+                    original_path,
+                    *move_from,
+                    local,
+                    opt_match_place,
+                    match_span,
+                    stmt_source_info.span,
+                );
+                return;
             }
         }
 
@@ -251,62 +249,56 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     }
 
     fn report(&mut self, error: GroupedMoveError<'tcx>) {
-        let (mut err, err_span) = {
-            let (span, use_spans, original_path, kind) = match error {
-                GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
-                | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
-                    (span, None, original_path, kind)
-                }
-                GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
-                    (use_spans.args_or_use(), Some(use_spans), original_path, kind)
-                }
-            };
-            debug!(
-                "report: original_path={:?} span={:?}, kind={:?} \
-                   original_path.is_upvar_field_projection={:?}",
-                original_path,
-                span,
-                kind,
-                self.is_upvar_field_projection(original_path.as_ref())
-            );
-            if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
-                // If the type may implement Copy, skip the error.
-                // It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check
-                self.dcx().span_delayed_bug(
+        let (span, use_spans, original_path, kind) = match error {
+            GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
+            | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
+                (span, None, original_path, kind)
+            }
+            GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
+                (use_spans.args_or_use(), Some(use_spans), original_path, kind)
+            }
+        };
+        debug!(
+            "report: original_path={:?} span={:?}, kind={:?} \
+             original_path.is_upvar_field_projection={:?}",
+            original_path,
+            span,
+            kind,
+            self.is_upvar_field_projection(original_path.as_ref())
+        );
+        if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
+            // If the type may implement Copy, skip the error.
+            // It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check
+            self.dcx()
+                .span_delayed_bug(span, "Type may implement copy, but there is no other error.");
+            return;
+        }
+        let mut err = match kind {
+            &IllegalMoveOriginKind::BorrowedContent { target_place } => self
+                .report_cannot_move_from_borrowed_content(
+                    original_path,
+                    target_place,
                     span,
-                    "Type may implement copy, but there is no other error.",
-                );
-                return;
+                    use_spans,
+                ),
+            &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
+                self.cannot_move_out_of_interior_of_drop(span, ty)
+            }
+            &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
+                self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
             }
-            (
-                match kind {
-                    &IllegalMoveOriginKind::BorrowedContent { target_place } => self
-                        .report_cannot_move_from_borrowed_content(
-                            original_path,
-                            target_place,
-                            span,
-                            use_spans,
-                        ),
-                    &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
-                        self.cannot_move_out_of_interior_of_drop(span, ty)
-                    }
-                    &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
-                        self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
-                    }
-                },
-                span,
-            )
         };
 
-        self.add_move_hints(error, &mut err, err_span);
+        self.add_move_hints(error, &mut err, span);
         self.buffer_error(err);
     }
 
     fn has_ambiguous_copy(&mut self, ty: Ty<'tcx>) -> bool {
-        let Some(copy_trait_def) = self.infcx.tcx.lang_items().copy_trait() else { return false };
-        // This is only going to be ambiguous if there are incoherent impls, because otherwise
-        // ambiguity should never happen in MIR.
-        self.infcx.type_implements_trait(copy_trait_def, [ty], self.infcx.param_env).may_apply()
+        let Some(copy_def_id) = self.infcx.tcx.lang_items().copy_trait() else { return false };
+
+        // Avoid bogus move errors because of an incoherent `Copy` impl.
+        self.infcx.type_implements_trait(copy_def_id, [ty], self.infcx.param_env).may_apply()
+            && self.infcx.tcx.coherent_trait(copy_def_id).is_err()
     }
 
     fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'infcx> {
@@ -482,7 +474,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 self.cannot_move_out_of_interior_noncopy(span, ty, None)
             }
             ty::Closure(def_id, closure_args)
-                if def_id.as_local() == Some(self.mir_def_id()) && upvar_field.is_some() =>
+                if def_id.as_local() == Some(self.mir_def_id())
+                    && let Some(upvar_field) = upvar_field =>
             {
                 let closure_kind_ty = closure_args.as_closure().kind_ty();
                 let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
@@ -495,7 +488,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 let capture_description =
                     format!("captured variable in an `{closure_kind}` closure");
 
-                let upvar = &self.upvars[upvar_field.unwrap().index()];
+                let upvar = &self.upvars[upvar_field.index()];
                 let upvar_hir_id = upvar.get_root_variable();
                 let upvar_name = upvar.to_string(tcx);
                 let upvar_span = tcx.hir_span(upvar_hir_id);
@@ -515,12 +508,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 );
 
                 let closure_span = tcx.def_span(def_id);
+
                 self.cannot_move_out_of(span, &place_description)
                     .with_span_label(upvar_span, "captured outer variable")
                     .with_span_label(
                         closure_span,
                         format!("captured by this `{closure_kind}` closure"),
                     )
+                    .with_span_help(
+                        self.get_closure_bound_clause_span(*def_id),
+                        "`Fn` and `FnMut` closures require captured values to be able to be \
+                         consumed multiple times, but `FnOnce` closures may consume them only once",
+                    )
             }
             _ => {
                 let source = self.borrowed_content_source(deref_base);
@@ -569,6 +568,47 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         err
     }
 
+    fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
+        let tcx = self.infcx.tcx;
+        let typeck_result = tcx.typeck(self.mir_def_id());
+        // Check whether the closure is an argument to a call, if so,
+        // get the instantiated where-bounds of that call.
+        let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
+        let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP };
+
+        let predicates = match parent.kind {
+            hir::ExprKind::Call(callee, _) => {
+                let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
+                let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
+                tcx.predicates_of(fn_def_id).instantiate(tcx, args)
+            }
+            hir::ExprKind::MethodCall(..) => {
+                let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
+                    return DUMMY_SP;
+                };
+                let args = typeck_result.node_args(parent.hir_id);
+                tcx.predicates_of(method).instantiate(tcx, args)
+            }
+            _ => return DUMMY_SP,
+        };
+
+        // Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`.
+        for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
+            if let Some(clause) = pred.as_trait_clause()
+                && let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
+                && *clause_closure_def_id == def_id
+                && (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
+                    || tcx.lang_items().fn_trait() == Some(clause.def_id()))
+            {
+                // Found `<TyOfCapturingClosure as FnMut>`
+                // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
+                // could be changed to `FnOnce()` to avoid the move error.
+                return *span;
+            }
+        }
+        DUMMY_SP
+    }
+
     fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
         match error {
             GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
@@ -605,7 +645,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
             // No binding. Nothing to suggest.
             GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
-                let use_span = use_spans.var_or_use();
+                let mut use_span = use_spans.var_or_use();
                 let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
                 let place_desc = match self.describe_place(original_path.as_ref()) {
                     Some(desc) => format!("`{desc}`"),
@@ -622,6 +662,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     );
                 }
 
+                if let Some(upvar_field) = self
+                    .prefixes(original_path.as_ref(), PrefixSet::All)
+                    .find_map(|p| self.is_upvar_field_projection(p))
+                {
+                    // Look for the introduction of the original binding being moved.
+                    let upvar = &self.upvars[upvar_field.index()];
+                    let upvar_hir_id = upvar.get_root_variable();
+                    use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) {
+                        hir::Node::Param(param) => {
+                            // Instead of pointing at the path where we access the value within a
+                            // closure, we point at the type of the outer `fn` argument.
+                            param.ty_span
+                        }
+                        hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) {
+                            // We point at the type of the outer let-binding.
+                            (Some(ty), _) => ty.span,
+                            // We point at the initializer of the outer let-binding, but only if it
+                            // isn't something that spans multiple lines, like a closure, as the
+                            // ASCII art gets messy.
+                            (None, Some(init))
+                                if !self.infcx.tcx.sess.source_map().is_multiline(init.span) =>
+                            {
+                                init.span
+                            }
+                            _ => use_span,
+                        },
+                        _ => use_span,
+                    };
+                }
+
                 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
                     is_partial_move: false,
                     ty: place_ty,
@@ -629,12 +699,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     span: use_span,
                 });
 
+                let mut pointed_at_span = false;
                 use_spans.args_subdiag(err, |args_span| {
+                    if args_span == span || args_span == use_span {
+                        pointed_at_span = true;
+                    }
                     crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
-                        place: place_desc,
+                        place: place_desc.clone(),
                         args_span,
                     }
                 });
+                if !pointed_at_span && use_span != span {
+                    err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
+                        place: place_desc,
+                        args_span: span,
+                    });
+                }
 
                 self.add_note_for_packed_struct_derive(err, original_path.local);
             }
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index a06540f8325..6d69040c711 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -3,6 +3,7 @@
 
 use core::ops::ControlFlow;
 
+use either::Either;
 use hir::{ExprKind, Param};
 use rustc_abi::FieldIdx;
 use rustc_errors::{Applicability, Diag};
@@ -12,15 +13,16 @@ use rustc_middle::bug;
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::mir::visit::PlaceContext;
 use rustc_middle::mir::{
-    self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place,
-    PlaceRef, ProjectionElem,
+    self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location,
+    Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement,
+    StatementKind, TerminatorKind,
 };
 use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
 use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits;
-use tracing::debug;
+use tracing::{debug, trace};
 
 use crate::diagnostics::BorrowedContentSource;
 use crate::{MirBorrowckCtxt, session_diagnostics};
@@ -31,6 +33,33 @@ pub(crate) enum AccessKind {
     Mutate,
 }
 
+/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
+/// locations.
+fn find_assignments(body: &Body<'_>, local: Local) -> Vec<Location> {
+    use rustc_middle::mir::visit::Visitor;
+
+    struct FindLocalAssignmentVisitor {
+        needle: Local,
+        locations: Vec<Location>,
+    }
+
+    impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
+        fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
+            if self.needle != local {
+                return;
+            }
+
+            if place_context.is_place_assignment() {
+                self.locations.push(location);
+            }
+        }
+    }
+
+    let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
+    visitor.visit_body(body);
+    visitor.locations
+}
+
 impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     pub(crate) fn report_mutability_error(
         &mut self,
@@ -384,7 +413,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 }
             }
 
-            // Also suggest adding mut for upvars
+            // Also suggest adding mut for upvars.
             PlaceRef {
                 local,
                 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
@@ -438,9 +467,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 }
             }
 
-            // complete hack to approximate old AST-borrowck
-            // diagnostic: if the span starts with a mutable borrow of
-            // a local variable, then just suggest the user remove it.
+            // Complete hack to approximate old AST-borrowck diagnostic: if the span starts
+            // with a mutable borrow of a local variable, then just suggest the user remove it.
             PlaceRef { local: _, projection: [] }
                 if self
                     .infcx
@@ -677,17 +705,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     ///  - is the trait from the local crate? If not, we can't suggest changing signatures
     ///  - `Span` of the argument in the trait definition
     fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
+        let tcx = self.infcx.tcx;
         if self.body.local_kind(local) != LocalKind::Arg {
             return (false, false, None);
         }
         let my_def = self.body.source.def_id();
         let Some(td) =
-            self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
+            tcx.trait_impl_of_assoc(my_def).and_then(|id| self.infcx.tcx.trait_id_of_impl(id))
         else {
             return (false, false, None);
         };
 
-        let implemented_trait_item = self.infcx.tcx.associated_item(my_def).trait_item_def_id;
+        let implemented_trait_item = self.infcx.tcx.trait_item_of(my_def);
 
         (
             true,
@@ -768,7 +797,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         );
     }
 
-    // point to span of upvar making closure call require mutable borrow
+    // Point to span of upvar making closure call that requires a mutable borrow
     fn show_mutating_upvar(
         &self,
         tcx: TyCtxt<'_>,
@@ -824,7 +853,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             } else {
                 bug!("not an upvar")
             };
-            // sometimes we deliberately don't store the name of a place when coming from a macro in
+            // Sometimes we deliberately don't store the name of a place when coming from a macro in
             // another crate. We generally want to limit those diagnostics a little, to hide
             // implementation details (such as those from pin!() or format!()). In that case show a
             // slightly different error message, or none at all if something else happened. In other
@@ -880,7 +909,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                         let opt_suggestions = tcx
                             .typeck(path_segment.hir_id.owner.def_id)
                             .type_dependent_def_id(expr.hir_id)
-                            .and_then(|def_id| tcx.impl_of_method(def_id))
+                            .and_then(|def_id| tcx.impl_of_assoc(def_id))
                             .map(|def_id| tcx.associated_items(def_id))
                             .map(|assoc_items| {
                                 assoc_items
@@ -935,8 +964,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
         let mut look_at_return = true;
 
-        // If the HIR node is a function or method call gets the def ID
-        // of the called function or method and the span and args of the call expr
+        // If the HIR node is a function or method call, get the DefId
+        // of the callee function or method, the span, and args of the call expr
         let get_call_details = || {
             let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
                 return None;
@@ -1050,13 +1079,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             let mut cur_expr = expr;
             while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
                 if path_segment.ident.name == sym::iter {
-                    // check `_ty` has `iter_mut` method
+                    // Check that the type has an `iter_mut` method.
                     let res = self
                         .infcx
                         .tcx
                         .typeck(path_segment.hir_id.owner.def_id)
                         .type_dependent_def_id(cur_expr.hir_id)
-                        .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
+                        .and_then(|def_id| self.infcx.tcx.impl_of_assoc(def_id))
                         .map(|def_id| self.infcx.tcx.associated_items(def_id))
                         .map(|assoc_items| {
                             assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
@@ -1080,38 +1109,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
     }
 
-    /// Finds all statements that assign directly to local (i.e., X = ...) and returns their
-    /// locations.
-    fn find_assignments(&self, local: Local) -> Vec<Location> {
-        use rustc_middle::mir::visit::Visitor;
-
-        struct FindLocalAssignmentVisitor {
-            needle: Local,
-            locations: Vec<Location>,
-        }
-
-        impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
-            fn visit_local(
-                &mut self,
-                local: Local,
-                place_context: PlaceContext,
-                location: Location,
-            ) {
-                if self.needle != local {
-                    return;
-                }
-
-                if place_context.is_place_assignment() {
-                    self.locations.push(location);
-                }
-            }
-        }
-
-        let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
-        visitor.visit_body(self.body);
-        visitor.locations
-    }
-
     fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
         let local_decl = &self.body.local_decls[local];
 
@@ -1121,7 +1118,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
 
         if is_trait_sig && !is_local {
-            // Do not suggest to change the signature when the trait comes from another crate.
+            // Do not suggest changing the signature when the trait comes from another crate.
             err.span_label(
                 local_decl.source_info.span,
                 format!("this is an immutable {pointer_desc}"),
@@ -1130,11 +1127,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
         let decl_span = local_decl.source_info.span;
 
-        let amp_mut_sugg = match *local_decl.local_info() {
+        let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
             LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
                 let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
                 let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
-                Some(AmpMutSugg { has_sugg: true, span, suggestion, additional })
+                (AmpMutSugg::Type { span, suggestion, additional }, None)
             }
 
             LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@@ -1142,79 +1139,54 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 opt_ty_info,
                 ..
             })) => {
-                // check if the RHS is from desugaring
+                // Check if the RHS is from desugaring.
+                let first_assignment = find_assignments(&self.body, local).first().copied();
+                let first_assignment_stmt = first_assignment
+                    .and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
+                trace!(?first_assignment_stmt);
                 let opt_assignment_rhs_span =
-                    self.find_assignments(local).first().map(|&location| {
-                        if let Some(mir::Statement {
-                            source_info: _,
-                            kind:
-                                mir::StatementKind::Assign(box (
-                                    _,
-                                    mir::Rvalue::Use(mir::Operand::Copy(place)),
-                                )),
-                            ..
-                        }) = self.body[location.block].statements.get(location.statement_index)
-                        {
-                            self.body.local_decls[place.local].source_info.span
-                        } else {
-                            self.body.source_info(location).span
-                        }
-                    });
-                match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
-                    // on for loops, RHS points to the iterator part
-                    Some(DesugaringKind::ForLoop) => {
-                        let span = opt_assignment_rhs_span.unwrap();
-                        self.suggest_similar_mut_method_for_for_loop(err, span);
+                    first_assignment.map(|loc| self.body.source_info(loc).span);
+                let mut source_span = opt_assignment_rhs_span;
+                if let Some(mir::Statement {
+                    source_info: _,
+                    kind:
+                        mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))),
+                    ..
+                }) = first_assignment_stmt
+                {
+                    let local_span = self.body.local_decls[place.local].source_info.span;
+                    // `&self` in async functions have a `desugaring_kind`, but the local we assign
+                    // it with does not, so use the local_span for our checks later.
+                    source_span = Some(local_span);
+                    if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
+                        // On for loops, RHS points to the iterator part.
+                        self.suggest_similar_mut_method_for_for_loop(err, local_span);
                         err.span_label(
-                            span,
+                            local_span,
                             format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
                         );
-                        None
-                    }
-                    // don't create labels for compiler-generated spans
-                    Some(_) => None,
-                    // don't create labels for the span not from user's code
-                    None if opt_assignment_rhs_span
-                        .is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) =>
-                    {
-                        None
-                    }
-                    None => {
-                        if name != kw::SelfLower {
-                            suggest_ampmut(
-                                self.infcx.tcx,
-                                local_decl.ty,
-                                decl_span,
-                                opt_assignment_rhs_span,
-                                opt_ty_info,
-                            )
-                        } else {
-                            match local_decl.local_info() {
-                                LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
-                                    opt_ty_info: None,
-                                    ..
-                                })) => {
-                                    let (span, sugg) =
-                                        suggest_ampmut_self(self.infcx.tcx, decl_span);
-                                    Some(AmpMutSugg {
-                                        has_sugg: true,
-                                        span,
-                                        suggestion: sugg,
-                                        additional: None,
-                                    })
-                                }
-                                // explicit self (eg `self: &'a Self`)
-                                _ => suggest_ampmut(
-                                    self.infcx.tcx,
-                                    local_decl.ty,
-                                    decl_span,
-                                    opt_assignment_rhs_span,
-                                    opt_ty_info,
-                                ),
-                            }
-                        }
+                        return;
                     }
                 }
+
+                // Don't create labels for compiler-generated spans or spans not from users' code.
+                if source_span.is_some_and(|s| {
+                    s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
+                }) {
+                    return;
+                }
+
+                // This could be because we're in an `async fn`.
+                if name == kw::SelfLower && opt_ty_info.is_none() {
+                    let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
+                    (AmpMutSugg::Type { span, suggestion, additional: None }, None)
+                } else if let Some(sugg) =
+                    suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
+                {
+                    (sugg, opt_ty_info)
+                } else {
+                    return;
+                }
             }
 
             LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@@ -1222,181 +1194,238 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 ..
             })) => {
                 let pattern_span: Span = local_decl.source_info.span;
-                suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg {
-                    has_sugg: true,
-                    span,
-                    suggestion: "mut ".to_owned(),
-                    additional: None,
-                })
+                let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
+                    return;
+                };
+                (AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
             }
 
             _ => unreachable!(),
         };
 
-        match amp_mut_sugg {
-            Some(AmpMutSugg {
-                has_sugg: true,
-                span: err_help_span,
-                suggestion: suggested_code,
-                additional,
-            }) => {
-                let mut sugg = vec![(err_help_span, suggested_code)];
-                if let Some(s) = additional {
-                    sugg.push(s);
-                }
+        let mut suggest = |suggs: Vec<_>, applicability, extra| {
+            if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
+                return;
+            }
 
-                if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span))
-                {
-                    err.multipart_suggestion_verbose(
-                        format!(
-                            "consider changing this to be a mutable {pointer_desc}{}",
-                            if is_trait_sig {
-                                " in the `impl` method and the `trait` definition"
-                            } else {
-                                ""
-                            }
-                        ),
-                        sugg,
-                        Applicability::MachineApplicable,
-                    );
+            err.multipart_suggestion_verbose(
+                format!(
+                    "consider changing this to be a mutable {pointer_desc}{}{extra}",
+                    if is_trait_sig {
+                        " in the `impl` method and the `trait` definition"
+                    } else {
+                        ""
+                    }
+                ),
+                suggs,
+                applicability,
+            );
+        };
+
+        let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
+            AmpMutSugg::Type { span, suggestion, additional } => {
+                let mut sugg = vec![(span, suggestion)];
+                sugg.extend(additional);
+                suggest(sugg, Applicability::MachineApplicable, "");
+                return;
+            }
+            AmpMutSugg::MapGetMut { span, suggestion } => {
+                if self.infcx.tcx.sess.source_map().is_imported(span) {
+                    return;
                 }
+                err.multipart_suggestion_verbose(
+                    "consider using `get_mut`",
+                    vec![(span, suggestion)],
+                    Applicability::MaybeIncorrect,
+                );
+                return;
             }
-            Some(AmpMutSugg {
-                has_sugg: false, span: err_label_span, suggestion: message, ..
-            }) => {
-                let def_id = self.body.source.def_id();
-                let hir_id = if let Some(local_def_id) = def_id.as_local()
-                    && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
-                {
-                    BindingFinder { span: err_label_span }.visit_body(&body).break_value()
-                } else {
-                    None
-                };
+            AmpMutSugg::Expr { span, suggestion } => {
+                // `Expr` suggestions should change type annotations if they already exist (probably immut),
+                // but do not add new type annotations.
+                (vec![(span, suggestion)], false)
+            }
+            AmpMutSugg::ChangeBinding => (vec![], true),
+        };
 
-                if let Some(hir_id) = hir_id
-                    && let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id)
-                {
-                    let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
-                    if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
-                        && let Some(expr) = local.init
-                        && let ty = tables.node_type_opt(expr.hir_id)
-                        && let Some(ty) = ty
-                        && let ty::Ref(..) = ty.kind()
+        // Find a binding's type to make mutable.
+        let (binding_exists, span) = match local_var_ty_info {
+            // If this is a variable binding with an explicit type,
+            // then we will suggest changing it to be mutable.
+            // This is `Applicability::MachineApplicable`.
+            Some(ty_span) => (true, ty_span),
+
+            // Otherwise, we'll suggest *adding* an annotated type, we'll suggest
+            // the RHS's type for that.
+            // This is `Applicability::HasPlaceholders`.
+            None => (false, decl_span),
+        };
+
+        if !binding_exists && !add_type_annotation_if_not_exists {
+            suggest(sugg, Applicability::MachineApplicable, "");
+            return;
+        }
+
+        // If the binding already exists and is a reference with an explicit
+        // lifetime, then we can suggest adding ` mut`. This is special-cased from
+        // the path without an explicit lifetime.
+        let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
+            && src.starts_with("&'")
+            // Note that `&' a T` is invalid so this is correct.
+            && let Some(ws_pos) = src.find(char::is_whitespace)
+        {
+            let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
+            (span, " mut".to_owned(), true)
+        // If there is already a binding, we modify it to be `mut`.
+        } else if binding_exists {
+            // Shrink the span to just after the `&` in `&variable`.
+            let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
+            (span, "mut ".to_owned(), true)
+        } else {
+            // Otherwise, suggest that the user annotates the binding; We provide the
+            // type of the local.
+            let ty = local_decl.ty.builtin_deref(true).unwrap();
+
+            (span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false)
+        };
+
+        if suggest_now {
+            // Suggest changing `&x` to `&mut x` and changing `&T` to `&mut T` at the same time.
+            let has_change = !sugg.is_empty();
+            sugg.push((sugg_span, sugg_str));
+            suggest(
+                sugg,
+                Applicability::MachineApplicable,
+                // FIXME(fee1-dead) this somehow doesn't fire
+                if has_change { " and changing the binding's type" } else { "" },
+            );
+            return;
+        } else if !sugg.is_empty() {
+            suggest(sugg, Applicability::MachineApplicable, "");
+            return;
+        }
+
+        let def_id = self.body.source.def_id();
+        let hir_id = if let Some(local_def_id) = def_id.as_local()
+            && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
+        {
+            BindingFinder { span: sugg_span }.visit_body(&body).break_value()
+        } else {
+            None
+        };
+        let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
+
+        let Some(hir::Node::LetStmt(local)) = node else {
+            err.span_label(
+                sugg_span,
+                format!("consider changing this binding's type to be: `{sugg_str}`"),
+            );
+            return;
+        };
+
+        let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
+        if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
+            && let Some(expr) = local.init
+            && let ty = tables.node_type_opt(expr.hir_id)
+            && let Some(ty) = ty
+            && let ty::Ref(..) = ty.kind()
+        {
+            match self
+                .infcx
+                .type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
+                .as_deref()
+            {
+                Some([]) => {
+                    // FIXME: This error message isn't useful, since we're just
+                    // vaguely suggesting to clone a value that already
+                    // implements `Clone`.
+                    //
+                    // A correct suggestion here would take into account the fact
+                    // that inference may be affected by missing types on bindings,
+                    // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
+                    // example.
+                }
+                None => {
+                    if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
+                        && segment.ident.name == sym::clone
                     {
-                        match self
-                            .infcx
-                            .type_implements_trait_shallow(
-                                clone_trait,
-                                ty.peel_refs(),
-                                self.infcx.param_env,
-                            )
-                            .as_deref()
-                        {
-                            Some([]) => {
-                                // FIXME: This error message isn't useful, since we're just
-                                // vaguely suggesting to clone a value that already
-                                // implements `Clone`.
-                                //
-                                // A correct suggestion here would take into account the fact
-                                // that inference may be affected by missing types on bindings,
-                                // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
-                                // example.
-                            }
-                            None => {
-                                if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
-                                    expr.kind
-                                    && segment.ident.name == sym::clone
-                                {
-                                    err.span_help(
-                                        span,
-                                        format!(
-                                            "`{}` doesn't implement `Clone`, so this call clones \
+                        err.span_help(
+                            span,
+                            format!(
+                                "`{}` doesn't implement `Clone`, so this call clones \
                                              the reference `{ty}`",
-                                            ty.peel_refs(),
-                                        ),
-                                    );
-                                }
-                                // The type doesn't implement Clone.
-                                let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
-                                    self.infcx.tcx,
-                                    clone_trait,
-                                    [ty.peel_refs()],
-                                ));
-                                let obligation = traits::Obligation::new(
-                                    self.infcx.tcx,
-                                    traits::ObligationCause::dummy(),
-                                    self.infcx.param_env,
-                                    trait_ref,
-                                );
-                                self.infcx.err_ctxt().suggest_derive(
-                                    &obligation,
-                                    err,
-                                    trait_ref.upcast(self.infcx.tcx),
-                                );
-                            }
-                            Some(errors) => {
-                                if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
-                                    expr.kind
-                                    && segment.ident.name == sym::clone
-                                {
-                                    err.span_help(
-                                        span,
-                                        format!(
-                                            "`{}` doesn't implement `Clone` because its \
+                                ty.peel_refs(),
+                            ),
+                        );
+                    }
+                    // The type doesn't implement Clone.
+                    let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
+                        self.infcx.tcx,
+                        clone_trait,
+                        [ty.peel_refs()],
+                    ));
+                    let obligation = traits::Obligation::new(
+                        self.infcx.tcx,
+                        traits::ObligationCause::dummy(),
+                        self.infcx.param_env,
+                        trait_ref,
+                    );
+                    self.infcx.err_ctxt().suggest_derive(
+                        &obligation,
+                        err,
+                        trait_ref.upcast(self.infcx.tcx),
+                    );
+                }
+                Some(errors) => {
+                    if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
+                        && segment.ident.name == sym::clone
+                    {
+                        err.span_help(
+                            span,
+                            format!(
+                                "`{}` doesn't implement `Clone` because its \
                                              implementations trait bounds could not be met, so \
                                              this call clones the reference `{ty}`",
-                                            ty.peel_refs(),
-                                        ),
-                                    );
-                                    err.note(format!(
-                                        "the following trait bounds weren't met: {}",
-                                        errors
-                                            .iter()
-                                            .map(|e| e.obligation.predicate.to_string())
-                                            .collect::<Vec<_>>()
-                                            .join("\n"),
-                                    ));
-                                }
-                                // The type doesn't implement Clone because of unmet obligations.
-                                for error in errors {
-                                    if let traits::FulfillmentErrorCode::Select(
-                                        traits::SelectionError::Unimplemented,
-                                    ) = error.code
-                                        && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
-                                            pred,
-                                        )) = error.obligation.predicate.kind().skip_binder()
-                                    {
-                                        self.infcx.err_ctxt().suggest_derive(
-                                            &error.obligation,
-                                            err,
-                                            error.obligation.predicate.kind().rebind(pred),
-                                        );
-                                    }
-                                }
-                            }
-                        }
+                                ty.peel_refs(),
+                            ),
+                        );
+                        err.note(format!(
+                            "the following trait bounds weren't met: {}",
+                            errors
+                                .iter()
+                                .map(|e| e.obligation.predicate.to_string())
+                                .collect::<Vec<_>>()
+                                .join("\n"),
+                        ));
                     }
-                    let (changing, span, sugg) = match local.ty {
-                        Some(ty) => ("changing", ty.span, message),
-                        None => {
-                            ("specifying", local.pat.span.shrink_to_hi(), format!(": {message}"))
+                    // The type doesn't implement Clone because of unmet obligations.
+                    for error in errors {
+                        if let traits::FulfillmentErrorCode::Select(
+                            traits::SelectionError::Unimplemented,
+                        ) = error.code
+                            && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
+                                error.obligation.predicate.kind().skip_binder()
+                        {
+                            self.infcx.err_ctxt().suggest_derive(
+                                &error.obligation,
+                                err,
+                                error.obligation.predicate.kind().rebind(pred),
+                            );
                         }
-                    };
-                    err.span_suggestion_verbose(
-                        span,
-                        format!("consider {changing} this binding's type"),
-                        sugg,
-                        Applicability::HasPlaceholders,
-                    );
-                } else {
-                    err.span_label(
-                        err_label_span,
-                        format!("consider changing this binding's type to be: `{message}`"),
-                    );
+                    }
                 }
             }
-            None => {}
         }
+        let (changing, span, sugg) = match local.ty {
+            Some(ty) => ("changing", ty.span, sugg_str),
+            None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")),
+        };
+        err.span_suggestion_verbose(
+            span,
+            format!("consider {changing} this binding's type"),
+            sugg,
+            Applicability::HasPlaceholders,
+        );
     }
 }
 
@@ -1463,11 +1492,25 @@ fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
     }
 }
 
-struct AmpMutSugg {
-    has_sugg: bool,
-    span: Span,
-    suggestion: String,
-    additional: Option<(Span, String)>,
+enum AmpMutSugg {
+    /// Type suggestion. Changes `&self` to `&mut self`, `x: &T` to `x: &mut T`,
+    /// `ref x` to `ref mut x`, etc.
+    Type {
+        span: Span,
+        suggestion: String,
+        additional: Option<(Span, String)>,
+    },
+    /// Suggestion for expressions, `&x` to `&mut x`, `&x[i]` to `&mut x[i]`, etc.
+    Expr {
+        span: Span,
+        suggestion: String,
+    },
+    /// Suggests `.get_mut` in the case of `&map[&key]` for Hash/BTreeMap.
+    MapGetMut {
+        span: Span,
+        suggestion: String,
+    },
+    ChangeBinding,
 }
 
 // When we want to suggest a user change a local variable to be a `&mut`, there
@@ -1486,110 +1529,111 @@ struct AmpMutSugg {
 // This implementation attempts to emulate AST-borrowck prioritization
 // by trying (3.), then (2.) and finally falling back on (1.).
 fn suggest_ampmut<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    decl_ty: Ty<'tcx>,
-    decl_span: Span,
-    opt_assignment_rhs_span: Option<Span>,
-    opt_ty_info: Option<Span>,
+    infcx: &crate::BorrowckInferCtxt<'tcx>,
+    body: &Body<'tcx>,
+    opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
 ) -> Option<AmpMutSugg> {
-    // if there is a RHS and it starts with a `&` from it, then check if it is
+    let tcx = infcx.tcx;
+    // If there is a RHS and it starts with a `&` from it, then check if it is
     // mutable, and if not, put suggest putting `mut ` to make it mutable.
-    // we don't have to worry about lifetime annotations here because they are
+    // We don't have to worry about lifetime annotations here because they are
     // not valid when taking a reference. For example, the following is not valid Rust:
     //
     // let x: &i32 = &'a 5;
     //                ^^ lifetime annotation not allowed
     //
-    if let Some(rhs_span) = opt_assignment_rhs_span
-        && let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
-        && let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&')
+    if let Some(rhs_stmt) = opt_assignment_rhs_stmt
+        && let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind
+        && let mut rhs_span = rhs_stmt.source_info.span
+        && let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
     {
-        // Suggest changing `&raw const` to `&raw mut` if applicable.
-        if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() {
-            let const_idx = rhs_str.find("const").unwrap() as u32;
-            let const_span = rhs_span
-                .with_lo(rhs_span.lo() + BytePos(const_idx))
-                .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32));
-
-            return Some(AmpMutSugg {
-                has_sugg: true,
-                span: const_span,
-                suggestion: "mut".to_owned(),
-                additional: None,
-            });
+        let mut rvalue = rvalue;
+
+        // Take some special care when handling `let _x = &*_y`:
+        // We want to know if this is part of an overloaded index, so `let x = &a[0]`,
+        // or whether this is a usertype ascription (`let _x: &T = y`).
+        if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
+            && place.projection.len() == 1
+            && place.projection[0] == ProjectionElem::Deref
+            && let Some(assign) = find_assignments(&body, place.local).first()
+        {
+            // If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want
+            // to suggest `&mut` on the expression (handled here) or we return `None` and let the caller
+            // suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`).
+            if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
+                && let [user_ty_proj] = user_ty_projs.contents.as_slice()
+                && user_ty_proj.projs.is_empty()
+                && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
+                && let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
+                && let rhs_span_new = rhs_stmt_new.source_info.span
+                && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
+            {
+                (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
+            }
+
+            if let Either::Right(call) = body.stmt_at(*assign)
+                && let TerminatorKind::Call {
+                    func: Operand::Constant(box const_operand), args, ..
+                } = &call.kind
+                && let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
+                && let Some(trait_) = tcx.trait_of_assoc(method_def_id)
+                && tcx.is_lang_item(trait_, hir::LangItem::Index)
+            {
+                let trait_ref = ty::TraitRef::from_assoc(
+                    tcx,
+                    tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span),
+                    method_args,
+                );
+                // The type only implements `Index` but not `IndexMut`, we must not suggest `&mut`.
+                if !infcx
+                    .type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
+                    .must_apply_considering_regions()
+                {
+                    // Suggest `get_mut` if type is a `BTreeMap` or `HashMap`.
+                    if let ty::Adt(def, _) = trait_ref.self_ty().kind()
+                        && [sym::BTreeMap, sym::HashMap]
+                            .into_iter()
+                            .any(|s| tcx.is_diagnostic_item(s, def.did()))
+                        && let [map, key] = &**args
+                        && let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
+                        && let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
+                    {
+                        let span = rhs_span;
+                        let suggestion = format!("{map}.get_mut({key}).unwrap()");
+                        return Some(AmpMutSugg::MapGetMut { span, suggestion });
+                    }
+                    return None;
+                }
+            }
         }
 
-        // Figure out if rhs already is `&mut`.
-        let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") {
-            match rest.chars().next() {
-                // e.g. `&mut x`
-                Some(c) if c.is_whitespace() => true,
-                // e.g. `&mut(x)`
-                Some('(') => true,
-                // e.g. `&mut{x}`
-                Some('{') => true,
-                // e.g. `&mutablevar`
-                _ => false,
+        let sugg = match rvalue {
+            Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
+                // Shrink the span to just after the `&` in `&variable`.
+                Some((
+                    rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
+                    "mut ".to_owned(),
+                ))
             }
-        } else {
-            false
+            Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
+                // Suggest changing `&raw const` to `&raw mut` if applicable.
+                let const_idx = const_idx as u32;
+                Some((
+                    rhs_span
+                        .with_lo(rhs_span.lo() + BytePos(const_idx))
+                        .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
+                    "mut".to_owned(),
+                ))
+            }
+            _ => None,
         };
-        // if the reference is already mutable then there is nothing we can do
-        // here.
-        if !is_mut {
-            // shrink the span to just after the `&` in `&variable`
-            let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo();
-
-            // FIXME(Ezrashaw): returning is bad because we still might want to
-            // update the annotated type, see #106857.
-            return Some(AmpMutSugg {
-                has_sugg: true,
-                span,
-                suggestion: "mut ".to_owned(),
-                additional: None,
-            });
+
+        if let Some((span, suggestion)) = sugg {
+            return Some(AmpMutSugg::Expr { span, suggestion });
         }
     }
 
-    let (binding_exists, span) = match opt_ty_info {
-        // if this is a variable binding with an explicit type,
-        // then we will suggest changing it to be mutable.
-        // this is `Applicability::MachineApplicable`.
-        Some(ty_span) => (true, ty_span),
-
-        // otherwise, we'll suggest *adding* an annotated type, we'll suggest
-        // the RHS's type for that.
-        // this is `Applicability::HasPlaceholders`.
-        None => (false, decl_span),
-    };
-
-    // if the binding already exists and is a reference with an explicit
-    // lifetime, then we can suggest adding ` mut`. this is special-cased from
-    // the path without an explicit lifetime.
-    if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
-        && src.starts_with("&'")
-        // note that `&     'a T` is invalid so this is correct.
-        && let Some(ws_pos) = src.find(char::is_whitespace)
-    {
-        let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
-        Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None })
-    // if there is already a binding, we modify it to be `mut`
-    } else if binding_exists {
-        // shrink the span to just after the `&` in `&variable`
-        let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
-        Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None })
-    } else {
-        // otherwise, suggest that the user annotates the binding; we provide the
-        // type of the local.
-        let ty = decl_ty.builtin_deref(true).unwrap();
-
-        Some(AmpMutSugg {
-            has_sugg: false,
-            span,
-            suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty),
-            additional: None,
-        })
-    }
+    Some(AmpMutSugg::ChangeBinding)
 }
 
 /// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure`
diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs
index 7192a889adc..99913626418 100644
--- a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs
@@ -13,14 +13,83 @@ use rustc_middle::mir::{self, ConstraintCategory, Location};
 use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
+use rustc_span::Span;
+use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
 use rustc_trait_selection::errors::impl_trait_overcapture_suggestion;
 
 use crate::MirBorrowckCtxt;
 use crate::borrow_set::BorrowData;
 use crate::consumers::RegionInferenceContext;
+use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
 use crate::type_check::Locations;
 
 impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
+    pub(crate) fn report_opaque_type_errors(&mut self, errors: Vec<DeferredOpaqueTypeError<'tcx>>) {
+        if errors.is_empty() {
+            return;
+        }
+
+        let infcx = self.infcx;
+        let mut guar = None;
+        let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
+            None;
+        for error in errors {
+            guar = Some(match error {
+                DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx),
+                DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => {
+                    infcx.dcx().emit_err(err)
+                }
+                DeferredOpaqueTypeError::UnexpectedHiddenRegion {
+                    opaque_type_key,
+                    hidden_type,
+                    member_region,
+                } => {
+                    let named_ty =
+                        self.regioncx.name_regions_for_member_constraint(infcx.tcx, hidden_type.ty);
+                    let named_key = self
+                        .regioncx
+                        .name_regions_for_member_constraint(infcx.tcx, opaque_type_key);
+                    let named_region =
+                        self.regioncx.name_regions_for_member_constraint(infcx.tcx, member_region);
+                    let diag = unexpected_hidden_region_diagnostic(
+                        infcx,
+                        self.mir_def_id(),
+                        hidden_type.span,
+                        named_ty,
+                        named_region,
+                        named_key,
+                    );
+                    if last_unexpected_hidden_region
+                        != Some((hidden_type.span, named_ty, named_key))
+                    {
+                        last_unexpected_hidden_region =
+                            Some((hidden_type.span, named_ty, named_key));
+                        diag.emit()
+                    } else {
+                        diag.delay_as_bug()
+                    }
+                }
+                DeferredOpaqueTypeError::NonDefiningUseInDefiningScope {
+                    span,
+                    opaque_type_key,
+                } => infcx.dcx().span_err(
+                    span,
+                    format!(
+                        "non-defining use of `{}` in the defining scope",
+                        Ty::new_opaque(
+                            infcx.tcx,
+                            opaque_type_key.def_id.to_def_id(),
+                            opaque_type_key.args
+                        )
+                    ),
+                ),
+            });
+        }
+        let guar = guar.unwrap();
+        self.root_cx.set_tainted_by_errors(guar);
+        self.infcx.set_tainted_by_errors(guar);
+    }
+
     /// Try to note when an opaque is involved in a borrowck error and that
     /// opaque captures lifetimes due to edition 2024.
     // FIXME: This code is otherwise somewhat general, and could easily be adapted
@@ -174,10 +243,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
                 let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
 
                 // Find a path between the borrow region and our opaque capture.
-                if let Some((path, _)) =
-                    self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
-                        r == opaque_region_vid
-                    })
+                if let Some(path) = self
+                    .regioncx
+                    .constraint_path_between_regions(self.borrow_region, opaque_region_vid)
                 {
                     for constraint in path {
                         // If we find a call in this path, then check if it defines the opaque.
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index b130cf8ed27..bec4c934502 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -23,7 +23,6 @@ use rustc_trait_selection::error_reporting::infer::nice_region_error::{
     self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type,
     find_param_with_region, suggest_adding_lifetime_params,
 };
-use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{Obligation, ObligationCtxt};
 use tracing::{debug, instrument, trace};
@@ -62,7 +61,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
             | ConstraintCategory::Boring
             | ConstraintCategory::BoringNoLocation
             | ConstraintCategory::Internal
-            | ConstraintCategory::IllegalUniverse => "",
+            | ConstraintCategory::OutlivesUnnameablePlaceholder(..) => "",
         }
     }
 }
@@ -92,9 +91,6 @@ impl<'tcx> RegionErrors<'tcx> {
     ) -> impl Iterator<Item = (RegionErrorKind<'tcx>, ErrorGuaranteed)> {
         self.0.into_iter()
     }
-    pub(crate) fn has_errors(&self) -> Option<ErrorGuaranteed> {
-        self.0.get(0).map(|x| x.1)
-    }
 }
 
 impl std::fmt::Debug for RegionErrors<'_> {
@@ -108,18 +104,6 @@ pub(crate) enum RegionErrorKind<'tcx> {
     /// A generic bound failure for a type test (`T: 'a`).
     TypeTestError { type_test: TypeTest<'tcx> },
 
-    /// An unexpected hidden region for an opaque type.
-    UnexpectedHiddenRegion {
-        /// The span for the member constraint.
-        span: Span,
-        /// The hidden type.
-        hidden_ty: Ty<'tcx>,
-        /// The opaque type.
-        key: ty::OpaqueTypeKey<'tcx>,
-        /// The unexpected region.
-        member_region: ty::Region<'tcx>,
-    },
-
     /// Higher-ranked subtyping error.
     BoundUniversalRegionError {
         /// The placeholder free region.
@@ -218,7 +202,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         diag: &mut Diag<'_>,
         lower_bound: RegionVid,
     ) {
-        let mut suggestions = vec![];
         let tcx = self.infcx.tcx;
 
         // find generic associated types in the given region 'lower_bound'
@@ -240,9 +223,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             .collect::<Vec<_>>();
         debug!(?gat_id_and_generics);
 
-        // find higher-ranked trait bounds bounded to the generic associated types
+        // Look for the where-bound which introduces the placeholder.
+        // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
+        // and `T: for<'a> Trait`<'a>.
         let mut hrtb_bounds = vec![];
-        gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
+        gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| {
             for pred in generics.predicates {
                 let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
                     pred.kind
@@ -251,17 +236,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 };
                 if bound_generic_params
                     .iter()
-                    .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
+                    .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
                     .is_some()
                 {
                     for bound in *bounds {
                         hrtb_bounds.push(bound);
                     }
+                } else {
+                    for bound in *bounds {
+                        if let Trait(trait_bound) = bound {
+                            if trait_bound
+                                .bound_generic_params
+                                .iter()
+                                .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
+                                .is_some()
+                            {
+                                hrtb_bounds.push(bound);
+                                return;
+                            }
+                        }
+                    }
                 }
             }
         });
         debug!(?hrtb_bounds);
 
+        let mut suggestions = vec![];
         hrtb_bounds.iter().for_each(|bound| {
             let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
                 return;
@@ -310,11 +310,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
         // Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
         // buffered in the `MirBorrowckCtxt`.
-
         let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
-        let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
-            None;
-
         for (nll_error, _) in nll_errors.into_iter() {
             match nll_error {
                 RegionErrorKind::TypeTestError { type_test } => {
@@ -365,30 +361,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     }
                 }
 
-                RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => {
-                    let named_ty =
-                        self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, hidden_ty);
-                    let named_key =
-                        self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, key);
-                    let named_region = self
-                        .regioncx
-                        .name_regions_for_member_constraint(self.infcx.tcx, member_region);
-                    let diag = unexpected_hidden_region_diagnostic(
-                        self.infcx,
-                        self.mir_def_id(),
-                        span,
-                        named_ty,
-                        named_region,
-                        named_key,
-                    );
-                    if last_unexpected_hidden_region != Some((span, named_ty, named_key)) {
-                        self.buffer_error(diag);
-                        last_unexpected_hidden_region = Some((span, named_ty, named_key));
-                    } else {
-                        diag.delay_as_bug();
-                    }
-                }
-
                 RegionErrorKind::BoundUniversalRegionError {
                     longer_fr,
                     placeholder,
@@ -397,11 +369,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
 
                     // Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
-                    let (_, cause) = self.regioncx.find_outlives_blame_span(
-                        longer_fr,
-                        NllRegionVariableOrigin::Placeholder(placeholder),
-                        error_vid,
-                    );
+                    let cause = self
+                        .regioncx
+                        .best_blame_constraint(
+                            longer_fr,
+                            NllRegionVariableOrigin::Placeholder(placeholder),
+                            error_vid,
+                        )
+                        .0
+                        .cause;
 
                     let universe = placeholder.universe;
                     let universe_info = self.regioncx.universe_info(universe);
@@ -457,9 +433,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     ) {
         debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
 
-        let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
-            self.regioncx.provides_universal_region(r, fr, outlived_fr)
-        });
+        let (blame_constraint, path) =
+            self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
         let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
 
         debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index edd14d155f6..517f9e88cd9 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -528,15 +528,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                         // match_adt_and_segment in this case.
                         Res::Def(DefKind::TyAlias, _) => (),
                         _ => {
-                            if let Some(last_segment) = path.segments.last() {
-                                if let Some(highlight) = self.match_adt_and_segment(
+                            if let Some(last_segment) = path.segments.last()
+                                && let Some(highlight) = self.match_adt_and_segment(
                                     args,
                                     needle_fr,
                                     last_segment,
                                     search_stack,
-                                ) {
-                                    return Some(highlight);
-                                }
+                                )
+                            {
+                                return Some(highlight);
                             }
                         }
                     }
diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs
index aaaf2f45c86..6be90994015 100644
--- a/compiler/rustc_borrowck/src/handle_placeholders.rs
+++ b/compiler/rustc_borrowck/src/handle_placeholders.rs
@@ -1,7 +1,6 @@
 //! Logic for lowering higher-kinded outlives constraints
 //! (with placeholders and universes) and turn them into regular
 //! outlives constraints.
-
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::graph::scc;
@@ -10,12 +9,11 @@ use rustc_index::IndexVec;
 use rustc_infer::infer::RegionVariableOrigin;
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::{RegionVid, UniverseIndex};
-use tracing::debug;
+use tracing::{debug, trace};
 
 use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
 use crate::consumers::OutlivesConstraint;
 use crate::diagnostics::UniverseInfo;
-use crate::member_constraints::MemberConstraintSet;
 use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
 use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
 use crate::ty::VarianceDiagInfo;
@@ -30,7 +28,6 @@ pub(crate) struct LoweredConstraints<'tcx> {
     pub(crate) constraint_sccs: Sccs<RegionVid, ConstraintSccIndex>,
     pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
     pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
-    pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
     pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
     pub(crate) type_tests: Vec<TypeTest<'tcx>>,
     pub(crate) liveness_constraints: LivenessValues,
@@ -64,18 +61,71 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
     type SccIdx = ConstraintSccIndex;
 }
 
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+enum PlaceholderReachability {
+    /// This SCC reaches no placeholders.
+    NoPlaceholders,
+    /// This SCC reaches at least one placeholder.
+    Placeholders {
+        /// The largest-universed placeholder we can reach
+        max_universe: (UniverseIndex, RegionVid),
+
+        /// The placeholder with the smallest ID
+        min_placeholder: RegionVid,
+
+        /// The placeholder with the largest ID
+        max_placeholder: RegionVid,
+    },
+}
+
+impl PlaceholderReachability {
+    /// Merge the reachable placeholders of two graph components.
+    fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability {
+        use PlaceholderReachability::*;
+        match (self, other) {
+            (NoPlaceholders, NoPlaceholders) => NoPlaceholders,
+            (NoPlaceholders, p @ Placeholders { .. })
+            | (p @ Placeholders { .. }, NoPlaceholders) => p,
+            (
+                Placeholders {
+                    min_placeholder: min_pl,
+                    max_placeholder: max_pl,
+                    max_universe: max_u,
+                },
+                Placeholders { min_placeholder, max_placeholder, max_universe },
+            ) => Placeholders {
+                min_placeholder: min_pl.min(min_placeholder),
+                max_placeholder: max_pl.max(max_placeholder),
+                max_universe: max_u.max(max_universe),
+            },
+        }
+    }
+
+    fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> {
+        match self {
+            Self::NoPlaceholders => None,
+            Self::Placeholders { max_universe, .. } => Some(*max_universe),
+        }
+    }
+
+    /// If we have reached placeholders, determine if they can
+    /// be named from this universe.
+    fn can_be_named_by(&self, from: UniverseIndex) -> bool {
+        self.max_universe()
+            .is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe))
+    }
+}
+
 /// An annotation for region graph SCCs that tracks
 /// the values of its elements. This annotates a single SCC.
 #[derive(Copy, Debug, Clone)]
 pub(crate) struct RegionTracker {
-    /// The largest universe of a placeholder reached from this SCC.
-    /// This includes placeholders within this SCC.
-    max_placeholder_universe_reached: UniverseIndex,
+    reachable_placeholders: PlaceholderReachability,
 
     /// The largest universe nameable from this SCC.
     /// It is the smallest nameable universes of all
-    /// existential regions reachable from it.
-    max_nameable_universe: UniverseIndex,
+    /// existential regions reachable from it. Small Rvids are preferred.
+    max_nameable_universe: (UniverseIndex, RegionVid),
 
     /// The representative Region Variable Id for this SCC.
     pub(crate) representative: Representative,
@@ -83,69 +133,78 @@ pub(crate) struct RegionTracker {
 
 impl RegionTracker {
     pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
-        let placeholder_universe =
+        let reachable_placeholders =
             if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
-                definition.universe
+                PlaceholderReachability::Placeholders {
+                    max_universe: (definition.universe, rvid),
+                    min_placeholder: rvid,
+                    max_placeholder: rvid,
+                }
             } else {
-                UniverseIndex::ROOT
+                PlaceholderReachability::NoPlaceholders
             };
 
         Self {
-            max_placeholder_universe_reached: placeholder_universe,
-            max_nameable_universe: definition.universe,
+            reachable_placeholders,
+            max_nameable_universe: (definition.universe, rvid),
             representative: Representative::new(rvid, definition),
         }
     }
 
     /// The largest universe this SCC can name. It's the smallest
-    /// largest nameable uninverse of any reachable region.
+    /// largest nameable universe of any reachable region, or
+    /// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)`
     pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
-        self.max_nameable_universe
+        self.max_nameable_universe.0
     }
 
-    fn merge_min_max_seen(&mut self, other: &Self) {
-        self.max_placeholder_universe_reached = std::cmp::max(
-            self.max_placeholder_universe_reached,
-            other.max_placeholder_universe_reached,
-        );
-
-        self.max_nameable_universe =
-            std::cmp::min(self.max_nameable_universe, other.max_nameable_universe);
+    pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex {
+        if let Some((universe, _)) = self.reachable_placeholders.max_universe() {
+            universe
+        } else {
+            UniverseIndex::ROOT
+        }
     }
 
-    /// Returns `true` if during the annotated SCC reaches a placeholder
-    /// with a universe larger than the smallest nameable universe of any
-    /// reachable existential region.
-    pub(crate) fn has_incompatible_universes(&self) -> bool {
-        self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached)
+    /// Determine if we can name all the placeholders in `other`.
+    pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool {
+        other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0)
     }
 
-    /// Determine if the tracked universes of the two SCCs are compatible.
-    pub(crate) fn universe_compatible_with(&self, other: Self) -> bool {
-        self.max_nameable_universe().can_name(other.max_nameable_universe())
-            || self.max_nameable_universe().can_name(other.max_placeholder_universe_reached)
+    /// If this SCC reaches a placeholder it can't name, return it.
+    fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> {
+        self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| {
+            !self.max_nameable_universe().can_name(placeholder_universe)
+        })
     }
 }
 
 impl scc::Annotation for RegionTracker {
-    fn merge_scc(mut self, other: Self) -> Self {
-        self.representative = self.representative.merge_scc(other.representative);
-        self.merge_min_max_seen(&other);
-        self
+    fn merge_scc(self, other: Self) -> Self {
+        trace!("{:?} << {:?}", self.representative, other.representative);
+
+        Self {
+            representative: self.representative.min(other.representative),
+            max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
+            reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
+        }
     }
 
-    fn merge_reached(mut self, other: Self) -> Self {
-        // No update to in-component values, only add seen values.
-        self.merge_min_max_seen(&other);
-        self
+    fn merge_reached(self, other: Self) -> Self {
+        Self {
+            max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
+            reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
+            representative: self.representative,
+        }
     }
 }
 
 /// Determines if the region variable definitions contain
 /// placeholders, and compute them for later use.
-fn region_definitions<'tcx>(
-    universal_regions: &UniversalRegions<'tcx>,
+// FIXME: This is also used by opaque type handling. Move it to a separate file.
+pub(super) fn region_definitions<'tcx>(
     infcx: &BorrowckInferCtxt<'tcx>,
+    universal_regions: &UniversalRegions<'tcx>,
 ) -> (Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>, bool) {
     let var_infos = infcx.get_region_var_infos();
     // Create a RegionDefinition for each inference variable. This happens here because
@@ -157,7 +216,7 @@ fn region_definitions<'tcx>(
     for info in var_infos.iter() {
         let origin = match info.origin {
             RegionVariableOrigin::Nll(origin) => origin,
-            _ => NllRegionVariableOrigin::Existential { from_forall: false },
+            _ => NllRegionVariableOrigin::Existential { name: None },
         };
 
         let definition = RegionDefinition { origin, universe: info.universe, external_name: None };
@@ -209,29 +268,17 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
     infcx: &BorrowckInferCtxt<'tcx>,
 ) -> LoweredConstraints<'tcx> {
     let universal_regions = &universal_region_relations.universal_regions;
-    let (definitions, has_placeholders) = region_definitions(universal_regions, infcx);
+    let (definitions, has_placeholders) = region_definitions(infcx, universal_regions);
 
     let MirTypeckRegionConstraints {
         placeholder_indices,
         placeholder_index_to_region: _,
         liveness_constraints,
         mut outlives_constraints,
-        mut member_constraints,
         universe_causes,
         type_tests,
     } = constraints;
 
-    if let Some(guar) = universal_regions.tainted_by_errors() {
-        debug!("Universal regions tainted by errors; removing constraints!");
-        // Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all
-        // outlives bounds that we may end up checking.
-        outlives_constraints = Default::default();
-        member_constraints = Default::default();
-
-        // Also taint the entire scope.
-        infcx.set_tainted_by_errors(guar);
-    }
-
     let fr_static = universal_regions.fr_static;
     let compute_sccs =
         |constraints: &OutlivesConstraintSet<'tcx>,
@@ -253,7 +300,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
 
         return LoweredConstraints {
             type_tests,
-            member_constraints,
             constraint_sccs,
             scc_annotations: scc_annotations.scc_to_annotation,
             definitions,
@@ -290,7 +336,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
         constraint_sccs,
         definitions,
         scc_annotations,
-        member_constraints,
         outlives_constraints: Frozen::freeze(outlives_constraints),
         type_tests,
         liveness_constraints,
@@ -321,28 +366,52 @@ fn rewrite_placeholder_outlives<'tcx>(
 
         let annotation = annotations[scc];
 
-        // If this SCC participates in a universe violation,
-        // e.g. if it reaches a region with a universe smaller than
-        // the largest region reached, add a requirement that it must
-        // outlive `'static`.
-        if annotation.has_incompatible_universes() {
-            // Optimisation opportunity: this will add more constraints than
-            // needed for correctness, since an SCC upstream of another with
-            // a universe violation will "infect" its downstream SCCs to also
-            // outlive static.
-            let scc_representative_outlives_static = OutlivesConstraint {
-                sup: annotation.representative.rvid(),
-                sub: fr_static,
-                category: ConstraintCategory::IllegalUniverse,
-                locations: Locations::All(rustc_span::DUMMY_SP),
-                span: rustc_span::DUMMY_SP,
-                variance_info: VarianceDiagInfo::None,
-                from_closure: false,
-            };
-            outlives_constraints.push(scc_representative_outlives_static);
-            added_constraints = true;
-            debug!("Added {:?}: 'static!", annotation.representative.rvid());
-        }
+        let Some((max_u, max_u_rvid)) = annotation.unnameable_placeholder() else {
+            continue;
+        };
+
+        debug!(
+            "Placeholder universe {max_u:?} is too large for its SCC, represented by {:?}",
+            annotation.representative
+        );
+
+        // We only add one `r: 'static` constraint per SCC, where `r` is the SCC representative.
+        // That constraint is annotated with some placeholder `unnameable` where
+        // `unnameable` is unnameable from `r` and there is a path in the constraint graph
+        // between them.
+        //
+        // There is one exception; if some other region in this SCC can't name `'r`, then
+        // we pick the region with the smallest universe in the SCC, so that a path can
+        // always start in `'r` to find a motivation that isn't cyclic.
+        let blame_to = if annotation.representative.rvid() == max_u_rvid {
+            // Assertion: the region that lowered our universe is an existential one and we are a placeholder!
+
+            // The SCC's representative is not nameable from some region
+            // that ends up in the SCC.
+            let small_universed_rvid = annotation.max_nameable_universe.1;
+            debug!(
+                "{small_universed_rvid:?} lowered our universe to {:?}",
+                annotation.max_nameable_universe()
+            );
+            small_universed_rvid
+        } else {
+            // `max_u_rvid` is not nameable by the SCC's representative.
+            max_u_rvid
+        };
+
+        // FIXME: if we can extract a useful blame span here, future error
+        // reporting and constraint search can be simplified.
+
+        added_constraints = true;
+        outlives_constraints.push(OutlivesConstraint {
+            sup: annotation.representative.rvid(),
+            sub: fr_static,
+            category: ConstraintCategory::OutlivesUnnameablePlaceholder(blame_to),
+            locations: Locations::All(rustc_span::DUMMY_SP),
+            span: rustc_span::DUMMY_SP,
+            variance_info: VarianceDiagInfo::None,
+            from_closure: false,
+        });
     }
     added_constraints
 }
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 321b18c9b78..4c380ddcf70 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -19,10 +19,13 @@ use std::borrow::Cow;
 use std::cell::{OnceCell, RefCell};
 use std::marker::PhantomData;
 use std::ops::{ControlFlow, Deref};
+use std::rc::Rc;
 
 use borrow_set::LocalsStateAtExit;
+use polonius_engine::AllFacts;
 use root_cx::BorrowCheckRootCtxt;
 use rustc_abi::FieldIdx;
+use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_errors::LintDiagnostic;
@@ -31,6 +34,7 @@ use rustc_hir::CRATE_HIR_ID;
 use rustc_hir::def_id::LocalDefId;
 use rustc_index::bit_set::MixedBitSet;
 use rustc_index::{IndexSlice, IndexVec};
+use rustc_infer::infer::outlives::env::RegionBoundPairs;
 use rustc_infer::infer::{
     InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
 };
@@ -44,6 +48,7 @@ use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces}
 use rustc_mir_dataflow::move_paths::{
     InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
 };
+use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results};
 use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
 use rustc_span::{ErrorGuaranteed, Span, Symbol};
@@ -51,7 +56,7 @@ use smallvec::SmallVec;
 use tracing::{debug, instrument};
 
 use crate::borrow_set::{BorrowData, BorrowSet};
-use crate::consumers::BodyWithBorrowckFacts;
+use crate::consumers::{BodyWithBorrowckFacts, RustcFacts};
 use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
 use crate::diagnostics::{
     AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
@@ -59,12 +64,17 @@ use crate::diagnostics::{
 use crate::path_utils::*;
 use crate::place_ext::PlaceExt;
 use crate::places_conflict::{PlaceConflictBias, places_conflict};
-use crate::polonius::PoloniusDiagnosticsContext;
-use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput};
+use crate::polonius::legacy::{
+    PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
+};
+use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
 use crate::prefixes::PrefixSet;
 use crate::region_infer::RegionInferenceContext;
+use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
 use crate::renumber::RegionCtxt;
 use crate::session_diagnostics::VarNeedNotMut;
+use crate::type_check::free_region_relations::UniversalRegionRelations;
+use crate::type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults};
 
 mod borrow_set;
 mod borrowck_errors;
@@ -73,7 +83,6 @@ mod dataflow;
 mod def_use;
 mod diagnostics;
 mod handle_placeholders;
-mod member_constraints;
 mod nll;
 mod path_utils;
 mod place_ext;
@@ -125,18 +134,7 @@ fn mir_borrowck(
         Ok(tcx.arena.alloc(opaque_types))
     } else {
         let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None);
-        // We need to manually borrowck all nested bodies from the HIR as
-        // we do not generate MIR for dead code. Not doing so causes us to
-        // never check closures in dead code.
-        let nested_bodies = tcx.nested_bodies_within(def);
-        for def_id in nested_bodies {
-            root_cx.get_or_insert_nested(def_id);
-        }
-
-        let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
-            do_mir_borrowck(&mut root_cx, def);
-        debug_assert!(closure_requirements.is_none());
-        debug_assert!(used_mut_upvars.is_empty());
+        root_cx.do_mir_borrowck();
         root_cx.finalize()
     }
 }
@@ -149,6 +147,8 @@ struct PropagatedBorrowCheckResults<'tcx> {
     used_mut_upvars: SmallVec<[FieldIdx; 8]>,
 }
 
+type DeferredClosureRequirements<'tcx> = Vec<(LocalDefId, ty::GenericArgsRef<'tcx>, Locations)>;
+
 /// After we borrow check a closure, we are left with various
 /// requirements that we have inferred between the free regions that
 /// appear in the closure's signature or on its field types. These
@@ -287,16 +287,33 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
     }
 }
 
-/// Perform the actual borrow checking.
-///
-/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`.
-#[instrument(skip(root_cx), level = "debug")]
-fn do_mir_borrowck<'tcx>(
+struct CollectRegionConstraintsResult<'tcx> {
+    infcx: BorrowckInferCtxt<'tcx>,
+    body_owned: Body<'tcx>,
+    promoted: IndexVec<Promoted, Body<'tcx>>,
+    move_data: MoveData<'tcx>,
+    borrow_set: BorrowSet<'tcx>,
+    location_table: PoloniusLocationTable,
+    location_map: Rc<DenseLocationMap>,
+    universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
+    region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
+    known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
+    constraints: MirTypeckRegionConstraints<'tcx>,
+    deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
+    deferred_opaque_type_errors: Vec<DeferredOpaqueTypeError<'tcx>>,
+    polonius_facts: Option<AllFacts<RustcFacts>>,
+    polonius_context: Option<PoloniusContext>,
+}
+
+/// Start borrow checking by collecting the region constraints for
+/// the current body. This initializes the relevant data structures
+/// and then type checks the MIR body.
+fn borrowck_collect_region_constraints<'tcx>(
     root_cx: &mut BorrowCheckRootCtxt<'tcx>,
     def: LocalDefId,
-) -> PropagatedBorrowCheckResults<'tcx> {
+) -> CollectRegionConstraintsResult<'tcx> {
     let tcx = root_cx.tcx;
-    let infcx = BorrowckInferCtxt::new(tcx, def);
+    let infcx = BorrowckInferCtxt::new(tcx, def, root_cx.root_def_id());
     let (input_body, promoted) = tcx.mir_promoted(def);
     let input_body: &Body<'_> = &input_body.borrow();
     let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
@@ -321,7 +338,84 @@ fn do_mir_borrowck<'tcx>(
     let locals_are_invalidated_at_exit = tcx.hir_body_owner_kind(def).is_fn_or_closure();
     let borrow_set = BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &move_data);
 
-    // Compute non-lexical lifetimes.
+    let location_map = Rc::new(DenseLocationMap::new(body));
+
+    let polonius_input = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_input())
+        || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
+    let mut polonius_facts =
+        (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());
+
+    // Run the MIR type-checker.
+    let MirTypeckResults {
+        constraints,
+        universal_region_relations,
+        region_bound_pairs,
+        known_type_outlives_obligations,
+        deferred_closure_requirements,
+        polonius_context,
+    } = type_check::type_check(
+        root_cx,
+        &infcx,
+        body,
+        &promoted,
+        universal_regions,
+        &location_table,
+        &borrow_set,
+        &mut polonius_facts,
+        &move_data,
+        Rc::clone(&location_map),
+    );
+
+    CollectRegionConstraintsResult {
+        infcx,
+        body_owned,
+        promoted,
+        move_data,
+        borrow_set,
+        location_table,
+        location_map,
+        universal_region_relations,
+        region_bound_pairs,
+        known_type_outlives_obligations,
+        constraints,
+        deferred_closure_requirements,
+        deferred_opaque_type_errors: Default::default(),
+        polonius_facts,
+        polonius_context,
+    }
+}
+
+/// Using the region constraints computed by [borrowck_collect_region_constraints]
+/// and the additional constraints from [BorrowCheckRootCtxt::handle_opaque_type_uses],
+/// compute the region graph and actually check for any borrowck errors.
+fn borrowck_check_region_constraints<'tcx>(
+    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+    CollectRegionConstraintsResult {
+        infcx,
+        body_owned,
+        promoted,
+        move_data,
+        borrow_set,
+        location_table,
+        location_map,
+        universal_region_relations,
+        region_bound_pairs: _,
+        known_type_outlives_obligations: _,
+        constraints,
+        deferred_closure_requirements,
+        deferred_opaque_type_errors,
+        polonius_facts,
+        polonius_context,
+    }: CollectRegionConstraintsResult<'tcx>,
+) -> PropagatedBorrowCheckResults<'tcx> {
+    assert!(!infcx.has_opaque_types_in_storage());
+    assert!(deferred_closure_requirements.is_empty());
+    let tcx = root_cx.tcx;
+    let body = &body_owned;
+    let def = body.source.def_id().expect_local();
+
+    // Compute non-lexical lifetimes using the constraints computed
+    // by typechecking the MIR body.
     let nll::NllOutput {
         regioncx,
         polonius_input,
@@ -332,12 +426,15 @@ fn do_mir_borrowck<'tcx>(
     } = nll::compute_regions(
         root_cx,
         &infcx,
-        universal_regions,
         body,
-        &promoted,
         &location_table,
         &move_data,
         &borrow_set,
+        location_map,
+        universal_region_relations,
+        constraints,
+        polonius_facts,
+        polonius_context,
     );
 
     // Dump MIR results into a file, if that is enabled. This lets us
@@ -434,7 +531,11 @@ fn do_mir_borrowck<'tcx>(
     };
 
     // Compute and report region errors, if any.
-    mbcx.report_region_errors(nll_errors);
+    if nll_errors.is_empty() {
+        mbcx.report_opaque_type_errors(deferred_opaque_type_errors);
+    } else {
+        mbcx.report_region_errors(nll_errors);
+    }
 
     let (mut flow_analysis, flow_entry_states) =
         get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
@@ -540,12 +641,13 @@ fn get_flow_results<'a, 'tcx>(
 
 pub(crate) struct BorrowckInferCtxt<'tcx> {
     pub(crate) infcx: InferCtxt<'tcx>,
-    pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
+    pub(crate) root_def_id: LocalDefId,
     pub(crate) param_env: ParamEnv<'tcx>,
+    pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
 }
 
 impl<'tcx> BorrowckInferCtxt<'tcx> {
-    pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
+    pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, root_def_id: LocalDefId) -> Self {
         let typing_mode = if tcx.use_typing_mode_borrowck() {
             TypingMode::borrowck(tcx, def_id)
         } else {
@@ -553,7 +655,12 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
         };
         let infcx = tcx.infer_ctxt().build(typing_mode);
         let param_env = tcx.param_env(def_id);
-        BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env }
+        BorrowckInferCtxt {
+            infcx,
+            root_def_id,
+            reg_var_to_origin: RefCell::new(Default::default()),
+            param_env,
+        }
     }
 
     pub(crate) fn next_region_var<F>(
@@ -1450,9 +1557,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                 );
             }
 
-            &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
+            &Rvalue::Discriminant(place) => {
                 let af = match *rvalue {
-                    Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
                     Rvalue::Discriminant(..) => None,
                     _ => unreachable!(),
                 };
@@ -1797,7 +1903,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                     | ty::Slice(_)
                     | ty::FnDef(_, _)
                     | ty::FnPtr(..)
-                    | ty::Dynamic(_, _, _)
+                    | ty::Dynamic(_, _)
                     | ty::Closure(_, _)
                     | ty::CoroutineClosure(_, _)
                     | ty::Coroutine(_, _)
@@ -1843,7 +1949,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                     | ty::Ref(_, _, _)
                     | ty::FnDef(_, _)
                     | ty::FnPtr(..)
-                    | ty::Dynamic(_, _, _)
+                    | ty::Dynamic(_, _)
                     | ty::CoroutineWitness(..)
                     | ty::Never
                     | ty::UnsafeBinder(_)
diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs
deleted file mode 100644
index bdd0f6fe11e..00000000000
--- a/compiler/rustc_borrowck/src/member_constraints.rs
+++ /dev/null
@@ -1,226 +0,0 @@
-use std::hash::Hash;
-use std::ops::Index;
-
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_index::{IndexSlice, IndexVec};
-use rustc_middle::ty::{self, Ty};
-use rustc_span::Span;
-use tracing::instrument;
-
-/// Compactly stores a set of `R0 member of [R1...Rn]` constraints,
-/// indexed by the region `R0`.
-#[derive(Debug)]
-pub(crate) struct MemberConstraintSet<'tcx, R>
-where
-    R: Copy + Eq,
-{
-    /// Stores the first "member" constraint for a given `R0`. This is an
-    /// index into the `constraints` vector below.
-    first_constraints: FxIndexMap<R, NllMemberConstraintIndex>,
-
-    /// Stores the data about each `R0 member of [R1..Rn]` constraint.
-    /// These are organized into a linked list, so each constraint
-    /// contains the index of the next constraint with the same `R0`.
-    constraints: IndexVec<NllMemberConstraintIndex, MemberConstraint<'tcx>>,
-
-    /// Stores the `R1..Rn` regions for *all* sets. For any given
-    /// constraint, we keep two indices so that we can pull out a
-    /// slice.
-    choice_regions: Vec<ty::RegionVid>,
-}
-
-/// Represents a `R0 member of [R1..Rn]` constraint
-#[derive(Debug)]
-pub(crate) struct MemberConstraint<'tcx> {
-    next_constraint: Option<NllMemberConstraintIndex>,
-
-    /// The span where the hidden type was instantiated.
-    pub(crate) definition_span: Span,
-
-    /// The hidden type in which `R0` appears. (Used in error reporting.)
-    pub(crate) hidden_ty: Ty<'tcx>,
-
-    pub(crate) key: ty::OpaqueTypeKey<'tcx>,
-
-    /// The region `R0`.
-    pub(crate) member_region_vid: ty::RegionVid,
-
-    /// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`.
-    start_index: usize,
-
-    /// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`.
-    end_index: usize,
-}
-
-rustc_index::newtype_index! {
-    #[debug_format = "MemberConstraintIndex({})"]
-    pub(crate) struct NllMemberConstraintIndex {}
-}
-
-impl Default for MemberConstraintSet<'_, ty::RegionVid> {
-    fn default() -> Self {
-        Self {
-            first_constraints: Default::default(),
-            constraints: Default::default(),
-            choice_regions: Default::default(),
-        }
-    }
-}
-
-impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
-    pub(crate) fn is_empty(&self) -> bool {
-        self.constraints.is_empty()
-    }
-
-    /// Pushes a member constraint into the set.
-    #[instrument(level = "debug", skip(self))]
-    pub(crate) fn add_member_constraint(
-        &mut self,
-        key: ty::OpaqueTypeKey<'tcx>,
-        hidden_ty: Ty<'tcx>,
-        definition_span: Span,
-        member_region_vid: ty::RegionVid,
-        choice_regions: &[ty::RegionVid],
-    ) {
-        let next_constraint = self.first_constraints.get(&member_region_vid).cloned();
-        let start_index = self.choice_regions.len();
-        self.choice_regions.extend(choice_regions);
-        let end_index = self.choice_regions.len();
-        let constraint_index = self.constraints.push(MemberConstraint {
-            next_constraint,
-            member_region_vid,
-            definition_span,
-            hidden_ty,
-            key,
-            start_index,
-            end_index,
-        });
-        self.first_constraints.insert(member_region_vid, constraint_index);
-    }
-}
-
-impl<'tcx, R1> MemberConstraintSet<'tcx, R1>
-where
-    R1: Copy + Hash + Eq,
-{
-    /// Remap the "member region" key using `map_fn`, producing a new
-    /// member constraint set. This is used in the NLL code to map from
-    /// the original `RegionVid` to an scc index. In some cases, we
-    /// may have multiple `R1` values mapping to the same `R2` key -- that
-    /// is ok, the two sets will be merged.
-    pub(crate) fn into_mapped<R2>(
-        self,
-        mut map_fn: impl FnMut(R1) -> R2,
-    ) -> MemberConstraintSet<'tcx, R2>
-    where
-        R2: Copy + Hash + Eq,
-    {
-        // We can re-use most of the original data, just tweaking the
-        // linked list links a bit.
-        //
-        // For example if we had two keys `Ra` and `Rb` that both now
-        // wind up mapped to the same key `S`, we would append the
-        // linked list for `Ra` onto the end of the linked list for
-        // `Rb` (or vice versa) -- this basically just requires
-        // rewriting the final link from one list to point at the other
-        // other (see `append_list`).
-
-        let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self;
-
-        let mut first_constraints2 = FxIndexMap::default();
-        first_constraints2.reserve(first_constraints.len());
-
-        for (r1, start1) in first_constraints {
-            let r2 = map_fn(r1);
-            if let Some(&start2) = first_constraints2.get(&r2) {
-                append_list(&mut constraints, start1, start2);
-            }
-            first_constraints2.insert(r2, start1);
-        }
-
-        MemberConstraintSet { first_constraints: first_constraints2, constraints, choice_regions }
-    }
-}
-
-impl<'tcx, R> MemberConstraintSet<'tcx, R>
-where
-    R: Copy + Hash + Eq,
-{
-    pub(crate) fn all_indices(&self) -> impl Iterator<Item = NllMemberConstraintIndex> {
-        self.constraints.indices()
-    }
-
-    /// Iterate down the constraint indices associated with a given
-    /// peek-region. You can then use `choice_regions` and other
-    /// methods to access data.
-    pub(crate) fn indices(
-        &self,
-        member_region_vid: R,
-    ) -> impl Iterator<Item = NllMemberConstraintIndex> {
-        let mut next = self.first_constraints.get(&member_region_vid).cloned();
-        std::iter::from_fn(move || -> Option<NllMemberConstraintIndex> {
-            if let Some(current) = next {
-                next = self.constraints[current].next_constraint;
-                Some(current)
-            } else {
-                None
-            }
-        })
-    }
-
-    /// Returns the "choice regions" for a given member
-    /// constraint. This is the `R1..Rn` from a constraint like:
-    ///
-    /// ```text
-    /// R0 member of [R1..Rn]
-    /// ```
-    pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] {
-        let MemberConstraint { start_index, end_index, .. } = &self.constraints[pci];
-        &self.choice_regions[*start_index..*end_index]
-    }
-}
-
-impl<'tcx, R> Index<NllMemberConstraintIndex> for MemberConstraintSet<'tcx, R>
-where
-    R: Copy + Eq,
-{
-    type Output = MemberConstraint<'tcx>;
-
-    fn index(&self, i: NllMemberConstraintIndex) -> &MemberConstraint<'tcx> {
-        &self.constraints[i]
-    }
-}
-
-/// Given a linked list starting at `source_list` and another linked
-/// list starting at `target_list`, modify `target_list` so that it is
-/// followed by `source_list`.
-///
-/// Before:
-///
-/// ```text
-/// target_list: A -> B -> C -> (None)
-/// source_list: D -> E -> F -> (None)
-/// ```
-///
-/// After:
-///
-/// ```text
-/// target_list: A -> B -> C -> D -> E -> F -> (None)
-/// ```
-fn append_list(
-    constraints: &mut IndexSlice<NllMemberConstraintIndex, MemberConstraint<'_>>,
-    target_list: NllMemberConstraintIndex,
-    source_list: NllMemberConstraintIndex,
-) {
-    let mut p = target_list;
-    loop {
-        let r = &mut constraints[p];
-        match r.next_constraint {
-            Some(q) => p = q,
-            None => {
-                r.next_constraint = Some(source_list);
-                return;
-            }
-        }
-    }
-}
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 41f67e78930..5537d90e297 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -5,10 +5,11 @@ use std::path::PathBuf;
 use std::rc::Rc;
 use std::str::FromStr;
 
-use polonius_engine::{Algorithm, Output};
+use polonius_engine::{Algorithm, AllFacts, Output};
+use rustc_data_structures::frozen::Frozen;
 use rustc_index::IndexSlice;
-use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
-use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
+use rustc_middle::mir::pretty::PrettyPrintMirOptions;
+use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_mir_dataflow::move_paths::MoveData;
@@ -18,14 +19,16 @@ use rustc_span::sym;
 use tracing::{debug, instrument};
 
 use crate::borrow_set::BorrowSet;
+use crate::consumers::RustcFacts;
 use crate::diagnostics::RegionErrors;
 use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints;
-use crate::polonius::PoloniusDiagnosticsContext;
 use crate::polonius::legacy::{
     PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
 };
+use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
 use crate::region_infer::RegionInferenceContext;
-use crate::type_check::{self, MirTypeckResults};
+use crate::type_check::MirTypeckRegionConstraints;
+use crate::type_check::free_region_relations::UniversalRegionRelations;
 use crate::universal_regions::UniversalRegions;
 use crate::{
     BorrowCheckRootCtxt, BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements,
@@ -65,52 +68,63 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
     // Replace all remaining regions with fresh inference variables.
     renumber::renumber_mir(infcx, body, promoted);
 
-    dump_mir(infcx.tcx, false, "renumber", &0, body, |_, _| Ok(()));
+    if let Some(dumper) = MirDumper::new(infcx.tcx, "renumber", body) {
+        dumper.dump_mir(body);
+    }
 
     universal_regions
 }
 
+/// Computes the closure requirements given the current inference state.
+///
+/// This is intended to be used by before [BorrowCheckRootCtxt::handle_opaque_type_uses]
+/// because applying member constraints may rely on closure requirements.
+/// This is frequently the case of async functions where pretty much everything
+/// happens inside of the inner async block but the opaque only gets constrained
+/// in the parent function.
+pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>(
+    infcx: &BorrowckInferCtxt<'tcx>,
+    body: &Body<'tcx>,
+    location_map: Rc<DenseLocationMap>,
+    universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
+) -> Option<ClosureRegionRequirements<'tcx>> {
+    // FIXME(#146079): we shouldn't have to clone all this stuff here.
+    // Computing the region graph should take at least some of it by reference/`Rc`.
+    let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
+        constraints.clone(),
+        &universal_region_relations,
+        infcx,
+    );
+    let mut regioncx = RegionInferenceContext::new(
+        &infcx,
+        lowered_constraints,
+        universal_region_relations.clone(),
+        location_map,
+    );
+
+    let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, body, None);
+    closure_region_requirements
+}
+
 /// Computes the (non-lexical) regions from the input MIR.
 ///
 /// This may result in errors being reported.
 pub(crate) fn compute_regions<'tcx>(
     root_cx: &mut BorrowCheckRootCtxt<'tcx>,
     infcx: &BorrowckInferCtxt<'tcx>,
-    universal_regions: UniversalRegions<'tcx>,
     body: &Body<'tcx>,
-    promoted: &IndexSlice<Promoted, Body<'tcx>>,
     location_table: &PoloniusLocationTable,
     move_data: &MoveData<'tcx>,
     borrow_set: &BorrowSet<'tcx>,
+    location_map: Rc<DenseLocationMap>,
+    universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
+    constraints: MirTypeckRegionConstraints<'tcx>,
+    mut polonius_facts: Option<AllFacts<RustcFacts>>,
+    polonius_context: Option<PoloniusContext>,
 ) -> NllOutput<'tcx> {
-    let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
-    let polonius_input = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_input())
-        || is_polonius_legacy_enabled;
     let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
-        || is_polonius_legacy_enabled;
-    let mut polonius_facts =
-        (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());
-
-    let location_map = Rc::new(DenseLocationMap::new(body));
-
-    // Run the MIR type-checker.
-    let MirTypeckResults {
-        constraints,
-        universal_region_relations,
-        opaque_type_values,
-        polonius_context,
-    } = type_check::type_check(
-        root_cx,
-        infcx,
-        body,
-        promoted,
-        universal_regions,
-        location_table,
-        borrow_set,
-        &mut polonius_facts,
-        move_data,
-        Rc::clone(&location_map),
-    );
+        || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
 
     let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
         constraints,
@@ -168,13 +182,6 @@ pub(crate) fn compute_regions<'tcx>(
     let (closure_region_requirements, nll_errors) =
         regioncx.solve(infcx, body, polonius_output.clone());
 
-    if let Some(guar) = nll_errors.has_errors() {
-        // Suppress unhelpful extra errors in `infer_opaque_types`.
-        infcx.set_tainted_by_errors(guar);
-    }
-
-    regioncx.infer_opaque_types(root_cx, infcx, opaque_type_values);
-
     NllOutput {
         regioncx,
         polonius_input: polonius_facts.map(Box::new),
@@ -202,9 +209,7 @@ pub(super) fn dump_nll_mir<'tcx>(
     borrow_set: &BorrowSet<'tcx>,
 ) {
     let tcx = infcx.tcx;
-    if !dump_enabled(tcx, "nll", body.source.def_id()) {
-        return;
-    }
+    let Some(dumper) = MirDumper::new(tcx, "nll", body) else { return };
 
     // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
     // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
@@ -215,27 +220,24 @@ pub(super) fn dump_nll_mir<'tcx>(
             MirIncludeSpans::On | MirIncludeSpans::Nll
         ),
     };
-    dump_mir_with_options(
-        tcx,
-        false,
-        "nll",
-        &0,
-        body,
-        |pass_where, out| {
-            emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
-        },
-        options,
-    );
+
+    let extra_data = &|pass_where, out: &mut dyn std::io::Write| {
+        emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
+    };
+
+    let dumper = dumper.set_extra_data(extra_data).set_options(options);
+
+    dumper.dump_mir(body);
 
     // Also dump the region constraint graph as a graphviz file.
     let _: io::Result<()> = try {
-        let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
+        let mut file = dumper.create_dump_file("regioncx.all.dot", body)?;
         regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
     };
 
     // Also dump the region constraint SCC graph as a graphviz file.
     let _: io::Result<()> = try {
-        let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
+        let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?;
         regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
     };
 }
diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs
index 50f59dd0dee..52595757859 100644
--- a/compiler/rustc_borrowck/src/polonius/constraints.rs
+++ b/compiler/rustc_borrowck/src/polonius/constraints.rs
@@ -7,9 +7,7 @@ use rustc_mir_dataflow::points::PointIndex;
 ///
 /// This models two sources of constraints:
 /// - constraints that traverse the subsets between regions at a given point, `a@p: b@p`. These
-///   depend on typeck constraints generated via assignments, calls, etc. (In practice there are
-///   subtleties where a statement's effect only starts being visible at the successor point, via
-///   the "result" of that statement).
+///   depend on typeck constraints generated via assignments, calls, etc.
 /// - constraints that traverse the CFG via the same region, `a@p: a@q`, where `p` is a predecessor
 ///   of `q`. These depend on the liveness of the regions at these points, as well as their
 ///   variance.
diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs
index 6b13b5ad081..62f9ae17347 100644
--- a/compiler/rustc_borrowck/src/polonius/dump.rs
+++ b/compiler/rustc_borrowck/src/polonius/dump.rs
@@ -2,9 +2,7 @@ use std::io;
 
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_index::IndexVec;
-use rustc_middle::mir::pretty::{
-    PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
-};
+use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions};
 use rustc_middle::mir::{Body, Location};
 use rustc_middle::ty::{RegionVid, TyCtxt};
 use rustc_mir_dataflow::points::PointIndex;
@@ -33,22 +31,41 @@ pub(crate) fn dump_polonius_mir<'tcx>(
         return;
     }
 
-    if !dump_enabled(tcx, "polonius", body.source.def_id()) {
-        return;
-    }
+    let Some(dumper) = MirDumper::new(tcx, "polonius", body) else { return };
 
     let polonius_diagnostics =
         polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
 
+    let extra_data = &|pass_where, out: &mut dyn io::Write| {
+        emit_polonius_mir(
+            tcx,
+            regioncx,
+            closure_region_requirements,
+            borrow_set,
+            &polonius_diagnostics.localized_outlives_constraints,
+            pass_where,
+            out,
+        )
+    };
+    // We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
+    // mir-include-spans` on the CLI still has priority.
+    let options = PrettyPrintMirOptions {
+        include_extra_comments: matches!(
+            tcx.sess.opts.unstable_opts.mir_include_spans,
+            MirIncludeSpans::On | MirIncludeSpans::Nll
+        ),
+    };
+
+    let dumper = dumper.set_extra_data(extra_data).set_options(options);
+
     let _: io::Result<()> = try {
-        let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?;
+        let mut file = dumper.create_dump_file("html", body)?;
         emit_polonius_dump(
-            tcx,
+            &dumper,
             body,
             regioncx,
             borrow_set,
             &polonius_diagnostics.localized_outlives_constraints,
-            closure_region_requirements,
             &mut file,
         )?;
     };
@@ -61,12 +78,11 @@ pub(crate) fn dump_polonius_mir<'tcx>(
 /// - a mermaid graph of the NLL regions and the constraints between them
 /// - a mermaid graph of the NLL SCCs and the constraints between them
 fn emit_polonius_dump<'tcx>(
-    tcx: TyCtxt<'tcx>,
+    dumper: &MirDumper<'_, '_, 'tcx>,
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     borrow_set: &BorrowSet<'tcx>,
     localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
-    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
     // Prepare the HTML dump file prologue.
@@ -79,15 +95,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "<div>")?;
     writeln!(out, "Raw MIR dump")?;
     writeln!(out, "<pre><code>")?;
-    emit_html_mir(
-        tcx,
-        body,
-        regioncx,
-        borrow_set,
-        &localized_outlives_constraints,
-        closure_region_requirements,
-        out,
-    )?;
+    emit_html_mir(dumper, body, out)?;
     writeln!(out, "</code></pre>")?;
     writeln!(out, "</div>")?;
 
@@ -116,7 +124,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "<div>")?;
     writeln!(out, "NLL regions")?;
     writeln!(out, "<pre class='mermaid'>")?;
-    emit_mermaid_nll_regions(tcx, regioncx, out)?;
+    emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?;
     writeln!(out, "</pre>")?;
     writeln!(out, "</div>")?;
 
@@ -124,7 +132,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "<div>")?;
     writeln!(out, "NLL SCCs")?;
     writeln!(out, "<pre class='mermaid'>")?;
-    emit_mermaid_nll_sccs(tcx, regioncx, out)?;
+    emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?;
     writeln!(out, "</pre>")?;
     writeln!(out, "</div>")?;
 
@@ -149,45 +157,14 @@ fn emit_polonius_dump<'tcx>(
 
 /// Emits the polonius MIR, as escaped HTML.
 fn emit_html_mir<'tcx>(
-    tcx: TyCtxt<'tcx>,
+    dumper: &MirDumper<'_, '_, 'tcx>,
     body: &Body<'tcx>,
-    regioncx: &RegionInferenceContext<'tcx>,
-    borrow_set: &BorrowSet<'tcx>,
-    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
-    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
     // Buffer the regular MIR dump to be able to escape it.
     let mut buffer = Vec::new();
 
-    // We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
-    // mir-include-spans` on the CLI still has priority.
-    let options = PrettyPrintMirOptions {
-        include_extra_comments: matches!(
-            tcx.sess.opts.unstable_opts.mir_include_spans,
-            MirIncludeSpans::On | MirIncludeSpans::Nll
-        ),
-    };
-
-    dump_mir_to_writer(
-        tcx,
-        "polonius",
-        &0,
-        body,
-        &mut buffer,
-        |pass_where, out| {
-            emit_polonius_mir(
-                tcx,
-                regioncx,
-                closure_region_requirements,
-                borrow_set,
-                localized_outlives_constraints,
-                pass_where,
-                out,
-            )
-        },
-        options,
-    )?;
+    dumper.dump_mir_to_writer(body, &mut buffer)?;
 
     // Escape the handful of characters that need it. We don't need to be particularly efficient:
     // we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8.
diff --git a/compiler/rustc_borrowck/src/polonius/legacy/facts.rs b/compiler/rustc_borrowck/src/polonius/legacy/facts.rs
index 64389b11a65..1f8177477e6 100644
--- a/compiler/rustc_borrowck/src/polonius/legacy/facts.rs
+++ b/compiler/rustc_borrowck/src/polonius/legacy/facts.rs
@@ -184,22 +184,6 @@ where
     }
 }
 
-impl<A, B, C, D> FactRow for (A, B, C, D)
-where
-    A: FactCell,
-    B: FactCell,
-    C: FactCell,
-    D: FactCell,
-{
-    fn write(
-        &self,
-        out: &mut dyn Write,
-        location_table: &PoloniusLocationTable,
-    ) -> Result<(), Box<dyn Error>> {
-        write_row(out, location_table, &[&self.0, &self.1, &self.2, &self.3])
-    }
-}
-
 fn write_row(
     out: &mut dyn Write,
     location_table: &PoloniusLocationTable,
diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
index 99dd0b2dd46..c2ad6fcb4b7 100644
--- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
+++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
@@ -306,16 +306,11 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
                 self.consume_operand(location, op);
             }
 
-            &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
-                let af = match rvalue {
-                    Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
-                    Rvalue::Discriminant(..) => None,
-                    _ => unreachable!(),
-                };
+            &Rvalue::Discriminant(place) => {
                 self.access_place(
                     location,
                     place,
-                    (Shallow(af), Read(ReadKind::Copy)),
+                    (Shallow(None), Read(ReadKind::Copy)),
                     LocalMutationIsAllowed::No,
                 );
             }
diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
index 6ab09f731c0..2ba72180d66 100644
--- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
+++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
@@ -105,22 +105,14 @@ fn propagate_loans_between_points(
         });
     }
 
-    let Some(current_live_regions) = live_regions.row(current_point) else {
-        // There are no constraints to add: there are no live regions at the current point.
-        return;
-    };
     let Some(next_live_regions) = live_regions.row(next_point) else {
         // There are no constraints to add: there are no live regions at the next point.
         return;
     };
 
     for region in next_live_regions.iter() {
-        if !current_live_regions.contains(region) {
-            continue;
-        }
-
-        // `region` is indeed live at both points, add a constraint between them, according to
-        // variance.
+        // `region` could be live at the current point, and is live at the next point: add a
+        // constraint between them, according to variance.
         if let Some(&direction) = live_region_variances.get(&region) {
             add_liveness_constraint(
                 region,
diff --git a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
index 5cd265e0db9..bdc3047e5ba 100644
--- a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
+++ b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
@@ -1,27 +1,18 @@
-use std::collections::{BTreeMap, BTreeSet};
-
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-use rustc_middle::mir::visit::Visitor;
-use rustc_middle::mir::{
-    Body, Local, Location, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
-};
-use rustc_middle::ty::{RegionVid, TyCtxt};
+use rustc_middle::ty::RegionVid;
 use rustc_mir_dataflow::points::PointIndex;
 
 use super::{LiveLoans, LocalizedOutlivesConstraintSet};
+use crate::BorrowSet;
 use crate::constraints::OutlivesConstraint;
-use crate::dataflow::BorrowIndex;
 use crate::region_infer::values::LivenessValues;
 use crate::type_check::Locations;
-use crate::{BorrowSet, PlaceConflictBias, places_conflict};
 
-/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by
+/// Compute loan reachability to approximately trace loan liveness throughout the CFG, by
 /// traversing the full graph of constraints that combines:
 /// - the localized constraints (the physical edges),
 /// - with the constraints that hold at all points (the logical edges).
 pub(super) fn compute_loan_liveness<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    body: &Body<'tcx>,
     liveness: &LivenessValues,
     outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
     borrow_set: &BorrowSet<'tcx>,
@@ -29,11 +20,6 @@ pub(super) fn compute_loan_liveness<'tcx>(
 ) -> LiveLoans {
     let mut live_loans = LiveLoans::new(borrow_set.len());
 
-    // FIXME: it may be preferable for kills to be encoded in the edges themselves, to simplify and
-    // likely make traversal (and constraint generation) more efficient. We also display kills on
-    // edges when visualizing the constraint graph anyways.
-    let kills = collect_kills(body, tcx, borrow_set);
-
     // Create the full graph with the physical edges we've localized earlier, and the logical edges
     // of constraints that hold at all points.
     let logical_constraints =
@@ -59,15 +45,15 @@ pub(super) fn compute_loan_liveness<'tcx>(
                 continue;
             }
 
-            // Record the loan as being live on entry to this point.
-            live_loans.insert(node.point, loan_idx);
-
-            // Here, we have a conundrum. There's currently a weakness in our theory, in that
-            // we're using a single notion of reachability to represent what used to be _two_
-            // different transitive closures. It didn't seem impactful when coming up with the
-            // single-graph and reachability through space (regions) + time (CFG) concepts, but in
-            // practice the combination of time-traveling with kills is more impactful than
-            // initially anticipated.
+            // Record the loan as being live on entry to this point if it reaches a live region
+            // there.
+            //
+            // This is an approximation of liveness (which is the thing we want), in that we're
+            // using a single notion of reachability to represent what used to be _two_ different
+            // transitive closures. It didn't seem impactful when coming up with the single-graph
+            // and reachability through space (regions) + time (CFG) concepts, but in practice the
+            // combination of time-traveling with kills is more impactful than initially
+            // anticipated.
             //
             // Kills should prevent a loan from reaching its successor points in the CFG, but not
             // while time-traveling: we're not actually at that CFG point, but looking for
@@ -92,40 +78,20 @@ pub(super) fn compute_loan_liveness<'tcx>(
             //   two-step traversal described above: only kills encountered on exit via a backward
             //   edge are ignored.
             //
-            // In our test suite, there are a couple of cases where kills are encountered while
-            // time-traveling, however as far as we can tell, always in cases where they would be
-            // unreachable. We have reason to believe that this is a property of the single-graph
-            // approach (but haven't proved it yet):
-            // - reachable kills while time-traveling would also be encountered via regular
-            //   traversal
-            // - it makes _some_ sense to ignore unreachable kills, but subtleties around dead code
-            //   in general need to be better thought through (like they were for NLLs).
-            // - ignoring kills is a conservative approximation: the loan is still live and could
-            //   cause false positive errors at another place access. Soundness issues in this
-            //   domain should look more like the absence of reachability instead.
-            //
-            // This is enough in practice to pass tests, and therefore is what we have implemented
-            // for now.
+            // This version of the analysis, however, is enough in practice to pass the tests that
+            // we care about and NLLs reject, without regressions on crater, and is an actionable
+            // subset of the full analysis. It also naturally points to areas of improvement that we
+            // wish to explore later, namely handling kills appropriately during traversal, instead
+            // of continuing traversal to all the reachable nodes.
             //
-            // FIXME: all of the above. Analyze potential unsoundness, possibly in concert with a
-            // borrowck implementation in a-mir-formality, fuzzing, or manually crafting
-            // counter-examples.
+            // FIXME: analyze potential unsoundness, possibly in concert with a borrowck
+            // implementation in a-mir-formality, fuzzing, or manually crafting counter-examples.
 
-            // Continuing traversal will depend on whether the loan is killed at this point, and
-            // whether we're time-traveling.
-            let current_location = liveness.location_from_point(node.point);
-            let is_loan_killed =
-                kills.get(&current_location).is_some_and(|kills| kills.contains(&loan_idx));
+            if liveness.is_live_at(node.region, liveness.location_from_point(node.point)) {
+                live_loans.insert(node.point, loan_idx);
+            }
 
             for succ in graph.outgoing_edges(node) {
-                // If the loan is killed at this point, it is killed _on exit_. But only during
-                // forward traversal.
-                if is_loan_killed {
-                    let destination = liveness.location_from_point(succ.point);
-                    if current_location.is_predecessor_of(destination, body) {
-                        continue;
-                    }
-                }
                 stack.push(succ);
             }
         }
@@ -192,116 +158,3 @@ impl LocalizedConstraintGraph {
         physical_edges.chain(materialized_edges)
     }
 }
-
-/// Traverses the MIR and collects kills.
-fn collect_kills<'tcx>(
-    body: &Body<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    borrow_set: &BorrowSet<'tcx>,
-) -> BTreeMap<Location, BTreeSet<BorrowIndex>> {
-    let mut collector = KillsCollector { borrow_set, tcx, body, kills: BTreeMap::default() };
-    for (block, data) in body.basic_blocks.iter_enumerated() {
-        collector.visit_basic_block_data(block, data);
-    }
-    collector.kills
-}
-
-struct KillsCollector<'a, 'tcx> {
-    body: &'a Body<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    borrow_set: &'a BorrowSet<'tcx>,
-
-    /// The set of loans killed at each location.
-    kills: BTreeMap<Location, BTreeSet<BorrowIndex>>,
-}
-
-// This visitor has a similar structure to the `Borrows` dataflow computation with respect to kills,
-// and the datalog polonius fact generation for the `loan_killed_at` relation.
-impl<'tcx> KillsCollector<'_, 'tcx> {
-    /// Records the borrows on the specified place as `killed`. For example, when assigning to a
-    /// local, or on a call's return destination.
-    fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) {
-        // For the reasons described in graph traversal, we also filter out kills
-        // unreachable from the loan's introduction point, as they would stop traversal when
-        // e.g. checking for reachability in the subset graph through invariance constraints
-        // higher up.
-        let filter_unreachable_kills = |loan| {
-            let introduction = self.borrow_set[loan].reserve_location;
-            let reachable = introduction.is_predecessor_of(location, self.body);
-            reachable
-        };
-
-        let other_borrows_of_local = self
-            .borrow_set
-            .local_map
-            .get(&place.local)
-            .into_iter()
-            .flat_map(|bs| bs.iter())
-            .copied();
-
-        // If the borrowed place is a local with no projections, all other borrows of this
-        // local must conflict. This is purely an optimization so we don't have to call
-        // `places_conflict` for every borrow.
-        if place.projection.is_empty() {
-            if !self.body.local_decls[place.local].is_ref_to_static() {
-                self.kills
-                    .entry(location)
-                    .or_default()
-                    .extend(other_borrows_of_local.filter(|&loan| filter_unreachable_kills(loan)));
-            }
-            return;
-        }
-
-        // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
-        // pair of array indices are not equal, so that when `places_conflict` returns true, we
-        // will be assured that two places being compared definitely denotes the same sets of
-        // locations.
-        let definitely_conflicting_borrows = other_borrows_of_local
-            .filter(|&i| {
-                places_conflict(
-                    self.tcx,
-                    self.body,
-                    self.borrow_set[i].borrowed_place,
-                    place,
-                    PlaceConflictBias::NoOverlap,
-                )
-            })
-            .filter(|&loan| filter_unreachable_kills(loan));
-
-        self.kills.entry(location).or_default().extend(definitely_conflicting_borrows);
-    }
-
-    /// Records the borrows on the specified local as `killed`.
-    fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) {
-        if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
-            self.kills.entry(location).or_default().extend(borrow_indices.iter());
-        }
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for KillsCollector<'_, 'tcx> {
-    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
-        // Make sure there are no remaining borrows for locals that have gone out of scope.
-        if let StatementKind::StorageDead(local) = statement.kind {
-            self.record_killed_borrows_for_local(local, location);
-        }
-
-        self.super_statement(statement, location);
-    }
-
-    fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
-        // When we see `X = ...`, then kill borrows of `(*X).foo` and so forth.
-        self.record_killed_borrows_for_place(*place, location);
-        self.super_assign(place, rvalue, location);
-    }
-
-    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
-        // A `Call` terminator's return value can be a local which has borrows, so we need to record
-        // those as killed as well.
-        if let TerminatorKind::Call { destination, .. } = terminator.kind {
-            self.record_killed_borrows_for_place(destination, location);
-        }
-
-        self.super_terminator(terminator, location);
-    }
-}
diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs
index 142ef8ba28e..a9092b1981e 100644
--- a/compiler/rustc_borrowck/src/polonius/mod.rs
+++ b/compiler/rustc_borrowck/src/polonius/mod.rs
@@ -146,8 +146,8 @@ impl PoloniusContext {
     /// - converting NLL typeck constraints to be localized
     /// - encoding liveness constraints
     ///
-    /// Then, this graph is traversed, and combined with kills, reachability is recorded as loan
-    /// liveness, to be used by the loan scope and active loans computations.
+    /// Then, this graph is traversed, reachability is recorded as loan liveness, to be used by the
+    /// loan scope and active loans computations.
     ///
     /// The constraint data will be used to compute errors and diagnostics.
     pub(crate) fn compute_loan_liveness<'tcx>(
@@ -182,8 +182,6 @@ impl PoloniusContext {
         // Now that we have a complete graph, we can compute reachability to trace the liveness of
         // loans for the next step in the chain, the NLL loan scope and active loans computations.
         let live_loans = compute_loan_liveness(
-            tcx,
-            body,
             regioncx.liveness_constraints(),
             regioncx.outlives_constraints(),
             borrow_set,
diff --git a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
index 1289b1899eb..e4e52962bf7 100644
--- a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
+++ b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
@@ -47,9 +47,7 @@ pub(super) fn convert_typeck_constraints<'tcx>(
                         tcx,
                         body,
                         stmt,
-                        liveness,
                         &outlives_constraint,
-                        location,
                         point,
                         universal_regions,
                     )
@@ -78,9 +76,7 @@ fn localize_statement_constraint<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
     stmt: &Statement<'tcx>,
-    liveness: &LivenessValues,
     outlives_constraint: &OutlivesConstraint<'tcx>,
-    current_location: Location,
     current_point: PointIndex,
     universal_regions: &UniversalRegions<'tcx>,
 ) -> LocalizedOutlivesConstraint {
@@ -98,8 +94,8 @@ fn localize_statement_constraint<'tcx>(
             // - and that should be impossible in MIR
             //
             // When we have a more complete implementation in the future, tested with crater, etc,
-            // we can relax this to a debug assert instead, or remove it.
-            assert!(
+            // we can remove this assertion. It's a debug assert because it can be expensive.
+            debug_assert!(
                 {
                     let mut lhs_regions = FxHashSet::default();
                     tcx.for_each_free_region(lhs, |region| {
@@ -119,16 +115,8 @@ fn localize_statement_constraint<'tcx>(
                 "there should be no common regions between the LHS and RHS of an assignment"
             );
 
-            // As mentioned earlier, we should be tracking these better upstream but: we want to
-            // relate the types on entry to the type of the place on exit. That is, outlives
-            // constraints on the RHS are on entry, and outlives constraints to/from the LHS are on
-            // exit (i.e. on entry to the successor location).
             let lhs_ty = body.local_decls[lhs.local].ty;
-            let successor_location = Location {
-                block: current_location.block,
-                statement_index: current_location.statement_index + 1,
-            };
-            let successor_point = liveness.point_from_location(successor_location);
+            let successor_point = current_point;
             compute_constraint_direction(
                 tcx,
                 outlives_constraint,
@@ -195,6 +183,7 @@ fn localize_terminator_constraint<'tcx>(
         }
     }
 }
+
 /// For a given outlives constraint and CFG edge, returns the localized constraint with the
 /// appropriate `from`-`to` direction. This is computed according to whether the constraint flows to
 /// or from a free region in the given `value`, some kind of result for an effectful operation, like
diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs
index a3e29982e90..526e1850c2e 100644
--- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs
+++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs
@@ -12,9 +12,13 @@ use rustc_middle::ty::UniverseIndex;
 use super::*;
 
 fn render_outlives_constraint(constraint: &OutlivesConstraint<'_>) -> String {
-    match constraint.locations {
-        Locations::All(_) => "All(...)".to_string(),
-        Locations::Single(loc) => format!("{loc:?}"),
+    if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = constraint.category {
+        format!("{unnameable:?} unnameable")
+    } else {
+        match constraint.locations {
+            Locations::All(_) => "All(...)".to_string(),
+            Locations::Single(loc) => format!("{loc:?}"),
+        }
     }
 }
 
@@ -41,7 +45,22 @@ fn render_region_vid<'tcx>(
         "".to_string()
     };
 
-    format!("{:?}{universe_str}{external_name_str}", rvid)
+    let extra_info = match regioncx.region_definition(rvid).origin {
+        NllRegionVariableOrigin::FreeRegion => "".to_string(),
+        NllRegionVariableOrigin::Placeholder(p) => match p.bound.kind {
+            ty::BoundRegionKind::Named(def_id) => {
+                format!(" (for<{}>)", tcx.item_name(def_id))
+            }
+            ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => " (for<'_>)".to_string(),
+            ty::BoundRegionKind::NamedAnon(_) => {
+                bug!("only used for pretty printing")
+            }
+        },
+        NllRegionVariableOrigin::Existential { name: Some(name), .. } => format!(" (ex<{name}>)"),
+        NllRegionVariableOrigin::Existential { .. } => format!(" (ex<'_>)"),
+    };
+
+    format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid)
 }
 
 impl<'tcx> RegionInferenceContext<'tcx> {
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 5f1b655c6b6..5f4bfd9df48 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1,11 +1,9 @@
-use std::cell::OnceCell;
 use std::collections::VecDeque;
 use std::rc::Rc;
 
-use rustc_data_structures::binary_search_util;
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
-use rustc_data_structures::graph::scc::{self, Sccs};
+use rustc_data_structures::graph::scc::Sccs;
 use rustc_errors::Diag;
 use rustc_hir::def_id::CRATE_DEF_ID;
 use rustc_index::IndexVec;
@@ -24,15 +22,13 @@ use rustc_span::hygiene::DesugaringKind;
 use rustc_span::{DUMMY_SP, Span};
 use tracing::{Level, debug, enabled, instrument, trace};
 
-use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
+use crate::constraints::graph::NormalConstraintGraph;
 use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
 use crate::dataflow::BorrowIndex;
 use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
 use crate::handle_placeholders::{LoweredConstraints, RegionTracker};
-use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
 use crate::polonius::LiveLoans;
 use crate::polonius::legacy::PoloniusOutput;
-use crate::region_infer::reverse_sccs::ReverseSccGraph;
 use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
 use crate::type_check::Locations;
 use crate::type_check::free_region_relations::UniversalRegionRelations;
@@ -44,7 +40,7 @@ use crate::{
 
 mod dump_mir;
 mod graphviz;
-mod opaque_types;
+pub(crate) mod opaque_types;
 mod reverse_sccs;
 
 pub(crate) mod values;
@@ -78,18 +74,6 @@ impl Representative {
     }
 }
 
-impl scc::Annotation for Representative {
-    fn merge_scc(self, other: Self) -> Self {
-        // Just pick the smallest one. Note that we order by tag first!
-        std::cmp::min(self, other)
-    }
-
-    // For reachability, we do nothing since the representative doesn't change.
-    fn merge_reached(self, _other: Self) -> Self {
-        self
-    }
-}
-
 pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex>;
 
 pub struct RegionInferenceContext<'tcx> {
@@ -120,20 +104,6 @@ pub struct RegionInferenceContext<'tcx> {
 
     scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
 
-    /// Reverse of the SCC constraint graph --  i.e., an edge `A -> B` exists if
-    /// `B: A`. This is used to compute the universal regions that are required
-    /// to outlive a given SCC.
-    rev_scc_graph: OnceCell<ReverseSccGraph>,
-
-    /// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
-    member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
-
-    /// Records the member constraints that we applied to each scc.
-    /// This is useful for error reporting. Once constraint
-    /// propagation is done, this vector is sorted according to
-    /// `member_region_scc`.
-    member_constraints_applied: Vec<AppliedMemberConstraint>,
-
     /// Map universe indexes to information on why we created it.
     universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
 
@@ -150,32 +120,6 @@ pub struct RegionInferenceContext<'tcx> {
     universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
 }
 
-/// Each time that `apply_member_constraint` is successful, it appends
-/// one of these structs to the `member_constraints_applied` field.
-/// This is used in error reporting to trace out what happened.
-///
-/// The way that `apply_member_constraint` works is that it effectively
-/// adds a new lower bound to the SCC it is analyzing: so you wind up
-/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
-/// minimal viable option.
-#[derive(Debug)]
-pub(crate) struct AppliedMemberConstraint {
-    /// The SCC that was affected. (The "member region".)
-    ///
-    /// The vector if `AppliedMemberConstraint` elements is kept sorted
-    /// by this field.
-    pub(crate) member_region_scc: ConstraintSccIndex,
-
-    /// The "best option" that `apply_member_constraint` found -- this was
-    /// added as an "ad-hoc" lower-bound to `member_region_scc`.
-    pub(crate) min_choice: ty::RegionVid,
-
-    /// The "member constraint index" -- we can find out details about
-    /// the constraint from
-    /// `set.member_constraints[member_constraint_index]`.
-    pub(crate) member_constraint_index: NllMemberConstraintIndex,
-}
-
 #[derive(Debug)]
 pub(crate) struct RegionDefinition<'tcx> {
     /// What kind of variable is this -- a free region? existential
@@ -268,7 +212,6 @@ enum Trace<'a, 'tcx> {
     StartRegion,
     FromGraph(&'a OutlivesConstraint<'tcx>),
     FromStatic(RegionVid),
-    FromMember(RegionVid, RegionVid, Span),
     NotVisited,
 }
 
@@ -363,7 +306,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             liveness_constraints,
             universe_causes,
             placeholder_indices,
-            member_constraints,
         } = lowered_constraints;
 
         debug!("universal_regions: {:#?}", universal_region_relations.universal_regions);
@@ -385,9 +327,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             scc_values.merge_liveness(scc, region, &liveness_constraints);
         }
 
-        let member_constraints =
-            Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r)));
-
         let mut result = Self {
             definitions,
             liveness_constraints,
@@ -395,9 +334,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             constraint_graph,
             constraint_sccs,
             scc_annotations,
-            rev_scc_graph: OnceCell::new(),
-            member_constraints,
-            member_constraints_applied: Vec::new(),
             universe_causes,
             scc_values,
             type_tests,
@@ -417,7 +353,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// minimum values.
     ///
     /// For example:
-    /// ```
+    /// ```ignore (illustrative)
     /// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ }
     /// ```
     /// would initialize two variables like so:
@@ -550,19 +486,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.scc_values.placeholders_contained_in(scc)
     }
 
-    /// Once region solving has completed, this function will return the member constraints that
-    /// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
-    pub(crate) fn applied_member_constraints(
-        &self,
-        scc: ConstraintSccIndex,
-    ) -> &[AppliedMemberConstraint] {
-        binary_search_util::binary_search_slice(
-            &self.member_constraints_applied,
-            |applied| applied.member_region_scc,
-            &scc,
-        )
-    }
-
     /// Performs region inference and report errors if we see any
     /// unsatisfiable constraints. If this is a closure, returns the
     /// region requirements to propagate to our creator, if any.
@@ -607,12 +530,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         debug!(?errors_buffer);
 
-        if errors_buffer.is_empty() {
-            self.check_member_constraints(infcx, &mut errors_buffer);
-        }
-
-        debug!(?errors_buffer);
-
         let outlives_requirements = outlives_requirements.unwrap_or_default();
 
         if outlives_requirements.is_empty() {
@@ -642,154 +559,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         });
 
         // To propagate constraints, we walk the DAG induced by the
-        // SCC. For each SCC, we visit its successors and compute
+        // SCC. For each SCC `A`, we visit its successors and compute
         // their values, then we union all those values to get our
         // own.
-        for scc in self.constraint_sccs.all_sccs() {
-            self.compute_value_for_scc(scc);
-        }
-
-        // Sort the applied member constraints so we can binary search
-        // through them later.
-        self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc);
-    }
-
-    /// Computes the value of the SCC `scc_a`, which has not yet been
-    /// computed, by unioning the values of its successors.
-    /// Assumes that all successors have been computed already
-    /// (which is assured by iterating over SCCs in dependency order).
-    #[instrument(skip(self), level = "debug")]
-    fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
-        // Walk each SCC `B` such that `A: B`...
-        for &scc_b in self.constraint_sccs.successors(scc_a) {
-            debug!(?scc_b);
-            self.scc_values.add_region(scc_a, scc_b);
-        }
-
-        // Now take member constraints into account.
-        let member_constraints = Rc::clone(&self.member_constraints);
-        for m_c_i in member_constraints.indices(scc_a) {
-            self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i));
-        }
-
-        debug!(value = ?self.scc_values.region_value_str(scc_a));
-    }
-
-    /// Invoked for each `R0 member of [R1..Rn]` constraint.
-    ///
-    /// `scc` is the SCC containing R0, and `choice_regions` are the
-    /// `R1..Rn` regions -- they are always known to be universal
-    /// regions (and if that's not true, we just don't attempt to
-    /// enforce the constraint).
-    ///
-    /// The current value of `scc` at the time the method is invoked
-    /// is considered a *lower bound*. If possible, we will modify
-    /// the constraint to set it equal to one of the option regions.
-    /// If we make any changes, returns true, else false.
-    ///
-    /// This function only adds the member constraints to the region graph,
-    /// it does not check them. They are later checked in
-    /// `check_member_constraints` after the region graph has been computed.
-    #[instrument(skip(self, member_constraint_index), level = "debug")]
-    fn apply_member_constraint(
-        &mut self,
-        scc: ConstraintSccIndex,
-        member_constraint_index: NllMemberConstraintIndex,
-        choice_regions: &[ty::RegionVid],
-    ) {
-        // Create a mutable vector of the options. We'll try to winnow
-        // them down.
-        let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
-
-        // Convert to the SCC representative: sometimes we have inference
-        // variables in the member constraint that wind up equated with
-        // universal regions. The scc representative is the minimal numbered
-        // one from the corresponding scc so it will be the universal region
-        // if one exists.
-        for c_r in &mut choice_regions {
-            let scc = self.constraint_sccs.scc(*c_r);
-            *c_r = self.scc_representative(scc);
-        }
-
-        // If the member region lives in a higher universe, we currently choose
-        // the most conservative option by leaving it unchanged.
-        if !self.max_nameable_universe(scc).is_root() {
-            return;
-        }
-
-        // The existing value for `scc` is a lower-bound. This will
-        // consist of some set `{P} + {LB}` of points `{P}` and
-        // lower-bound free regions `{LB}`. As each choice region `O`
-        // is a free region, it will outlive the points. But we can
-        // only consider the option `O` if `O: LB`.
-        choice_regions.retain(|&o_r| {
-            self.scc_values
-                .universal_regions_outlived_by(scc)
-                .all(|lb| self.universal_region_relations.outlives(o_r, lb))
-        });
-        debug!(?choice_regions, "after lb");
-
-        // Now find all the *upper bounds* -- that is, each UB is a
-        // free region that must outlive the member region `R0` (`UB:
-        // R0`). Therefore, we need only keep an option `O` if `UB: O`
-        // for all UB.
-        let universal_region_relations = &self.universal_region_relations;
-        for ub in self.reverse_scc_graph().upper_bounds(scc) {
-            debug!(?ub);
-            choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
-        }
-        debug!(?choice_regions, "after ub");
-
-        // At this point we can pick any member of `choice_regions` and would like to choose
-        // it to be a small as possible. To avoid potential non-determinism we will pick the
-        // smallest such choice.
-        //
-        // Because universal regions are only partially ordered (i.e, not every two regions are
-        // comparable), we will ignore any region that doesn't compare to all others when picking
-        // the minimum choice.
-        //
-        // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
-        // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
-        // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
-        let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
-            choice_regions.iter().all(|&r2| {
-                self.universal_region_relations.outlives(r1, r2)
-                    || self.universal_region_relations.outlives(r2, r1)
-            })
-        });
-        // Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
-        let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
-            let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
-            let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
-            match (r1_outlives_r2, r2_outlives_r1) {
-                (true, true) => r1.min(r2),
-                (true, false) => r2,
-                (false, true) => r1,
-                (false, false) => bug!("incomparable regions in total order"),
+        for scc_a in self.constraint_sccs.all_sccs() {
+            // Walk each SCC `B` such that `A: B`...
+            for &scc_b in self.constraint_sccs.successors(scc_a) {
+                debug!(?scc_b);
+                self.scc_values.add_region(scc_a, scc_b);
             }
-        }) else {
-            debug!("no unique minimum choice");
-            return;
-        };
-
-        // As we require `'scc: 'min_choice`, we have definitely already computed
-        // its `scc_values` at this point.
-        let min_choice_scc = self.constraint_sccs.scc(min_choice);
-        debug!(?min_choice, ?min_choice_scc);
-        if self.scc_values.add_region(scc, min_choice_scc) {
-            self.member_constraints_applied.push(AppliedMemberConstraint {
-                member_region_scc: scc,
-                min_choice,
-                member_constraint_index,
-            });
         }
     }
 
-    /// Returns `true` if all the elements in the value of `scc_b` are nameable
+    /// Returns `true` if all the placeholders in the value of `scc_b` are nameable
     /// in `scc_a`. Used during constraint propagation, and only once
     /// the value of `scc_b` has been computed.
-    fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
-        self.scc_annotations[scc_a].universe_compatible_with(self.scc_annotations[scc_b])
+    fn can_name_all_placeholders(
+        &self,
+        scc_a: ConstraintSccIndex,
+        scc_b: ConstraintSccIndex,
+    ) -> bool {
+        self.scc_annotations[scc_a].can_name_all_placeholders(self.scc_annotations[scc_b])
     }
 
     /// Once regions have been propagated, this method is used to see
@@ -822,14 +612,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 continue;
             }
 
-            if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements {
-                if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) {
-                    continue;
-                }
+            if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements
+                && self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements)
+            {
+                continue;
             }
 
             // Type-test failed. Report the error.
-            let erased_generic_kind = infcx.tcx.erase_regions(type_test.generic_kind);
+            let erased_generic_kind = infcx.tcx.erase_and_anonymize_regions(type_test.generic_kind);
 
             // Skip duplicate-ish errors.
             if deduplicate_errors.insert((
@@ -1178,16 +968,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             return true;
         }
 
+        let fr_static = self.universal_regions().fr_static;
+
         // If we are checking that `'sup: 'sub`, and `'sub` contains
         // some placeholder that `'sup` cannot name, then this is only
         // true if `'sup` outlives static.
-        if !self.universe_compatible(sub_region_scc, sup_region_scc) {
+        //
+        // Avoid infinite recursion if `sub_region` is already `'static`
+        if sub_region != fr_static
+            && !self.can_name_all_placeholders(sup_region_scc, sub_region_scc)
+        {
             debug!(
                 "sub universe `{sub_region_scc:?}` is not nameable \
                 by super `{sup_region_scc:?}`, promoting to static",
             );
 
-            return self.eval_outlives(sup_region, self.universal_regions().fr_static);
+            return self.eval_outlives(sup_region, fr_static);
         }
 
         // Both the `sub_region` and `sup_region` consist of the union
@@ -1479,40 +1275,34 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         shorter_fr: RegionVid,
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
     ) -> RegionRelationCheckResult {
-        if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
+        if let Some(propagated_outlives_requirements) = propagated_outlives_requirements
             // Shrink `longer_fr` until we find a non-local region (if we do).
             // We'll call it `fr-` -- it's ever so slightly smaller than
             // `longer_fr`.
-            if let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr)
-            {
-                debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus);
-
-                let blame_span_category = self.find_outlives_blame_span(
-                    longer_fr,
-                    NllRegionVariableOrigin::FreeRegion,
-                    shorter_fr,
-                );
-
-                // Grow `shorter_fr` until we find some non-local regions. (We
-                // always will.)  We'll call them `shorter_fr+` -- they're ever
-                // so slightly larger than `shorter_fr`.
-                let shorter_fr_plus =
-                    self.universal_region_relations.non_local_upper_bounds(shorter_fr);
-                debug!(
-                    "try_propagate_universal_region_error: shorter_fr_plus={:?}",
-                    shorter_fr_plus
-                );
-                for fr in shorter_fr_plus {
-                    // Push the constraint `fr-: shorter_fr+`
-                    propagated_outlives_requirements.push(ClosureOutlivesRequirement {
-                        subject: ClosureOutlivesSubject::Region(fr_minus),
-                        outlived_free_region: fr,
-                        blame_span: blame_span_category.1.span,
-                        category: blame_span_category.0,
-                    });
-                }
-                return RegionRelationCheckResult::Propagated;
+            && let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr)
+        {
+            debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus);
+
+            let blame_constraint = self
+                .best_blame_constraint(longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr)
+                .0;
+
+            // Grow `shorter_fr` until we find some non-local regions. (We
+            // always will.)  We'll call them `shorter_fr+` -- they're ever
+            // so slightly larger than `shorter_fr`.
+            let shorter_fr_plus =
+                self.universal_region_relations.non_local_upper_bounds(shorter_fr);
+            debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus);
+            for fr in shorter_fr_plus {
+                // Push the constraint `fr-: shorter_fr+`
+                propagated_outlives_requirements.push(ClosureOutlivesRequirement {
+                    subject: ClosureOutlivesSubject::Region(fr_minus),
+                    outlived_free_region: fr,
+                    blame_span: blame_constraint.cause.span,
+                    category: blame_constraint.category,
+                });
             }
+            return RegionRelationCheckResult::Propagated;
         }
 
         RegionRelationCheckResult::Error
@@ -1548,118 +1338,67 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         }
     }
 
-    #[instrument(level = "debug", skip(self, infcx, errors_buffer))]
-    fn check_member_constraints(
-        &self,
-        infcx: &InferCtxt<'tcx>,
-        errors_buffer: &mut RegionErrors<'tcx>,
-    ) {
-        let member_constraints = Rc::clone(&self.member_constraints);
-        for m_c_i in member_constraints.all_indices() {
-            debug!(?m_c_i);
-            let m_c = &member_constraints[m_c_i];
-            let member_region_vid = m_c.member_region_vid;
-            debug!(
-                ?member_region_vid,
-                value = ?self.region_value_str(member_region_vid),
-            );
-            let choice_regions = member_constraints.choice_regions(m_c_i);
-            debug!(?choice_regions);
-
-            // Did the member region wind up equal to any of the option regions?
-            if let Some(o) =
-                choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid))
-            {
-                debug!("evaluated as equal to {:?}", o);
-                continue;
-            }
-
-            // If not, report an error.
-            let member_region = ty::Region::new_var(infcx.tcx, member_region_vid);
-            errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion {
-                span: m_c.definition_span,
-                hidden_ty: m_c.hidden_ty,
-                key: m_c.key,
-                member_region,
-            });
-        }
-    }
-
-    /// We have a constraint `fr1: fr2` that is not satisfied, where
-    /// `fr2` represents some universal region. Here, `r` is some
-    /// region where we know that `fr1: r` and this function has the
-    /// job of determining whether `r` is "to blame" for the fact that
-    /// `fr1: fr2` is required.
-    ///
-    /// This is true under two conditions:
-    ///
-    /// - `r == fr2`
-    /// - `fr2` is `'static` and `r` is some placeholder in a universe
-    ///   that cannot be named by `fr1`; in that case, we will require
-    ///   that `fr1: 'static` because it is the only way to `fr1: r` to
-    ///   be satisfied. (See `add_incompatible_universe`.)
-    pub(crate) fn provides_universal_region(
+    pub(crate) fn constraint_path_between_regions(
         &self,
-        r: RegionVid,
-        fr1: RegionVid,
-        fr2: RegionVid,
-    ) -> bool {
-        debug!("provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", r, fr1, fr2);
-        let result = {
-            r == fr2 || {
-                fr2 == self.universal_regions().fr_static && self.cannot_name_placeholder(fr1, r)
-            }
-        };
-        debug!("provides_universal_region: result = {:?}", result);
-        result
-    }
-
-    /// If `r2` represents a placeholder region, then this returns
-    /// `true` if `r1` cannot name that placeholder in its
-    /// value; otherwise, returns `false`.
-    pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
-        match self.definitions[r2].origin {
-            NllRegionVariableOrigin::Placeholder(placeholder) => {
-                let r1_universe = self.definitions[r1].universe;
-                debug!(
-                    "cannot_name_value_of: universe1={r1_universe:?} placeholder={:?}",
-                    placeholder
-                );
-                r1_universe.cannot_name(placeholder.universe)
-            }
-
-            NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => {
-                false
-            }
+        from_region: RegionVid,
+        to_region: RegionVid,
+    ) -> Option<Vec<OutlivesConstraint<'tcx>>> {
+        if from_region == to_region {
+            bug!("Tried to find a path between {from_region:?} and itself!");
         }
-    }
-
-    /// Finds a good `ObligationCause` to blame for the fact that `fr1` outlives `fr2`.
-    pub(crate) fn find_outlives_blame_span(
-        &self,
-        fr1: RegionVid,
-        fr1_origin: NllRegionVariableOrigin,
-        fr2: RegionVid,
-    ) -> (ConstraintCategory<'tcx>, ObligationCause<'tcx>) {
-        let BlameConstraint { category, cause, .. } = self
-            .best_blame_constraint(fr1, fr1_origin, |r| self.provides_universal_region(r, fr1, fr2))
-            .0;
-        (category, cause)
+        self.constraint_path_to(from_region, |to| to == to_region, true).map(|o| o.0)
     }
 
     /// Walks the graph of constraints (where `'a: 'b` is considered
-    /// an edge `'a -> 'b`) to find all paths from `from_region` to
-    /// `to_region`. The paths are accumulated into the vector
-    /// `results`. The paths are stored as a series of
-    /// `ConstraintIndex` values -- in other words, a list of *edges*.
+    /// an edge `'a -> 'b`) to find a path from `from_region` to
+    /// `to_region`.
     ///
     /// Returns: a series of constraints as well as the region `R`
     /// that passed the target test.
+    /// If `include_static_outlives_all` is `true`, then the synthetic
+    /// outlives constraints `'static -> a` for every region `a` are
+    /// considered in the search, otherwise they are ignored.
     #[instrument(skip(self, target_test), ret)]
-    pub(crate) fn find_constraint_paths_between_regions(
+    pub(crate) fn constraint_path_to(
         &self,
         from_region: RegionVid,
         target_test: impl Fn(RegionVid) -> bool,
+        include_placeholder_static: bool,
+    ) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
+        self.find_constraint_path_between_regions_inner(
+            true,
+            from_region,
+            &target_test,
+            include_placeholder_static,
+        )
+        .or_else(|| {
+            self.find_constraint_path_between_regions_inner(
+                false,
+                from_region,
+                &target_test,
+                include_placeholder_static,
+            )
+        })
+    }
+
+    /// The constraints we get from equating the hidden type of each use of an opaque
+    /// with its final concrete type may end up getting preferred over other, potentially
+    /// longer constraint paths.
+    ///
+    /// Given that we compute the final concrete type by relying on this existing constraint
+    /// path, this can easily end up hiding the actual reason for why we require these regions
+    /// to be equal.
+    ///
+    /// To handle this, we first look at the path while ignoring these constraints and then
+    /// retry while considering them. This is not perfect, as the `from_region` may have already
+    /// been partially related to its argument region, so while we rely on a member constraint
+    /// to get a complete path, the most relevant step of that path already existed before then.
+    fn find_constraint_path_between_regions_inner(
+        &self,
+        ignore_opaque_type_constraints: bool,
+        from_region: RegionVid,
+        target_test: impl Fn(RegionVid) -> bool,
+        include_placeholder_static: bool,
     ) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
         let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
         context[from_region] = Trace::StartRegion;
@@ -1674,7 +1413,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         while let Some(r) = deque.pop_front() {
             debug!(
-                "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
+                "constraint_path_to: from_region={:?} r={:?} value={}",
                 from_region,
                 r,
                 self.region_value_str(r),
@@ -1708,20 +1447,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                             result.push(c);
                         }
 
-                        Trace::FromMember(sup, sub, span) => {
-                            let c = OutlivesConstraint {
-                                sup,
-                                sub,
-                                locations: Locations::All(span),
-                                span,
-                                category: ConstraintCategory::OpaqueType,
-                                variance_info: ty::VarianceDiagInfo::default(),
-                                from_closure: false,
-                            };
-                            p = c.sup;
-                            result.push(c);
-                        }
-
                         Trace::StartRegion => {
                             result.reverse();
                             return Some((result, r));
@@ -1760,23 +1485,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints);
                 // This loop can be hot.
                 for constraint in edges {
-                    if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
-                        debug!("Ignoring illegal universe constraint: {constraint:?}");
-                        continue;
+                    match constraint.category {
+                        ConstraintCategory::OutlivesUnnameablePlaceholder(_)
+                            if !include_placeholder_static =>
+                        {
+                            debug!("Ignoring illegal placeholder constraint: {constraint:?}");
+                            continue;
+                        }
+                        ConstraintCategory::OpaqueType if ignore_opaque_type_constraints => {
+                            debug!("Ignoring member constraint: {constraint:?}");
+                            continue;
+                        }
+                        _ => {}
                     }
+
                     debug_assert_eq!(constraint.sup, r);
                     handle_trace(constraint.sub, Trace::FromGraph(constraint));
                 }
             }
-
-            // Member constraints can also give rise to `'r: 'x` edges that
-            // were not part of the graph initially, so watch out for those.
-            // (But they are extremely rare; this loop is very cold.)
-            for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
-                let sub = constraint.min_choice;
-                let p_c = &self.member_constraints[constraint.member_constraint_index];
-                handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span));
-            }
         }
 
         None
@@ -1787,37 +1513,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
         trace!(scc = ?self.constraint_sccs.scc(fr1));
         trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1)));
-        self.find_constraint_paths_between_regions(fr1, |r| {
-            // First look for some `r` such that `fr1: r` and `r` is live at `location`
+        self.constraint_path_to(fr1, |r| {
             trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
             self.liveness_constraints.is_live_at(r, location)
-        })
-        .or_else(|| {
-            // If we fail to find that, we may find some `r` such that
-            // `fr1: r` and `r` is a placeholder from some universe
-            // `fr1` cannot name. This would force `fr1` to be
-            // `'static`.
-            self.find_constraint_paths_between_regions(fr1, |r| {
-                self.cannot_name_placeholder(fr1, r)
-            })
-        })
-        .or_else(|| {
-            // If we fail to find THAT, it may be that `fr1` is a
-            // placeholder that cannot "fit" into its SCC. In that
-            // case, there should be some `r` where `fr1: r` and `fr1` is a
-            // placeholder that `r` cannot name. We can blame that
-            // edge.
-            //
-            // Remember that if `R1: R2`, then the universe of R1
-            // must be able to name the universe of R2, because R2 will
-            // be at least `'empty(Universe(R2))`, and `R1` must be at
-            // larger than that.
-            self.find_constraint_paths_between_regions(fr1, |r| {
-                self.cannot_name_placeholder(r, fr1)
-            })
-        })
-        .map(|(_path, r)| r)
-        .unwrap()
+        }, true).unwrap().1
     }
 
     /// Get the region outlived by `longer_fr` and live at `element`.
@@ -1861,22 +1560,38 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// creating a constraint path that forces `R` to outlive
     /// `from_region`, and then finding the best choices within that
     /// path to blame.
-    #[instrument(level = "debug", skip(self, target_test))]
+    #[instrument(level = "debug", skip(self))]
     pub(crate) fn best_blame_constraint(
         &self,
         from_region: RegionVid,
         from_region_origin: NllRegionVariableOrigin,
-        target_test: impl Fn(RegionVid) -> bool,
+        to_region: RegionVid,
     ) -> (BlameConstraint<'tcx>, Vec<OutlivesConstraint<'tcx>>) {
-        // Find all paths
-        let (path, target_region) = self
-            .find_constraint_paths_between_regions(from_region, target_test)
-            .or_else(|| {
-                self.find_constraint_paths_between_regions(from_region, |r| {
-                    self.cannot_name_placeholder(from_region, r)
-                })
-            })
-            .unwrap();
+        assert!(from_region != to_region, "Trying to blame a region for itself!");
+
+        let path = self.constraint_path_between_regions(from_region, to_region).unwrap();
+
+        // If we are passing through a constraint added because we reached an unnameable placeholder `'unnameable`,
+        // redirect search towards `'unnameable`.
+        let due_to_placeholder_outlives = path.iter().find_map(|c| {
+            if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = c.category {
+                Some(unnameable)
+            } else {
+                None
+            }
+        });
+
+        // Edge case: it's possible that `'from_region` is an unnameable placeholder.
+        let path = if let Some(unnameable) = due_to_placeholder_outlives
+            && unnameable != from_region
+        {
+            // We ignore the extra edges due to unnameable placeholders to get
+            // an explanation that was present in the original constraint graph.
+            self.constraint_path_to(from_region, |r| r == unnameable, false).unwrap().0
+        } else {
+            path
+        };
+
         debug!(
             "path={:#?}",
             path.iter()
@@ -1943,10 +1658,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         //
         // and here we prefer to blame the source (the y = x statement).
         let blame_source = match from_region_origin {
-            NllRegionVariableOrigin::FreeRegion
-            | NllRegionVariableOrigin::Existential { from_forall: false } => true,
-            NllRegionVariableOrigin::Placeholder(_)
-            | NllRegionVariableOrigin::Existential { from_forall: true } => false,
+            NllRegionVariableOrigin::FreeRegion => true,
+            NllRegionVariableOrigin::Placeholder(_) => false,
+            // `'existential: 'whatever` never results in a region error by itself.
+            // We may always infer it to `'static` afterall. This means while an error
+            // path may go through an existential, these existentials are never the
+            // `from_region`.
+            NllRegionVariableOrigin::Existential { name: _ } => {
+                unreachable!("existentials can outlive everything")
+            }
         };
 
         // To pick a constraint to blame, we organize constraints by how interesting we expect them
@@ -1979,7 +1699,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 ConstraintCategory::Cast {
                     unsize_to: Some(unsize_ty),
                     is_implicit_coercion: true,
-                } if target_region == self.universal_regions().fr_static
+                } if to_region == self.universal_regions().fr_static
                     // Mirror the note's condition, to minimize how often this diverts blame.
                     && let ty::Adt(_, args) = unsize_ty.kind()
                     && args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait()))
@@ -2017,7 +1737,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 // specific, and are not used for relations that would make sense to blame.
                 ConstraintCategory::BoringNoLocation => 6,
                 // Do not blame internal constraints.
-                ConstraintCategory::IllegalUniverse => 7,
+                ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 7,
                 ConstraintCategory::Internal => 8,
             };
 
@@ -2054,6 +1774,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             path[best_choice]
         };
 
+        assert!(
+            !matches!(
+                best_constraint.category,
+                ConstraintCategory::OutlivesUnnameablePlaceholder(_)
+            ),
+            "Illegal placeholder constraint blamed; should have redirected to other region relation"
+        );
+
         let blame_constraint = BlameConstraint {
             category: best_constraint.category,
             from_closure: best_constraint.from_closure,
@@ -2085,11 +1813,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let locations = self.scc_values.locations_outlived_by(scc);
         for location in locations {
             let bb = &body[location.block];
-            if let Some(terminator) = &bb.terminator {
+            if let Some(terminator) = &bb.terminator
                 // terminator of a loop should be TerminatorKind::FalseUnwind
-                if let TerminatorKind::FalseUnwind { .. } = terminator.kind {
-                    return Some(location);
-                }
+                && let TerminatorKind::FalseUnwind { .. } = terminator.kind
+            {
+                return Some(location);
             }
         }
         None
@@ -2103,11 +1831,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self.constraint_sccs
     }
 
-    /// Access to the region graph, built from the outlives constraints.
-    pub(crate) fn region_graph(&self) -> RegionGraph<'_, 'tcx, graph::Normal> {
-        self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static)
-    }
-
     /// Returns the representative `RegionVid` for a given SCC.
     /// See `RegionTracker` for how a region variable ID is chosen.
     ///
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
deleted file mode 100644
index 6270e6d9a60..00000000000
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ /dev/null
@@ -1,291 +0,0 @@
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
-use rustc_macros::extension;
-use rustc_middle::ty::{
-    self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
-    TypeVisitableExt, fold_regions,
-};
-use rustc_span::Span;
-use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
-use tracing::{debug, instrument};
-
-use super::RegionInferenceContext;
-use crate::BorrowCheckRootCtxt;
-use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
-use crate::universal_regions::RegionClassification;
-
-impl<'tcx> RegionInferenceContext<'tcx> {
-    /// Resolve any opaque types that were encountered while borrow checking
-    /// this item. This is then used to get the type in the `type_of` query.
-    ///
-    /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
-    /// This is lowered to give HIR something like
-    ///
-    /// type f<'a>::_Return<'_x> = impl Sized + '_x;
-    /// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x }
-    ///
-    /// When checking the return type record the type from the return and the
-    /// type used in the return value. In this case they might be `_Return<'1>`
-    /// and `&'2 i32` respectively.
-    ///
-    /// Once we to this method, we have completed region inference and want to
-    /// call `infer_opaque_definition_from_instantiation` to get the inferred
-    /// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation`
-    /// compares lifetimes directly, so we need to map the inference variables
-    /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
-    ///
-    /// First we map the regions in the generic parameters `_Return<'1>` to
-    /// their `external_name` giving `_Return<'a>`. This step is a bit involved.
-    /// See the [rustc-dev-guide chapter] for more info.
-    ///
-    /// Then we map all the lifetimes in the concrete type to an equal
-    /// universal region that occurs in the opaque type's args, in this case
-    /// this would result in `&'a i32`. We only consider regions in the args
-    /// in case there is an equal region that does not. For example, this should
-    /// be allowed:
-    /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
-    ///
-    /// This will then allow `infer_opaque_definition_from_instantiation` to
-    /// determine that `_Return<'_x> = &'_x i32`.
-    ///
-    /// There's a slight complication around closures. Given
-    /// `fn f<'a: 'a>() { || {} }` the closure's type is something like
-    /// `f::<'a>::{{closure}}`. The region parameter from f is essentially
-    /// ignored by type checking so ends up being inferred to an empty region.
-    /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
-    /// which has no `external_name` in which case we use `'{erased}` as the
-    /// region to pass to `infer_opaque_definition_from_instantiation`.
-    ///
-    /// [rustc-dev-guide chapter]:
-    /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
-    #[instrument(level = "debug", skip(self, root_cx, infcx), ret)]
-    pub(crate) fn infer_opaque_types(
-        &self,
-        root_cx: &mut BorrowCheckRootCtxt<'tcx>,
-        infcx: &InferCtxt<'tcx>,
-        opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
-    ) {
-        let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
-            FxIndexMap::default();
-
-        for (opaque_type_key, concrete_type) in opaque_ty_decls {
-            debug!(?opaque_type_key, ?concrete_type);
-
-            let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
-                vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)];
-
-            let opaque_type_key =
-                opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
-                    // Use the SCC representative instead of directly using `region`.
-                    // See [rustc-dev-guide chapter] § "Strict lifetime equality".
-                    let scc = self.constraint_sccs.scc(region.as_var());
-                    let vid = self.scc_representative(scc);
-                    let named = match self.definitions[vid].origin {
-                        // Iterate over all universal regions in a consistent order and find the
-                        // *first* equal region. This makes sure that equal lifetimes will have
-                        // the same name and simplifies subsequent handling.
-                        // See [rustc-dev-guide chapter] § "Semantic lifetime equality".
-                        NllRegionVariableOrigin::FreeRegion => self
-                            .universal_regions()
-                            .universal_regions_iter()
-                            .filter(|&ur| {
-                                // See [rustc-dev-guide chapter] § "Closure restrictions".
-                                !matches!(
-                                    self.universal_regions().region_classification(ur),
-                                    Some(RegionClassification::External)
-                                )
-                            })
-                            .find(|&ur| self.universal_region_relations.equal(vid, ur))
-                            .map(|ur| self.definitions[ur].external_name.unwrap()),
-                        NllRegionVariableOrigin::Placeholder(placeholder) => {
-                            Some(ty::Region::new_placeholder(infcx.tcx, placeholder))
-                        }
-                        NllRegionVariableOrigin::Existential { .. } => None,
-                    }
-                    .unwrap_or_else(|| {
-                        ty::Region::new_error_with_message(
-                            infcx.tcx,
-                            concrete_type.span,
-                            "opaque type with non-universal region args",
-                        )
-                    });
-
-                    arg_regions.push((vid, named));
-                    named
-                });
-            debug!(?opaque_type_key, ?arg_regions);
-
-            let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| {
-                arg_regions
-                    .iter()
-                    .find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid))
-                    .map(|&(_, arg_named)| arg_named)
-                    .unwrap_or(infcx.tcx.lifetimes.re_erased)
-            });
-            debug!(?concrete_type);
-
-            let ty =
-                infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type);
-
-            // Sometimes, when the hidden type is an inference variable, it can happen that
-            // the hidden type becomes the opaque type itself. In this case, this was an opaque
-            // usage of the opaque type and we can ignore it. This check is mirrored in typeck's
-            // writeback.
-            if !infcx.next_trait_solver() {
-                if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
-                    && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
-                    && alias_ty.args == opaque_type_key.args
-                {
-                    continue;
-                }
-            }
-
-            root_cx.add_concrete_opaque_type(
-                opaque_type_key.def_id,
-                OpaqueHiddenType { span: concrete_type.span, ty },
-            );
-
-            // Check that all opaque types have the same region parameters if they have the same
-            // non-region parameters. This is necessary because within the new solver we perform
-            // various query operations modulo regions, and thus could unsoundly select some impls
-            // that don't hold.
-            if !ty.references_error()
-                && let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert(
-                    infcx.tcx.erase_regions(opaque_type_key),
-                    (opaque_type_key, concrete_type.span),
-                )
-                && let Some((arg1, arg2)) = std::iter::zip(
-                    prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
-                    opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
-                )
-                .find(|(arg1, arg2)| arg1 != arg2)
-            {
-                infcx.dcx().emit_err(LifetimeMismatchOpaqueParam {
-                    arg: arg1,
-                    prev: arg2,
-                    span: prev_span,
-                    prev_span: concrete_type.span,
-                });
-            }
-        }
-    }
-
-    /// Map the regions in the type to named regions. This is similar to what
-    /// `infer_opaque_types` does, but can infer any universal region, not only
-    /// ones from the args for the opaque type. It also doesn't double check
-    /// that the regions produced are in fact equal to the named region they are
-    /// replaced with. This is fine because this function is only to improve the
-    /// region names in error messages.
-    ///
-    /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
-    /// lax with mapping region vids that are *shorter* than a universal region to
-    /// that universal region. This is useful for member region constraints since
-    /// we want to suggest a universal region name to capture even if it's technically
-    /// not equal to the error region.
-    pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
-    where
-        T: TypeFoldable<TyCtxt<'tcx>>,
-    {
-        fold_regions(tcx, ty, |region, _| match region.kind() {
-            ty::ReVar(vid) => {
-                let scc = self.constraint_sccs.scc(vid);
-
-                // Special handling of higher-ranked regions.
-                if !self.max_nameable_universe(scc).is_root() {
-                    match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
-                        // If the region contains a single placeholder then they're equal.
-                        Some((0, placeholder)) => {
-                            return ty::Region::new_placeholder(tcx, placeholder);
-                        }
-
-                        // Fallback: this will produce a cryptic error message.
-                        _ => return region,
-                    }
-                }
-
-                // Find something that we can name
-                let upper_bound = self.approx_universal_upper_bound(vid);
-                if let Some(universal_region) = self.definitions[upper_bound].external_name {
-                    return universal_region;
-                }
-
-                // Nothing exact found, so we pick a named upper bound, if there's only one.
-                // If there's >1 universal region, then we probably are dealing w/ an intersection
-                // region which cannot be mapped back to a universal.
-                // FIXME: We could probably compute the LUB if there is one.
-                let scc = self.constraint_sccs.scc(vid);
-                let upper_bounds: Vec<_> = self
-                    .reverse_scc_graph()
-                    .upper_bounds(scc)
-                    .filter_map(|vid| self.definitions[vid].external_name)
-                    .filter(|r| !r.is_static())
-                    .collect();
-                match &upper_bounds[..] {
-                    [universal_region] => *universal_region,
-                    _ => region,
-                }
-            }
-            _ => region,
-        })
-    }
-}
-
-#[extension(pub trait InferCtxtExt<'tcx>)]
-impl<'tcx> InferCtxt<'tcx> {
-    /// Given the fully resolved, instantiated type for an opaque
-    /// type, i.e., the value of an inference variable like C1 or C2
-    /// (*), computes the "definition type" for an opaque type
-    /// definition -- that is, the inferred value of `Foo1<'x>` or
-    /// `Foo2<'x>` that we would conceptually use in its definition:
-    /// ```ignore (illustrative)
-    /// type Foo1<'x> = impl Bar<'x> = AAA;  // <-- this type AAA
-    /// type Foo2<'x> = impl Bar<'x> = BBB;  // <-- or this type BBB
-    /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
-    /// ```
-    /// Note that these values are defined in terms of a distinct set of
-    /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main
-    /// purpose of this function is to do that translation.
-    ///
-    /// (*) C1 and C2 were introduced in the comments on
-    /// `register_member_constraints`. Read that comment for more context.
-    ///
-    /// # Parameters
-    ///
-    /// - `def_id`, the `impl Trait` type
-    /// - `args`, the args used to instantiate this opaque type
-    /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
-    ///   `opaque_defn.concrete_ty`
-    #[instrument(level = "debug", skip(self))]
-    fn infer_opaque_definition_from_instantiation(
-        &self,
-        opaque_type_key: OpaqueTypeKey<'tcx>,
-        instantiated_ty: OpaqueHiddenType<'tcx>,
-    ) -> Ty<'tcx> {
-        if let Some(e) = self.tainted_by_errors() {
-            return Ty::new_error(self.tcx, e);
-        }
-
-        if let Err(err) = check_opaque_type_parameter_valid(
-            self,
-            opaque_type_key,
-            instantiated_ty.span,
-            DefiningScopeKind::MirBorrowck,
-        ) {
-            return Ty::new_error(self.tcx, err.report(self));
-        }
-
-        let definition_ty = instantiated_ty
-            .remap_generic_params_to_declaration_params(
-                opaque_type_key,
-                self.tcx,
-                DefiningScopeKind::MirBorrowck,
-            )
-            .ty;
-
-        if let Err(e) = definition_ty.error_reported() {
-            return Ty::new_error(self.tcx, e);
-        }
-
-        definition_ty
-    }
-}
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs
new file mode 100644
index 00000000000..667fc440ac0
--- /dev/null
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs
@@ -0,0 +1,194 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def_id::DefId;
+use rustc_middle::bug;
+use rustc_middle::ty::{
+    self, GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+    TypeVisitor,
+};
+use tracing::{debug, instrument};
+
+use super::DefiningUse;
+use super::region_ctxt::RegionCtxt;
+use crate::constraints::ConstraintSccIndex;
+
+pub(super) fn apply_member_constraints<'tcx>(
+    rcx: &mut RegionCtxt<'_, 'tcx>,
+    defining_uses: &[DefiningUse<'tcx>],
+) {
+    // Start by collecting the member constraints of all defining uses.
+    //
+    // Applying member constraints can influence other member constraints,
+    // so we first collect and then apply them.
+    let mut member_constraints = Default::default();
+    for defining_use in defining_uses {
+        let mut visitor = CollectMemberConstraintsVisitor {
+            rcx,
+            defining_use,
+            member_constraints: &mut member_constraints,
+        };
+        defining_use.hidden_type.ty.visit_with(&mut visitor);
+    }
+
+    // Now walk over the region graph, visiting the smallest regions first and then all
+    // regions which have to outlive that one.
+    //
+    // Whenever we encounter a member region, we mutate the value of this SCC. This is
+    // as if we'd introduce new outlives constraints. However, we discard these region
+    // values after we've inferred the hidden types of opaques and apply the region
+    // constraints by simply equating the actual hidden type with the inferred one.
+    debug!(?member_constraints);
+    for scc_a in rcx.constraint_sccs.all_sccs() {
+        debug!(?scc_a);
+        // Start by  adding the region values required by outlives constraints. This
+        // matches how we compute the final region values in `fn compute_regions`.
+        //
+        // We need to do this here to get a lower bound when applying member constraints.
+        // This propagates the region values added by previous member constraints.
+        for &scc_b in rcx.constraint_sccs.successors(scc_a) {
+            debug!(?scc_b);
+            rcx.scc_values.add_region(scc_a, scc_b);
+        }
+
+        for defining_use in member_constraints.get(&scc_a).into_iter().flatten() {
+            apply_member_constraint(rcx, scc_a, &defining_use.arg_regions);
+        }
+    }
+}
+
+#[instrument(level = "debug", skip(rcx))]
+fn apply_member_constraint<'tcx>(
+    rcx: &mut RegionCtxt<'_, 'tcx>,
+    member: ConstraintSccIndex,
+    arg_regions: &[RegionVid],
+) {
+    // If the member region lives in a higher universe, we currently choose
+    // the most conservative option by leaving it unchanged.
+    if !rcx.max_placeholder_universe_reached(member).is_root() {
+        return;
+    }
+
+    // The existing value of `'member` is a lower-bound. If its is already larger than
+    // some universal region, we cannot equate it with that region. Said differently, we
+    // ignore choice regions which are smaller than this member region.
+    let mut choice_regions = arg_regions
+        .iter()
+        .copied()
+        .map(|r| rcx.representative(r).rvid())
+        .filter(|&choice_region| {
+            rcx.scc_values.universal_regions_outlived_by(member).all(|lower_bound| {
+                rcx.universal_region_relations.outlives(choice_region, lower_bound)
+            })
+        })
+        .collect::<Vec<_>>();
+    debug!(?choice_regions, "after enforcing lower-bound");
+
+    // Now find all the *upper bounds* -- that is, each UB is a
+    // free region that must outlive the member region `R0` (`UB:
+    // R0`). Therefore, we need only keep an option `O` if `UB: O`
+    // for all UB.
+    //
+    // If we have a requirement `'upper_bound: 'member`, equating `'member`
+    // with some region `'choice` means we now also require `'upper_bound: 'choice`.
+    // Avoid choice regions for which this does not hold.
+    for ub in rcx.rev_scc_graph.upper_bounds(member) {
+        choice_regions
+            .retain(|&choice_region| rcx.universal_region_relations.outlives(ub, choice_region));
+    }
+    debug!(?choice_regions, "after enforcing upper-bound");
+
+    // At this point we can pick any member of `choice_regions` and would like to choose
+    // it to be a small as possible. To avoid potential non-determinism we will pick the
+    // smallest such choice.
+    //
+    // Because universal regions are only partially ordered (i.e, not every two regions are
+    // comparable), we will ignore any region that doesn't compare to all others when picking
+    // the minimum choice.
+    //
+    // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
+    // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
+    // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
+    let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
+        choice_regions.iter().all(|&r2| {
+            rcx.universal_region_relations.outlives(r1, r2)
+                || rcx.universal_region_relations.outlives(r2, r1)
+        })
+    });
+    // Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
+    let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
+        let r1_outlives_r2 = rcx.universal_region_relations.outlives(r1, r2);
+        let r2_outlives_r1 = rcx.universal_region_relations.outlives(r2, r1);
+        match (r1_outlives_r2, r2_outlives_r1) {
+            (true, true) => r1.min(r2),
+            (true, false) => r2,
+            (false, true) => r1,
+            (false, false) => bug!("incomparable regions in total order"),
+        }
+    }) else {
+        debug!("no unique minimum choice");
+        return;
+    };
+
+    debug!(?min_choice);
+    // Lift the member region to be at least as large as this `min_choice` by directly
+    // mutating the `scc_values` as we compute it. This acts as if we've added a
+    // `'member: 'min_choice` while not recomputing sccs. This means different sccs
+    // may now actually be equal.
+    let min_choice_scc = rcx.constraint_sccs.scc(min_choice);
+    rcx.scc_values.add_region(member, min_choice_scc);
+}
+
+struct CollectMemberConstraintsVisitor<'a, 'b, 'tcx> {
+    rcx: &'a RegionCtxt<'a, 'tcx>,
+    defining_use: &'b DefiningUse<'tcx>,
+    member_constraints: &'a mut FxHashMap<ConstraintSccIndex, Vec<&'b DefiningUse<'tcx>>>,
+}
+impl<'tcx> CollectMemberConstraintsVisitor<'_, '_, 'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.rcx.infcx.tcx
+    }
+    fn visit_closure_args(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) {
+        let generics = self.cx().generics_of(def_id);
+        for arg in args.iter().skip(generics.parent_count) {
+            arg.visit_with(self);
+        }
+    }
+}
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CollectMemberConstraintsVisitor<'_, '_, 'tcx> {
+    fn visit_region(&mut self, r: Region<'tcx>) {
+        match r.kind() {
+            ty::ReBound(..) => return,
+            ty::ReVar(vid) => {
+                let scc = self.rcx.constraint_sccs.scc(vid);
+                self.member_constraints.entry(scc).or_default().push(self.defining_use);
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) {
+        if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
+            return;
+        }
+
+        match *ty.kind() {
+            ty::Closure(def_id, args)
+            | ty::CoroutineClosure(def_id, args)
+            | ty::Coroutine(def_id, args) => self.visit_closure_args(def_id, args),
+
+            ty::Alias(kind, ty::AliasTy { def_id, args, .. })
+                if let Some(variances) = self.cx().opt_alias_variances(kind, def_id) =>
+            {
+                // Skip lifetime parameters that are not captured, since they do
+                // not need member constraints registered for them; we'll erase
+                // them (and hopefully in the future replace them with placeholders).
+                for (&v, arg) in std::iter::zip(variances, args.iter()) {
+                    if v != ty::Bivariant {
+                        arg.visit_with(self)
+                    }
+                }
+            }
+
+            _ => ty.super_visit_with(self),
+        }
+    }
+}
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
new file mode 100644
index 00000000000..0af636aa734
--- /dev/null
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
@@ -0,0 +1,716 @@
+use std::iter;
+use std::rc::Rc;
+
+use rustc_data_structures::frozen::Frozen;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_infer::infer::outlives::env::RegionBoundPairs;
+use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, OpaqueTypeStorageEntries};
+use rustc_infer::traits::ObligationCause;
+use rustc_macros::extension;
+use rustc_middle::mir::{Body, ConcreteOpaqueTypes, ConstraintCategory};
+use rustc_middle::ty::{
+    self, DefiningScopeKind, EarlyBinder, FallibleTypeFolder, GenericArg, GenericArgsRef,
+    OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable,
+    TypeSuperFoldable, TypeVisitableExt, fold_regions,
+};
+use rustc_mir_dataflow::points::DenseLocationMap;
+use rustc_span::Span;
+use rustc_trait_selection::opaque_types::{
+    NonDefiningUseReason, opaque_type_has_defining_use_args,
+};
+use rustc_trait_selection::solve::NoSolution;
+use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
+use tracing::{debug, instrument};
+
+use super::reverse_sccs::ReverseSccGraph;
+use crate::BorrowckInferCtxt;
+use crate::consumers::RegionInferenceContext;
+use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
+use crate::type_check::canonical::fully_perform_op_raw;
+use crate::type_check::free_region_relations::UniversalRegionRelations;
+use crate::type_check::{Locations, MirTypeckRegionConstraints};
+use crate::universal_regions::{RegionClassification, UniversalRegions};
+
+mod member_constraints;
+mod region_ctxt;
+
+use member_constraints::apply_member_constraints;
+use region_ctxt::RegionCtxt;
+
+/// We defer errors from [fn handle_opaque_type_uses] and only report them
+/// if there are no `RegionErrors`. If there are region errors, it's likely
+/// that errors here are caused by them and don't need to be handled separately.
+pub(crate) enum DeferredOpaqueTypeError<'tcx> {
+    InvalidOpaqueTypeArgs(NonDefiningUseReason<'tcx>),
+    LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>),
+    UnexpectedHiddenRegion {
+        /// The opaque type.
+        opaque_type_key: OpaqueTypeKey<'tcx>,
+        /// The hidden type containing the member region.
+        hidden_type: OpaqueHiddenType<'tcx>,
+        /// The unexpected region.
+        member_region: Region<'tcx>,
+    },
+    NonDefiningUseInDefiningScope {
+        span: Span,
+        opaque_type_key: OpaqueTypeKey<'tcx>,
+    },
+}
+
+/// We eagerly map all regions to NLL vars here, as we need to make sure we've
+/// introduced nll vars for all used placeholders.
+///
+/// We need to resolve inference vars as even though we're in MIR typeck, we may still
+/// encounter inference variables, e.g. when checking user types.
+pub(crate) fn clone_and_resolve_opaque_types<'tcx>(
+    infcx: &BorrowckInferCtxt<'tcx>,
+    universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
+    constraints: &mut MirTypeckRegionConstraints<'tcx>,
+) -> (OpaqueTypeStorageEntries, Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>) {
+    let opaque_types = infcx.clone_opaque_types();
+    let opaque_types_storage_num_entries = infcx.inner.borrow_mut().opaque_types().num_entries();
+    let opaque_types = opaque_types
+        .into_iter()
+        .map(|entry| {
+            fold_regions(infcx.tcx, infcx.resolve_vars_if_possible(entry), |r, _| {
+                let vid = if let ty::RePlaceholder(placeholder) = r.kind() {
+                    constraints.placeholder_region(infcx, placeholder).as_var()
+                } else {
+                    universal_region_relations.universal_regions.to_region_vid(r)
+                };
+                Region::new_var(infcx.tcx, vid)
+            })
+        })
+        .collect::<Vec<_>>();
+    (opaque_types_storage_num_entries, opaque_types)
+}
+
+/// Maps an NLL var to a deterministically chosen equal universal region.
+///
+/// See the corresponding [rustc-dev-guide chapter] for more details. This
+/// ignores changes to the region values due to member constraints. Applying
+/// member constraints does not impact the result of this function.
+///
+/// [rustc-dev-guide chapter]: https://rustc-dev-guide.rust-lang.org/borrow_check/opaque-types-region-inference-restrictions.html
+fn nll_var_to_universal_region<'tcx>(
+    rcx: &RegionCtxt<'_, 'tcx>,
+    r: RegionVid,
+) -> Option<Region<'tcx>> {
+    // Use the SCC representative instead of directly using `region`.
+    // See [rustc-dev-guide chapter] § "Strict lifetime equality".
+    let vid = rcx.representative(r).rvid();
+    match rcx.definitions[vid].origin {
+        // Iterate over all universal regions in a consistent order and find the
+        // *first* equal region. This makes sure that equal lifetimes will have
+        // the same name and simplifies subsequent handling.
+        // See [rustc-dev-guide chapter] § "Semantic lifetime equality".
+        NllRegionVariableOrigin::FreeRegion => rcx
+            .universal_regions()
+            .universal_regions_iter()
+            .filter(|&ur| {
+                // See [rustc-dev-guide chapter] § "Closure restrictions".
+                !matches!(
+                    rcx.universal_regions().region_classification(ur),
+                    Some(RegionClassification::External)
+                )
+            })
+            .find(|&ur| rcx.universal_region_relations.equal(vid, ur))
+            .map(|ur| rcx.definitions[ur].external_name.unwrap()),
+        NllRegionVariableOrigin::Placeholder(placeholder) => {
+            Some(ty::Region::new_placeholder(rcx.infcx.tcx, placeholder))
+        }
+        // If `r` were equal to any universal region, its SCC representative
+        // would have been set to a free region.
+        NllRegionVariableOrigin::Existential { .. } => None,
+    }
+}
+
+/// Collect all defining uses of opaque types inside of this typeck root. This
+/// expects the hidden type to be mapped to the definition parameters of the opaque
+/// and errors if we end up with distinct hidden types.
+fn add_concrete_opaque_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
+    def_id: LocalDefId,
+    hidden_ty: OpaqueHiddenType<'tcx>,
+) {
+    // Sometimes two opaque types are the same only after we remap the generic parameters
+    // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
+    // `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
+    // only know that once we convert the generic parameters to those of the opaque type.
+    if let Some(prev) = concrete_opaque_types.0.get_mut(&def_id) {
+        if prev.ty != hidden_ty.ty {
+            let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| {
+                let (Ok(e) | Err(e)) = prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit());
+                e
+            });
+            prev.ty = Ty::new_error(tcx, guar);
+        }
+        // Pick a better span if there is one.
+        // FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
+        prev.span = prev.span.substitute_dummy(hidden_ty.span);
+    } else {
+        concrete_opaque_types.0.insert(def_id, hidden_ty);
+    }
+}
+
+fn get_concrete_opaque_type<'tcx>(
+    concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>,
+    def_id: LocalDefId,
+) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
+    concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty))
+}
+
+#[derive(Debug)]
+struct DefiningUse<'tcx> {
+    /// The opaque type using non NLL vars. This uses the actual
+    /// free regions and placeholders. This is necessary
+    /// to interact with code outside of `rustc_borrowck`.
+    opaque_type_key: OpaqueTypeKey<'tcx>,
+    arg_regions: Vec<RegionVid>,
+    hidden_type: OpaqueHiddenType<'tcx>,
+}
+
+/// This computes the actual hidden types of the opaque types and maps them to their
+/// definition sites. Outside of registering the computed concrete types this function
+/// does not mutate the current borrowck state.
+///
+/// While it may fail to infer the hidden type and return errors, we always apply
+/// the computed concrete hidden type to all opaque type uses to check whether they
+/// are correct. This is necessary to support non-defining uses of opaques in their
+/// defining scope.
+///
+/// It also means that this whole function is not really soundness critical as we
+/// recheck all uses of the opaques regardless.
+pub(crate) fn compute_concrete_opaque_types<'tcx>(
+    infcx: &BorrowckInferCtxt<'tcx>,
+    universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
+    location_map: Rc<DenseLocationMap>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
+    opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
+) -> Vec<DeferredOpaqueTypeError<'tcx>> {
+    let mut errors = Vec::new();
+    // When computing the hidden type we need to track member constraints.
+    // We don't mutate the region graph used by `fn compute_regions` but instead
+    // manually track region information via a `RegionCtxt`. We discard this
+    // information at the end of this function.
+    let mut rcx = RegionCtxt::new(infcx, universal_region_relations, location_map, constraints);
+
+    // We start by checking each use of an opaque type during type check and
+    // check whether the generic arguments of the opaque type are fully
+    // universal, if so, it's a defining use.
+    let defining_uses =
+        collect_defining_uses(&mut rcx, concrete_opaque_types, opaque_types, &mut errors);
+
+    // We now compute and apply member constraints for all regions in the hidden
+    // types of each defining use. This mutates the region values of the `rcx` which
+    // is used when mapping the defining uses to the definition site.
+    apply_member_constraints(&mut rcx, &defining_uses);
+
+    // After applying member constraints, we now check whether all member regions ended
+    // up equal to one of their choice regions and compute the actual concrete type of
+    // the opaque type definition. This is stored in the `root_cx`.
+    compute_concrete_types_from_defining_uses(
+        &rcx,
+        concrete_opaque_types,
+        &defining_uses,
+        &mut errors,
+    );
+    errors
+}
+
+#[instrument(level = "debug", skip_all, ret)]
+fn collect_defining_uses<'tcx>(
+    rcx: &mut RegionCtxt<'_, 'tcx>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
+    opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
+    errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
+) -> Vec<DefiningUse<'tcx>> {
+    let infcx = rcx.infcx;
+    let mut defining_uses = vec![];
+    for &(opaque_type_key, hidden_type) in opaque_types {
+        let non_nll_opaque_type_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |r| {
+            nll_var_to_universal_region(&rcx, r.as_var()).unwrap_or(r)
+        });
+        if let Err(err) = opaque_type_has_defining_use_args(
+            infcx,
+            non_nll_opaque_type_key,
+            hidden_type.span,
+            DefiningScopeKind::MirBorrowck,
+        ) {
+            // A non-defining use. This is a hard error on stable and gets ignored
+            // with `TypingMode::Borrowck`.
+            if infcx.tcx.use_typing_mode_borrowck() {
+                match err {
+                    NonDefiningUseReason::Tainted(guar) => add_concrete_opaque_type(
+                        infcx.tcx,
+                        concrete_opaque_types,
+                        opaque_type_key.def_id,
+                        OpaqueHiddenType::new_error(infcx.tcx, guar),
+                    ),
+                    _ => debug!(?non_nll_opaque_type_key, ?err, "ignoring non-defining use"),
+                }
+            } else {
+                errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err));
+            }
+            continue;
+        }
+
+        // We use the original `opaque_type_key` to compute the `arg_regions`.
+        let arg_regions = iter::once(rcx.universal_regions().fr_static)
+            .chain(
+                opaque_type_key
+                    .iter_captured_args(infcx.tcx)
+                    .filter_map(|(_, arg)| arg.as_region())
+                    .map(Region::as_var),
+            )
+            .collect();
+        defining_uses.push(DefiningUse {
+            opaque_type_key: non_nll_opaque_type_key,
+            arg_regions,
+            hidden_type,
+        });
+    }
+
+    defining_uses
+}
+
+fn compute_concrete_types_from_defining_uses<'tcx>(
+    rcx: &RegionCtxt<'_, 'tcx>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
+    defining_uses: &[DefiningUse<'tcx>],
+    errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
+) {
+    let infcx = rcx.infcx;
+    let tcx = infcx.tcx;
+    let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
+        FxIndexMap::default();
+    for &DefiningUse { opaque_type_key, ref arg_regions, hidden_type } in defining_uses {
+        // After applying member constraints, we now map all regions in the hidden type
+        // to the `arg_regions` of this defining use. In case a region in the hidden type
+        // ended up not being equal to any such region, we error.
+        let hidden_type =
+            match hidden_type.try_fold_with(&mut ToArgRegionsFolder::new(rcx, arg_regions)) {
+                Ok(hidden_type) => hidden_type,
+                Err(r) => {
+                    errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion {
+                        hidden_type,
+                        opaque_type_key,
+                        member_region: ty::Region::new_var(tcx, r),
+                    });
+                    let guar = tcx.dcx().span_delayed_bug(
+                        hidden_type.span,
+                        "opaque type with non-universal region args",
+                    );
+                    ty::OpaqueHiddenType::new_error(tcx, guar)
+                }
+            };
+
+        // Now that we mapped the member regions to their final value,
+        // map the arguments of the opaque type key back to the parameters
+        // of the opaque type definition.
+        let ty = infcx
+            .infer_opaque_definition_from_instantiation(opaque_type_key, hidden_type)
+            .unwrap_or_else(|_| {
+                Ty::new_error_with_message(
+                    rcx.infcx.tcx,
+                    hidden_type.span,
+                    "deferred invalid opaque type args",
+                )
+            });
+
+        // Sometimes, when the hidden type is an inference variable, it can happen that
+        // the hidden type becomes the opaque type itself. In this case, this was an opaque
+        // usage of the opaque type and we can ignore it. This check is mirrored in typeck's
+        // writeback.
+        if !rcx.infcx.tcx.use_typing_mode_borrowck() {
+            if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
+                && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
+                && alias_ty.args == opaque_type_key.args
+            {
+                continue;
+            }
+        }
+
+        // Check that all opaque types have the same region parameters if they have the same
+        // non-region parameters. This is necessary because within the new solver we perform
+        // various query operations modulo regions, and thus could unsoundly select some impls
+        // that don't hold.
+        //
+        // FIXME(-Znext-solver): This isn't necessary after all. We can remove this check again.
+        if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert(
+            rcx.infcx.tcx.erase_and_anonymize_regions(opaque_type_key),
+            (opaque_type_key, hidden_type.span),
+        ) && let Some((arg1, arg2)) = std::iter::zip(
+            prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
+            opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
+        )
+        .find(|(arg1, arg2)| arg1 != arg2)
+        {
+            errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(
+                LifetimeMismatchOpaqueParam {
+                    arg: arg1,
+                    prev: arg2,
+                    span: prev_span,
+                    prev_span: hidden_type.span,
+                },
+            ));
+        }
+        add_concrete_opaque_type(
+            tcx,
+            concrete_opaque_types,
+            opaque_type_key.def_id,
+            OpaqueHiddenType { span: hidden_type.span, ty },
+        );
+    }
+}
+
+/// A folder to map the regions in the hidden type to their corresponding `arg_regions`.
+///
+/// This folder has to differentiate between member regions and other regions in the hidden
+/// type. Member regions have to be equal to one of the `arg_regions` while other regions simply
+/// get treated as an existential region in the opaque if they are not. Existential
+/// regions are currently represented using `'erased`.
+struct ToArgRegionsFolder<'a, 'tcx> {
+    rcx: &'a RegionCtxt<'a, 'tcx>,
+    // When folding closure args or bivariant alias arguments, we simply
+    // ignore non-member regions. However, we still need to map member
+    // regions to their arg region even if its in a closure argument.
+    //
+    // See tests/ui/type-alias-impl-trait/closure_wf_outlives.rs for an example.
+    erase_unknown_regions: bool,
+    arg_regions: &'a [RegionVid],
+}
+
+impl<'a, 'tcx> ToArgRegionsFolder<'a, 'tcx> {
+    fn new(
+        rcx: &'a RegionCtxt<'a, 'tcx>,
+        arg_regions: &'a [RegionVid],
+    ) -> ToArgRegionsFolder<'a, 'tcx> {
+        ToArgRegionsFolder { rcx, erase_unknown_regions: false, arg_regions }
+    }
+
+    fn fold_non_member_arg(&mut self, arg: GenericArg<'tcx>) -> GenericArg<'tcx> {
+        let prev = self.erase_unknown_regions;
+        self.erase_unknown_regions = true;
+        let res = arg.try_fold_with(self).unwrap();
+        self.erase_unknown_regions = prev;
+        res
+    }
+
+    fn fold_closure_args(
+        &mut self,
+        def_id: DefId,
+        args: GenericArgsRef<'tcx>,
+    ) -> Result<GenericArgsRef<'tcx>, RegionVid> {
+        let generics = self.cx().generics_of(def_id);
+        self.cx().mk_args_from_iter(args.iter().enumerate().map(|(index, arg)| {
+            if index < generics.parent_count {
+                Ok(self.fold_non_member_arg(arg))
+            } else {
+                arg.try_fold_with(self)
+            }
+        }))
+    }
+}
+impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for ToArgRegionsFolder<'_, 'tcx> {
+    type Error = RegionVid;
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.rcx.infcx.tcx
+    }
+
+    fn try_fold_region(&mut self, r: Region<'tcx>) -> Result<Region<'tcx>, RegionVid> {
+        match r.kind() {
+            // ignore bound regions, keep visiting
+            ty::ReBound(_, _) => Ok(r),
+            _ => {
+                let r = r.as_var();
+                if let Some(arg_region) = self
+                    .arg_regions
+                    .iter()
+                    .copied()
+                    .find(|&arg_vid| self.rcx.eval_equal(r, arg_vid))
+                    .and_then(|r| nll_var_to_universal_region(self.rcx, r))
+                {
+                    Ok(arg_region)
+                } else if self.erase_unknown_regions {
+                    Ok(self.cx().lifetimes.re_erased)
+                } else {
+                    Err(r)
+                }
+            }
+        }
+    }
+
+    fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, RegionVid> {
+        if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
+            return Ok(ty);
+        }
+
+        let tcx = self.cx();
+        Ok(match *ty.kind() {
+            ty::Closure(def_id, args) => {
+                Ty::new_closure(tcx, def_id, self.fold_closure_args(def_id, args)?)
+            }
+
+            ty::CoroutineClosure(def_id, args) => {
+                Ty::new_coroutine_closure(tcx, def_id, self.fold_closure_args(def_id, args)?)
+            }
+
+            ty::Coroutine(def_id, args) => {
+                Ty::new_coroutine(tcx, def_id, self.fold_closure_args(def_id, args)?)
+            }
+
+            ty::Alias(kind, ty::AliasTy { def_id, args, .. })
+                if let Some(variances) = tcx.opt_alias_variances(kind, def_id) =>
+            {
+                let args = tcx.mk_args_from_iter(std::iter::zip(variances, args.iter()).map(
+                    |(&v, s)| {
+                        if v == ty::Bivariant {
+                            Ok(self.fold_non_member_arg(s))
+                        } else {
+                            s.try_fold_with(self)
+                        }
+                    },
+                ))?;
+                ty::AliasTy::new_from_args(tcx, def_id, args).to_ty(tcx)
+            }
+
+            _ => ty.try_super_fold_with(self)?,
+        })
+    }
+}
+
+/// This function is what actually applies member constraints to the borrowck
+/// state. It is also responsible to check all uses of the opaques in their
+/// defining scope.
+///
+/// It does this by equating the hidden type of each use with the instantiated final
+/// hidden type of the opaque.
+pub(crate) fn apply_computed_concrete_opaque_types<'tcx>(
+    infcx: &BorrowckInferCtxt<'tcx>,
+    body: &Body<'tcx>,
+    universal_regions: &UniversalRegions<'tcx>,
+    region_bound_pairs: &RegionBoundPairs<'tcx>,
+    known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
+    constraints: &mut MirTypeckRegionConstraints<'tcx>,
+    concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
+    opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
+) -> Vec<DeferredOpaqueTypeError<'tcx>> {
+    let tcx = infcx.tcx;
+    let mut errors = Vec::new();
+    for &(key, hidden_type) in opaque_types {
+        let Some(expected) = get_concrete_opaque_type(concrete_opaque_types, key.def_id) else {
+            if !tcx.use_typing_mode_borrowck() {
+                if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
+                    && alias_ty.def_id == key.def_id.to_def_id()
+                    && alias_ty.args == key.args
+                {
+                    continue;
+                } else {
+                    unreachable!("non-defining use in defining scope");
+                }
+            }
+            errors.push(DeferredOpaqueTypeError::NonDefiningUseInDefiningScope {
+                span: hidden_type.span,
+                opaque_type_key: key,
+            });
+            let guar = tcx.dcx().span_delayed_bug(
+                hidden_type.span,
+                "non-defining use in the defining scope with no defining uses",
+            );
+            add_concrete_opaque_type(
+                tcx,
+                concrete_opaque_types,
+                key.def_id,
+                OpaqueHiddenType::new_error(tcx, guar),
+            );
+            continue;
+        };
+
+        // We erase all non-member region of the opaque and need to treat these as existentials.
+        let expected = ty::fold_regions(tcx, expected.instantiate(tcx, key.args), |re, _dbi| {
+            match re.kind() {
+                ty::ReErased => infcx.next_nll_region_var(
+                    NllRegionVariableOrigin::Existential { name: None },
+                    || crate::RegionCtxt::Existential(None),
+                ),
+                _ => re,
+            }
+        });
+
+        // We now simply equate the expected with the actual hidden type.
+        let locations = Locations::All(hidden_type.span);
+        if let Err(guar) = fully_perform_op_raw(
+            infcx,
+            body,
+            universal_regions,
+            region_bound_pairs,
+            known_type_outlives_obligations,
+            constraints,
+            locations,
+            ConstraintCategory::OpaqueType,
+            CustomTypeOp::new(
+                |ocx| {
+                    let cause = ObligationCause::misc(
+                        hidden_type.span,
+                        body.source.def_id().expect_local(),
+                    );
+                    // We need to normalize both types in the old solver before equatingt them.
+                    let actual_ty = ocx.normalize(&cause, infcx.param_env, hidden_type.ty);
+                    let expected_ty = ocx.normalize(&cause, infcx.param_env, expected.ty);
+                    ocx.eq(&cause, infcx.param_env, actual_ty, expected_ty).map_err(|_| NoSolution)
+                },
+                "equating opaque types",
+            ),
+        ) {
+            add_concrete_opaque_type(
+                tcx,
+                concrete_opaque_types,
+                key.def_id,
+                OpaqueHiddenType::new_error(tcx, guar),
+            );
+        }
+    }
+    errors
+}
+
+/// In theory `apply_concrete_opaque_types` could introduce new uses of opaque types.
+/// We do not check these new uses so this could be unsound.
+///
+/// We detect any new uses and simply delay a bug if they occur. If this results in
+/// an ICE we can properly handle this, but we haven't encountered any such test yet.
+///
+/// See the related comment in `FnCtxt::detect_opaque_types_added_during_writeback`.
+pub(crate) fn detect_opaque_types_added_while_handling_opaque_types<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    opaque_types_storage_num_entries: OpaqueTypeStorageEntries,
+) {
+    for (key, hidden_type) in infcx
+        .inner
+        .borrow_mut()
+        .opaque_types()
+        .opaque_types_added_since(opaque_types_storage_num_entries)
+    {
+        let opaque_type_string = infcx.tcx.def_path_str(key.def_id);
+        let msg = format!("unexpected cyclic definition of `{opaque_type_string}`");
+        infcx.dcx().span_delayed_bug(hidden_type.span, msg);
+    }
+
+    let _ = infcx.take_opaque_types();
+}
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+    /// Map the regions in the type to named regions. This is similar to what
+    /// `infer_opaque_types` does, but can infer any universal region, not only
+    /// ones from the args for the opaque type. It also doesn't double check
+    /// that the regions produced are in fact equal to the named region they are
+    /// replaced with. This is fine because this function is only to improve the
+    /// region names in error messages.
+    ///
+    /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
+    /// lax with mapping region vids that are *shorter* than a universal region to
+    /// that universal region. This is useful for member region constraints since
+    /// we want to suggest a universal region name to capture even if it's technically
+    /// not equal to the error region.
+    pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
+    where
+        T: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        fold_regions(tcx, ty, |region, _| match region.kind() {
+            ty::ReVar(vid) => {
+                let scc = self.constraint_sccs.scc(vid);
+
+                // Special handling of higher-ranked regions.
+                if !self.max_nameable_universe(scc).is_root() {
+                    match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
+                        // If the region contains a single placeholder then they're equal.
+                        Some((0, placeholder)) => {
+                            return ty::Region::new_placeholder(tcx, placeholder);
+                        }
+
+                        // Fallback: this will produce a cryptic error message.
+                        _ => return region,
+                    }
+                }
+
+                // Find something that we can name
+                let upper_bound = self.approx_universal_upper_bound(vid);
+                if let Some(universal_region) = self.definitions[upper_bound].external_name {
+                    return universal_region;
+                }
+
+                // Nothing exact found, so we pick a named upper bound, if there's only one.
+                // If there's >1 universal region, then we probably are dealing w/ an intersection
+                // region which cannot be mapped back to a universal.
+                // FIXME: We could probably compute the LUB if there is one.
+                let scc = self.constraint_sccs.scc(vid);
+                let rev_scc_graph =
+                    ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions());
+                let upper_bounds: Vec<_> = rev_scc_graph
+                    .upper_bounds(scc)
+                    .filter_map(|vid| self.definitions[vid].external_name)
+                    .filter(|r| !r.is_static())
+                    .collect();
+                match &upper_bounds[..] {
+                    [universal_region] => *universal_region,
+                    _ => region,
+                }
+            }
+            _ => region,
+        })
+    }
+}
+
+#[extension(pub trait InferCtxtExt<'tcx>)]
+impl<'tcx> InferCtxt<'tcx> {
+    /// Given the fully resolved, instantiated type for an opaque
+    /// type, i.e., the value of an inference variable like C1 or C2
+    /// (*), computes the "definition type" for an opaque type
+    /// definition -- that is, the inferred value of `Foo1<'x>` or
+    /// `Foo2<'x>` that we would conceptually use in its definition:
+    /// ```ignore (illustrative)
+    /// type Foo1<'x> = impl Bar<'x> = AAA;  // <-- this type AAA
+    /// type Foo2<'x> = impl Bar<'x> = BBB;  // <-- or this type BBB
+    /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
+    /// ```
+    /// Note that these values are defined in terms of a distinct set of
+    /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main
+    /// purpose of this function is to do that translation.
+    ///
+    /// (*) C1 and C2 were introduced in the comments on
+    /// `register_member_constraints`. Read that comment for more context.
+    ///
+    /// # Parameters
+    ///
+    /// - `def_id`, the `impl Trait` type
+    /// - `args`, the args used to instantiate this opaque type
+    /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
+    ///   `opaque_defn.concrete_ty`
+    #[instrument(level = "debug", skip(self))]
+    fn infer_opaque_definition_from_instantiation(
+        &self,
+        opaque_type_key: OpaqueTypeKey<'tcx>,
+        instantiated_ty: OpaqueHiddenType<'tcx>,
+    ) -> Result<Ty<'tcx>, NonDefiningUseReason<'tcx>> {
+        opaque_type_has_defining_use_args(
+            self,
+            opaque_type_key,
+            instantiated_ty.span,
+            DefiningScopeKind::MirBorrowck,
+        )?;
+
+        let definition_ty = instantiated_ty
+            .remap_generic_params_to_declaration_params(
+                opaque_type_key,
+                self.tcx,
+                DefiningScopeKind::MirBorrowck,
+            )
+            .ty;
+
+        definition_ty.error_reported()?;
+        Ok(definition_ty)
+    }
+}
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs
new file mode 100644
index 00000000000..88326e4eebf
--- /dev/null
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs
@@ -0,0 +1,114 @@
+use std::rc::Rc;
+
+use rustc_data_structures::frozen::Frozen;
+use rustc_index::IndexVec;
+use rustc_infer::infer::NllRegionVariableOrigin;
+use rustc_middle::ty::{RegionVid, UniverseIndex};
+use rustc_mir_dataflow::points::DenseLocationMap;
+
+use crate::BorrowckInferCtxt;
+use crate::constraints::ConstraintSccIndex;
+use crate::handle_placeholders::{SccAnnotations, region_definitions};
+use crate::region_infer::reverse_sccs::ReverseSccGraph;
+use crate::region_infer::values::RegionValues;
+use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker, Representative};
+use crate::type_check::MirTypeckRegionConstraints;
+use crate::type_check::free_region_relations::UniversalRegionRelations;
+use crate::universal_regions::UniversalRegions;
+
+/// A slimmed down version of [crate::region_infer::RegionInferenceContext] used
+/// only by opaque type handling.
+pub(super) struct RegionCtxt<'a, 'tcx> {
+    pub(super) infcx: &'a BorrowckInferCtxt<'tcx>,
+    pub(super) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
+    pub(super) universal_region_relations: &'a UniversalRegionRelations<'tcx>,
+    pub(super) constraint_sccs: ConstraintSccs,
+    pub(super) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
+    pub(super) rev_scc_graph: ReverseSccGraph,
+    pub(super) scc_values: RegionValues<ConstraintSccIndex>,
+}
+
+impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
+    /// Creates a new `RegionCtxt` used to compute defining opaque type uses.
+    ///
+    /// This does not yet propagate region values. This is instead done lazily
+    /// when applying member constraints.
+    pub(super) fn new(
+        infcx: &'a BorrowckInferCtxt<'tcx>,
+        universal_region_relations: &'a Frozen<UniversalRegionRelations<'tcx>>,
+        location_map: Rc<DenseLocationMap>,
+        constraints: &MirTypeckRegionConstraints<'tcx>,
+    ) -> RegionCtxt<'a, 'tcx> {
+        let universal_regions = &universal_region_relations.universal_regions;
+        let (definitions, _has_placeholders) = region_definitions(infcx, universal_regions);
+        let mut scc_annotations = SccAnnotations::init(&definitions);
+        let constraint_sccs = ConstraintSccs::new_with_annotation(
+            &constraints
+                .outlives_constraints
+                .graph(definitions.len())
+                .region_graph(&constraints.outlives_constraints, universal_regions.fr_static),
+            &mut scc_annotations,
+        );
+        let scc_annotations = scc_annotations.scc_to_annotation;
+
+        // Unlike the `RegionInferenceContext`, we only care about free regions
+        // and fully ignore liveness and placeholders.
+        let placeholder_indices = Default::default();
+        let mut scc_values =
+            RegionValues::new(location_map, universal_regions.len(), placeholder_indices);
+        for variable in definitions.indices() {
+            let scc = constraint_sccs.scc(variable);
+            match definitions[variable].origin {
+                NllRegionVariableOrigin::FreeRegion => {
+                    scc_values.add_element(scc, variable);
+                }
+                _ => {}
+            }
+        }
+
+        let rev_scc_graph = ReverseSccGraph::compute(&constraint_sccs, universal_regions);
+        RegionCtxt {
+            infcx,
+            definitions,
+            universal_region_relations,
+            constraint_sccs,
+            scc_annotations,
+            rev_scc_graph,
+            scc_values,
+        }
+    }
+
+    pub(super) fn representative(&self, vid: RegionVid) -> Representative {
+        let scc = self.constraint_sccs.scc(vid);
+        self.scc_annotations[scc].representative
+    }
+
+    pub(crate) fn max_placeholder_universe_reached(
+        &self,
+        scc: ConstraintSccIndex,
+    ) -> UniverseIndex {
+        self.scc_annotations[scc].max_placeholder_universe_reached()
+    }
+
+    pub(super) fn universal_regions(&self) -> &UniversalRegions<'tcx> {
+        &self.universal_region_relations.universal_regions
+    }
+
+    pub(super) fn eval_equal(&self, r1_vid: RegionVid, r2_vid: RegionVid) -> bool {
+        let r1 = self.constraint_sccs.scc(r1_vid);
+        let r2 = self.constraint_sccs.scc(r2_vid);
+
+        if r1 == r2 {
+            return true;
+        }
+
+        let universal_outlives = |sub, sup| {
+            self.scc_values.universal_regions_outlived_by(sub).all(|r1| {
+                self.scc_values
+                    .universal_regions_outlived_by(sup)
+                    .any(|r2| self.universal_region_relations.outlives(r2, r1))
+            })
+        };
+        universal_outlives(r1, r2) && universal_outlives(r2, r1)
+    }
+}
diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
index 604265f8940..e8da85eccef 100644
--- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
+++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
@@ -5,7 +5,6 @@ use rustc_data_structures::graph;
 use rustc_data_structures::graph::vec_graph::VecGraph;
 use rustc_middle::ty::RegionVid;
 
-use crate::RegionInferenceContext;
 use crate::constraints::ConstraintSccIndex;
 use crate::region_infer::ConstraintSccs;
 use crate::universal_regions::UniversalRegions;
@@ -57,12 +56,3 @@ impl ReverseSccGraph {
             .filter(move |r| duplicates.insert(*r))
     }
 }
-
-impl RegionInferenceContext<'_> {
-    /// Return the reverse graph of the region SCCs, initialising it if needed.
-    pub(super) fn reverse_scc_graph(&self) -> &ReverseSccGraph {
-        self.rev_scc_graph.get_or_init(|| {
-            ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions())
-        })
-    }
-}
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index f1427218cdb..eb611fa3475 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -37,6 +37,7 @@ pub(crate) enum RegionElement {
 
 /// Records the CFG locations where each region is live. When we initially compute liveness, we use
 /// an interval matrix storing liveness ranges for each region-vid.
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct LivenessValues {
     /// The map from locations to points.
     location_map: Rc<DenseLocationMap>,
@@ -194,6 +195,7 @@ impl LivenessValues {
 /// rustc to the internal `PlaceholderIndex` values that are used in
 /// NLL.
 #[derive(Debug, Default)]
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct PlaceholderIndices {
     indices: FxIndexSet<ty::PlaceholderRegion>,
 }
diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs
index ff92b4168a8..fa3064afee8 100644
--- a/compiler/rustc_borrowck/src/renumber.rs
+++ b/compiler/rustc_borrowck/src/renumber.rs
@@ -66,7 +66,7 @@ impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> {
         T: TypeFoldable<TyCtxt<'tcx>>,
         F: Fn() -> RegionCtxt,
     {
-        let origin = NllRegionVariableOrigin::Existential { from_forall: false };
+        let origin = NllRegionVariableOrigin::Existential { name: None };
         fold_regions(self.infcx.tcx, value, |_region, _depth| {
             self.infcx.next_nll_region_var(origin, || region_ctxt_fn())
         })
diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs
index 9b1d12aede5..cd4e9683f2d 100644
--- a/compiler/rustc_borrowck/src/root_cx.rs
+++ b/compiler/rustc_borrowck/src/root_cx.rs
@@ -1,13 +1,26 @@
+use std::mem;
+use std::rc::Rc;
+
 use rustc_abi::FieldIdx;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_hir::def_id::LocalDefId;
-use rustc_middle::bug;
-use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::mir::ConstraintCategory;
+use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::ErrorGuaranteed;
 use smallvec::SmallVec;
 
 use crate::consumers::BorrowckConsumer;
-use crate::{ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults};
+use crate::nll::compute_closure_requirements_modulo_opaques;
+use crate::region_infer::opaque_types::{
+    apply_computed_concrete_opaque_types, clone_and_resolve_opaque_types,
+    compute_concrete_opaque_types, detect_opaque_types_added_while_handling_opaque_types,
+};
+use crate::type_check::{Locations, constraint_conversion};
+use crate::{
+    ClosureRegionRequirements, CollectRegionConstraintsResult, ConcreteOpaqueTypes,
+    PropagatedBorrowCheckResults, borrowck_check_region_constraints,
+    borrowck_collect_region_constraints,
+};
 
 /// The shared context used by both the root as well as all its nested
 /// items.
@@ -15,11 +28,16 @@ pub(super) struct BorrowCheckRootCtxt<'tcx> {
     pub tcx: TyCtxt<'tcx>,
     root_def_id: LocalDefId,
     concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
-    nested_bodies: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
+    /// The region constraints computed by [borrowck_collect_region_constraints]. This uses
+    /// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before
+    /// their parents.
+    collect_region_constraints_results:
+        FxIndexMap<LocalDefId, CollectRegionConstraintsResult<'tcx>>,
+    propagated_borrowck_results: FxHashMap<LocalDefId, PropagatedBorrowCheckResults<'tcx>>,
     tainted_by_errors: Option<ErrorGuaranteed>,
     /// This should be `None` during normal compilation. See [`crate::consumers`] for more
     /// information on how this is used.
-    pub(crate) consumer: Option<BorrowckConsumer<'tcx>>,
+    pub consumer: Option<BorrowckConsumer<'tcx>>,
 }
 
 impl<'tcx> BorrowCheckRootCtxt<'tcx> {
@@ -32,75 +50,26 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
             tcx,
             root_def_id,
             concrete_opaque_types: Default::default(),
-            nested_bodies: Default::default(),
+            collect_region_constraints_results: Default::default(),
+            propagated_borrowck_results: Default::default(),
             tainted_by_errors: None,
             consumer,
         }
     }
 
-    /// Collect all defining uses of opaque types inside of this typeck root. This
-    /// expects the hidden type to be mapped to the definition parameters of the opaque
-    /// and errors if we end up with distinct hidden types.
-    pub(super) fn add_concrete_opaque_type(
-        &mut self,
-        def_id: LocalDefId,
-        hidden_ty: OpaqueHiddenType<'tcx>,
-    ) {
-        // Sometimes two opaque types are the same only after we remap the generic parameters
-        // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
-        // `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
-        // only know that once we convert the generic parameters to those of the opaque type.
-        if let Some(prev) = self.concrete_opaque_types.0.get_mut(&def_id) {
-            if prev.ty != hidden_ty.ty {
-                let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| {
-                    let (Ok(e) | Err(e)) =
-                        prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit());
-                    e
-                });
-                prev.ty = Ty::new_error(self.tcx, guar);
-            }
-            // Pick a better span if there is one.
-            // FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
-            prev.span = prev.span.substitute_dummy(hidden_ty.span);
-        } else {
-            self.concrete_opaque_types.0.insert(def_id, hidden_ty);
-        }
+    pub(super) fn root_def_id(&self) -> LocalDefId {
+        self.root_def_id
     }
 
     pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) {
         self.tainted_by_errors = Some(guar);
     }
 
-    pub(super) fn get_or_insert_nested(
-        &mut self,
-        def_id: LocalDefId,
-    ) -> &PropagatedBorrowCheckResults<'tcx> {
-        debug_assert_eq!(
-            self.tcx.typeck_root_def_id(def_id.to_def_id()),
-            self.root_def_id.to_def_id()
-        );
-        if !self.nested_bodies.contains_key(&def_id) {
-            let result = super::do_mir_borrowck(self, def_id);
-            if let Some(prev) = self.nested_bodies.insert(def_id, result) {
-                bug!("unexpected previous nested body: {prev:?}");
-            }
-        }
-
-        self.nested_bodies.get(&def_id).unwrap()
-    }
-
-    pub(super) fn closure_requirements(
-        &mut self,
-        nested_body_def_id: LocalDefId,
-    ) -> &Option<ClosureRegionRequirements<'tcx>> {
-        &self.get_or_insert_nested(nested_body_def_id).closure_requirements
-    }
-
     pub(super) fn used_mut_upvars(
         &mut self,
         nested_body_def_id: LocalDefId,
     ) -> &SmallVec<[FieldIdx; 8]> {
-        &self.get_or_insert_nested(nested_body_def_id).used_mut_upvars
+        &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars
     }
 
     pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> {
@@ -110,4 +79,214 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
             Ok(self.tcx.arena.alloc(self.concrete_opaque_types))
         }
     }
+
+    fn handle_opaque_type_uses(&mut self) {
+        let mut per_body_info = Vec::new();
+        for input in self.collect_region_constraints_results.values_mut() {
+            let (num_entries, opaque_types) = clone_and_resolve_opaque_types(
+                &input.infcx,
+                &input.universal_region_relations,
+                &mut input.constraints,
+            );
+            input.deferred_opaque_type_errors = compute_concrete_opaque_types(
+                &input.infcx,
+                &input.universal_region_relations,
+                &input.constraints,
+                Rc::clone(&input.location_map),
+                &mut self.concrete_opaque_types,
+                &opaque_types,
+            );
+            per_body_info.push((num_entries, opaque_types));
+        }
+
+        for (input, (opaque_types_storage_num_entries, opaque_types)) in
+            self.collect_region_constraints_results.values_mut().zip(per_body_info)
+        {
+            if input.deferred_opaque_type_errors.is_empty() {
+                input.deferred_opaque_type_errors = apply_computed_concrete_opaque_types(
+                    &input.infcx,
+                    &input.body_owned,
+                    &input.universal_region_relations.universal_regions,
+                    &input.region_bound_pairs,
+                    &input.known_type_outlives_obligations,
+                    &mut input.constraints,
+                    &mut self.concrete_opaque_types,
+                    &opaque_types,
+                );
+            }
+
+            detect_opaque_types_added_while_handling_opaque_types(
+                &input.infcx,
+                opaque_types_storage_num_entries,
+            )
+        }
+    }
+
+    /// Computing defining uses of opaques may depend on the propagated region
+    /// requirements of nested bodies, while applying defining uses may introduce
+    /// additional region requirements we need to propagate.
+    ///
+    /// This results in cyclic dependency. To compute the defining uses in parent
+    /// bodies, we need the closure requirements of its nested bodies, but to check
+    /// non-defining uses in nested bodies, we may rely on the defining uses in the
+    /// parent.
+    ///
+    /// We handle this issue by applying closure requirements twice. Once using the
+    /// region constraints from before we've handled opaque types in the nested body
+    /// - which is used by the parent to handle its defining uses - and once after.
+    ///
+    /// As a performance optimization, we also eagerly finish borrowck for bodies
+    /// which don't depend on opaque types. In this case they get removed from
+    /// `collect_region_constraints_results` and the final result gets put into
+    /// `propagated_borrowck_results`.
+    fn apply_closure_requirements_modulo_opaques(&mut self) {
+        let mut closure_requirements_modulo_opaques = FxHashMap::default();
+        // We need to `mem::take` both `self.collect_region_constraints_results` and
+        // `input.deferred_closure_requirements` as we otherwise can't iterate over
+        // them while mutably using the containing struct.
+        let collect_region_constraints_results =
+            mem::take(&mut self.collect_region_constraints_results);
+        // We iterate over all bodies here, visiting nested bodies before their parent.
+        for (def_id, mut input) in collect_region_constraints_results {
+            // A body depends on opaque types if it either has any opaque type uses itself,
+            // or it has a nested body which does.
+            //
+            // If the current body does not depend on any opaque types, we eagerly compute
+            // its final result and write it into `self.propagated_borrowck_results`. This
+            // avoids having to compute its closure requirements modulo regions, as they
+            // are just the same as its final closure requirements.
+            let mut depends_on_opaques = input.infcx.has_opaque_types_in_storage();
+
+            // Iterate over all nested bodies of `input`. If that nested body depends on
+            // opaque types, we apply its closure requirements modulo opaques. Otherwise
+            // we use the closure requirements from its final borrowck result.
+            //
+            // In case we've only applied the closure requirements modulo opaques, we have
+            // to later apply its closure requirements considering opaques, so we put that
+            // nested body back into `deferred_closure_requirements`.
+            for (def_id, args, locations) in mem::take(&mut input.deferred_closure_requirements) {
+                let closure_requirements = match self.propagated_borrowck_results.get(&def_id) {
+                    None => {
+                        depends_on_opaques = true;
+                        input.deferred_closure_requirements.push((def_id, args, locations));
+                        &closure_requirements_modulo_opaques[&def_id]
+                    }
+                    Some(result) => &result.closure_requirements,
+                };
+
+                Self::apply_closure_requirements(
+                    &mut input,
+                    closure_requirements,
+                    def_id,
+                    args,
+                    locations,
+                );
+            }
+
+            // In case the current body does depend on opaques and is a nested body,
+            // we need to compute its closure requirements modulo opaques so that
+            // we're able to use it when visiting its parent later in this function.
+            //
+            // If the current body does not depend on opaque types, we finish borrowck
+            // and write its result into `propagated_borrowck_results`.
+            if depends_on_opaques {
+                if def_id != self.root_def_id {
+                    let req = Self::compute_closure_requirements_modulo_opaques(&input);
+                    closure_requirements_modulo_opaques.insert(def_id, req);
+                }
+                self.collect_region_constraints_results.insert(def_id, input);
+            } else {
+                assert!(input.deferred_closure_requirements.is_empty());
+                let result = borrowck_check_region_constraints(self, input);
+                self.propagated_borrowck_results.insert(def_id, result);
+            }
+        }
+    }
+
+    fn compute_closure_requirements_modulo_opaques(
+        input: &CollectRegionConstraintsResult<'tcx>,
+    ) -> Option<ClosureRegionRequirements<'tcx>> {
+        compute_closure_requirements_modulo_opaques(
+            &input.infcx,
+            &input.body_owned,
+            Rc::clone(&input.location_map),
+            &input.universal_region_relations,
+            &input.constraints,
+        )
+    }
+
+    fn apply_closure_requirements(
+        input: &mut CollectRegionConstraintsResult<'tcx>,
+        closure_requirements: &Option<ClosureRegionRequirements<'tcx>>,
+        closure_def_id: LocalDefId,
+        args: ty::GenericArgsRef<'tcx>,
+        locations: Locations,
+    ) {
+        if let Some(closure_requirements) = closure_requirements {
+            constraint_conversion::ConstraintConversion::new(
+                &input.infcx,
+                &input.universal_region_relations.universal_regions,
+                &input.region_bound_pairs,
+                &input.known_type_outlives_obligations,
+                locations,
+                input.body_owned.span,      // irrelevant; will be overridden.
+                ConstraintCategory::Boring, // same as above.
+                &mut input.constraints,
+            )
+            .apply_closure_requirements(closure_requirements, closure_def_id, args);
+        }
+    }
+
+    pub(super) fn do_mir_borrowck(&mut self) {
+        // The list of all bodies we need to borrowck. This first looks at
+        // nested bodies, and then their parents. This means accessing e.g.
+        // `used_mut_upvars` for a closure can assume that we've already
+        // checked that closure.
+        let all_bodies = self
+            .tcx
+            .nested_bodies_within(self.root_def_id)
+            .iter()
+            .chain(std::iter::once(self.root_def_id));
+        for def_id in all_bodies {
+            let result = borrowck_collect_region_constraints(self, def_id);
+            self.collect_region_constraints_results.insert(def_id, result);
+        }
+
+        // We now apply the closure requirements of nested bodies modulo
+        // regions. In case a body does not depend on opaque types, we
+        // eagerly check its region constraints and use the final closure
+        // requirements.
+        //
+        // We eagerly finish borrowck for bodies which don't depend on
+        // opaques.
+        self.apply_closure_requirements_modulo_opaques();
+
+        // We handle opaque type uses for all bodies together.
+        self.handle_opaque_type_uses();
+
+        // Now walk over all bodies which depend on opaque types and finish borrowck.
+        //
+        // We first apply the final closure requirements from nested bodies which also
+        // depend on opaque types and then finish borrow checking the parent. Bodies
+        // which don't depend on opaques have already been fully borrowchecked in
+        // `apply_closure_requirements_modulo_opaques` as an optimization.
+        for (def_id, mut input) in mem::take(&mut self.collect_region_constraints_results) {
+            for (def_id, args, locations) in mem::take(&mut input.deferred_closure_requirements) {
+                // We visit nested bodies before their parent, so we're already
+                // done with nested bodies at this point.
+                let closure_requirements =
+                    &self.propagated_borrowck_results[&def_id].closure_requirements;
+                Self::apply_closure_requirements(
+                    &mut input,
+                    closure_requirements,
+                    def_id,
+                    args,
+                    locations,
+                );
+            }
+
+            let result = borrowck_check_region_constraints(self, input);
+            self.propagated_borrowck_results.insert(def_id, result);
+        }
+    }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index b3fa786a517..aece0bda346 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -2,8 +2,9 @@ use std::fmt;
 
 use rustc_errors::ErrorGuaranteed;
 use rustc_infer::infer::canonical::Canonical;
+use rustc_infer::infer::outlives::env::RegionBoundPairs;
 use rustc_middle::bug;
-use rustc_middle::mir::ConstraintCategory;
+use rustc_middle::mir::{Body, ConstraintCategory};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast};
 use rustc_span::Span;
 use rustc_span::def_id::DefId;
@@ -14,7 +15,68 @@ use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
 use tracing::{debug, instrument};
 
 use super::{Locations, NormalizeLocation, TypeChecker};
+use crate::BorrowckInferCtxt;
 use crate::diagnostics::ToUniverseInfo;
+use crate::type_check::{MirTypeckRegionConstraints, constraint_conversion};
+use crate::universal_regions::UniversalRegions;
+
+#[instrument(skip(infcx, constraints, op), level = "trace")]
+pub(crate) fn fully_perform_op_raw<'tcx, R: fmt::Debug, Op>(
+    infcx: &BorrowckInferCtxt<'tcx>,
+    body: &Body<'tcx>,
+    universal_regions: &UniversalRegions<'tcx>,
+    region_bound_pairs: &RegionBoundPairs<'tcx>,
+    known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
+    constraints: &mut MirTypeckRegionConstraints<'tcx>,
+    locations: Locations,
+    category: ConstraintCategory<'tcx>,
+    op: Op,
+) -> Result<R, ErrorGuaranteed>
+where
+    Op: type_op::TypeOp<'tcx, Output = R>,
+    Op::ErrorInfo: ToUniverseInfo<'tcx>,
+{
+    let old_universe = infcx.universe();
+
+    let TypeOpOutput { output, constraints: query_constraints, error_info } =
+        op.fully_perform(infcx, infcx.root_def_id, locations.span(body))?;
+    if cfg!(debug_assertions) {
+        let data = infcx.take_and_reset_region_constraints();
+        if !data.is_empty() {
+            panic!("leftover region constraints: {data:#?}");
+        }
+    }
+
+    debug!(?output, ?query_constraints);
+
+    if let Some(data) = query_constraints {
+        constraint_conversion::ConstraintConversion::new(
+            infcx,
+            universal_regions,
+            region_bound_pairs,
+            known_type_outlives_obligations,
+            locations,
+            locations.span(body),
+            category,
+            constraints,
+        )
+        .convert_all(data);
+    }
+
+    // If the query has created new universes and errors are going to be emitted, register the
+    // cause of these new universes for improved diagnostics.
+    let universe = infcx.universe();
+    if old_universe != universe
+        && let Some(error_info) = error_info
+    {
+        let universe_info = error_info.to_universe_info(old_universe);
+        for u in (old_universe + 1)..=universe {
+            constraints.universe_causes.insert(u, universe_info.clone());
+        }
+    }
+
+    Ok(output)
+}
 
 impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     /// Given some operation `op` that manipulates types, proves
@@ -38,36 +100,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         Op: type_op::TypeOp<'tcx, Output = R>,
         Op::ErrorInfo: ToUniverseInfo<'tcx>,
     {
-        let old_universe = self.infcx.universe();
-
-        let TypeOpOutput { output, constraints, error_info } =
-            op.fully_perform(self.infcx, locations.span(self.body))?;
-        if cfg!(debug_assertions) {
-            let data = self.infcx.take_and_reset_region_constraints();
-            if !data.is_empty() {
-                panic!("leftover region constraints: {data:#?}");
-            }
-        }
-
-        debug!(?output, ?constraints);
-
-        if let Some(data) = constraints {
-            self.push_region_constraints(locations, category, data);
-        }
-
-        // If the query has created new universes and errors are going to be emitted, register the
-        // cause of these new universes for improved diagnostics.
-        let universe = self.infcx.universe();
-        if old_universe != universe
-            && let Some(error_info) = error_info
-        {
-            let universe_info = error_info.to_universe_info(old_universe);
-            for u in (old_universe + 1)..=universe {
-                self.constraints.universe_causes.insert(u, universe_info.clone());
-            }
-        }
-
-        Ok(output)
+        fully_perform_op_raw(
+            self.infcx,
+            self.body,
+            self.universal_regions,
+            self.region_bound_pairs,
+            self.known_type_outlives_obligations,
+            self.constraints,
+            locations,
+            category,
+            op,
+        )
     }
 
     pub(super) fn instantiate_canonical<T>(
@@ -187,8 +230,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         location: impl NormalizeLocation,
     ) -> Ty<'tcx> {
         let tcx = self.tcx();
+        let body = self.body;
+
+        let cause = ObligationCause::misc(
+            location.to_locations().span(body),
+            body.source.def_id().expect_local(),
+        );
+
         if self.infcx.next_trait_solver() {
-            let body = self.body;
             let param_env = self.infcx.param_env;
             // FIXME: Make this into a real type op?
             self.fully_perform_op(
@@ -198,10 +247,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     |ocx| {
                         let structurally_normalize = |ty| {
                             ocx.structurally_normalize_ty(
-                                &ObligationCause::misc(
-                                    location.to_locations().span(body),
-                                    body.source.def_id().expect_local(),
-                                ),
+                                &cause,
                                 param_env,
                                 ty,
                             )
@@ -210,6 +256,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
                         let tail = tcx.struct_tail_raw(
                             ty,
+                            &cause,
                             structurally_normalize,
                             || {},
                         );
@@ -222,7 +269,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             .unwrap_or_else(|guar| Ty::new_error(tcx, guar))
         } else {
             let mut normalize = |ty| self.normalize(ty, location);
-            let tail = tcx.struct_tail_raw(ty, &mut normalize, || {});
+            let tail = tcx.struct_tail_raw(ty, &cause, &mut normalize, || {});
             normalize(tail)
         }
     }
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index ca636a8c999..703223e2e54 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -1,13 +1,14 @@
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::LocalDefId;
+use rustc_infer::infer::SubregionOrigin;
 use rustc_infer::infer::canonical::QueryRegionConstraints;
 use rustc_infer::infer::outlives::env::RegionBoundPairs;
 use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
 use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
-use rustc_infer::infer::{InferCtxt, SubregionOrigin};
 use rustc_infer::traits::query::type_op::DeeplyNormalize;
 use rustc_middle::bug;
 use rustc_middle::ty::{
-    self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions,
+    self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions,
 };
 use rustc_span::Span;
 use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -17,10 +18,12 @@ use crate::constraints::OutlivesConstraint;
 use crate::region_infer::TypeTest;
 use crate::type_check::{Locations, MirTypeckRegionConstraints};
 use crate::universal_regions::UniversalRegions;
-use crate::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory};
+use crate::{
+    BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory,
+};
 
 pub(crate) struct ConstraintConversion<'a, 'tcx> {
-    infcx: &'a InferCtxt<'tcx>,
+    infcx: &'a BorrowckInferCtxt<'tcx>,
     universal_regions: &'a UniversalRegions<'tcx>,
     /// Each RBP `GK: 'a` is assumed to be true. These encode
     /// relationships like `T: 'a` that are added via implicit bounds
@@ -33,7 +36,6 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> {
     /// logic expecting to see (e.g.) `ReStatic`, and if we supplied
     /// our special inference variable there, we would mess that up.
     region_bound_pairs: &'a RegionBoundPairs<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
     known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>],
     locations: Locations,
     span: Span,
@@ -44,10 +46,9 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> {
 
 impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
     pub(crate) fn new(
-        infcx: &'a InferCtxt<'tcx>,
+        infcx: &'a BorrowckInferCtxt<'tcx>,
         universal_regions: &'a UniversalRegions<'tcx>,
         region_bound_pairs: &'a RegionBoundPairs<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
         known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>],
         locations: Locations,
         span: Span,
@@ -58,7 +59,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
             infcx,
             universal_regions,
             region_bound_pairs,
-            param_env,
             known_type_outlives_obligations,
             locations,
             span,
@@ -70,10 +70,12 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
 
     #[instrument(skip(self), level = "debug")]
     pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
-        let QueryRegionConstraints { outlives } = query_constraints;
+        let QueryRegionConstraints { outlives, assumptions } = query_constraints;
+        let assumptions =
+            elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());
 
         for &(predicate, constraint_category) in outlives {
-            self.convert(predicate, constraint_category);
+            self.convert(predicate, constraint_category, &assumptions);
         }
     }
 
@@ -112,15 +114,20 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
 
             self.category = outlives_requirement.category;
             self.span = outlives_requirement.blame_span;
-            self.convert(ty::OutlivesPredicate(subject, outlived_region), self.category);
+            self.convert(
+                ty::OutlivesPredicate(subject, outlived_region),
+                self.category,
+                &Default::default(),
+            );
         }
         (self.category, self.span, self.from_closure) = backup;
     }
 
     fn convert(
         &mut self,
-        predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
+        predicate: ty::ArgOutlivesPredicate<'tcx>,
         constraint_category: ConstraintCategory<'tcx>,
+        higher_ranked_assumptions: &FxHashSet<ty::ArgOutlivesPredicate<'tcx>>,
     ) {
         let tcx = self.infcx.tcx;
         debug!("generate: constraints at: {:#?}", self.locations);
@@ -150,7 +157,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
             }
 
             let mut next_outlives_predicates = vec![];
-            for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates {
+            for (pred, constraint_category) in outlives_predicates {
+                // Constraint is implied by a coroutine's well-formedness.
+                if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
+                    && higher_ranked_assumptions.contains(&pred)
+                {
+                    continue;
+                }
+
+                let ty::OutlivesPredicate(k1, r2) = pred;
                 match k1.kind() {
                     GenericArgKind::Lifetime(r1) => {
                         let r1_vid = self.to_region_vid(r1);
@@ -266,14 +281,18 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         &self,
         ty: Ty<'tcx>,
         next_outlives_predicates: &mut Vec<(
-            ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
+            ty::ArgOutlivesPredicate<'tcx>,
             ConstraintCategory<'tcx>,
         )>,
     ) -> Ty<'tcx> {
-        match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span)
-        {
+        match self.infcx.param_env.and(DeeplyNormalize { value: ty }).fully_perform(
+            self.infcx,
+            self.infcx.root_def_id,
+            self.span,
+        ) {
             Ok(TypeOpOutput { output: ty, constraints, .. }) => {
-                if let Some(QueryRegionConstraints { outlives }) = constraints {
+                // FIXME(higher_ranked_auto): What should we do with the assumptions here?
+                if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints {
                     next_outlives_predicates.extend(outlives.iter().copied());
                 }
                 ty
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index f642d34ea67..d27a73535ba 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -2,9 +2,9 @@ use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
 use rustc_hir::def::DefKind;
 use rustc_infer::infer::canonical::QueryRegionConstraints;
+use rustc_infer::infer::outlives;
 use rustc_infer::infer::outlives::env::RegionBoundPairs;
 use rustc_infer::infer::region_constraints::GenericKind;
-use rustc_infer::infer::{InferCtxt, outlives};
 use rustc_infer::traits::query::type_op::DeeplyNormalize;
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::traits::query::OutlivesBound;
@@ -14,10 +14,12 @@ use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
 use tracing::{debug, instrument};
 use type_op::TypeOpOutput;
 
+use crate::BorrowckInferCtxt;
 use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion};
 use crate::universal_regions::UniversalRegions;
 
 #[derive(Debug)]
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct UniversalRegionRelations<'tcx> {
     pub(crate) universal_regions: UniversalRegions<'tcx>,
 
@@ -47,14 +49,12 @@ pub(crate) struct CreateResult<'tcx> {
 }
 
 pub(crate) fn create<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
+    infcx: &BorrowckInferCtxt<'tcx>,
     universal_regions: UniversalRegions<'tcx>,
     constraints: &mut MirTypeckRegionConstraints<'tcx>,
 ) -> CreateResult<'tcx> {
     UniversalRegionRelationsBuilder {
         infcx,
-        param_env,
         constraints,
         universal_regions,
         region_bound_pairs: Default::default(),
@@ -177,8 +177,7 @@ impl UniversalRegionRelations<'_> {
 }
 
 struct UniversalRegionRelationsBuilder<'a, 'tcx> {
-    infcx: &'a InferCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
+    infcx: &'a BorrowckInferCtxt<'tcx>,
     universal_regions: UniversalRegions<'tcx>,
     constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
 
@@ -205,7 +204,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
 
         // Insert the `'a: 'b` we know from the predicates.
         // This does not consider the type-outlives.
-        let param_env = self.param_env;
+        let param_env = self.infcx.param_env;
         self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env));
 
         // - outlives is reflexive, so `'r: 'r` for every region `'r`
@@ -263,7 +262,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
             let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } =
                 param_env
                     .and(DeeplyNormalize { value: ty })
-                    .fully_perform(self.infcx, span)
+                    .fully_perform(self.infcx, self.infcx.root_def_id, span)
                     .unwrap_or_else(|guar| TypeOpOutput {
                         output: Ty::new_error(self.infcx.tcx, guar),
                         constraints: None,
@@ -298,8 +297,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
         // Add implied bounds from impl header.
         if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) {
             for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) {
-                let result: Result<_, ErrorGuaranteed> =
-                    param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, span);
+                let result: Result<_, ErrorGuaranteed> = param_env
+                    .and(DeeplyNormalize { value: ty })
+                    .fully_perform(self.infcx, self.infcx.root_def_id, span);
                 let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else {
                     continue;
                 };
@@ -318,7 +318,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                 self.infcx,
                 &self.universal_regions,
                 &self.region_bound_pairs,
-                param_env,
                 &known_type_outlives_obligations,
                 Locations::All(span),
                 span,
@@ -353,10 +352,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                 output: normalized_outlives,
                 constraints: constraints_normalize,
                 error_info: _,
-            }) = self
-                .param_env
-                .and(DeeplyNormalize { value: outlives })
-                .fully_perform(self.infcx, span)
+            }) = self.infcx.param_env.and(DeeplyNormalize { value: outlives }).fully_perform(
+                self.infcx,
+                self.infcx.root_def_id,
+                span,
+            )
             else {
                 self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}"));
                 return;
@@ -381,9 +381,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
         span: Span,
     ) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
         let TypeOpOutput { output: bounds, constraints, .. } = self
+            .infcx
             .param_env
             .and(type_op::ImpliedOutlivesBounds { ty })
-            .fully_perform(self.infcx, span)
+            .fully_perform(self.infcx, self.infcx.root_def_id, span)
             .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty))
             .ok()?;
         debug!(?bounds, ?constraints);
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 99392ea1915..eb31b5de05d 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -86,7 +86,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         // them with fresh ty vars.
                         resume_ty: next_ty_var(),
                         yield_ty: next_ty_var(),
-                        witness: next_ty_var(),
                     },
                 )
                 .args,
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 5d30fa71e92..b704d8f0a76 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -640,7 +640,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> {
 
         let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty });
 
-        match op.fully_perform(typeck.infcx, DUMMY_SP) {
+        match op.fully_perform(typeck.infcx, typeck.root_cx.root_def_id(), DUMMY_SP) {
             Ok(TypeOpOutput { output, constraints, .. }) => {
                 DropData { dropck_result: output, region_constraint_data: constraints }
             }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index f877e5eaadb..19cbcd139aa 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -26,8 +26,7 @@ use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::cast::CastTy;
 use rustc_middle::ty::{
     self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt,
-    GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, TypeVisitableExt,
-    UserArgs, UserTypeAnnotationIndex, fold_regions,
+    GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_mir_dataflow::move_paths::MoveData;
@@ -35,6 +34,7 @@ use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::source_map::Spanned;
 use rustc_span::{Span, sym};
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
 use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
 use tracing::{debug, instrument, trace};
@@ -42,7 +42,6 @@ use tracing::{debug, instrument, trace};
 use crate::borrow_set::BorrowSet;
 use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet};
 use crate::diagnostics::UniverseInfo;
-use crate::member_constraints::MemberConstraintSet;
 use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable};
 use crate::polonius::{PoloniusContext, PoloniusLivenessContext};
 use crate::region_infer::TypeTest;
@@ -50,7 +49,7 @@ use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderI
 use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst};
 use crate::type_check::free_region_relations::{CreateResult, UniversalRegionRelations};
 use crate::universal_regions::{DefiningTy, UniversalRegions};
-use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, path_utils};
+use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, DeferredClosureRequirements, path_utils};
 
 macro_rules! span_mirbug {
     ($context:expr, $elem:expr, $($message:tt)*) => ({
@@ -67,12 +66,11 @@ macro_rules! span_mirbug {
     })
 }
 
-mod canonical;
-mod constraint_conversion;
+pub(crate) mod canonical;
+pub(crate) mod constraint_conversion;
 pub(crate) mod free_region_relations;
 mod input_output;
 pub(crate) mod liveness;
-mod opaque_types;
 mod relate_tys;
 
 /// Type checks the given `mir` in the context of the inference
@@ -114,7 +112,6 @@ pub(crate) fn type_check<'tcx>(
         placeholder_index_to_region: IndexVec::default(),
         liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&location_map)),
         outlives_constraints: OutlivesConstraintSet::default(),
-        member_constraints: MemberConstraintSet::default(),
         type_tests: Vec::default(),
         universe_causes: FxIndexMap::default(),
     };
@@ -124,13 +121,18 @@ pub(crate) fn type_check<'tcx>(
         region_bound_pairs,
         normalized_inputs_and_output,
         known_type_outlives_obligations,
-    } = free_region_relations::create(infcx, infcx.param_env, universal_regions, &mut constraints);
+    } = free_region_relations::create(infcx, universal_regions, &mut constraints);
 
     let pre_obligations = infcx.take_registered_region_obligations();
     assert!(
         pre_obligations.is_empty(),
         "there should be no incoming region obligations = {pre_obligations:#?}",
     );
+    let pre_assumptions = infcx.take_registered_region_assumptions();
+    assert!(
+        pre_assumptions.is_empty(),
+        "there should be no incoming region assumptions = {pre_assumptions:#?}",
+    );
 
     debug!(?normalized_inputs_and_output);
 
@@ -140,6 +142,7 @@ pub(crate) fn type_check<'tcx>(
         None
     };
 
+    let mut deferred_closure_requirements = Default::default();
     let mut typeck = TypeChecker {
         root_cx,
         infcx,
@@ -155,6 +158,7 @@ pub(crate) fn type_check<'tcx>(
         polonius_facts,
         borrow_set,
         constraints: &mut constraints,
+        deferred_closure_requirements: &mut deferred_closure_requirements,
         polonius_liveness,
     };
 
@@ -165,9 +169,6 @@ pub(crate) fn type_check<'tcx>(
 
     liveness::generate(&mut typeck, &location_map, move_data);
 
-    let opaque_type_values =
-        opaque_types::take_opaques_and_register_member_constraints(&mut typeck);
-
     // We're done with typeck, we can finalize the polonius liveness context for region inference.
     let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| {
         PoloniusContext::create_from_liveness(
@@ -177,10 +178,22 @@ pub(crate) fn type_check<'tcx>(
         )
     });
 
+    // In case type check encountered an error region, we suppress unhelpful extra
+    // errors in by clearing out all outlives bounds that we may end up checking.
+    if let Some(guar) = universal_region_relations.universal_regions.encountered_re_error() {
+        debug!("encountered an error region; removing constraints!");
+        constraints.outlives_constraints = Default::default();
+        constraints.type_tests = Default::default();
+        root_cx.set_tainted_by_errors(guar);
+        infcx.set_tainted_by_errors(guar);
+    }
+
     MirTypeckResults {
         constraints,
         universal_region_relations,
-        opaque_type_values,
+        region_bound_pairs,
+        known_type_outlives_obligations,
+        deferred_closure_requirements,
         polonius_context,
     }
 }
@@ -220,6 +233,7 @@ struct TypeChecker<'a, 'tcx> {
     polonius_facts: &'a mut Option<PoloniusFacts>,
     borrow_set: &'a BorrowSet<'tcx>,
     constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
+    deferred_closure_requirements: &'a mut DeferredClosureRequirements<'tcx>,
     /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints.
     polonius_liveness: Option<PoloniusLivenessContext>,
 }
@@ -229,12 +243,15 @@ struct TypeChecker<'a, 'tcx> {
 pub(crate) struct MirTypeckResults<'tcx> {
     pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
     pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
-    pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
+    pub(crate) region_bound_pairs: Frozen<RegionBoundPairs<'tcx>>,
+    pub(crate) known_type_outlives_obligations: Frozen<Vec<ty::PolyTypeOutlivesPredicate<'tcx>>>,
+    pub(crate) deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
     pub(crate) polonius_context: Option<PoloniusContext>,
 }
 
 /// A collection of region constraints that must be satisfied for the
 /// program to be considered well-typed.
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct MirTypeckRegionConstraints<'tcx> {
     /// Maps from a `ty::Placeholder` to the corresponding
     /// `PlaceholderIndex` bit that we will use for it.
@@ -261,8 +278,6 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> {
 
     pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>,
 
-    pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
-
     pub(crate) universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
 
     pub(crate) type_tests: Vec<TypeTest<'tcx>>,
@@ -271,7 +286,7 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> {
 impl<'tcx> MirTypeckRegionConstraints<'tcx> {
     /// Creates a `Region` for a given `PlaceholderRegion`, or returns the
     /// region that corresponds to a previously created one.
-    fn placeholder_region(
+    pub(crate) fn placeholder_region(
         &mut self,
         infcx: &InferCtxt<'tcx>,
         placeholder: ty::PlaceholderRegion,
@@ -364,14 +379,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         self.body
     }
 
-    fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid {
-        if let ty::RePlaceholder(placeholder) = r.kind() {
-            self.constraints.placeholder_region(self.infcx, placeholder).as_var()
-        } else {
-            self.universal_regions.to_region_vid(r)
-        }
-    }
-
     fn unsized_feature_enabled(&self) -> bool {
         self.tcx().features().unsized_fn_params()
     }
@@ -408,7 +415,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             self.infcx,
             self.universal_regions,
             self.region_bound_pairs,
-            self.infcx.param_env,
             self.known_type_outlives_obligations,
             locations,
             locations.span(self.body),
@@ -664,24 +670,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     );
                 }
 
-                if let Some(annotation_index) = self.rvalue_user_ty(rv) {
-                    if let Err(terr) = self.relate_type_and_user_type(
+                if let Some(annotation_index) = self.rvalue_user_ty(rv)
+                    && let Err(terr) = self.relate_type_and_user_type(
                         rv_ty,
                         ty::Invariant,
                         &UserTypeProjection { base: annotation_index, projs: vec![] },
                         location.to_locations(),
                         ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg),
-                    ) {
-                        let annotation = &self.user_type_annotations[annotation_index];
-                        span_mirbug!(
-                            self,
-                            stmt,
-                            "bad user type on rvalue ({:?} = {:?}): {:?}",
-                            annotation,
-                            rv_ty,
-                            terr
-                        );
-                    }
+                    )
+                {
+                    let annotation = &self.user_type_annotations[annotation_index];
+                    span_mirbug!(
+                        self,
+                        stmt,
+                        "bad user type on rvalue ({:?} = {:?}): {:?}",
+                        annotation,
+                        rv_ty,
+                        terr
+                    );
                 }
 
                 if !self.unsized_feature_enabled() {
@@ -764,9 +770,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             }
             TerminatorKind::Call { func, args, .. }
             | TerminatorKind::TailCall { func, args, .. } => {
-                let call_source = match term.kind {
-                    TerminatorKind::Call { call_source, .. } => call_source,
-                    TerminatorKind::TailCall { .. } => CallSource::Normal,
+                let (call_source, destination, is_diverging) = match term.kind {
+                    TerminatorKind::Call { call_source, destination, target, .. } => {
+                        (call_source, destination, target.is_none())
+                    }
+                    TerminatorKind::TailCall { .. } => {
+                        (CallSource::Normal, RETURN_PLACE.into(), false)
+                    }
                     _ => unreachable!(),
                 };
 
@@ -840,9 +850,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     );
                 }
 
-                if let TerminatorKind::Call { destination, target, .. } = term.kind {
-                    self.check_call_dest(term, &sig, destination, target, term_location);
-                }
+                self.check_call_dest(term, &sig, destination, is_diverging, term_location);
 
                 // The ordinary liveness rules will ensure that all
                 // regions in the type of the callee are live here. We
@@ -1453,68 +1461,77 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     }
                     CastKind::PtrToPtr => {
                         let ty_from = op.ty(self.body, tcx);
-                        let cast_ty_from = CastTy::from_ty(ty_from);
-                        let cast_ty_to = CastTy::from_ty(*ty);
-                        match (cast_ty_from, cast_ty_to) {
-                            (Some(CastTy::Ptr(src)), Some(CastTy::Ptr(dst))) => {
-                                let src_tail = self.struct_tail(src.ty, location);
-                                let dst_tail = self.struct_tail(dst.ty, location);
-
-                                // This checks (lifetime part of) vtable validity for pointer casts,
-                                // which is irrelevant when there are aren't principal traits on
-                                // both sides (aka only auto traits).
-                                //
-                                // Note that other checks (such as denying `dyn Send` -> `dyn
-                                // Debug`) are in `rustc_hir_typeck`.
-                                if let ty::Dynamic(src_tty, _src_lt, ty::Dyn) = *src_tail.kind()
-                                    && let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) = *dst_tail.kind()
-                                    && src_tty.principal().is_some()
-                                    && dst_tty.principal().is_some()
-                                {
-                                    // Remove auto traits.
-                                    // Auto trait checks are handled in `rustc_hir_typeck` as FCW.
-                                    let src_obj = Ty::new_dynamic(
-                                        tcx,
-                                        tcx.mk_poly_existential_predicates(
-                                            &src_tty.without_auto_traits().collect::<Vec<_>>(),
-                                        ),
-                                        // FIXME: Once we disallow casting `*const dyn Trait + 'short`
-                                        // to `*const dyn Trait + 'long`, then this can just be `src_lt`.
-                                        dst_lt,
-                                        ty::Dyn,
-                                    );
-                                    let dst_obj = Ty::new_dynamic(
-                                        tcx,
-                                        tcx.mk_poly_existential_predicates(
-                                            &dst_tty.without_auto_traits().collect::<Vec<_>>(),
-                                        ),
-                                        dst_lt,
-                                        ty::Dyn,
-                                    );
-
-                                    debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
-
-                                    self.sub_types(
-                                        src_obj,
-                                        dst_obj,
-                                        location.to_locations(),
-                                        ConstraintCategory::Cast {
-                                            is_implicit_coercion: false,
-                                            unsize_to: None,
-                                        },
-                                    )
-                                    .unwrap();
-                                }
-                            }
-                            _ => {
-                                span_mirbug!(
-                                    self,
-                                    rvalue,
-                                    "Invalid PtrToPtr cast {:?} -> {:?}",
-                                    ty_from,
-                                    ty
-                                )
-                            }
+                        let Some(CastTy::Ptr(src)) = CastTy::from_ty(ty_from) else {
+                            unreachable!();
+                        };
+                        let Some(CastTy::Ptr(dst)) = CastTy::from_ty(*ty) else {
+                            unreachable!();
+                        };
+
+                        if self.infcx.type_is_sized_modulo_regions(self.infcx.param_env, dst.ty) {
+                            // Wide to thin ptr cast. This may even occur in an env with
+                            // impossible predicates, such as `where dyn Trait: Sized`.
+                            // In this case, we don't want to fall into the case below,
+                            // since the types may not actually be equatable, but it's
+                            // fine to perform this operation in an impossible env.
+                            let trait_ref = ty::TraitRef::new(
+                                tcx,
+                                tcx.require_lang_item(LangItem::Sized, self.last_span),
+                                [dst.ty],
+                            );
+                            self.prove_trait_ref(
+                                trait_ref,
+                                location.to_locations(),
+                                ConstraintCategory::Cast {
+                                    is_implicit_coercion: true,
+                                    unsize_to: None,
+                                },
+                            );
+                        } else if let ty::Dynamic(src_tty, _src_lt) =
+                            *self.struct_tail(src.ty, location).kind()
+                            && let ty::Dynamic(dst_tty, dst_lt) =
+                                *self.struct_tail(dst.ty, location).kind()
+                            && src_tty.principal().is_some()
+                            && dst_tty.principal().is_some()
+                        {
+                            // This checks (lifetime part of) vtable validity for pointer casts,
+                            // which is irrelevant when there are aren't principal traits on
+                            // both sides (aka only auto traits).
+                            //
+                            // Note that other checks (such as denying `dyn Send` -> `dyn
+                            // Debug`) are in `rustc_hir_typeck`.
+
+                            // Remove auto traits.
+                            // Auto trait checks are handled in `rustc_hir_typeck` as FCW.
+                            let src_obj = Ty::new_dynamic(
+                                tcx,
+                                tcx.mk_poly_existential_predicates(
+                                    &src_tty.without_auto_traits().collect::<Vec<_>>(),
+                                ),
+                                // FIXME: Once we disallow casting `*const dyn Trait + 'short`
+                                // to `*const dyn Trait + 'long`, then this can just be `src_lt`.
+                                dst_lt,
+                            );
+                            let dst_obj = Ty::new_dynamic(
+                                tcx,
+                                tcx.mk_poly_existential_predicates(
+                                    &dst_tty.without_auto_traits().collect::<Vec<_>>(),
+                                ),
+                                dst_lt,
+                            );
+
+                            debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
+
+                            self.sub_types(
+                                src_obj,
+                                dst_obj,
+                                location.to_locations(),
+                                ConstraintCategory::Cast {
+                                    is_implicit_coercion: false,
+                                    unsize_to: None,
+                                },
+                            )
+                            .unwrap();
                         }
                     }
                     CastKind::Transmute => {
@@ -1612,7 +1629,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             | Rvalue::BinaryOp(..)
             | Rvalue::RawPtr(..)
             | Rvalue::ThreadLocalRef(..)
-            | Rvalue::Len(..)
             | Rvalue::Discriminant(..)
             | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {}
         }
@@ -1755,10 +1771,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     locations,
                 );
 
-                assert!(!matches!(
-                    tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)),
-                    Some(DefKind::Impl { of_trait: true })
-                ));
+                assert_eq!(tcx.trait_impl_of_assoc(def_id), None);
                 self.prove_predicates(
                     args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())),
                     locations,
@@ -1869,65 +1882,61 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         term: &Terminator<'tcx>,
         sig: &ty::FnSig<'tcx>,
         destination: Place<'tcx>,
-        target: Option<BasicBlock>,
+        is_diverging: bool,
         term_location: Location,
     ) {
         let tcx = self.tcx();
-        match target {
-            Some(_) => {
-                let dest_ty = destination.ty(self.body, tcx).ty;
-                let dest_ty = self.normalize(dest_ty, term_location);
-                let category = match destination.as_local() {
-                    Some(RETURN_PLACE) => {
-                        if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
-                            self.universal_regions.defining_ty
-                        {
-                            if tcx.is_static(def_id) {
-                                ConstraintCategory::UseAsStatic
-                            } else {
-                                ConstraintCategory::UseAsConst
-                            }
+        if is_diverging {
+            // The signature in this call can reference region variables,
+            // so erase them before calling a query.
+            let output_ty = self.tcx().erase_and_anonymize_regions(sig.output());
+            if !output_ty
+                .is_privately_uninhabited(self.tcx(), self.infcx.typing_env(self.infcx.param_env))
+            {
+                span_mirbug!(self, term, "call to non-diverging function {:?} w/o dest", sig);
+            }
+        } else {
+            let dest_ty = destination.ty(self.body, tcx).ty;
+            let dest_ty = self.normalize(dest_ty, term_location);
+            let category = match destination.as_local() {
+                Some(RETURN_PLACE) => {
+                    if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
+                        self.universal_regions.defining_ty
+                    {
+                        if tcx.is_static(def_id) {
+                            ConstraintCategory::UseAsStatic
                         } else {
-                            ConstraintCategory::Return(ReturnConstraint::Normal)
+                            ConstraintCategory::UseAsConst
                         }
+                    } else {
+                        ConstraintCategory::Return(ReturnConstraint::Normal)
                     }
-                    Some(l) if !self.body.local_decls[l].is_user_variable() => {
-                        ConstraintCategory::Boring
-                    }
-                    // The return type of a call is interesting for diagnostics.
-                    _ => ConstraintCategory::Assignment,
-                };
-
-                let locations = term_location.to_locations();
-
-                if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
-                    span_mirbug!(
-                        self,
-                        term,
-                        "call dest mismatch ({:?} <- {:?}): {:?}",
-                        dest_ty,
-                        sig.output(),
-                        terr
-                    );
                 }
-
-                // When `unsized_fn_params` is not enabled,
-                // this check is done at `check_local`.
-                if self.unsized_feature_enabled() {
-                    let span = term.source_info.span;
-                    self.ensure_place_sized(dest_ty, span);
+                Some(l) if !self.body.local_decls[l].is_user_variable() => {
+                    ConstraintCategory::Boring
                 }
+                // The return type of a call is interesting for diagnostics.
+                _ => ConstraintCategory::Assignment,
+            };
+
+            let locations = term_location.to_locations();
+
+            if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
+                span_mirbug!(
+                    self,
+                    term,
+                    "call dest mismatch ({:?} <- {:?}): {:?}",
+                    dest_ty,
+                    sig.output(),
+                    terr
+                );
             }
-            None => {
-                // The signature in this call can reference region variables,
-                // so erase them before calling a query.
-                let output_ty = self.tcx().erase_regions(sig.output());
-                if !output_ty.is_privately_uninhabited(
-                    self.tcx(),
-                    self.infcx.typing_env(self.infcx.param_env),
-                ) {
-                    span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
-                }
+
+            // When `unsized_fn_params` is not enabled,
+            // this check is done at `check_local`.
+            if self.unsized_feature_enabled() {
+                let span = term.source_info.span;
+                self.ensure_place_sized(dest_ty, span);
             }
         }
     }
@@ -1974,7 +1983,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
             let op_arg_ty = self.normalize(op_arg_ty, term_location);
             let category = if call_source.from_hir_call() {
-                ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty)))
+                ConstraintCategory::CallArgument(Some(
+                    self.infcx.tcx.erase_and_anonymize_regions(func_ty),
+                ))
             } else {
                 ConstraintCategory::Boring
             };
@@ -2108,7 +2119,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         // Erase the regions from `ty` to get a global type. The
         // `Sized` bound in no way depends on precise regions, so this
         // shouldn't affect `is_sized`.
-        let erased_ty = tcx.erase_regions(ty);
+        let erased_ty = tcx.erase_and_anonymize_regions(ty);
         // FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here.
         if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.param_env)) {
             // in current MIR construction, all non-control-flow rvalue
@@ -2187,7 +2198,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             | Rvalue::Repeat(..)
             | Rvalue::Ref(..)
             | Rvalue::RawPtr(..)
-            | Rvalue::Len(..)
             | Rvalue::Cast(..)
             | Rvalue::ShallowInitBox(..)
             | Rvalue::BinaryOp(..)
@@ -2463,24 +2473,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         args: GenericArgsRef<'tcx>,
         locations: Locations,
     ) -> ty::InstantiatedPredicates<'tcx> {
-        if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) {
-            constraint_conversion::ConstraintConversion::new(
-                self.infcx,
-                self.universal_regions,
-                self.region_bound_pairs,
-                self.infcx.param_env,
-                self.known_type_outlives_obligations,
-                locations,
-                self.body.span,             // irrelevant; will be overridden.
-                ConstraintCategory::Boring, // same as above.
-                self.constraints,
-            )
-            .apply_closure_requirements(closure_requirements, def_id, args);
-        }
+        let root_def_id = self.root_cx.root_def_id();
+        // We will have to handle propagated closure requirements for this closure,
+        // but need to defer this until the nested body has been fully borrow checked.
+        self.deferred_closure_requirements.push((def_id, args, locations));
 
-        // Now equate closure args to regions inherited from `typeck_root_def_id`. Fixes #98589.
-        let typeck_root_def_id = tcx.typeck_root_def_id(self.body.source.def_id());
-        let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id);
+        // Equate closure args to regions inherited from `root_def_id`. Fixes #98589.
+        let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, root_def_id);
 
         let parent_args = match tcx.def_kind(def_id) {
             // We don't want to dispatch on 3 different kind of closures here, so take
@@ -2555,17 +2554,14 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
     fn fully_perform(
         mut self,
         infcx: &InferCtxt<'tcx>,
+        root_def_id: LocalDefId,
         span: Span,
     ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
-        let (mut output, region_constraints) = scrape_region_constraints(
-            infcx,
-            |ocx| {
+        let (mut output, region_constraints) =
+            scrape_region_constraints(infcx, root_def_id, "InstantiateOpaqueType", span, |ocx| {
                 ocx.register_obligations(self.obligations.clone());
                 Ok(())
-            },
-            "InstantiateOpaqueType",
-            span,
-        )?;
+            })?;
         self.region_constraints = Some(region_constraints);
         output.error_info = Some(self);
         Ok(output)
diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs
deleted file mode 100644
index 5a422483eef..00000000000
--- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs
+++ /dev/null
@@ -1,333 +0,0 @@
-use std::iter;
-
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_middle::span_bug;
-use rustc_middle::ty::{
-    self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable,
-    TypeVisitable, TypeVisitableExt, TypeVisitor, fold_regions,
-};
-use tracing::{debug, trace};
-
-use super::{MemberConstraintSet, TypeChecker};
-
-/// Once we're done with typechecking the body, we take all the opaque types
-/// defined by this function and add their 'member constraints'.
-pub(super) fn take_opaques_and_register_member_constraints<'tcx>(
-    typeck: &mut TypeChecker<'_, 'tcx>,
-) -> FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>> {
-    let infcx = typeck.infcx;
-    // Annoying: to invoke `typeck.to_region_vid`, we need access to
-    // `typeck.constraints`, but we also want to be mutating
-    // `typeck.member_constraints`. For now, just swap out the value
-    // we want and replace at the end.
-    let mut member_constraints = std::mem::take(&mut typeck.constraints.member_constraints);
-    let opaque_types = infcx
-        .take_opaque_types()
-        .into_iter()
-        .map(|(opaque_type_key, hidden_type)| {
-            let hidden_type = infcx.resolve_vars_if_possible(hidden_type);
-            register_member_constraints(
-                typeck,
-                &mut member_constraints,
-                opaque_type_key,
-                hidden_type,
-            );
-            trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
-            if hidden_type.has_non_region_infer() {
-                span_bug!(hidden_type.span, "could not resolve {:?}", hidden_type.ty);
-            }
-
-            // Convert all regions to nll vars.
-            let (opaque_type_key, hidden_type) =
-                fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |r, _| {
-                    ty::Region::new_var(infcx.tcx, typeck.to_region_vid(r))
-                });
-
-            (opaque_type_key, hidden_type)
-        })
-        .collect();
-    assert!(typeck.constraints.member_constraints.is_empty());
-    typeck.constraints.member_constraints = member_constraints;
-    opaque_types
-}
-
-/// Given the map `opaque_types` containing the opaque
-/// `impl Trait` types whose underlying, hidden types are being
-/// inferred, this method adds constraints to the regions
-/// appearing in those underlying hidden types to ensure that they
-/// at least do not refer to random scopes within the current
-/// function. These constraints are not (quite) sufficient to
-/// guarantee that the regions are actually legal values; that
-/// final condition is imposed after region inference is done.
-///
-/// # The Problem
-///
-/// Let's work through an example to explain how it works. Assume
-/// the current function is as follows:
-///
-/// ```text
-/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>)
-/// ```
-///
-/// Here, we have two `impl Trait` types whose values are being
-/// inferred (the `impl Bar<'a>` and the `impl
-/// Bar<'b>`). Conceptually, this is sugar for a setup where we
-/// define underlying opaque types (`Foo1`, `Foo2`) and then, in
-/// the return type of `foo`, we *reference* those definitions:
-///
-/// ```text
-/// type Foo1<'x> = impl Bar<'x>;
-/// type Foo2<'x> = impl Bar<'x>;
-/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
-///                    //  ^^^^ ^^
-///                    //  |    |
-///                    //  |    args
-///                    //  def_id
-/// ```
-///
-/// As indicating in the comments above, each of those references
-/// is (in the compiler) basically generic parameters (`args`)
-/// applied to the type of a suitable `def_id` (which identifies
-/// `Foo1` or `Foo2`).
-///
-/// Now, at this point in compilation, what we have done is to
-/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with
-/// fresh inference variables C1 and C2. We wish to use the values
-/// of these variables to infer the underlying types of `Foo1` and
-/// `Foo2`. That is, this gives rise to higher-order (pattern) unification
-/// constraints like:
-///
-/// ```text
-/// for<'a> (Foo1<'a> = C1)
-/// for<'b> (Foo1<'b> = C2)
-/// ```
-///
-/// For these equation to be satisfiable, the types `C1` and `C2`
-/// can only refer to a limited set of regions. For example, `C1`
-/// can only refer to `'static` and `'a`, and `C2` can only refer
-/// to `'static` and `'b`. The job of this function is to impose that
-/// constraint.
-///
-/// Up to this point, C1 and C2 are basically just random type
-/// inference variables, and hence they may contain arbitrary
-/// regions. In fact, it is fairly likely that they do! Consider
-/// this possible definition of `foo`:
-///
-/// ```text
-/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) {
-///         (&*x, &*y)
-///     }
-/// ```
-///
-/// Here, the values for the concrete types of the two impl
-/// traits will include inference variables:
-///
-/// ```text
-/// &'0 i32
-/// &'1 i32
-/// ```
-///
-/// Ordinarily, the subtyping rules would ensure that these are
-/// sufficiently large. But since `impl Bar<'a>` isn't a specific
-/// type per se, we don't get such constraints by default. This
-/// is where this function comes into play. It adds extra
-/// constraints to ensure that all the regions which appear in the
-/// inferred type are regions that could validly appear.
-///
-/// This is actually a bit of a tricky constraint in general. We
-/// want to say that each variable (e.g., `'0`) can only take on
-/// values that were supplied as arguments to the opaque type
-/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in
-/// scope. We don't have a constraint quite of this kind in the current
-/// region checker.
-///
-/// # The Solution
-///
-/// We generally prefer to make `<=` constraints, since they
-/// integrate best into the region solver. To do that, we find the
-/// "minimum" of all the arguments that appear in the args: that
-/// is, some region which is less than all the others. In the case
-/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after
-/// all). Then we apply that as a least bound to the variables
-/// (e.g., `'a <= '0`).
-///
-/// In some cases, there is no minimum. Consider this example:
-///
-/// ```text
-/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }
-/// ```
-///
-/// Here we would report a more complex "in constraint", like `'r
-/// in ['a, 'b, 'static]` (where `'r` is some region appearing in
-/// the hidden type).
-///
-/// # Constrain regions, not the hidden concrete type
-///
-/// Note that generating constraints on each region `Rc` is *not*
-/// the same as generating an outlives constraint on `Tc` itself.
-/// For example, if we had a function like this:
-///
-/// ```
-/// # #![feature(type_alias_impl_trait)]
-/// # fn main() {}
-/// # trait Foo<'a> {}
-/// # impl<'a, T> Foo<'a> for (&'a u32, T) {}
-/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> {
-///   (x, y)
-/// }
-///
-/// // Equivalent to:
-/// # mod dummy { use super::*;
-/// type FooReturn<'a, T> = impl Foo<'a>;
-/// #[define_opaque(FooReturn)]
-/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> {
-///   (x, y)
-/// }
-/// # }
-/// ```
-///
-/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0`
-/// is an inference variable). If we generated a constraint that
-/// `Tc: 'a`, then this would incorrectly require that `T: 'a` --
-/// but this is not necessary, because the opaque type we
-/// create will be allowed to reference `T`. So we only generate a
-/// constraint that `'0: 'a`.
-fn register_member_constraints<'tcx>(
-    typeck: &mut TypeChecker<'_, 'tcx>,
-    member_constraints: &mut MemberConstraintSet<'tcx, ty::RegionVid>,
-    opaque_type_key: OpaqueTypeKey<'tcx>,
-    OpaqueHiddenType { span, ty: hidden_ty }: OpaqueHiddenType<'tcx>,
-) {
-    let tcx = typeck.tcx();
-    let hidden_ty = typeck.infcx.resolve_vars_if_possible(hidden_ty);
-    debug!(?hidden_ty);
-
-    let variances = tcx.variances_of(opaque_type_key.def_id);
-    debug!(?variances);
-
-    // For a case like `impl Foo<'a, 'b>`, we would generate a constraint
-    // `'r in ['a, 'b, 'static]` for each region `'r` that appears in the
-    // hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`).
-    //
-    // `conflict1` and `conflict2` are the two region bounds that we
-    // detected which were unrelated. They are used for diagnostics.
-
-    // Create the set of choice regions: each region in the hidden
-    // type can be equal to any of the region parameters of the
-    // opaque type definition.
-    let fr_static = typeck.universal_regions.fr_static;
-    let choice_regions: Vec<_> = opaque_type_key
-        .args
-        .iter()
-        .enumerate()
-        .filter(|(i, _)| variances[*i] == ty::Invariant)
-        .filter_map(|(_, arg)| match arg.kind() {
-            GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)),
-            GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
-        })
-        .chain(iter::once(fr_static))
-        .collect();
-
-    // FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's
-    // not currently sound until we have existential regions.
-    hidden_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
-        tcx,
-        op: |r| {
-            member_constraints.add_member_constraint(
-                opaque_type_key,
-                hidden_ty,
-                span,
-                typeck.to_region_vid(r),
-                &choice_regions,
-            )
-        },
-    });
-}
-
-/// Visitor that requires that (almost) all regions in the type visited outlive
-/// `least_region`. We cannot use `push_outlives_components` because regions in
-/// closure signatures are not included in their outlives components. We need to
-/// ensure all regions outlive the given bound so that we don't end up with,
-/// say, `ReVar` appearing in a return type and causing ICEs when other
-/// functions end up with region constraints involving regions from other
-/// functions.
-///
-/// We also cannot use `for_each_free_region` because for closures it includes
-/// the regions parameters from the enclosing item.
-///
-/// We ignore any type parameters because impl trait values are assumed to
-/// capture all the in-scope type parameters.
-struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
-    tcx: TyCtxt<'tcx>,
-    op: OP,
-}
-
-impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
-where
-    OP: FnMut(ty::Region<'tcx>),
-{
-    fn visit_region(&mut self, r: ty::Region<'tcx>) {
-        match r.kind() {
-            // ignore bound regions, keep visiting
-            ty::ReBound(_, _) => {}
-            _ => (self.op)(r),
-        }
-    }
-
-    fn visit_ty(&mut self, ty: Ty<'tcx>) {
-        // We're only interested in types involving regions
-        if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
-            return;
-        }
-
-        match *ty.kind() {
-            ty::Closure(_, args) => {
-                // Skip lifetime parameters of the enclosing item(s)
-
-                for upvar in args.as_closure().upvar_tys() {
-                    upvar.visit_with(self);
-                }
-                args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
-            }
-
-            ty::CoroutineClosure(_, args) => {
-                // Skip lifetime parameters of the enclosing item(s)
-
-                for upvar in args.as_coroutine_closure().upvar_tys() {
-                    upvar.visit_with(self);
-                }
-
-                args.as_coroutine_closure().signature_parts_ty().visit_with(self);
-            }
-
-            ty::Coroutine(_, args) => {
-                // Skip lifetime parameters of the enclosing item(s)
-                // Also skip the witness type, because that has no free regions.
-
-                for upvar in args.as_coroutine().upvar_tys() {
-                    upvar.visit_with(self);
-                }
-                args.as_coroutine().return_ty().visit_with(self);
-                args.as_coroutine().yield_ty().visit_with(self);
-                args.as_coroutine().resume_ty().visit_with(self);
-            }
-
-            ty::Alias(kind, ty::AliasTy { def_id, args, .. })
-                if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) =>
-            {
-                // Skip lifetime parameters that are not captured, since they do
-                // not need member constraints registered for them; we'll erase
-                // them (and hopefully in the future replace them with placeholders).
-                for (v, s) in std::iter::zip(variances, args.iter()) {
-                    if *v != ty::Bivariant {
-                        s.visit_with(self);
-                    }
-                }
-            }
-
-            _ => {
-                ty.super_visit_with(self);
-            }
-        }
-    }
-}
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index e023300f1c2..7ac2dff12f7 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -124,8 +124,13 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
         // by using `ty_vid rel B` and then finally and end by equating `ty_vid` to
         // the opaque.
         let mut enable_subtyping = |ty, opaque_is_expected| {
-            let ty_vid = infcx.next_ty_var_id_in_universe(self.span(), ty::UniverseIndex::ROOT);
-
+            // We create the fresh inference variable in the highest universe.
+            // In theory we could limit it to the highest universe in the args of
+            // the opaque but that isn't really worth the effort.
+            //
+            // We'll make sure that the opaque type can actually name everything
+            // in its hidden type later on.
+            let ty_vid = infcx.next_ty_vid(self.span());
             let variance = if opaque_is_expected {
                 self.ambient_variance
             } else {
@@ -187,7 +192,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
                 types: &mut |_bound_ty: ty::BoundTy| {
                     unreachable!("we only replace regions in nll_relate, not types")
                 },
-                consts: &mut |_bound_var: ty::BoundVar| {
+                consts: &mut |_bound_const: ty::BoundConst| {
                     unreachable!("we only replace regions in nll_relate, not consts")
                 },
             };
@@ -216,7 +221,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
                     *ex_reg_var
                 } else {
                     let ex_reg_var =
-                        self.next_existential_region_var(true, br.kind.get_name(infcx.infcx.tcx));
+                        self.next_existential_region_var(br.kind.get_name(infcx.infcx.tcx));
                     debug!(?ex_reg_var);
                     reg_map.insert(br, ex_reg_var);
 
@@ -226,7 +231,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
             types: &mut |_bound_ty: ty::BoundTy| {
                 unreachable!("we only replace regions in nll_relate, not types")
             },
-            consts: &mut |_bound_var: ty::BoundVar| {
+            consts: &mut |_bound_const: ty::BoundConst| {
                 unreachable!("we only replace regions in nll_relate, not consts")
             },
         };
@@ -244,17 +249,9 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
     }
 
     #[instrument(skip(self), level = "debug")]
-    fn next_existential_region_var(
-        &mut self,
-        from_forall: bool,
-        name: Option<Symbol>,
-    ) -> ty::Region<'tcx> {
-        let origin = NllRegionVariableOrigin::Existential { from_forall };
-
-        let reg_var =
-            self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name));
-
-        reg_var
+    fn next_existential_region_var(&mut self, name: Option<Symbol>) -> ty::Region<'tcx> {
+        let origin = NllRegionVariableOrigin::Existential { name };
+        self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name))
     }
 
     #[instrument(skip(self), level = "debug")]
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index f138f265320..64a7b408434 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -40,6 +40,7 @@ use crate::BorrowckInferCtxt;
 use crate::renumber::RegionCtxt;
 
 #[derive(Debug)]
+#[derive(Clone)] // FIXME(#146079)
 pub(crate) struct UniversalRegions<'tcx> {
     indices: UniversalRegionIndices<'tcx>,
 
@@ -200,6 +201,7 @@ impl<'tcx> DefiningTy<'tcx> {
 }
 
 #[derive(Debug)]
+#[derive(Clone)] // FIXME(#146079)
 struct UniversalRegionIndices<'tcx> {
     /// For those regions that may appear in the parameter environment
     /// ('static and early-bound regions), we maintain a map from the
@@ -217,7 +219,7 @@ struct UniversalRegionIndices<'tcx> {
 
     /// Whether we've encountered an error region. If we have, cancel all
     /// outlives errors, as they are likely bogus.
-    pub tainted_by_errors: Cell<Option<ErrorGuaranteed>>,
+    pub encountered_re_error: Cell<Option<ErrorGuaranteed>>,
 }
 
 #[derive(Debug, PartialEq)]
@@ -442,8 +444,8 @@ impl<'tcx> UniversalRegions<'tcx> {
         self.fr_fn_body
     }
 
-    pub(crate) fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
-        self.indices.tainted_by_errors.get()
+    pub(crate) fn encountered_re_error(&self) -> Option<ErrorGuaranteed> {
+        self.indices.encountered_re_error.get()
     }
 }
 
@@ -706,7 +708,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
         UniversalRegionIndices {
             indices: global_mapping.chain(arg_mapping).collect(),
             fr_static,
-            tainted_by_errors: Cell::new(None),
+            encountered_re_error: Cell::new(None),
         }
     }
 
@@ -916,7 +918,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
         match r.kind() {
             ty::ReVar(..) => r.as_var(),
             ty::ReError(guar) => {
-                self.tainted_by_errors.set(Some(guar));
+                self.encountered_re_error.set(Some(guar));
                 // We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the
                 // `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if
                 // errors are being emitted and 2) it leaves the happy path unaffected.
@@ -969,13 +971,28 @@ fn for_each_late_bound_region_in_item<'tcx>(
     mir_def_id: LocalDefId,
     mut f: impl FnMut(ty::Region<'tcx>),
 ) {
-    if !tcx.def_kind(mir_def_id).is_fn_like() {
-        return;
-    }
+    let bound_vars = match tcx.def_kind(mir_def_id) {
+        DefKind::Fn | DefKind::AssocFn => {
+            tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id))
+        }
+        // We extract the bound vars from the deduced closure signature, since we may have
+        // only deduced that a param in the closure signature is late-bound from a constraint
+        // that we discover during typeck.
+        DefKind::Closure => {
+            let ty = tcx.type_of(mir_def_id).instantiate_identity();
+            match *ty.kind() {
+                ty::Closure(_, args) => args.as_closure().sig().bound_vars(),
+                ty::CoroutineClosure(_, args) => {
+                    args.as_coroutine_closure().coroutine_closure_sig().bound_vars()
+                }
+                ty::Coroutine(_, _) | ty::Error(_) => return,
+                _ => unreachable!("unexpected type for closure: {ty}"),
+            }
+        }
+        _ => return,
+    };
 
-    for (idx, bound_var) in
-        tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)).iter().enumerate()
-    {
+    for (idx, bound_var) in bound_vars.iter().enumerate() {
         if let ty::BoundVariableKind::Region(kind) = bound_var {
             let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind);
             let liberated_region = ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), kind);