diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 0e99df4f581..4df572f6199 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -284,6 +284,17 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
     };
     check_object_unsafe_self_trait_by_name(tcx, trait_item);
     check_associated_item(tcx, def_id, span, method_sig);
+
+    if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
+        for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) {
+            check_associated_item(
+                tcx,
+                assoc_ty_def_id.expect_local(),
+                tcx.def_span(assoc_ty_def_id),
+                None,
+            );
+        }
+    }
 }
 
 /// Require that the user writes where clauses on GATs for the implicit
@@ -1466,13 +1477,6 @@ fn check_fn_or_method<'tcx>(
 
     check_where_clauses(wfcx, span, def_id);
 
-    check_return_position_impl_trait_in_trait_bounds(
-        wfcx,
-        def_id,
-        sig.output(),
-        hir_decl.output.span(),
-    );
-
     if sig.abi == Abi::RustCall {
         let span = tcx.def_span(def_id);
         let has_implicit_self = hir_decl.implicit_self != hir::ImplicitSelfKind::None;
@@ -1507,87 +1511,6 @@ fn check_fn_or_method<'tcx>(
     }
 }
 
-/// Basically `check_associated_type_bounds`, but separated for now and should be
-/// deduplicated when RPITITs get lowered into real associated items.
-#[tracing::instrument(level = "trace", skip(wfcx))]
-fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
-    wfcx: &WfCheckingCtxt<'_, 'tcx>,
-    fn_def_id: LocalDefId,
-    fn_output: Ty<'tcx>,
-    span: Span,
-) {
-    let tcx = wfcx.tcx();
-    let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) else {
-        return;
-    };
-    if assoc_item.container != ty::AssocItemContainer::TraitContainer {
-        return;
-    }
-    fn_output.visit_with(&mut ImplTraitInTraitFinder {
-        wfcx,
-        fn_def_id,
-        depth: ty::INNERMOST,
-        seen: FxHashSet::default(),
-    });
-}
-
-// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): Even with the new lowering
-// strategy, we can't just call `check_associated_item` on the new RPITITs,
-// because tests like `tests/ui/async-await/in-trait/implied-bounds.rs` will fail.
-// That's because we need to check that the bounds of the RPITIT hold using
-// the special args that we create during opaque type lowering, otherwise we're
-// getting a bunch of early bound and free regions mixed up... Haven't looked too
-// deep into this, though.
-struct ImplTraitInTraitFinder<'a, 'tcx> {
-    wfcx: &'a WfCheckingCtxt<'a, 'tcx>,
-    fn_def_id: LocalDefId,
-    depth: ty::DebruijnIndex,
-    seen: FxHashSet<DefId>,
-}
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
-    type BreakTy = !;
-
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<!> {
-        let tcx = self.wfcx.tcx();
-        if let ty::Alias(ty::Opaque, unshifted_opaque_ty) = *ty.kind()
-            && self.seen.insert(unshifted_opaque_ty.def_id)
-            && let Some(opaque_def_id) = unshifted_opaque_ty.def_id.as_local()
-            && let origin = tcx.opaque_type_origin(opaque_def_id)
-            && let hir::OpaqueTyOrigin::FnReturn(source) | hir::OpaqueTyOrigin::AsyncFn(source) = origin
-            && source == self.fn_def_id
-        {
-            let opaque_ty = tcx.fold_regions(unshifted_opaque_ty, |re, _depth| {
-                match re.kind() {
-                    ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReError(_) | ty::ReStatic => re,
-                    r => bug!("unexpected region: {r:?}"),
-                }
-            });
-            for (bound, bound_span) in tcx
-                .explicit_item_bounds(opaque_ty.def_id)
-                .iter_instantiated_copied(tcx, opaque_ty.args)
-            {
-                let bound = self.wfcx.normalize(bound_span, None, bound);
-                self.wfcx.register_obligations(traits::wf::predicate_obligations(
-                    self.wfcx.infcx,
-                    self.wfcx.param_env,
-                    self.wfcx.body_def_id,
-                    bound.as_predicate(),
-                    bound_span,
-                ));
-                // Set the debruijn index back to innermost here, since we already eagerly
-                // shifted the args that we use to generate these bounds. This is unfortunately
-                // subtly different behavior than the `ImplTraitInTraitFinder` we use in `param_env`,
-                // but that function doesn't actually need to normalize the bound it's visiting
-                // (whereas we have to do so here)...
-                let old_depth = std::mem::replace(&mut self.depth, ty::INNERMOST);
-                bound.visit_with(self);
-                self.depth = old_depth;
-            }
-        }
-        ty.super_visit_with(self)
-    }
-}
-
 const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \
      `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
      of the previous types except `Self`)";
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index a8815ee0908..7dbfe0e0cb0 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -246,6 +246,7 @@ provide! { tcx, def_id, other, cdata,
         debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy);
         cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index)
     }
+    assumed_wf_types_for_rpitit => { table }
     collect_return_position_impl_trait_in_trait_tys => {
         Ok(cdata
             .root
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 8f065dd6b58..0025dc9033c 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1560,6 +1560,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         }
         if let Some(rpitit_info) = item.opt_rpitit_info {
             record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info);
+            if matches!(rpitit_info, ty::ImplTraitInTraitData::Trait { .. }) {
+                record_array!(
+                    self.tables.assumed_wf_types_for_rpitit[def_id]
+                        <- self.tcx.assumed_wf_types_for_rpitit(def_id)
+                );
+            }
         }
     }
 
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 0bc16fc64ff..09e21fd4455 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -457,6 +457,7 @@ define_tables! {
     trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, ty::EarlyBinder<Ty<'static>>>>>,
     doc_link_resolutions: Table<DefIndex, LazyValue<DocLinkResMap>>,
     doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>,
+    assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>,
 }
 
 #[derive(TyEncodable, TyDecodable)]
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index fb796587352..0cdb7714729 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -885,6 +885,13 @@ rustc_queries! {
         desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) }
     }
 
+    /// We need to store the assumed_wf_types for an RPITIT so that impls of foreign
+    /// traits with return-position impl trait in traits can inherit the right wf types.
+    query assumed_wf_types_for_rpitit(key: DefId) -> &'tcx [(Ty<'tcx>, Span)] {
+        desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) }
+        separate_provide_extern
+    }
+
     /// Computes the signature of the function.
     query fn_sig(key: DefId) -> ty::EarlyBinder<ty::PolyFnSig<'tcx>> {
         desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) }
diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs
index fa1976510cf..7eb1042d2f8 100644
--- a/compiler/rustc_ty_utils/src/implied_bounds.rs
+++ b/compiler/rustc_ty_utils/src/implied_bounds.rs
@@ -1,13 +1,22 @@
+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;
 use std::iter;
 
 pub fn provide(providers: &mut Providers) {
-    *providers = Providers { assumed_wf_types, ..*providers };
+    *providers = Providers {
+        assumed_wf_types,
+        assumed_wf_types_for_rpitit: |tcx, def_id| {
+            assert!(tcx.is_impl_trait_in_trait(def_id.to_def_id()));
+            tcx.assumed_wf_types(def_id)
+        },
+        ..*providers
+    };
 }
 
 fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] {
@@ -42,6 +51,81 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
             let mut impl_spans = impl_spans(tcx, def_id);
             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();
+                // 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).
+                // Luckily, this is very easy to do because we already have that mapping
+                // stored in the HIR of this RPITIT.
+                //
+                // Side-note: We don't really need to do this remapping for early-bound
+                // lifetimes because they're already "linked" by the bidirectional outlives
+                // 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.expect("expected lifetime mapping for RPITIT")
+                {
+                    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();
+                        mapping.insert(
+                            ty::Region::new_free(
+                                tcx,
+                                fn_def_id,
+                                ty::BoundRegionKind::BrNamed(def_id, name),
+                            ),
+                            ty::Region::new_early_bound(
+                                tcx,
+                                ty::EarlyBoundRegion {
+                                    def_id: new_early_bound_def_id.to_def_id(),
+                                    index,
+                                    name,
+                                },
+                            ),
+                        );
+                    }
+                }
+                // FIXME: This could use a real folder, I guess.
+                let remapped_wf_tys = tcx.fold_regions(
+                    tcx.assumed_wf_types(fn_def_id.expect_local()).to_vec(),
+                    |region, _| {
+                        // If `region` is a `ReFree` that is captured by the
+                        // opaque, remap it to its corresponding the early-
+                        // bound region.
+                        if let Some(remapped_region) = mapping.get(&region) {
+                            *remapped_region
+                        } else {
+                            region
+                        }
+                    },
+                );
+                tcx.arena.alloc_from_iter(remapped_wf_tys)
+            }
+            // Assumed wf types for RPITITs in an impl just inherit (and instantiate)
+            // the assumed wf types of the trait's RPITIT GAT.
+            ty::ImplTraitInTraitData::Impl { .. } => {
+                let impl_def_id = tcx.local_parent(def_id);
+                let rpitit_def_id = tcx.associated_item(def_id).trait_item_def_id.unwrap();
+                let args = ty::GenericArgs::identity_for_item(tcx, def_id).rebase_onto(
+                    tcx,
+                    impl_def_id.to_def_id(),
+                    tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity().args,
+                );
+                tcx.arena.alloc_from_iter(
+                    ty::EarlyBinder::bind(tcx.assumed_wf_types_for_rpitit(rpitit_def_id))
+                        .iter_instantiated_copied(tcx, args)
+                        .chain(tcx.assumed_wf_types(impl_def_id).into_iter().copied()),
+                )
+            }
+        },
         DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
         DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) {
             DefKind::TyAlias => ty::List::empty(),
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 55b8857ed39..147b600f7ba 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -8,6 +8,7 @@
 #![feature(assert_matches)]
 #![feature(iterator_try_collect)]
 #![feature(let_chains)]
+#![feature(if_let_guard)]
 #![feature(never_type)]
 #![feature(box_patterns)]
 #![recursion_limit = "256"]
diff --git a/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr b/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
index f1f0d7e5907..5c8d64fc6cb 100644
--- a/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
+++ b/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
@@ -4,11 +4,11 @@ error[E0311]: the parameter type `U` may not live long enough
 LL |     async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
    |                            ^^^^^^^
    |
-note: the parameter type `U` must be valid for the anonymous lifetime defined here...
+note: the parameter type `U` must be valid for the anonymous lifetime as defined here...
   --> $DIR/async-generics-and-bounds.rs:12:18
    |
 LL |     async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
-   |                  ^^^^^
+   |                  ^
 note: ...so that the reference type `&(T, U)` does not outlive the data it points at
   --> $DIR/async-generics-and-bounds.rs:12:28
    |
@@ -21,11 +21,11 @@ error[E0311]: the parameter type `T` may not live long enough
 LL |     async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
    |                            ^^^^^^^
    |
-note: the parameter type `T` must be valid for the anonymous lifetime defined here...
+note: the parameter type `T` must be valid for the anonymous lifetime as defined here...
   --> $DIR/async-generics-and-bounds.rs:12:18
    |
 LL |     async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
-   |                  ^^^^^
+   |                  ^
 note: ...so that the reference type `&(T, U)` does not outlive the data it points at
   --> $DIR/async-generics-and-bounds.rs:12:28
    |
diff --git a/tests/ui/async-await/in-trait/async-generics.stderr b/tests/ui/async-await/in-trait/async-generics.stderr
index 2f05564564c..6ae73d9e3a6 100644
--- a/tests/ui/async-await/in-trait/async-generics.stderr
+++ b/tests/ui/async-await/in-trait/async-generics.stderr
@@ -4,11 +4,11 @@ error[E0311]: the parameter type `U` may not live long enough
 LL |     async fn foo(&self) -> &(T, U);
    |                            ^^^^^^^
    |
-note: the parameter type `U` must be valid for the anonymous lifetime defined here...
+note: the parameter type `U` must be valid for the anonymous lifetime as defined here...
   --> $DIR/async-generics.rs:9:18
    |
 LL |     async fn foo(&self) -> &(T, U);
-   |                  ^^^^^
+   |                  ^
 note: ...so that the reference type `&(T, U)` does not outlive the data it points at
   --> $DIR/async-generics.rs:9:28
    |
@@ -21,11 +21,11 @@ error[E0311]: the parameter type `T` may not live long enough
 LL |     async fn foo(&self) -> &(T, U);
    |                            ^^^^^^^
    |
-note: the parameter type `T` must be valid for the anonymous lifetime defined here...
+note: the parameter type `T` must be valid for the anonymous lifetime as defined here...
   --> $DIR/async-generics.rs:9:18
    |
 LL |     async fn foo(&self) -> &(T, U);
-   |                  ^^^^^
+   |                  ^
 note: ...so that the reference type `&(T, U)` does not outlive the data it points at
   --> $DIR/async-generics.rs:9:28
    |
diff --git a/tests/ui/impl-trait/in-trait/assumed-wf-bounds-in-impl.rs b/tests/ui/impl-trait/in-trait/assumed-wf-bounds-in-impl.rs
new file mode 100644
index 00000000000..2a61c5cc8df
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/assumed-wf-bounds-in-impl.rs
@@ -0,0 +1,29 @@
+// check-pass
+// edition: 2021
+// issue: 113796
+
+#![feature(async_fn_in_trait)]
+
+trait AsyncLendingIterator {
+    type Item<'a>
+    where
+        Self: 'a;
+
+    async fn next(&mut self) -> Option<Self::Item<'_>>;
+}
+
+struct Lend<I>(I);
+impl<I> AsyncLendingIterator for Lend<I> {
+    type Item<'a> = &'a I
+    where
+        Self: 'a;
+
+    // Checking that the synthetic `<Self as AsyncLendingIterator>::next()` GAT
+    // is well-formed requires being able to assume the WF types of `next`.
+
+    async fn next(&mut self) -> Option<Self::Item<'_>> {
+        todo!()
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs
index 8b97336fe0c..ff7ad4bf389 100644
--- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs
+++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs
@@ -17,7 +17,6 @@ impl<'a, I: 'a + Iterable> Iterable for &'a I {
     //~^ ERROR impl has stricter requirements than trait
 
     fn iter(&self) -> impl 'a + Iterator<Item = I::Item<'a>> {
-        //~^ ERROR the type `&'a I` does not fulfill the required lifetime
         (*self).iter()
     }
 }
diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr
index 54a08c5b516..106b8a7c804 100644
--- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr
+++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr
@@ -12,19 +12,6 @@ help: copy the `where` clause predicates from the trait
 LL |     where Self: 'b;
    |     ~~~~~~~~~~~~~~
 
-error[E0477]: the type `&'a I` does not fulfill the required lifetime
-  --> $DIR/bad-item-bound-within-rpitit.rs:19:23
-   |
-LL |     fn iter(&self) -> impl 'a + Iterator<Item = I::Item<'a>> {
-   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: type must outlive the anonymous lifetime as defined here
-  --> $DIR/bad-item-bound-within-rpitit.rs:10:28
-   |
-LL |     fn iter(&self) -> impl '_ + Iterator<Item = Self::Item<'_>>;
-   |                            ^^
+error: aborting due to previous error
 
-error: aborting due to 2 previous errors
-
-Some errors have detailed explanations: E0276, E0477.
-For more information about an error, try `rustc --explain E0276`.
+For more information about this error, try `rustc --explain E0276`.
diff --git a/tests/ui/impl-trait/in-trait/wf-bounds.stderr b/tests/ui/impl-trait/in-trait/wf-bounds.stderr
index beac6620911..4d60b133048 100644
--- a/tests/ui/impl-trait/in-trait/wf-bounds.stderr
+++ b/tests/ui/impl-trait/in-trait/wf-bounds.stderr
@@ -47,10 +47,6 @@ note: required by a bound in `NeedsDisplay`
    |
 LL | struct NeedsDisplay<T: Display>(T);
    |                        ^^^^^^^ required by this bound in `NeedsDisplay`
-help: consider restricting type parameter `T`
-   |
-LL |     fn nya4<T: std::fmt::Display>() -> impl Wf<NeedsDisplay<T>>;
-   |              +++++++++++++++++++
 
 error: aborting due to 4 previous errors