about summary refs log tree commit diff
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2025-08-12 11:51:46 +0200
committerlcnr <rust@lcnr.de>2025-08-20 11:04:38 +0200
commit8365ad17da97bdc774aa167ad0d52c35d04745a0 (patch)
treebdcfcf291e0b7f28da7f1e82a56bda1b5fb84c7c
parente255a9b28af44240686928df243f3afbf6999ad1 (diff)
downloadrust-8365ad17da97bdc774aa167ad0d52c35d04745a0.tar.gz
rust-8365ad17da97bdc774aa167ad0d52c35d04745a0.zip
handle opaque types before region inference
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs44
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/opaque_types.rs54
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs41
-rw-r--r--compiler/rustc_borrowck/src/handle_placeholders.rs12
-rw-r--r--compiler/rustc_borrowck/src/lib.rs19
-rw-r--r--compiler/rustc_borrowck/src/member_constraints.rs226
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs288
-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.rs709
-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/root_cx.rs11
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs30
-rw-r--r--compiler/rustc_borrowck/src/type_check/opaque_types.rs333
-rw-r--r--compiler/rustc_trait_selection/src/opaque_types.rs1
-rw-r--r--tests/ui/async-await/async-borrowck-escaping-block-error.stderr6
-rw-r--r--tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr4
-rw-r--r--tests/ui/async-await/issue-76547.rs4
-rw-r--r--tests/ui/async-await/issue-76547.stderr34
-rw-r--r--tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr2
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs6
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr37
-rw-r--r--tests/ui/impl-trait/does-not-live-long-enough.stderr4
-rw-r--r--tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs4
-rw-r--r--tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs4
-rw-r--r--tests/ui/impl-trait/nested-rpit-hrtb.rs1
-rw-r--r--tests/ui/impl-trait/nested-rpit-hrtb.stderr14
-rw-r--r--tests/ui/impl-trait/no-anonymize-regions.rs4
-rw-r--r--tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr6
-rw-r--r--tests/ui/impl-trait/non-defining-uses/as-projection-term.rs2
-rw-r--r--tests/ui/impl-trait/wf-check-hidden-type.stderr2
-rw-r--r--tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs4
-rw-r--r--tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr40
-rw-r--r--tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs2
-rw-r--r--tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr16
-rw-r--r--tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr2
-rw-r--r--tests/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr4
-rw-r--r--tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr9
-rw-r--r--tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr9
-rw-r--r--tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs3
-rw-r--r--tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr7
-rw-r--r--tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr8
45 files changed, 1094 insertions, 1236 deletions
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/opaque_types.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs
index 83fe4a9f98f..e16af988f69 100644
--- a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs
@@ -13,6 +13,8 @@ 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;
@@ -26,13 +28,61 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, '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(self.infcx),
+                DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx),
                 DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => {
-                    self.infcx.dcx().emit_err(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();
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index fe4e1b6011e..c7d2267e5f7 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};
@@ -105,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.
@@ -323,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 } => {
@@ -378,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,
diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs
index 1614c112ab5..4661906fbeb 100644
--- a/compiler/rustc_borrowck/src/handle_placeholders.rs
+++ b/compiler/rustc_borrowck/src/handle_placeholders.rs
@@ -15,7 +15,6 @@ use tracing::debug;
 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 +29,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,
@@ -147,9 +145,10 @@ impl scc::Annotation for RegionTracker {
 
 /// 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
@@ -213,14 +212,13 @@ 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,
-        member_constraints,
         universe_causes,
         type_tests,
     } = constraints;
@@ -246,7 +244,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,
@@ -283,7 +280,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,
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index d76d6a04e6e..a081e0dee5d 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -78,7 +78,6 @@ mod dataflow;
 mod def_use;
 mod diagnostics;
 mod handle_placeholders;
-mod member_constraints;
 mod nll;
 mod path_utils;
 mod place_ext;
@@ -335,9 +334,10 @@ fn do_mir_borrowck<'tcx>(
 
     // Run the MIR type-checker.
     let MirTypeckResults {
-        constraints,
+        mut constraints,
         universal_region_relations,
-        opaque_type_values,
+        region_bound_pairs,
+        known_type_outlives_obligations,
         polonius_context,
     } = type_check::type_check(
         root_cx,
@@ -352,6 +352,17 @@ fn do_mir_borrowck<'tcx>(
         Rc::clone(&location_map),
     );
 
+    let opaque_type_errors = region_infer::opaque_types::handle_opaque_type_uses(
+        root_cx,
+        &infcx,
+        &body,
+        &universal_region_relations,
+        &region_bound_pairs,
+        &known_type_outlives_obligations,
+        &location_map,
+        &mut constraints,
+    );
+
     // Compute non-lexical lifetimes using the constraints computed
     // by typechecking the MIR body.
     let nll::NllOutput {
@@ -375,8 +386,6 @@ fn do_mir_borrowck<'tcx>(
         polonius_context,
     );
 
-    let opaque_type_errors = regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values);
-
     // Dump MIR results into a file, if that is enabled. This lets us
     // write unit-tests, as well as helping with debugging.
     nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);
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/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index c76c5c17431..d6d6b8a6bf2 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1,8 +1,6 @@
-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};
@@ -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;
@@ -120,20 +116,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 +132,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 +224,6 @@ enum Trace<'a, 'tcx> {
     StartRegion,
     FromGraph(&'a OutlivesConstraint<'tcx>),
     FromStatic(RegionVid),
-    FromMember(RegionVid, RegionVid, Span),
     NotVisited,
 }
 
@@ -363,7 +318,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 +339,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 +346,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,
@@ -550,19 +498,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 +542,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,146 +571,15 @@ 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_placeholder_universe_reached(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,
-            });
         }
     }
 
@@ -1376,13 +1174,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.scc_annotations[scc].max_nameable_universe()
     }
 
-    pub(crate) fn max_placeholder_universe_reached(
-        &self,
-        scc: ConstraintSccIndex,
-    ) -> UniverseIndex {
-        self.scc_annotations[scc].max_placeholder_universe_reached()
-    }
-
     /// Checks the final value for the free region `fr` to see if it
     /// grew too large. In particular, examine what `end(X)` points
     /// wound up in `fr`'s final value; for each `end(X)` where `X !=
@@ -1551,43 +1342,6 @@ 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
@@ -1711,20 +1465,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));
@@ -1771,15 +1511,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     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
@@ -2111,11 +1842,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/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
index 23c4554aa15..33c4879af98 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
@@ -1,192 +1,590 @@
+use std::iter;
+use std::rc::Rc;
+
+use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
+use rustc_hir::def_id::DefId;
+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, ConstraintCategory};
 use rustc_middle::ty::{
-    self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
+    self, DefiningScopeKind, 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::{
     InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
 };
+use rustc_trait_selection::solve::NoSolution;
+use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
 use tracing::{debug, instrument};
 
-use super::RegionInferenceContext;
-use crate::BorrowCheckRootCtxt;
+use super::reverse_sccs::ReverseSccGraph;
+use crate::consumers::RegionInferenceContext;
 use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
-use crate::universal_regions::RegionClassification;
+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};
+use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt};
+
+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(InvalidOpaqueTypeArgs<'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>,
+    },
 }
 
-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))]
-    pub(crate) fn infer_opaque_types(
-        &self,
-        root_cx: &mut BorrowCheckRootCtxt<'tcx>,
-        infcx: &InferCtxt<'tcx>,
-        opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
-    ) -> Vec<DeferredOpaqueTypeError<'tcx>> {
-        let mut errors = Vec::new();
-        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",
-                        )
-                    });
+/// This looks at all uses of opaque types in their defining scope inside
+/// of this function.
+///
+/// It first uses all defining uses to compute the actual concrete type of each
+/// opaque type definition.
+///
+/// We then apply this inferred type to actually check all uses of the opaque.
+pub(crate) fn handle_opaque_type_uses<'tcx>(
+    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+    infcx: &BorrowckInferCtxt<'tcx>,
+    body: &Body<'tcx>,
+    universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
+    region_bound_pairs: &RegionBoundPairs<'tcx>,
+    known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>],
+    location_map: &Rc<DenseLocationMap>,
+    constraints: &mut MirTypeckRegionConstraints<'tcx>,
+) -> Vec<DeferredOpaqueTypeError<'tcx>> {
+    let tcx = infcx.tcx;
+    let opaque_types = infcx.clone_opaque_types();
+    if opaque_types.is_empty() {
+        return Vec::new();
+    }
 
-                    arg_regions.push((vid, named));
-                    named
-                });
-            debug!(?opaque_type_key, ?arg_regions);
+    // We need to 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.
+    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(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(tcx, vid)
+            })
+        })
+        .collect::<Vec<_>>();
 
-            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);
+    debug!(?opaque_types);
 
-            let ty = match infcx
-                .infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type)
-            {
-                Ok(ty) => ty,
-                Err(err) => {
-                    errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err));
-                    continue;
+    let errors = compute_concrete_opaque_types(
+        root_cx,
+        infcx,
+        constraints,
+        universal_region_relations,
+        Rc::clone(location_map),
+        &opaque_types,
+    );
+
+    if !errors.is_empty() {
+        return errors;
+    }
+
+    let errors = apply_computed_concrete_opaque_types(
+        root_cx,
+        infcx,
+        body,
+        &universal_region_relations.universal_regions,
+        region_bound_pairs,
+        known_type_outlives_obligations,
+        constraints,
+        &opaque_types,
+    );
+
+    detect_opaque_types_added_while_handling_opaque_types(infcx, opaque_types_storage_num_entries);
+
+    errors
+}
+
+/// 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,
+    }
+}
+
+#[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.
+fn compute_concrete_opaque_types<'tcx>(
+    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+    infcx: &BorrowckInferCtxt<'tcx>,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
+    universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
+    location_map: Rc<DenseLocationMap>,
+    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(root_cx, &mut rcx, 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(root_cx, &rcx, &defining_uses, &mut errors);
+    errors
+}
+
+#[instrument(level = "debug", skip_all, ret)]
+fn collect_defining_uses<'tcx>(
+    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+    rcx: &mut RegionCtxt<'_, '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) = check_opaque_type_parameter_valid(
+            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 {
+                    InvalidOpaqueTypeArgs::AlreadyReported(guar) => root_cx
+                        .add_concrete_opaque_type(
+                            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>(
+    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+    rcx: &RegionCtxt<'_, '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)
                 }
             };
 
-            // 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
+        // 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_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,
+                },
+            ));
+        }
+        root_cx.add_concrete_opaque_type(
+            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))
                 {
-                    continue;
+                    Ok(arg_region)
+                } else if self.erase_unknown_regions {
+                    Ok(self.cx().lifetimes.re_erased)
+                } else {
+                    Err(r)
                 }
             }
+        }
+    }
 
-            root_cx.add_concrete_opaque_type(
-                opaque_type_key.def_id,
-                OpaqueHiddenType { span: concrete_type.span, ty },
-            );
+    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);
+        }
 
-            // 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 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)
+        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) =>
             {
-                errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(
-                    LifetimeMismatchOpaqueParam {
-                        arg: arg1,
-                        prev: arg2,
-                        span: prev_span,
-                        prev_span: concrete_type.span,
+                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.
+fn apply_computed_concrete_opaque_types<'tcx>(
+    root_cx: &mut BorrowCheckRootCtxt<'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>,
+    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) = root_cx.get_concrete_opaque_type(key.def_id) else {
+            assert!(tcx.use_typing_mode_borrowck(), "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",
+            );
+            root_cx.add_concrete_opaque_type(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",
+            ),
+        ) {
+            root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar));
         }
+    }
+    errors
+}
 
-        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`.
+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
@@ -231,8 +629,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 // 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()
+                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())
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/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs
index 9b1d12aede5..40c0448cf0b 100644
--- a/compiler/rustc_borrowck/src/root_cx.rs
+++ b/compiler/rustc_borrowck/src/root_cx.rs
@@ -2,7 +2,7 @@ use rustc_abi::FieldIdx;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::bug;
-use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{EarlyBinder, OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::ErrorGuaranteed;
 use smallvec::SmallVec;
 
@@ -19,7 +19,7 @@ pub(super) struct BorrowCheckRootCtxt<'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> {
@@ -67,6 +67,13 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
         }
     }
 
+    pub(super) fn get_concrete_opaque_type(
+        &mut self,
+        def_id: LocalDefId,
+    ) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> {
+        self.concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty))
+    }
+
     pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) {
         self.tainted_by_errors = Some(guar);
     }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index a960b96b91c..540700a6894 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;
@@ -42,7 +41,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;
@@ -67,12 +65,11 @@ macro_rules! span_mirbug {
     })
 }
 
-mod canonical;
+pub(crate) mod canonical;
 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 +111,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(),
     };
@@ -170,9 +166,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(
@@ -187,7 +180,6 @@ pub(crate) fn type_check<'tcx>(
     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.member_constraints = Default::default();
         constraints.type_tests = Default::default();
         root_cx.set_tainted_by_errors(guar);
         infcx.set_tainted_by_errors(guar);
@@ -196,7 +188,8 @@ pub(crate) fn type_check<'tcx>(
     MirTypeckResults {
         constraints,
         universal_region_relations,
-        opaque_type_values,
+        region_bound_pairs,
+        known_type_outlives_obligations,
         polonius_context,
     }
 }
@@ -245,7 +238,8 @@ 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) polonius_context: Option<PoloniusContext>,
 }
 
@@ -277,8 +271,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>>,
@@ -287,7 +279,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,
@@ -380,14 +372,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()
     }
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_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs
index d5bde9192d5..70a08f34f33 100644
--- a/compiler/rustc_trait_selection/src/opaque_types.rs
+++ b/compiler/rustc_trait_selection/src/opaque_types.rs
@@ -13,6 +13,7 @@ use crate::errors::NonGenericOpaqueTypeParam;
 use crate::regions::OutlivesEnvironmentBuildExt;
 use crate::traits::ObligationCtxt;
 
+#[derive(Debug)]
 pub enum InvalidOpaqueTypeArgs<'tcx> {
     AlreadyReported(ErrorGuaranteed),
     NotAParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_index: usize, span: Span },
diff --git a/tests/ui/async-await/async-borrowck-escaping-block-error.stderr b/tests/ui/async-await/async-borrowck-escaping-block-error.stderr
index 8410e7eca57..6ea03c99aa0 100644
--- a/tests/ui/async-await/async-borrowck-escaping-block-error.stderr
+++ b/tests/ui/async-await/async-borrowck-escaping-block-error.stderr
@@ -6,11 +6,7 @@ LL |     Box::new(async { x } )
    |              |
    |              may outlive borrowed value `x`
    |
-note: async block is returned here
-  --> $DIR/async-borrowck-escaping-block-error.rs:6:5
-   |
-LL |     Box::new(async { x } )
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   = note: async blocks are not executed immediately and must either take a reference or ownership of outside variables they use
 help: to force the async block to take ownership of `x` (and any other referenced variables), use the `move` keyword
    |
 LL |     Box::new(async move { x } )
diff --git a/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr b/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr
index e1f268116fc..96975f5ec72 100644
--- a/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr
+++ b/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr
@@ -53,7 +53,7 @@ LL | ||         let y = &*x;
 LL | ||         *x += 1;
 LL | ||         y
 LL | ||     })()
-   | ||______^_- argument requires that borrow lasts for `'1`
+   | ||______^_- opaque type requires that borrow lasts for `'1`
    | |_______|
    |         creates a temporary value which is freed while still in use
 LL |    }
@@ -102,7 +102,7 @@ LL | ||         let y = &*x;
 LL | ||         *x += 1;
 LL | ||         y
 LL | ||     })()
-   | ||______^_- argument requires that borrow lasts for `'1`
+   | ||______^_- opaque type requires that borrow lasts for `'1`
    | |_______|
    |         creates a temporary value which is freed while still in use
 LL |    }
diff --git a/tests/ui/async-await/issue-76547.rs b/tests/ui/async-await/issue-76547.rs
index 30a39c89437..24decf9b5f7 100644
--- a/tests/ui/async-await/issue-76547.rs
+++ b/tests/ui/async-await/issue-76547.rs
@@ -17,8 +17,8 @@ impl<'a> Future for ListFut<'a> {
 }
 
 async fn fut(bufs: &mut [&mut [u8]]) {
-    ListFut(bufs).await
     //~^ ERROR lifetime may not live long enough
+    ListFut(bufs).await
 }
 
 pub struct ListFut2<'a>(&'a mut [&'a mut [u8]]);
@@ -31,8 +31,8 @@ impl<'a> Future for ListFut2<'a> {
 }
 
 async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
-    ListFut2(bufs).await
     //~^ ERROR lifetime may not live long enough
+    ListFut2(bufs).await
 }
 
 fn main() {}
diff --git a/tests/ui/async-await/issue-76547.stderr b/tests/ui/async-await/issue-76547.stderr
index 4d96cce824b..5131f054234 100644
--- a/tests/ui/async-await/issue-76547.stderr
+++ b/tests/ui/async-await/issue-76547.stderr
@@ -1,12 +1,15 @@
 error: lifetime may not live long enough
-  --> $DIR/issue-76547.rs:20:13
+  --> $DIR/issue-76547.rs:19:38
    |
-LL | async fn fut(bufs: &mut [&mut [u8]]) {
-   |                    -     - let's call the lifetime of this reference `'2`
-   |                    |
-   |                    let's call the lifetime of this reference `'1`
-LL |     ListFut(bufs).await
-   |             ^^^^ this usage requires that `'1` must outlive `'2`
+LL |   async fn fut(bufs: &mut [&mut [u8]]) {
+   |  ____________________-_____-___________^
+   | |                    |     |
+   | |                    |     let's call the lifetime of this reference `'2`
+   | |                    let's call the lifetime of this reference `'1`
+LL | |
+LL | |     ListFut(bufs).await
+LL | | }
+   | |_^ opaque type requires that `'1` must outlive `'2`
    |
 help: consider introducing a named lifetime parameter
    |
@@ -14,14 +17,17 @@ LL | async fn fut<'a>(bufs: &'a mut [&'a mut [u8]]) {
    |             ++++        ++       ++
 
 error: lifetime may not live long enough
-  --> $DIR/issue-76547.rs:34:14
+  --> $DIR/issue-76547.rs:33:46
    |
-LL | async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
-   |                     -     - let's call the lifetime of this reference `'2`
-   |                     |
-   |                     let's call the lifetime of this reference `'1`
-LL |     ListFut2(bufs).await
-   |              ^^^^ this usage requires that `'1` must outlive `'2`
+LL |   async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
+   |  _____________________-_____-__________________^
+   | |                     |     |
+   | |                     |     let's call the lifetime of this reference `'2`
+   | |                     let's call the lifetime of this reference `'1`
+LL | |
+LL | |     ListFut2(bufs).await
+LL | | }
+   | |_^ opaque type requires that `'1` must outlive `'2`
    |
 help: consider introducing a named lifetime parameter
    |
diff --git a/tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr b/tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr
index 167b5ee4425..8fea6bf9580 100644
--- a/tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr
+++ b/tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr
@@ -9,7 +9,7 @@ LL |   async fn async_ret_impl_trait3<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trai
 LL | |
 LL | |     (a, b)
 LL | | }
-   | |_^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
+   | |_^ opaque type requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
 
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs
index c7a37a81848..123e23d6107 100644
--- a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs
@@ -5,12 +5,12 @@ struct Point {
     x: i32,
     y: i32,
 }
-fn foo () -> impl FnMut()->() {
+fn foo () -> impl FnMut() {
     let mut p = Point {x: 1, y: 2 };
     let mut c = || {
-    //~^ ERROR closure may outlive the current function, but it borrows `p`
-       p.x+=5;
+       p.x += 5;
        println!("{:?}", p);
+        //~^ ERROR `p` does not live long enough
     };
     c
 }
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr
index d47f0539b84..96910375e09 100644
--- a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr
@@ -1,22 +1,23 @@
-error[E0373]: closure may outlive the current function, but it borrows `p`, which is owned by the current function
-  --> $DIR/borrowck-4.rs:10:17
+error[E0597]: `p` does not live long enough
+  --> $DIR/borrowck-4.rs:12:25
    |
-LL |     let mut c = || {
-   |                 ^^ may outlive borrowed value `p`
-...
-LL |        println!("{:?}", p);
-   |                         - `p` is borrowed here
-   |
-note: closure is returned here
-  --> $DIR/borrowck-4.rs:15:5
-   |
-LL |     c
-   |     ^
-help: to force the closure to take ownership of `p` (and any other referenced variables), use the `move` keyword
-   |
-LL |     let mut c = move || {
-   |                 ++++
+LL |       let mut p = Point {x: 1, y: 2 };
+   |           ----- binding `p` declared here
+LL |       let mut c = || {
+   |                   --
+   |                   |
+   |  _________________value captured here
+   | |
+LL | |        p.x += 5;
+LL | |        println!("{:?}", p);
+   | |                         ^ borrowed value does not live long enough
+LL | |
+LL | |     };
+   | |_____- assignment requires that `p` is borrowed for `'static`
+LL |       c
+LL |   }
+   |   - `p` dropped here while still borrowed
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0373`.
+For more information about this error, try `rustc --explain E0597`.
diff --git a/tests/ui/impl-trait/does-not-live-long-enough.stderr b/tests/ui/impl-trait/does-not-live-long-enough.stderr
index cfc13298071..9f3918ce3e0 100644
--- a/tests/ui/impl-trait/does-not-live-long-enough.stderr
+++ b/tests/ui/impl-trait/does-not-live-long-enough.stderr
@@ -1,12 +1,14 @@
 error[E0373]: closure may outlive the current function, but it borrows `prefix`, which is owned by the current function
   --> $DIR/does-not-live-long-enough.rs:6:33
    |
+LL |     fn started_with<'a>(&'a self, prefix: &'a str) -> impl Iterator<Item=&'a str> {
+   |                     -- lifetime `'a` defined here
 LL |         self.data.iter().filter(|s| s.starts_with(prefix)).map(|s| s.as_ref())
    |                                 ^^^               ------ `prefix` is borrowed here
    |                                 |
    |                                 may outlive borrowed value `prefix`
    |
-note: closure is returned here
+note: function requires argument type to outlive `'a`
   --> $DIR/does-not-live-long-enough.rs:6:9
    |
 LL |         self.data.iter().filter(|s| s.starts_with(prefix)).map(|s| s.as_ref())
diff --git a/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs b/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs
index 3aa52fe2644..e9af42b30e9 100644
--- a/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs
+++ b/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs
@@ -1,5 +1,7 @@
 //@ check-pass
-// FIXME(-Znext-solver): enable this test
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 trait Id {
     type This;
diff --git a/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs
index 6860fc5e6a5..878b8746bfa 100644
--- a/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs
+++ b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs
@@ -1,7 +1,9 @@
 // Nested impl-traits can impose different member constraints on the same region variable.
 
 //@ check-pass
-// FIXME(-Znext-solver): enable this test
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 trait Cap<'a> {}
 impl<T> Cap<'_> for T {}
diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.rs b/tests/ui/impl-trait/nested-rpit-hrtb.rs
index 11d79bcff73..f4ff13d6c20 100644
--- a/tests/ui/impl-trait/nested-rpit-hrtb.rs
+++ b/tests/ui/impl-trait/nested-rpit-hrtb.rs
@@ -48,6 +48,7 @@ fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = im
 // This should resolve.
 fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {}
 //~^ ERROR implementation of `Bar` is not general enough
+//~| ERROR lifetime may not live long enough
 
 // This should resolve.
 fn two_htrb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Qux<'b>> {}
diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.stderr b/tests/ui/impl-trait/nested-rpit-hrtb.stderr
index 2e95ef370c7..93cd7bd788f 100644
--- a/tests/ui/impl-trait/nested-rpit-hrtb.stderr
+++ b/tests/ui/impl-trait/nested-rpit-hrtb.stderr
@@ -1,5 +1,5 @@
 error[E0261]: use of undeclared lifetime name `'b`
-  --> $DIR/nested-rpit-hrtb.rs:56:77
+  --> $DIR/nested-rpit-hrtb.rs:57:77
    |
 LL | fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {}
    |                                                                             ^^ undeclared lifetime
@@ -15,7 +15,7 @@ LL | fn two_htrb_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Siz
    |                     ++++
 
 error[E0261]: use of undeclared lifetime name `'b`
-  --> $DIR/nested-rpit-hrtb.rs:64:82
+  --> $DIR/nested-rpit-hrtb.rs:65:82
    |
 LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {}
    |                                                                                  ^^ undeclared lifetime
@@ -87,6 +87,12 @@ LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc
            but trait `Qux<'_>` is implemented for `()`
    = help: for that trait implementation, expected `()`, found `&'a ()`
 
+error: lifetime may not live long enough
+  --> $DIR/nested-rpit-hrtb.rs:49:93
+   |
+LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {}
+   |                                      -- lifetime `'b` defined here                          ^^ opaque type requires that `'b` must outlive `'static`
+
 error: implementation of `Bar` is not general enough
   --> $DIR/nested-rpit-hrtb.rs:49:93
    |
@@ -97,7 +103,7 @@ LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc =
    = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0`
 
 error[E0277]: the trait bound `for<'a, 'b> &'a (): Qux<'b>` is not satisfied
-  --> $DIR/nested-rpit-hrtb.rs:60:64
+  --> $DIR/nested-rpit-hrtb.rs:61:64
    |
 LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {}
    |                                                                ^^^^^^^^^^^^^^^^^^^^ the trait `for<'a, 'b> Qux<'b>` is not implemented for `&'a ()`
@@ -106,7 +112,7 @@ LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b>
            but trait `Qux<'_>` is implemented for `()`
    = help: for that trait implementation, expected `()`, found `&'a ()`
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
 
 Some errors have detailed explanations: E0261, E0277, E0657.
 For more information about an error, try `rustc --explain E0261`.
diff --git a/tests/ui/impl-trait/no-anonymize-regions.rs b/tests/ui/impl-trait/no-anonymize-regions.rs
index 4f7f7c0641c..a89c9e05a34 100644
--- a/tests/ui/impl-trait/no-anonymize-regions.rs
+++ b/tests/ui/impl-trait/no-anonymize-regions.rs
@@ -1,5 +1,7 @@
 //@ check-pass
-// FIXME(-Znext-solver): enable this test
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 // A regression test for an error in `redis` while working on #139587.
 //
diff --git a/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr b/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr
index 08c8365b180..96e242d5d48 100644
--- a/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr
+++ b/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr
@@ -1,12 +1,8 @@
-error[E0792]: expected generic lifetime parameter, found `'_`
+error: non-defining use of `impl Sized + '_` in the defining scope
   --> $DIR/as-projection-term.rs:14:19
    |
-LL | fn recur<'a>() -> impl Sized + 'a {
-   |                                -- this generic parameter must be used with a generic lifetime parameter
-...
 LL |     prove_proj(|| recur());
    |                   ^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0792`.
diff --git a/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs b/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs
index 4c5adc7a00a..f0cf333b6a1 100644
--- a/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs
+++ b/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs
@@ -12,6 +12,6 @@ fn recur<'a>() -> impl Sized + 'a {
     // inference variable at this point, we unify it with `opaque<'1>` and
     // end up ignoring that defining use as the hidden type is equal to its key.
     prove_proj(|| recur());
-    //[next]~^ ERROR expected generic lifetime parameter, found `'_`
+    //[next]~^ ERROR non-defining use of `impl Sized + '_` in the defining scope
 }
 fn main() {}
diff --git a/tests/ui/impl-trait/wf-check-hidden-type.stderr b/tests/ui/impl-trait/wf-check-hidden-type.stderr
index 86ba7aff54a..0cbfc502720 100644
--- a/tests/ui/impl-trait/wf-check-hidden-type.stderr
+++ b/tests/ui/impl-trait/wf-check-hidden-type.stderr
@@ -6,7 +6,7 @@ LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
    |         |
    |         lifetime `'a` defined here
 LL |     None::<&'_ &'_ ()>
-   |     ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
+   |     ^^^^^^^^^^^^^^^^^^ opaque type requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
 
diff --git a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs
index 80a4ab35527..5fa13152653 100644
--- a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs
+++ b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs
@@ -31,6 +31,7 @@ mod capture_tait {
     #[define_opaque(Opq2)]
     fn test() -> Opq2 {}
     //~^ ERROR hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds
+    //~| ERROR expected generic lifetime parameter, found `'a`
 }
 
 mod capture_tait_complex_pass {
@@ -52,7 +53,8 @@ mod capture_tait_complex_fail {
     type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>;
     #[define_opaque(Opq2)]
     fn test() -> Opq2 {}
-    //~^ ERROR hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds
+    //~^ ERROR expected generic lifetime parameter, found `'a`
+    //~| ERROR expected generic lifetime parameter, found `'a`
 }
 
 // non-defining use because 'static is used.
diff --git a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr
index 665b9a91696..8e2c02f15c5 100644
--- a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr
+++ b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr
@@ -16,6 +16,15 @@ LL |     fn test() -> impl for<'a> Trait<'a, Ty = impl Sized> {}
    |                           |                  opaque type defined here
    |                           hidden type `&'a ()` captures the lifetime `'a` as defined here
 
+error[E0792]: expected generic lifetime parameter, found `'a`
+  --> $DIR/higher-ranked-regions-basic.rs:32:23
+   |
+LL |     type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0>;
+   |               -- this generic parameter must be used with a generic lifetime parameter
+...
+LL |     fn test() -> Opq2 {}
+   |                       ^^
+
 error[E0700]: hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds
   --> $DIR/higher-ranked-regions-basic.rs:32:23
    |
@@ -28,7 +37,7 @@ LL |     fn test() -> Opq2 {}
    |                       ^^
 
 error[E0792]: expected generic lifetime parameter, found `'a`
-  --> $DIR/higher-ranked-regions-basic.rs:42:23
+  --> $DIR/higher-ranked-regions-basic.rs:43:23
    |
 LL |     type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'b>>; // <- Note 'b
    |               -- this generic parameter must be used with a generic lifetime parameter
@@ -37,7 +46,7 @@ LL |     fn test() -> Opq2 {}
    |                       ^^
 
 error[E0792]: expected generic lifetime parameter, found `'b`
-  --> $DIR/higher-ranked-regions-basic.rs:42:23
+  --> $DIR/higher-ranked-regions-basic.rs:43:23
    |
 LL |     type Opq0<'a> = impl Sized;
    |               -- this generic parameter must be used with a generic lifetime parameter
@@ -45,19 +54,26 @@ LL |     type Opq0<'a> = impl Sized;
 LL |     fn test() -> Opq2 {}
    |                       ^^
 
-error[E0700]: hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds
-  --> $DIR/higher-ranked-regions-basic.rs:54:23
+error[E0792]: expected generic lifetime parameter, found `'a`
+  --> $DIR/higher-ranked-regions-basic.rs:55:23
    |
-LL |     type Opq0<'a> = impl Sized;
-   |                     ---------- opaque type defined here
 LL |     type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a>>; // <- Note 'a
-   |                              -- hidden type `&'b ()` captures the lifetime `'b` as defined here
+   |               -- this generic parameter must be used with a generic lifetime parameter
+...
+LL |     fn test() -> Opq2 {}
+   |                       ^^
+
+error[E0792]: expected generic lifetime parameter, found `'a`
+  --> $DIR/higher-ranked-regions-basic.rs:55:23
+   |
+LL |     type Opq0<'a> = impl Sized;
+   |               -- this generic parameter must be used with a generic lifetime parameter
 ...
 LL |     fn test() -> Opq2 {}
    |                       ^^
 
 error[E0792]: expected generic lifetime parameter, found `'a`
-  --> $DIR/higher-ranked-regions-basic.rs:63:65
+  --> $DIR/higher-ranked-regions-basic.rs:65:65
    |
 LL |     type Opq0<'a, 'b> = impl Sized;
    |               -- this generic parameter must be used with a generic lifetime parameter
@@ -66,7 +82,7 @@ LL |     fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {}
    |                                                                 ^^
 
 error[E0792]: expected generic lifetime parameter, found `'a`
-  --> $DIR/higher-ranked-regions-basic.rs:72:60
+  --> $DIR/higher-ranked-regions-basic.rs:74:60
    |
 LL |     type Opq0<'a, 'b> = impl Sized;
    |               -- this generic parameter must be used with a generic lifetime parameter
@@ -75,7 +91,7 @@ LL |     fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {}
    |                                                            ^^
 
 error[E0792]: expected generic lifetime parameter, found `'a`
-  --> $DIR/higher-ranked-regions-basic.rs:82:23
+  --> $DIR/higher-ranked-regions-basic.rs:84:23
    |
 LL |     type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>;
    |               -- this generic parameter must be used with a generic lifetime parameter
@@ -84,7 +100,7 @@ LL |     fn test() -> Opq2 {}
    |                       ^^
 
 error[E0792]: expected generic lifetime parameter, found `'a`
-  --> $DIR/higher-ranked-regions-basic.rs:82:23
+  --> $DIR/higher-ranked-regions-basic.rs:84:23
    |
 LL |     type Opq0<'a, 'b> = impl Sized;
    |               -- this generic parameter must be used with a generic lifetime parameter
@@ -92,7 +108,7 @@ LL |     type Opq0<'a, 'b> = impl Sized;
 LL |     fn test() -> Opq2 {}
    |                       ^^
 
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
 
 Some errors have detailed explanations: E0700, E0792.
 For more information about an error, try `rustc --explain E0700`.
diff --git a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs
index c24672816ac..0f257a3cd24 100644
--- a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs
+++ b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs
@@ -18,7 +18,7 @@ mod bav {
     impl Bar for i32 {}
 
     fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> {
-        val.use_self() //~ ERROR cannot return value referencing function parameter
+        val.use_self() //~ ERROR `val` does not live long enough
     }
 }
 
diff --git a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr
index 505765d2b41..6f33b33580b 100644
--- a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr
+++ b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr
@@ -1,11 +1,17 @@
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0597]: `val` does not live long enough
   --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:21:9
    |
+LL |     fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> {
+   |               --  --- binding `val` declared here
+   |               |
+   |               lifetime `'a` defined here
 LL |         val.use_self()
-   |         ---^^^^^^^^^^^
+   |         ^^^-----------
    |         |
-   |         returns a value referencing data owned by the current function
-   |         `val` is borrowed here
+   |         borrowed value does not live long enough
+   |         opaque type requires that `val` is borrowed for `'a`
+LL |     }
+   |     - `val` dropped here while still borrowed
 
 error[E0515]: cannot return value referencing function parameter `val`
   --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:43:9
@@ -70,5 +76,5 @@ LL |         val.use_self()
 
 error: aborting due to 6 previous errors
 
-Some errors have detailed explanations: E0515, E0521.
+Some errors have detailed explanations: E0515, E0521, E0597.
 For more information about an error, try `rustc --explain E0515`.
diff --git a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr
index 3a1f685f16b..9a3cfa9987a 100644
--- a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr
+++ b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr
@@ -8,7 +8,7 @@ LL | |
 LL | |         .filter_map(|_| foo(src))
    | |_________________________________^
    |
-   = note: hidden type `FilterMap<std::slice::Iter<'static, i32>, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures lifetime `'_`
+   = note: hidden type `FilterMap<std::slice::Iter<'_, i32>, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures lifetime `'_`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr b/tests/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr
index c77ef79e7ed..62c591a0db6 100644
--- a/tests/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr
+++ b/tests/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr
@@ -8,7 +8,7 @@ LL | |
 LL | |             current: None,
 LL | |             remaining: self.0.iter(),
 LL | |         }
-   | |_________^ returning this value requires that `'1` must outlive `'static`
+   | |_________^ opaque type requires that `'1` must outlive `'static`
    |
 help: to declare that `impl Iterator<Item = Box<(dyn Foo + 'static)>>` captures data from argument `self`, you can add an explicit `'_` lifetime bound
    |
@@ -63,7 +63,7 @@ LL | |
 LL | |             current: None,
 LL | |             remaining: self.0.iter(),
 LL | |         }
-   | |_________^ returning this value requires that `'a` must outlive `'static`
+   | |_________^ opaque type requires that `'a` must outlive `'static`
    |
 help: to declare that `impl Iterator<Item = Box<(dyn Foo + 'static)>>` captures data from argument `self`, you can add an explicit `'a` lifetime bound
    |
diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs
index 902d6ca57e6..5c6ad2cc725 100644
--- a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs
+++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs
@@ -8,7 +8,7 @@ fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator<Item = Opaque<'a>> {
         Some(a)
     } else {
         None::<Opaque<'static>>
-        //~^ ERROR hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds
+        //~^ ERROR expected generic lifetime parameter, found `'static`
     }
 }
 
diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr
index 562ab4168b5..2cb8f03235a 100644
--- a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr
+++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr
@@ -1,15 +1,12 @@
-error[E0700]: hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds
+error[E0792]: expected generic lifetime parameter, found `'static`
   --> $DIR/different_args_considered_equal2.rs:10:9
    |
 LL | pub type Opaque<'a> = impl Sized;
-   |                       ---------- opaque type defined here
-...
-LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator<Item = Opaque<'a>> {
-   |            -- hidden type `*mut &'a str` captures the lifetime `'a` as defined here
+   |                 -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
 ...
 LL |         None::<Opaque<'static>>
    |         ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0700`.
+For more information about this error, try `rustc --explain E0792`.
diff --git a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr
index 4a5360c9922..43e887f36c5 100644
--- a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr
+++ b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr
@@ -1,15 +1,12 @@
-error[E0700]: hidden type for `Opaque<'x>` captures lifetime that does not appear in bounds
+error[E0792]: expected generic lifetime parameter, found `'_`
   --> $DIR/generic-not-strictly-equal.rs:34:5
    |
 LL | type Opaque<'a> = impl Copy + Captures<'a>;
-   |                   ------------------------ opaque type defined here
-...
-LL | fn test<'x>(_: Opaque<'x>) {
-   |         -- hidden type `&'x u8` captures the lifetime `'x` as defined here
+   |             -- this generic parameter must be used with a generic lifetime parameter
 ...
 LL |     relate(opaque, hidden); // defining use: Opaque<'?1> := u8
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0700`.
+For more information about this error, try `rustc --explain E0792`.
diff --git a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs
index c1059e3da33..42f363d0e57 100644
--- a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs
+++ b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs
@@ -32,8 +32,7 @@ fn test<'x>(_: Opaque<'x>) {
 
     ensure_outlives::<'x>(opaque); // outlives constraint: '?1: 'x
     relate(opaque, hidden); // defining use: Opaque<'?1> := u8
-    //[basic]~^ ERROR expected generic lifetime parameter, found `'_`
-    //[member_constraints]~^^ ERROR captures lifetime that does not appear in bounds
+    //~^ ERROR expected generic lifetime parameter, found `'_`
 }
 
 fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs
index baeba1d3de6..013651e8775 100644
--- a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs
@@ -9,7 +9,7 @@ impl Foo for () {
     type Assoc<'a, 'b> = impl Sized;
     fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> {
         let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x };
-        //~^ ERROR `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds
+        //~^ ERROR expected generic lifetime parameter, found `'_`
         x
     }
 }
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr
index a7d3e7f0be4..f399520e7fd 100644
--- a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr
@@ -1,13 +1,12 @@
-error[E0700]: hidden type for `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds
+error[E0792]: expected generic lifetime parameter, found `'_`
   --> $DIR/in-assoc-ty-early-bound.rs:11:60
    |
 LL |     type Assoc<'a, 'b> = impl Sized;
-   |                          ---------- opaque type defined here
+   |                -- this generic parameter must be used with a generic lifetime parameter
 LL |     fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> {
-   |            -- hidden type `&'a ()` captures the lifetime `'a` as defined here
 LL |         let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x };
    |                                                            ^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0700`.
+For more information about this error, try `rustc --explain E0792`.
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs
index 92c8a8f3216..21df53c43d7 100644
--- a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs
@@ -13,7 +13,7 @@ impl Foo for () {
     {
         let _ = |x: &'a ()| {
             let _: Self::Assoc<'a> = x;
-            //~^ ERROR `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bound
+            //~^ ERROR expected generic lifetime parameter, found `'_`
         };
     }
 }
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr
index 7ce4517fb1e..aaed03e739b 100644
--- a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr
@@ -1,14 +1,12 @@
-error[E0700]: hidden type for `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bounds
+error[E0792]: expected generic lifetime parameter, found `'_`
   --> $DIR/in-assoc-ty-early-bound2.rs:15:20
    |
 LL |     type Assoc<'a> = impl Sized;
-   |                      ---------- opaque type defined here
-LL |     fn bar<'a: 'a>()
-   |            -- hidden type `&'a ()` captures the lifetime `'a` as defined here
+   |                -- this generic parameter must be used with a generic lifetime parameter
 ...
 LL |             let _: Self::Assoc<'a> = x;
    |                    ^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0700`.
+For more information about this error, try `rustc --explain E0792`.