diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 27490a09a3646..ccb07804b9661 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1101,9 +1101,18 @@ fn should_encode_const(def_kind: DefKind) -> bool {
     }
 }
 
-// Return `false` to avoid encoding impl trait in trait, while we don't use the query.
-fn should_encode_fn_impl_trait_in_trait<'tcx>(_tcx: TyCtxt<'tcx>, _def_id: DefId) -> bool {
-    false
+// We only encode impl trait in trait when using `lower-impl-trait-in-trait-to-assoc-ty` unstable
+// option.
+fn should_encode_fn_impl_trait_in_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+    if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty
+        && let Some(assoc_item) = tcx.opt_associated_item(def_id)
+        && assoc_item.container == ty::AssocItemContainer::TraitContainer
+        && assoc_item.kind == ty::AssocKind::Fn
+    {
+        true
+    } else {
+        false
+    }
 }
 
 impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index ad119c4e07306..c9da711e556f5 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -177,6 +177,7 @@ pub fn provide(providers: &mut Providers) {
         }
     };
     providers.opt_def_kind = |tcx, def_id| tcx.hir().opt_def_kind(def_id.expect_local());
+    providers.opt_rpitit_info = |_, _| None;
     providers.all_local_trait_impls = |tcx, ()| &tcx.resolutions(()).trait_impls;
     providers.expn_that_defined = |tcx, id| {
         let id = id.expect_local();
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index b07540cf58c11..51feae3cf8a61 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -90,6 +90,7 @@ rustc_queries! {
     /// Definitions that were generated with no HIR, would be feeded to return `None`.
     query opt_local_def_id_to_hir_id(key: LocalDefId) -> Option<hir::HirId>{
         desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key.to_def_id()) }
+        feedable
     }
 
     /// Gives access to the HIR node's parent for the HIR owner `key`.
@@ -166,6 +167,7 @@ rustc_queries! {
         }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     query collect_return_position_impl_trait_in_trait_tys(key: DefId)
@@ -222,6 +224,7 @@ rustc_queries! {
         arena_cache
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the
@@ -264,6 +267,7 @@ rustc_queries! {
         desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// Elaborated version of the predicates from `explicit_item_bounds`.
@@ -588,6 +592,7 @@ rustc_queries! {
         desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// Returns the inferred outlives predicates (e.g., for `struct
@@ -596,6 +601,7 @@ rustc_queries! {
         desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// Maps from the `DefId` of a trait to the list of
@@ -728,6 +734,7 @@ rustc_queries! {
         desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// Collects the associated items defined on a trait or impl.
@@ -1142,6 +1149,15 @@ rustc_queries! {
         desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) }
         cache_on_disk_if { def_id.is_local() }
         separate_provide_extern
+        feedable
+    }
+
+    /// The `opt_rpitit_info` query returns the pair of the def id of the function where the RPIT
+    /// is defined and the opaque def id if any.
+    query opt_rpitit_info(def_id: DefId) -> Option<ty::ImplTraitInTraitData> {
+        desc { |tcx| "opt_rpitit_info `{}`", tcx.def_path_str(def_id) }
+        cache_on_disk_if { def_id.is_local() }
+        feedable
     }
 
     /// Gets the span for the definition.
@@ -1157,6 +1173,7 @@ rustc_queries! {
         desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) }
         cache_on_disk_if { def_id.is_local() }
         separate_provide_extern
+        feedable
     }
 
     query lookup_stability(def_id: DefId) -> Option<attr::Stability> {
@@ -1498,6 +1515,7 @@ rustc_queries! {
         desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) }
         cache_on_disk_if { def_id.is_local() }
         separate_provide_extern
+        feedable
     }
 
     query check_well_formed(key: hir::OwnerId) -> () {
@@ -1695,6 +1713,7 @@ rustc_queries! {
     query visibility(def_id: DefId) -> ty::Visibility<DefId> {
         desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }
         separate_provide_extern
+        feedable
     }
 
     query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 5084bc9cec6c6..5df01b8abc3b2 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2071,6 +2071,12 @@ pub enum ImplOverlapKind {
     Issue33140,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
+pub enum ImplTraitInTraitData {
+    Trait { fn_def_id: DefId, opaque_def_id: DefId },
+    Impl { fn_def_id: DefId },
+}
+
 impl<'tcx> TyCtxt<'tcx> {
     pub fn typeck_body(self, body: hir::BodyId) -> &'tcx TypeckResults<'tcx> {
         self.typeck(self.hir().body_owner_def_id(body))
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 4beac931632be..b466a3fcdee91 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1503,6 +1503,9 @@ options! {
         "what location details should be tracked when using caller_location, either \
         `none`, or a comma separated list of location details, for which \
         valid options are `file`, `line`, and `column` (default: `file,line,column`)"),
+    lower_impl_trait_in_trait_to_assoc_ty: bool = (false, parse_bool, [TRACKED],
+        "modify the lowering strategy for `impl Trait` in traits so that they are lowered to \
+        generic associated types"),
     ls: bool = (false, parse_bool, [UNTRACKED],
         "list the symbols defined by a library crate (default: no)"),
     macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index d4866b5dbdd49..efbbfe6c24b56 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -4,7 +4,8 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::definitions::DefPathData;
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_middle::ty::{self, DefIdTree, TyCtxt};
+use rustc_middle::ty::{self, DefIdTree, ImplTraitInTraitData, InternalSubsts, TyCtxt};
+use rustc_span::symbol::kw;
 
 pub fn provide(providers: &mut ty::query::Providers) {
     *providers = ty::query::Providers {
@@ -21,9 +22,37 @@ pub fn provide(providers: &mut ty::query::Providers) {
 fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
     let item = tcx.hir().expect_item(def_id.expect_local());
     match item.kind {
-        hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter(
-            trait_item_refs.iter().map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id()),
-        ),
+        hir::ItemKind::Trait(.., ref trait_item_refs) => {
+            if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty {
+                // We collect RPITITs for each trait method's return type and create a
+                // corresponding associated item using associated_items_for_impl_trait_in_trait
+                // query.
+                tcx.arena.alloc_from_iter(
+                    trait_item_refs
+                        .iter()
+                        .map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id())
+                        .chain(
+                            trait_item_refs
+                                .iter()
+                                .filter(|trait_item_ref| {
+                                    matches!(trait_item_ref.kind, hir::AssocItemKind::Fn { .. })
+                                })
+                                .flat_map(|trait_item_ref| {
+                                    let trait_fn_def_id =
+                                        trait_item_ref.id.owner_id.def_id.to_def_id();
+                                    tcx.associated_items_for_impl_trait_in_trait(trait_fn_def_id)
+                                })
+                                .map(|def_id| *def_id),
+                        ),
+                )
+            } else {
+                tcx.arena.alloc_from_iter(
+                    trait_item_refs
+                        .iter()
+                        .map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id()),
+                )
+            }
+        }
         hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter(
             impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.owner_id.to_def_id()),
         ),
@@ -193,10 +222,65 @@ fn associated_item_for_impl_trait_in_trait(
     let span = tcx.def_span(opaque_ty_def_id);
     let trait_assoc_ty =
         tcx.at(span).create_def(trait_def_id.expect_local(), DefPathData::ImplTraitAssocTy);
-    trait_assoc_ty.def_id()
+
+    let local_def_id = trait_assoc_ty.def_id();
+    let def_id = local_def_id.to_def_id();
+
+    trait_assoc_ty.opt_def_kind(Some(DefKind::AssocTy));
+
+    // There's no HIR associated with this new synthesized `def_id`, so feed
+    // `opt_local_def_id_to_hir_id` with `None`.
+    trait_assoc_ty.opt_local_def_id_to_hir_id(None);
+
+    // Copy span of the opaque.
+    trait_assoc_ty.def_ident_span(Some(span));
+
+    // Add the def_id of the function and opaque that generated this synthesized associated type.
+    trait_assoc_ty.opt_rpitit_info(Some(ImplTraitInTraitData::Trait {
+        fn_def_id,
+        opaque_def_id: opaque_ty_def_id.to_def_id(),
+    }));
+
+    trait_assoc_ty.associated_item(ty::AssocItem {
+        name: kw::Empty,
+        kind: ty::AssocKind::Type,
+        def_id,
+        trait_item_def_id: None,
+        container: ty::TraitContainer,
+        fn_has_self_parameter: false,
+    });
+
+    // Copy visility of the containing function.
+    trait_assoc_ty.visibility(tcx.visibility(fn_def_id));
+
+    // Copy impl_defaultness of the containing function.
+    trait_assoc_ty.impl_defaultness(tcx.impl_defaultness(fn_def_id));
+
+    // Copy type_of of the opaque.
+    trait_assoc_ty.type_of(ty::EarlyBinder(tcx.mk_opaque(
+        opaque_ty_def_id.to_def_id(),
+        InternalSubsts::identity_for_item(tcx, opaque_ty_def_id.to_def_id()),
+    )));
+
+    // Copy generics_of of the opaque.
+    trait_assoc_ty.generics_of(tcx.generics_of(opaque_ty_def_id).clone());
+
+    // There are no predicates for the synthesized associated type.
+    trait_assoc_ty.explicit_predicates_of(ty::GenericPredicates {
+        parent: Some(trait_def_id),
+        predicates: &[],
+    });
+
+    // There are no inferred outlives for the synthesized associated type.
+    trait_assoc_ty.inferred_outlives_of(&[]);
+
+    // FIXME implement this.
+    trait_assoc_ty.explicit_item_bounds(&[]);
+
+    local_def_id
 }
 
-/// Given an `trait_assoc_def_id` that corresponds to a previously synthethized impl trait in trait
+/// Given an `trait_assoc_def_id` that corresponds to a previously synthesized impl trait in trait
 /// into an associated type and an `impl_def_id` corresponding to an impl block, create and return
 /// the corresponding associated item inside the impl block.
 fn impl_associated_item_for_impl_trait_in_trait(
diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout
index 6aa9785f44e33..79e6b94f1aca5 100644
--- a/tests/rustdoc-ui/z-help.stdout
+++ b/tests/rustdoc-ui/z-help.stdout
@@ -87,6 +87,7 @@
     -Z                          llvm-plugins=val -- a list LLVM plugins to enable (space separated)
     -Z                       llvm-time-trace=val -- generate JSON tracing data file from LLVM data (default: no)
     -Z                       location-detail=val -- what location details should be tracked when using caller_location, either `none`, or a comma separated list of location details, for which valid options are `file`, `line`, and `column` (default: `file,line,column`)
+    -Z lower-impl-trait-in-trait-to-assoc-ty=val -- modify the lowering strategy for `impl Trait` in traits so that they are lowered to generic associated types
     -Z                                    ls=val -- list the symbols defined by a library crate (default: no)
     -Z                       macro-backtrace=val -- show macro backtraces (default: no)
     -Z           maximal-hir-to-mir-coverage=val -- save as much information as possible about the correspondence between MIR and HIR as source scopes (default: no)