From 8dcb8e0759ea48df925968557bd81e37c084e2d0 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 7 Aug 2023 22:09:12 +0000
Subject: [PATCH 1/5] Unconditionally record lifetime mapping

---
 compiler/rustc_ast_lowering/src/lib.rs                   | 6 +-----
 compiler/rustc_hir/src/hir.rs                            | 2 +-
 compiler/rustc_hir_analysis/src/collect/predicates_of.rs | 2 +-
 compiler/rustc_ty_utils/src/implied_bounds.rs            | 2 +-
 4 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index d29e9f9b3f6..a8151ccfff3 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1663,11 +1663,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             );
             debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
 
-            let lifetime_mapping = if in_trait {
-                Some(&*self.arena.alloc_slice(&synthesized_lifetime_args))
-            } else {
-                None
-            };
+            let lifetime_mapping = self.arena.alloc_slice(&synthesized_lifetime_args);
 
             let opaque_ty_item = hir::OpaqueTy {
                 generics: this.arena.alloc(hir::Generics {
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index c6f8d1e211d..61353c7b1ec 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2675,7 +2675,7 @@ pub struct OpaqueTy<'hir> {
     ///
     /// This mapping associated a captured lifetime (first parameter) with the new
     /// early-bound lifetime that was generated for the opaque.
-    pub lifetime_mapping: Option<&'hir [(&'hir Lifetime, LocalDefId)]>,
+    pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)],
     /// Whether the opaque is a return-position impl trait (or async future)
     /// originating from a trait method. This makes it so that the opaque is
     /// lowered as an associated type.
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 83220be6883..f0a426722c3 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -59,7 +59,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local());
             let opaque_ty_node = tcx.hir().get(opaque_ty_id);
             let Node::Item(&Item {
-                kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping: Some(lifetime_mapping), .. }),
+                kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }),
                 ..
             }) = opaque_ty_node
             else {
diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs
index 7eb1042d2f8..01fe3a408e8 100644
--- a/compiler/rustc_ty_utils/src/implied_bounds.rs
+++ b/compiler/rustc_ty_utils/src/implied_bounds.rs
@@ -67,7 +67,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
                 let mut mapping = FxHashMap::default();
                 let generics = tcx.generics_of(def_id);
                 for &(lifetime, new_early_bound_def_id) in
-                    lifetime_mapping.expect("expected lifetime mapping for RPITIT")
+                    lifetime_mapping
                 {
                     if let Some(rbv::ResolvedArg::LateBound(_, _, def_id)) =
                         tcx.named_bound_var(lifetime.hir_id)

From 420ee167a8fbda2a078cb3a294fa1108c0180c98 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 7 Aug 2023 23:37:57 +0000
Subject: [PATCH 2/5] Install bidirectional outlives predicates for RPITITs
 (and RPITs) correctly

---
 .../src/collect/predicates_of.rs              | 94 ++++++-------------
 compiler/rustc_middle/src/ty/context.rs       | 69 ++++++++++++++
 compiler/rustc_ty_utils/src/implied_bounds.rs | 35 +++----
 .../in-trait/outlives-in-nested-rpit.rs       | 11 +++
 4 files changed, 121 insertions(+), 88 deletions(-)
 create mode 100644 tests/ui/impl-trait/in-trait/outlives-in-nested-rpit.rs

diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index f0a426722c3..495e663666c 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -2,16 +2,16 @@ use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
 use crate::bounds::Bounds;
 use crate::collect::ItemCtxt;
 use crate::constrained_generic_params as cgp;
-use hir::{HirId, Lifetime, Node};
+use hir::{HirId, Node};
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_middle::ty::{GenericPredicates, Generics, ImplTraitInTraitData, ToPredicate};
+use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate};
 use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_span::{Span, DUMMY_SP};
 
 /// Returns a list of all type predicates (explicit and implicit) for the definition with
 /// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
@@ -55,17 +55,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     use rustc_hir::*;
 
     match tcx.opt_rpitit_info(def_id.to_def_id()) {
-        Some(ImplTraitInTraitData::Trait { opaque_def_id, fn_def_id }) => {
-            let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local());
-            let opaque_ty_node = tcx.hir().get(opaque_ty_id);
-            let Node::Item(&Item {
-                kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }),
-                ..
-            }) = opaque_ty_node
-            else {
-                bug!("unexpected {opaque_ty_node:?}")
-            };
-
+        Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) => {
             let mut predicates = Vec::new();
 
             // RPITITs should inherit the predicates of their parent. This is
@@ -78,13 +68,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
 
             // We also install bidirectional outlives predicates for the RPITIT
             // to keep the duplicates lifetimes from opaque lowering in sync.
+            // We only need to compute bidirectional outlives for the duplicated
+            // opaque lifetimes, which explains the slicing below.
             compute_bidirectional_outlives_predicates(
                 tcx,
-                def_id,
-                lifetime_mapping.iter().map(|(lifetime, def_id)| {
-                    (**lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span))
-                }),
-                tcx.generics_of(def_id.to_def_id()),
+                &tcx.generics_of(def_id.to_def_id()).params
+                    [tcx.generics_of(fn_def_id).params.len()..],
                 &mut predicates,
             );
 
@@ -351,21 +340,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
         };
         debug!(?lifetimes);
 
-        let lifetime_mapping = std::iter::zip(lifetimes, ast_generics.params)
-            .map(|(arg, dup)| {
-                let hir::GenericArg::Lifetime(arg) = arg else { bug!() };
-                (**arg, dup)
-            })
-            .filter(|(_, dup)| matches!(dup.kind, hir::GenericParamKind::Lifetime { .. }))
-            .map(|(lifetime, dup)| (lifetime, (dup.def_id, dup.name.ident().name, dup.span)));
-
-        compute_bidirectional_outlives_predicates(
-            tcx,
-            def_id,
-            lifetime_mapping,
-            generics,
-            &mut predicates,
-        );
+        compute_bidirectional_outlives_predicates(tcx, &generics.params, &mut predicates);
         debug!(?predicates);
     }
 
@@ -379,41 +354,28 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
 /// enforce that these lifetimes stay in sync.
 fn compute_bidirectional_outlives_predicates<'tcx>(
     tcx: TyCtxt<'tcx>,
-    item_def_id: LocalDefId,
-    lifetime_mapping: impl Iterator<Item = (Lifetime, (LocalDefId, Symbol, Span))>,
-    generics: &Generics,
+    opaque_own_params: &[ty::GenericParamDef],
     predicates: &mut Vec<(ty::Clause<'tcx>, Span)>,
 ) {
-    let icx = ItemCtxt::new(tcx, item_def_id);
-
-    for (arg, (dup_def, name, span)) in lifetime_mapping {
-        let orig_region = icx.astconv().ast_region_to_region(&arg, None);
-        if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) {
-            // There is no late-bound lifetime to actually match up here, since the lifetime doesn't
-            // show up in the opaque's parent's args.
-            continue;
+    for param in opaque_own_params {
+        let orig_lifetime = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local());
+        if let ty::ReEarlyBound(..) = *orig_lifetime {
+            let dup_lifetime = ty::Region::new_early_bound(
+                tcx,
+                ty::EarlyBoundRegion { def_id: param.def_id, index: param.index, name: param.name },
+            );
+            let span = tcx.def_span(param.def_id);
+            predicates.push((
+                ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_lifetime, dup_lifetime))
+                    .to_predicate(tcx),
+                span,
+            ));
+            predicates.push((
+                ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_lifetime, orig_lifetime))
+                    .to_predicate(tcx),
+                span,
+            ));
         }
-
-        let Some(dup_index) = generics.param_def_id_to_index(icx.tcx, dup_def.to_def_id()) else {
-            bug!()
-        };
-
-        let dup_region = ty::Region::new_early_bound(
-            tcx,
-            ty::EarlyBoundRegion { def_id: dup_def.to_def_id(), index: dup_index, name },
-        );
-
-        predicates.push((
-            ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_region, dup_region))
-                .to_predicate(tcx),
-            span,
-        ));
-
-        predicates.push((
-            ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_region, orig_region))
-                .to_predicate(tcx),
-            span,
-        ));
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 5eef3e45f32..75a2c6cbd20 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1931,6 +1931,75 @@ impl<'tcx> TyCtxt<'tcx> {
         )
     }
 
+    /// Given the def-id of an early-bound lifetime on an RPIT corresponding to
+    /// a duplicated captured lifetime, map it back to the early- or late-bound
+    /// lifetime of the function from which it originally as captured. If it is
+    /// a late-bound lifetime, this will represent the liberated (`ReFree`) lifetime
+    /// of the signature.
+    // FIXME(RPITIT): if we ever synthesize new lifetimes for RPITITs and not just
+    // re-use the generics of the opaque, this function will need to be tweaked slightly.
+    pub fn map_rpit_lifetime_to_fn_lifetime(
+        self,
+        mut rpit_lifetime_param_def_id: LocalDefId,
+    ) -> ty::Region<'tcx> {
+        debug_assert!(
+            matches!(self.def_kind(rpit_lifetime_param_def_id), DefKind::LifetimeParam),
+            "{rpit_lifetime_param_def_id:?} is a {}",
+            self.def_descr(rpit_lifetime_param_def_id.to_def_id())
+        );
+
+        loop {
+            let parent = self.local_parent(rpit_lifetime_param_def_id);
+            let hir::OpaqueTy { lifetime_mapping, .. } =
+                self.hir().get_by_def_id(parent).expect_item().expect_opaque_ty();
+
+            let Some((lifetime, _)) = lifetime_mapping
+                .iter()
+                .find(|(_, duplicated_param)| *duplicated_param == rpit_lifetime_param_def_id)
+            else {
+                bug!("duplicated lifetime param should be present");
+            };
+
+            match self.named_bound_var(lifetime.hir_id) {
+                Some(resolve_bound_vars::ResolvedArg::EarlyBound(ebv)) => {
+                    let new_parent = self.parent(ebv);
+
+                    // If we map to another opaque, then it should be a parent
+                    // of the opaque we mapped from. Continue mapping.
+                    if matches!(self.def_kind(new_parent), DefKind::OpaqueTy) {
+                        debug_assert_eq!(self.parent(parent.to_def_id()), new_parent);
+                        rpit_lifetime_param_def_id = ebv.expect_local();
+                        continue;
+                    }
+
+                    let generics = self.generics_of(new_parent);
+                    return ty::Region::new_early_bound(
+                        self,
+                        ty::EarlyBoundRegion {
+                            def_id: ebv,
+                            index: generics
+                                .param_def_id_to_index(self, ebv)
+                                .expect("early-bound var should be present in fn generics"),
+                            name: self.hir().name(self.local_def_id_to_hir_id(ebv.expect_local())),
+                        },
+                    );
+                }
+                Some(resolve_bound_vars::ResolvedArg::LateBound(_, _, lbv)) => {
+                    let new_parent = self.parent(lbv);
+                    return ty::Region::new_free(
+                        self,
+                        new_parent,
+                        ty::BoundRegionKind::BrNamed(
+                            lbv,
+                            self.hir().name(self.local_def_id_to_hir_id(lbv.expect_local())),
+                        ),
+                    );
+                }
+                _ => bug!(),
+            }
+        }
+    }
+
     /// Whether the `def_id` counts as const fn in the current crate, considering all active
     /// feature gates
     pub fn is_const_fn(self, def_id: DefId) -> bool {
diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs
index 01fe3a408e8..ec2e0daaf88 100644
--- a/compiler/rustc_ty_utils/src/implied_bounds.rs
+++ b/compiler/rustc_ty_utils/src/implied_bounds.rs
@@ -2,7 +2,6 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
-use rustc_middle::middle::resolve_bound_vars as rbv;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
@@ -52,9 +51,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
             tcx.arena.alloc_from_iter(tys.into_iter().map(|ty| (ty, impl_spans.next().unwrap())))
         }
         DefKind::AssocTy if let Some(data) = tcx.opt_rpitit_info(def_id.to_def_id()) => match data {
-            ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => {
-                let hir::OpaqueTy { lifetime_mapping, .. } =
-                    *tcx.hir().expect_item(opaque_def_id.expect_local()).expect_opaque_ty();
+            ty::ImplTraitInTraitData::Trait { fn_def_id, .. } => {
                 // We need to remap all of the late-bound lifetimes in theassumed wf types
                 // of the fn (which are represented as ReFree) to the early-bound lifetimes
                 // of the RPITIT (which are represented by ReEarlyBound owned by the opaque).
@@ -66,28 +63,22 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
                 // predicates we insert in the `explicit_predicates_of` query for RPITITs.
                 let mut mapping = FxHashMap::default();
                 let generics = tcx.generics_of(def_id);
-                for &(lifetime, new_early_bound_def_id) in
-                    lifetime_mapping
-                {
-                    if let Some(rbv::ResolvedArg::LateBound(_, _, def_id)) =
-                        tcx.named_bound_var(lifetime.hir_id)
-                    {
-                        let name = tcx.hir().name(lifetime.hir_id);
-                        let index = generics
-                            .param_def_id_to_index(tcx, new_early_bound_def_id.to_def_id())
-                            .unwrap();
+
+                // For each captured opaque lifetime, if it's late-bound (`ReFree` in this case,
+                // since it has been liberated), map it back to the early-bound lifetime of
+                // the GAT. Since RPITITs also have all of the fn's generics, we slice only
+                // the end of the list corresponding to the opaque's generics.
+                for param in &generics.params[tcx.generics_of(fn_def_id).params.len()..] {
+                    let orig_lt = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local());
+                    if matches!(*orig_lt, ty::ReFree(..)) {
                         mapping.insert(
-                            ty::Region::new_free(
-                                tcx,
-                                fn_def_id,
-                                ty::BoundRegionKind::BrNamed(def_id, name),
-                            ),
+                            orig_lt,
                             ty::Region::new_early_bound(
                                 tcx,
                                 ty::EarlyBoundRegion {
-                                    def_id: new_early_bound_def_id.to_def_id(),
-                                    index,
-                                    name,
+                                    def_id: param.def_id,
+                                    index: param.index,
+                                    name: param.name,
                                 },
                             ),
                         );
diff --git a/tests/ui/impl-trait/in-trait/outlives-in-nested-rpit.rs b/tests/ui/impl-trait/in-trait/outlives-in-nested-rpit.rs
new file mode 100644
index 00000000000..6330242ceeb
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/outlives-in-nested-rpit.rs
@@ -0,0 +1,11 @@
+// check-pass
+
+#![feature(return_position_impl_trait_in_trait)]
+
+trait Foo {
+    fn early<'a, T: 'a>(x: &'a T) -> impl Iterator<Item = impl Into<&'a T>>;
+
+    fn late<'a, T>(x: &'a T) -> impl Iterator<Item = impl Into<&'a T>>;
+}
+
+fn main() {}

From 67703b9161e4869c5ac155e9a12317c1a2fee339 Mon Sep 17 00:00:00 2001
From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de>
Date: Mon, 7 Aug 2023 08:33:25 +0000
Subject: [PATCH 3/5] Stop using identity args for opaque type wf checks and
 instead load the args from the single use of a RPIT in its parent function's
 return type

---
 .../rustc_hir_analysis/src/check/check.rs     | 193 +++++++++++-------
 .../wf-check-rpit-lifetimes.rs                |  19 ++
 2 files changed, 134 insertions(+), 78 deletions(-)
 create mode 100644 tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs

diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 73627a818e5..f0a8ae9b7a8 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -13,7 +13,7 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::{ItemKind, Node, PathSegment};
 use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
+use rustc_infer::infer::{LateBoundRegionConversionTime, RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::{Obligation, TraitEngineExt as _};
 use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
 use rustc_middle::hir::nested_filter;
@@ -407,7 +407,38 @@ fn check_opaque_meets_bounds<'tcx>(
         .build();
     let ocx = ObligationCtxt::new(&infcx);
 
-    let args = GenericArgs::identity_for_item(tcx, def_id.to_def_id());
+    let mut args = GenericArgs::identity_for_item(tcx, def_id.to_def_id());
+    assert!(!args.has_escaping_bound_vars(), "{args:#?}");
+    if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
+        // Find use of the RPIT in the function signature and thus find the right args to
+        // convert it into the parameter space of the function signature. This is needed,
+        // because that's what `type_of` returns, against which we compare later.
+        let ret = tcx.fn_sig(defining_use_anchor).instantiate_identity().output();
+
+        let a = ret
+            .skip_binder()
+            .visit_with(&mut FindOpaqueTypeArgs {
+                tcx,
+                opaque: def_id.to_def_id(),
+                fn_def_id: defining_use_anchor.to_def_id(),
+                seen: Default::default(),
+                depth: ty::INNERMOST,
+            })
+            .break_value()
+            .ok_or_else(|| {
+                tcx.sess.delay_span_bug(
+                    tcx.def_span(defining_use_anchor),
+                    format!("return type of {defining_use_anchor:?} does not contain {def_id:?}"),
+                )
+            })?;
+        let a = infcx.instantiate_binder_with_fresh_vars(
+            span,
+            LateBoundRegionConversionTime::HigherRankedType,
+            ret.rebind(a),
+        );
+        assert!(!a.has_escaping_bound_vars(), "{a:#?}");
+        args = ty::EarlyBinder::bind(args).instantiate(tcx, a);
+    }
     let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
 
     // `ReErased` regions appear in the "parent_args" of closures/generators.
@@ -468,9 +499,10 @@ fn check_opaque_meets_bounds<'tcx>(
         }
     }
     // Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
-    for (key, mut ty) in infcx.take_opaque_types() {
+    for (mut key, mut ty) in infcx.take_opaque_types() {
         ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
-        sanity_check_found_hidden_type(tcx, key, ty.hidden_type, defining_use_anchor, origin)?;
+        key = infcx.resolve_vars_if_possible(key);
+        sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
     }
     Ok(())
 }
@@ -479,8 +511,6 @@ fn sanity_check_found_hidden_type<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: ty::OpaqueTypeKey<'tcx>,
     mut ty: ty::OpaqueHiddenType<'tcx>,
-    defining_use_anchor: LocalDefId,
-    origin: &hir::OpaqueTyOrigin,
 ) -> Result<(), ErrorGuaranteed> {
     if ty.ty.is_ty_var() {
         // Nothing was actually constrained.
@@ -493,29 +523,23 @@ fn sanity_check_found_hidden_type<'tcx>(
             return Ok(());
         }
     }
+    let strip_vars = |ty: Ty<'tcx>| {
+        ty.fold_with(&mut BottomUpFolder {
+            tcx,
+            ty_op: |t| t,
+            ct_op: |c| c,
+            lt_op: |l| match l.kind() {
+                RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
+                _ => l,
+            },
+        })
+    };
     // Closures frequently end up containing erased lifetimes in their final representation.
     // These correspond to lifetime variables that never got resolved, so we patch this up here.
-    ty.ty = ty.ty.fold_with(&mut BottomUpFolder {
-        tcx,
-        ty_op: |t| t,
-        ct_op: |c| c,
-        lt_op: |l| match l.kind() {
-            RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
-            _ => l,
-        },
-    });
+    ty.ty = strip_vars(ty.ty);
     // Get the hidden type.
-    let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
-    if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
-        if hidden_ty != ty.ty {
-            hidden_ty = find_and_apply_rpit_args(
-                tcx,
-                hidden_ty,
-                defining_use_anchor.to_def_id(),
-                key.def_id.to_def_id(),
-            )?;
-        }
-    }
+    let hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
+    let hidden_ty = strip_vars(hidden_ty);
 
     // If the hidden types differ, emit a type mismatch diagnostic.
     if hidden_ty == ty.ty {
@@ -563,67 +587,80 @@ fn sanity_check_found_hidden_type<'tcx>(
 ///     x
 /// }
 /// ```
-fn find_and_apply_rpit_args<'tcx>(
+struct FindOpaqueTypeArgs<'tcx> {
     tcx: TyCtxt<'tcx>,
-    mut hidden_ty: Ty<'tcx>,
-    function: DefId,
     opaque: DefId,
-) -> Result<Ty<'tcx>, ErrorGuaranteed> {
-    // Find use of the RPIT in the function signature and thus find the right args to
-    // convert it into the parameter space of the function signature. This is needed,
-    // because that's what `type_of` returns, against which we compare later.
-    let ret = tcx.fn_sig(function).instantiate_identity().output();
-    struct Visitor<'tcx> {
-        tcx: TyCtxt<'tcx>,
-        opaque: DefId,
-        seen: FxHashSet<DefId>,
-    }
-    impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> {
-        type BreakTy = GenericArgsRef<'tcx>;
+    seen: FxHashSet<DefId>,
+    fn_def_id: DefId,
+    depth: ty::DebruijnIndex,
+}
+impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FindOpaqueTypeArgs<'tcx> {
+    type BreakTy = GenericArgsRef<'tcx>;
 
-        #[instrument(level = "trace", skip(self), ret)]
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-            trace!("{:#?}", t.kind());
-            match t.kind() {
-                ty::Alias(ty::Opaque, alias) => {
-                    trace!(?alias.def_id);
-                    if alias.def_id == self.opaque {
-                        return ControlFlow::Break(alias.args);
-                    } else if self.seen.insert(alias.def_id) {
-                        for clause in self
-                            .tcx
-                            .explicit_item_bounds(alias.def_id)
-                            .iter_instantiated_copied(self.tcx, alias.args)
-                        {
-                            trace!(?clause);
-                            clause.visit_with(self)?;
+    #[instrument(level = "trace", skip(self), ret)]
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: &ty::Binder<'tcx, T>,
+    ) -> ControlFlow<Self::BreakTy> {
+        self.depth.shift_in(1);
+        let binder = t.super_visit_with(self);
+        self.depth.shift_out(1);
+        binder
+    }
+
+    #[instrument(level = "trace", skip(self), ret)]
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        trace!("{:#?}", t.kind());
+        match t.kind() {
+            ty::Alias(ty::Opaque, alias) => {
+                trace!(?alias.def_id);
+                if alias.def_id == self.opaque {
+                    let args = self.tcx.fold_regions(alias.args, |re, depth| {
+                        if let ty::ReLateBound(index, bv) = re.kind() {
+                            if depth != ty::INNERMOST {
+                                return ty::Region::new_error_with_message(
+                                    self.tcx,
+                                    self.tcx.def_span(self.opaque),
+                                    "opaque type behind meaningful binders are not supported yet",
+                                );
+                            }
+                            ty::Region::new_late_bound(
+                                self.tcx,
+                                index.shifted_out_to_binder(self.depth),
+                                bv,
+                            )
+                        } else {
+                            re
                         }
+                    });
+                    return ControlFlow::Break(args);
+                } else if self.seen.insert(alias.def_id) {
+                    for clause in self
+                        .tcx
+                        .explicit_item_bounds(alias.def_id)
+                        .iter_instantiated_copied(self.tcx, alias.args)
+                    {
+                        trace!(?clause);
+                        clause.visit_with(self)?;
                     }
                 }
-                ty::Alias(ty::Weak, alias) => {
-                    self.tcx
-                        .type_of(alias.def_id)
-                        .instantiate(self.tcx, alias.args)
-                        .visit_with(self)?;
-                }
-                _ => (),
             }
-
-            t.super_visit_with(self)
+            ty::Alias(ty::Projection, alias) => {
+                if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx.opt_rpitit_info(alias.def_id) && fn_def_id == self.fn_def_id {
+                    self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).visit_with(self)?;
+                }
+            }
+            ty::Alias(ty::Weak, alias) => {
+                self.tcx
+                    .type_of(alias.def_id)
+                    .instantiate(self.tcx, alias.args)
+                    .visit_with(self)?;
+            }
+            _ => (),
         }
+
+        t.super_visit_with(self)
     }
-    if let ControlFlow::Break(args) =
-        ret.visit_with(&mut Visitor { tcx, opaque, seen: Default::default() })
-    {
-        trace!(?args);
-        trace!("expected: {hidden_ty:#?}");
-        hidden_ty = ty::EarlyBinder::bind(hidden_ty).instantiate(tcx, args);
-        trace!("expected: {hidden_ty:#?}");
-    } else {
-        tcx.sess
-            .delay_span_bug(tcx.def_span(function), format!("{ret:?} does not contain {opaque:?}"));
-    }
-    Ok(hidden_ty)
 }
 
 fn is_enum_of_nonnullable_ptr<'tcx>(
diff --git a/tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs b/tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs
new file mode 100644
index 00000000000..b92e15aad56
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs
@@ -0,0 +1,19 @@
+//check-pass
+
+pub struct Key;
+#[derive(Clone)]
+pub struct Value;
+
+use std::collections::HashMap;
+
+pub struct DiagnosticBuilder<'db> {
+    inner: HashMap<&'db Key, Vec<&'db Value>>,
+}
+
+impl<'db> DiagnosticBuilder<'db> {
+    pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'db Key, impl Iterator<Item = &'a Value>)> {
+        self.inner.iter().map(|(key, values)| (*key, values.iter().map(|v| *v)))
+    }
+}
+
+fn main() {}

From ef2a611803964e50853333e34d41f4347434513c Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 7 Aug 2023 23:54:04 +0000
Subject: [PATCH 4/5] Simplify via map_rpit_lifetime_to_fn_lifetime

---
 .../rustc_hir_analysis/src/check/check.rs     | 155 ++----------------
 compiler/rustc_middle/src/ty/context.rs       |  11 +-
 2 files changed, 21 insertions(+), 145 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index f0a8ae9b7a8..1c4ee9a2bc3 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -13,7 +13,7 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::{ItemKind, Node, PathSegment};
 use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::{LateBoundRegionConversionTime, RegionVariableOrigin, TyCtxtInferExt};
+use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::{Obligation, TraitEngineExt as _};
 use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
 use rustc_middle::hir::nested_filter;
@@ -407,38 +407,17 @@ fn check_opaque_meets_bounds<'tcx>(
         .build();
     let ocx = ObligationCtxt::new(&infcx);
 
-    let mut args = GenericArgs::identity_for_item(tcx, def_id.to_def_id());
-    assert!(!args.has_escaping_bound_vars(), "{args:#?}");
-    if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
-        // Find use of the RPIT in the function signature and thus find the right args to
-        // convert it into the parameter space of the function signature. This is needed,
-        // because that's what `type_of` returns, against which we compare later.
-        let ret = tcx.fn_sig(defining_use_anchor).instantiate_identity().output();
-
-        let a = ret
-            .skip_binder()
-            .visit_with(&mut FindOpaqueTypeArgs {
+    let args = match *origin {
+        hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => {
+            GenericArgs::identity_for_item(tcx, parent).extend_to(
                 tcx,
-                opaque: def_id.to_def_id(),
-                fn_def_id: defining_use_anchor.to_def_id(),
-                seen: Default::default(),
-                depth: ty::INNERMOST,
-            })
-            .break_value()
-            .ok_or_else(|| {
-                tcx.sess.delay_span_bug(
-                    tcx.def_span(defining_use_anchor),
-                    format!("return type of {defining_use_anchor:?} does not contain {def_id:?}"),
-                )
-            })?;
-        let a = infcx.instantiate_binder_with_fresh_vars(
-            span,
-            LateBoundRegionConversionTime::HigherRankedType,
-            ret.rebind(a),
-        );
-        assert!(!a.has_escaping_bound_vars(), "{a:#?}");
-        args = ty::EarlyBinder::bind(args).instantiate(tcx, a);
-    }
+                def_id.to_def_id(),
+                |param, _| tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into(),
+            )
+        }
+        hir::OpaqueTyOrigin::TyAlias { .. } => GenericArgs::identity_for_item(tcx, def_id),
+    };
+
     let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
 
     // `ReErased` regions appear in the "parent_args" of closures/generators.
@@ -551,118 +530,6 @@ fn sanity_check_found_hidden_type<'tcx>(
     }
 }
 
-/// In case it is in a nested opaque type, find that opaque type's
-/// usage in the function signature and use the generic arguments from the usage site.
-/// We need to do because RPITs ignore the lifetimes of the function,
-/// as they have their own copies of all the lifetimes they capture.
-/// So the only way to get the lifetimes represented in terms of the function,
-/// is to look how they are used in the function signature (or do some other fancy
-/// recording of this mapping at ast -> hir lowering time).
-///
-/// As an example:
-/// ```text
-/// trait Id {
-///     type Assoc;
-/// }
-/// impl<'a> Id for &'a () {
-///     type Assoc = &'a ();
-/// }
-/// fn func<'a>(x: &'a ()) -> impl Id<Assoc = impl Sized + 'a> { x }
-/// // desugared to
-/// fn func<'a>(x: &'a () -> Outer<'a> where <Outer<'a> as Id>::Assoc = Inner<'a> {
-///     // Note that in contrast to other nested items, RPIT type aliases can
-///     // access their parents' generics.
-///
-///     // hidden type is `&'aDupOuter ()`
-///     // During wfcheck the hidden type of `Inner<'aDupOuter>` is `&'a ()`, but
-///     // `typeof(Inner<'aDupOuter>) = &'aDupOuter ()`.
-///     // So we walk the signature of `func` to find the use of `Inner<'a>`
-///     // and then use that to replace the lifetimes in the hidden type, obtaining
-///     // `&'a ()`.
-///     type Outer<'aDupOuter> = impl Id<Assoc = Inner<'aDupOuter>>;
-///
-///     // hidden type is `&'aDupInner ()`
-///     type Inner<'aDupInner> = impl Sized + 'aDupInner;
-///
-///     x
-/// }
-/// ```
-struct FindOpaqueTypeArgs<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    opaque: DefId,
-    seen: FxHashSet<DefId>,
-    fn_def_id: DefId,
-    depth: ty::DebruijnIndex,
-}
-impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FindOpaqueTypeArgs<'tcx> {
-    type BreakTy = GenericArgsRef<'tcx>;
-
-    #[instrument(level = "trace", skip(self), ret)]
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &ty::Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
-        self.depth.shift_in(1);
-        let binder = t.super_visit_with(self);
-        self.depth.shift_out(1);
-        binder
-    }
-
-    #[instrument(level = "trace", skip(self), ret)]
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        trace!("{:#?}", t.kind());
-        match t.kind() {
-            ty::Alias(ty::Opaque, alias) => {
-                trace!(?alias.def_id);
-                if alias.def_id == self.opaque {
-                    let args = self.tcx.fold_regions(alias.args, |re, depth| {
-                        if let ty::ReLateBound(index, bv) = re.kind() {
-                            if depth != ty::INNERMOST {
-                                return ty::Region::new_error_with_message(
-                                    self.tcx,
-                                    self.tcx.def_span(self.opaque),
-                                    "opaque type behind meaningful binders are not supported yet",
-                                );
-                            }
-                            ty::Region::new_late_bound(
-                                self.tcx,
-                                index.shifted_out_to_binder(self.depth),
-                                bv,
-                            )
-                        } else {
-                            re
-                        }
-                    });
-                    return ControlFlow::Break(args);
-                } else if self.seen.insert(alias.def_id) {
-                    for clause in self
-                        .tcx
-                        .explicit_item_bounds(alias.def_id)
-                        .iter_instantiated_copied(self.tcx, alias.args)
-                    {
-                        trace!(?clause);
-                        clause.visit_with(self)?;
-                    }
-                }
-            }
-            ty::Alias(ty::Projection, alias) => {
-                if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx.opt_rpitit_info(alias.def_id) && fn_def_id == self.fn_def_id {
-                    self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).visit_with(self)?;
-                }
-            }
-            ty::Alias(ty::Weak, alias) => {
-                self.tcx
-                    .type_of(alias.def_id)
-                    .instantiate(self.tcx, alias.args)
-                    .visit_with(self)?;
-            }
-            _ => (),
-        }
-
-        t.super_visit_with(self)
-    }
-}
-
 fn is_enum_of_nonnullable_ptr<'tcx>(
     tcx: TyCtxt<'tcx>,
     adt_def: AdtDef<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 75a2c6cbd20..e25ca75bba8 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1995,7 +1995,16 @@ impl<'tcx> TyCtxt<'tcx> {
                         ),
                     );
                 }
-                _ => bug!(),
+                Some(resolve_bound_vars::ResolvedArg::Error(guar)) => {
+                    return ty::Region::new_error(self, guar);
+                }
+                _ => {
+                    return ty::Region::new_error_with_message(
+                        self,
+                        lifetime.ident.span,
+                        "cannot resolve lifetime",
+                    );
+                }
             }
         }
     }

From 0adf7048d23649d561bb121d7e1ab00b4c0269b0 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 8 Aug 2023 00:28:17 +0000
Subject: [PATCH 5/5] add'l test

---
 .../mapping-duplicated-lifetimes-issue-114597.rs  | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
 create mode 100644 tests/ui/impl-trait/mapping-duplicated-lifetimes-issue-114597.rs

diff --git a/tests/ui/impl-trait/mapping-duplicated-lifetimes-issue-114597.rs b/tests/ui/impl-trait/mapping-duplicated-lifetimes-issue-114597.rs
new file mode 100644
index 00000000000..a2dd0a9308d
--- /dev/null
+++ b/tests/ui/impl-trait/mapping-duplicated-lifetimes-issue-114597.rs
@@ -0,0 +1,15 @@
+// check-pass
+// issue: 114597
+// edition: 2021
+
+struct A<'a> {
+    dat: &'a (),
+}
+
+impl<'a> A<'a> {
+    async fn a(&self) -> impl Iterator<Item = std::iter::Repeat<()>> {
+        std::iter::repeat(()).map(|()| std::iter::repeat(()))
+    }
+}
+
+fn main() {}