about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs7
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs167
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs1164
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs126
-rw-r--r--compiler/rustc_hir/src/hir.rs6
-rw-r--r--compiler/rustc_resolve/src/late.rs234
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs16
9 files changed, 743 insertions, 980 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 9442e0f1a1f..37ae41fabf9 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -53,7 +53,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         e.span,
                         seg,
                         ParamMode::Optional,
-                        0,
                         ParenthesizedGenericArgs::Err,
                         ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                     ));
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 0f90239f4c8..c506360aa8a 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -30,6 +30,7 @@ pub(super) struct NodeCollector<'a, 'hir> {
     definitions: &'a definitions::Definitions,
 }
 
+#[tracing::instrument(level = "debug", skip(sess, definitions, bodies))]
 pub(super) fn index_hir<'hir>(
     sess: &Session,
     definitions: &definitions::Definitions,
@@ -65,6 +66,7 @@ pub(super) fn index_hir<'hir>(
 }
 
 impl<'a, 'hir> NodeCollector<'a, 'hir> {
+    #[tracing::instrument(level = "debug", skip(self))]
     fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) {
         debug_assert_eq!(self.owner, hir_id.owner);
         debug_assert_ne!(hir_id.local_id.as_u32(), 0);
@@ -138,8 +140,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn visit_item(&mut self, i: &'hir Item<'hir>) {
-        debug!("visit_item: {:?}", i);
         debug_assert_eq!(i.def_id, self.owner);
         self.with_parent(i.hir_id(), |this| {
             if let ItemKind::Struct(ref struct_def, _) = i.kind {
@@ -152,6 +154,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) {
         debug_assert_eq!(fi.def_id, self.owner);
         self.with_parent(fi.hir_id(), |this| {
@@ -170,6 +173,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         })
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
         debug_assert_eq!(ti.def_id, self.owner);
         self.with_parent(ti.hir_id(), |this| {
@@ -177,6 +181,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) {
         debug_assert_eq!(ii.def_id, self.owner);
         self.with_parent(ii.hir_id(), |this| {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 29353d47b87..8ae07982f6f 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1,11 +1,11 @@
-use super::{AnonymousLifetimeMode, LoweringContext, ParamMode};
 use super::{AstOwner, ImplTraitContext, ImplTraitPosition, ResolverAstLowering};
+use super::{LoweringContext, ParamMode};
 use crate::{Arena, FnDeclKind};
 
 use rustc_ast::ptr::P;
 use rustc_ast::visit::AssocCtxt;
 use rustc_ast::*;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
@@ -81,13 +81,10 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
             is_in_loop_condition: false,
             is_in_trait_impl: false,
             is_in_dyn_type: false,
-            anonymous_lifetime_mode: AnonymousLifetimeMode::PassThrough,
             generator_kind: None,
             task_context: None,
             current_item: None,
-            lifetimes_to_define: Vec::new(),
-            is_collecting_anonymous_lifetimes: None,
-            in_scope_lifetimes: Vec::new(),
+            captured_lifetimes: None,
             allow_try_trait: Some([sym::try_trait_v2][..].into()),
             allow_gen_future: Some([sym::gen_future][..].into()),
             allow_into_future: Some([sym::into_future][..].into()),
@@ -143,32 +140,14 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
             LocalDefId { local_def_index }
         };
 
-        let parent_hir = self.lower_node(parent_id).unwrap().node().expect_item();
+        let parent_hir = self.lower_node(parent_id).unwrap();
         self.with_lctx(item.id, |lctx| {
             // Evaluate with the lifetimes in `params` in-scope.
             // This is used to track which lifetimes have already been defined,
             // and which need to be replicated when lowering an async fn.
-            match parent_hir.kind {
-                hir::ItemKind::Impl(hir::Impl { ref of_trait, ref generics, .. }) => {
+            match parent_hir.node().expect_item().kind {
+                hir::ItemKind::Impl(hir::Impl { ref of_trait, .. }) => {
                     lctx.is_in_trait_impl = of_trait.is_some();
-                    lctx.in_scope_lifetimes = generics
-                        .params
-                        .iter()
-                        .filter(|param| {
-                            matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
-                        })
-                        .map(|param| param.name)
-                        .collect();
-                }
-                hir::ItemKind::Trait(_, _, ref generics, ..) => {
-                    lctx.in_scope_lifetimes = generics
-                        .params
-                        .iter()
-                        .filter(|param| {
-                            matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
-                        })
-                        .map(|param| param.name)
-                        .collect();
                 }
                 _ => {}
             };
@@ -276,7 +255,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ref body,
                 ..
             }) => {
-                let fn_def_id = self.resolver.local_def_id(id);
                 self.with_new_scopes(|this| {
                     this.current_item = Some(ident.span);
 
@@ -288,20 +266,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let body_id =
                         this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref());
 
-                    let (generics, decl) = this.add_in_band_defs(
-                        generics,
-                        fn_def_id,
-                        AnonymousLifetimeMode::PassThrough,
-                        |this, idty| {
+                    let (generics, decl) =
+                        this.add_implicit_generics(generics, id, |this, idty| {
                             let ret_id = asyncness.opt_return_id();
-                            this.lower_fn_decl(
-                                &decl,
-                                Some((fn_def_id, idty)),
-                                FnDeclKind::Fn,
-                                ret_id,
-                            )
-                        },
-                    );
+                            this.lower_fn_decl(&decl, Some((id, idty)), FnDeclKind::Fn, ret_id)
+                        });
                     let sig = hir::FnSig {
                         decl,
                         header: this.lower_fn_header(header),
@@ -339,12 +308,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 //
                 // type Foo = Foo1
                 // opaque type Foo1: Trait
-                let ty = self.lower_ty(
-                    ty,
-                    ImplTraitContext::TypeAliasesOpaqueTy {
-                        capturable_lifetimes: &mut FxHashSet::default(),
-                    },
-                );
+                let ty = self.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
                 let mut generics = generics.clone();
                 add_ty_alias_where_clause(&mut generics, where_clauses, true);
                 let generics = self.lower_generics(
@@ -419,12 +383,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // method, it will not be considered an in-band
                 // lifetime to be added, but rather a reference to a
                 // parent lifetime.
-                let lowered_trait_def_id = hir_id.expect_owner();
-                let (generics, (trait_ref, lowered_ty)) = self.add_in_band_defs(
-                    ast_generics,
-                    lowered_trait_def_id,
-                    AnonymousLifetimeMode::CreateParameter,
-                    |this, _| {
+                let (generics, (trait_ref, lowered_ty)) =
+                    self.add_implicit_generics(ast_generics, id, |this, _| {
                         let trait_ref = trait_ref.as_ref().map(|trait_ref| {
                             this.lower_trait_ref(
                                 trait_ref,
@@ -436,16 +396,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
 
                         (trait_ref, lowered_ty)
-                    },
-                );
-
-                let new_impl_items =
-                    self.with_in_scope_lifetime_defs(&ast_generics.params, |this| {
-                        this.arena.alloc_from_iter(
-                            impl_items.iter().map(|item| this.lower_impl_item_ref(item)),
-                        )
                     });
 
+                let new_impl_items = self
+                    .arena
+                    .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
+
                 // `defaultness.has_value()` is never called for an `impl`, always `true` in order
                 // to not cause an assertion failure inside the `lower_defaultness` function.
                 let has_val = true;
@@ -692,18 +648,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
             kind: match i.kind {
                 ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => {
                     let fdec = &sig.decl;
-                    let (generics, (fn_dec, fn_args)) = self.add_in_band_defs(
-                        generics,
-                        def_id,
-                        AnonymousLifetimeMode::PassThrough,
-                        |this, _| {
+                    let (generics, (fn_dec, fn_args)) =
+                        self.add_implicit_generics(generics, i.id, |this, _| {
                             (
                                 // Disallow `impl Trait` in foreign items.
                                 this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None),
                                 this.lower_fn_params_to_names(fdec),
                             )
-                        },
-                    );
+                        });
 
                     hir::ForeignItemKind::Fn(fn_dec, fn_args, generics)
                 }
@@ -810,13 +762,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
             AssocItemKind::Fn(box Fn { ref sig, ref generics, body: None, .. }) => {
                 let names = self.lower_fn_params_to_names(&sig.decl);
-                let (generics, sig) = self.lower_method_sig(
-                    generics,
-                    sig,
-                    trait_item_def_id,
-                    FnDeclKind::Trait,
-                    None,
-                );
+                let (generics, sig) =
+                    self.lower_method_sig(generics, sig, i.id, FnDeclKind::Trait, None);
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)))
             }
             AssocItemKind::Fn(box Fn { ref sig, ref generics, body: Some(ref body), .. }) => {
@@ -826,7 +773,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, sig) = self.lower_method_sig(
                     generics,
                     sig,
-                    trait_item_def_id,
+                    i.id,
                     FnDeclKind::Trait,
                     asyncness.opt_return_id(),
                 );
@@ -900,8 +847,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
-        let impl_item_def_id = self.resolver.local_def_id(i.id);
-
         let (generics, kind) = match &i.kind {
             AssocItemKind::Const(_, ty, expr) => {
                 let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
@@ -918,7 +863,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, sig) = self.lower_method_sig(
                     generics,
                     sig,
-                    impl_item_def_id,
+                    i.id,
                     if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
                     asyncness.opt_return_id(),
                 );
@@ -938,12 +883,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         hir::ImplItemKind::TyAlias(ty)
                     }
                     Some(ty) => {
-                        let ty = self.lower_ty(
-                            ty,
-                            ImplTraitContext::TypeAliasesOpaqueTy {
-                                capturable_lifetimes: &mut FxHashSet::default(),
-                            },
-                        );
+                        let ty = self.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
                         hir::ImplItemKind::TyAlias(ty)
                     }
                 };
@@ -1283,17 +1223,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
         &mut self,
         generics: &Generics,
         sig: &FnSig,
-        fn_def_id: LocalDefId,
+        id: NodeId,
         kind: FnDeclKind,
         is_async: Option<NodeId>,
     ) -> (hir::Generics<'hir>, hir::FnSig<'hir>) {
         let header = self.lower_fn_header(sig.header);
-        let (generics, decl) = self.add_in_band_defs(
-            generics,
-            fn_def_id,
-            AnonymousLifetimeMode::PassThrough,
-            |this, idty| this.lower_fn_decl(&sig.decl, Some((fn_def_id, idty)), kind, is_async),
-        );
+        let (generics, decl) = self.add_implicit_generics(generics, id, |this, idty| {
+            this.lower_fn_decl(&sig.decl, Some((id, idty)), kind, is_async)
+        });
         (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
     }
 
@@ -1418,14 +1355,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_where_clause(&mut self, wc: &WhereClause) -> hir::WhereClause<'hir> {
-        self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
-            hir::WhereClause {
-                predicates: this.arena.alloc_from_iter(
-                    wc.predicates.iter().map(|predicate| this.lower_where_predicate(predicate)),
-                ),
-                span: this.lower_span(wc.span),
-            }
-        })
+        hir::WhereClause {
+            predicates: self.arena.alloc_from_iter(
+                wc.predicates.iter().map(|predicate| self.lower_where_predicate(predicate)),
+            ),
+            span: self.lower_span(wc.span),
+        }
     }
 
     fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
@@ -1435,24 +1370,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ref bounded_ty,
                 ref bounds,
                 span,
-            }) => self.with_in_scope_lifetime_defs(&bound_generic_params, |this| {
-                hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
-                    bound_generic_params: this.lower_generic_params(
-                        bound_generic_params,
-                        ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
-                    ),
-                    bounded_ty: this.lower_ty(
-                        bounded_ty,
-                        ImplTraitContext::Disallowed(ImplTraitPosition::Type),
-                    ),
-                    bounds: this.arena.alloc_from_iter(bounds.iter().map(|bound| {
-                        this.lower_param_bound(
-                            bound,
-                            ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
-                        )
-                    })),
-                    span: this.lower_span(span),
-                })
+            }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+                bound_generic_params: self.lower_generic_params(
+                    bound_generic_params,
+                    ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+                ),
+                bounded_ty: self
+                    .lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)),
+                bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| {
+                    self.lower_param_bound(
+                        bound,
+                        ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
+                    )
+                })),
+                span: self.lower_span(span),
             }),
             WherePredicate::RegionPredicate(WhereRegionPredicate {
                 ref lifetime,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index b15f299b5f2..97ed9c708b4 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -32,6 +32,7 @@
 
 #![feature(crate_visibility_modifier)]
 #![feature(box_patterns)]
+#![feature(let_chains)]
 #![feature(let_else)]
 #![feature(never_type)]
 #![recursion_limit = "256"]
@@ -53,7 +54,6 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{DefId, DefPathHash, LocalDefId, CRATE_DEF_ID};
 use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
-use rustc_hir::intravisit;
 use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_query_system::ich::StableHashingContext;
@@ -122,27 +122,8 @@ struct LoweringContext<'a, 'hir: 'a> {
     is_in_trait_impl: bool,
     is_in_dyn_type: bool,
 
-    /// What to do when we encounter an "anonymous lifetime
-    /// reference". The term "anonymous" is meant to encompass both
-    /// `'_` lifetimes as well as fully elided cases where nothing is
-    /// written at all (e.g., `&T` or `std::cell::Ref<T>`).
-    anonymous_lifetime_mode: AnonymousLifetimeMode,
-
-    /// Used to create lifetime definitions for anonymous lifetimes.
-    /// When an anonymous lifetime is encountered in a function or impl header and
-    /// requires to create a fresh lifetime parameter, it is added
-    /// to this list. The results of this list are then added to the list of
-    /// lifetime definitions in the corresponding impl or function generics.
-    lifetimes_to_define: Vec<(Span, NodeId)>,
-
-    /// If anonymous lifetimes are being collected, this field holds the parent
-    /// `LocalDefId` to create the fresh lifetime parameters' `LocalDefId`.
-    is_collecting_anonymous_lifetimes: Option<LocalDefId>,
-
-    /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
-    /// We always store a `normalize_to_macros_2_0()` version of the param-name in this
-    /// vector.
-    in_scope_lifetimes: Vec<ParamName>,
+    /// Used to handle lifetimes appearing in impl-traits.
+    captured_lifetimes: Option<LifetimeCaptureContext>,
 
     current_hir_id_owner: LocalDefId,
     item_local_id_counter: hir::ItemLocalId,
@@ -157,6 +138,68 @@ struct LoweringContext<'a, 'hir: 'a> {
     allow_into_future: Option<Lrc<[Symbol]>>,
 }
 
+/// Resolution for a lifetime appearing in a type.
+#[derive(Copy, Clone, Debug)]
+pub enum LifetimeRes {
+    /// Successfully linked the lifetime to a generic parameter.
+    Param {
+        /// Id of the generic parameter that introduced it.
+        param: LocalDefId,
+        /// Id of the introducing place. That can be:
+        /// - an item's id, for the item's generic parameters;
+        /// - a TraitRef's ref_id, identifying the `for<...>` binder;
+        /// - a BareFn type's id;
+        /// - a Path's id when this path has parenthesized generic args.
+        ///
+        /// This information is used for impl-trait lifetime captures, to know when to or not to
+        /// capture any given lifetime.
+        binder: NodeId,
+    },
+    /// Created a generic parameter for an anonymous lifetime.
+    Fresh {
+        /// Id of the generic parameter that introduced it.
+        param: LocalDefId,
+        /// Id of the introducing place. See `Param`.
+        binder: NodeId,
+    },
+    /// This variant is used for anonymous lifetimes that we did not resolve during
+    /// late resolution.  Shifting the work to the HIR lifetime resolver.
+    Anonymous {
+        /// Id of the introducing place. See `Param`.
+        binder: NodeId,
+        /// Whether this lifetime was spelled or elided.
+        elided: bool,
+    },
+    /// Explicit `'static` lifetime.
+    Static,
+    /// Resolution failure.
+    Error,
+    /// HACK: This is used to recover the NodeId of an elided lifetime.
+    ElidedAnchor { start: NodeId, end: NodeId },
+}
+
+/// When we lower a lifetime, it is inserted in `captures`, and the resolution is modified so
+/// to point to the lifetime parameter impl-trait will generate.
+/// When traversing `for<...>` binders, they are inserted in `binders_to_ignore` so we know *not*
+/// to rebind the introduced lifetimes.
+#[derive(Debug)]
+struct LifetimeCaptureContext {
+    /// parent def_id for new definitions
+    parent_def_id: LocalDefId,
+    /// Set of lifetimes to rebind.
+    captures: FxHashMap<
+        LocalDefId, // original parameter id
+        (
+            Span,        // Span
+            NodeId,      // synthetized parameter id
+            ParamName,   // parameter name
+            LifetimeRes, // original resolution
+        ),
+    >,
+    /// Traversed binders.  The ids in this set should *not* be rebound.
+    binders_to_ignore: FxHashSet<NodeId>,
+}
+
 pub trait ResolverAstLowering {
     fn def_key(&self, id: DefId) -> DefKey;
 
@@ -175,6 +218,12 @@ pub trait ResolverAstLowering {
     /// Obtains resolution for a label with the given `NodeId`.
     fn get_label_res(&self, id: NodeId) -> Option<NodeId>;
 
+    /// Obtains resolution for a lifetime with the given `NodeId`.
+    fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
+
+    /// Obtain the list of lifetimes parameters to add to an item.
+    fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>;
+
     fn create_stable_hashing_context(&self) -> StableHashingContext<'_>;
 
     fn definitions(&self) -> &Definitions;
@@ -217,29 +266,11 @@ enum ImplTraitContext<'b, 'a> {
     /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
     ///
     ReturnPositionOpaqueTy {
-        /// `DefId` for the parent function, used to look up necessary
-        /// information later.
-        fn_def_id: LocalDefId,
         /// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn,
         origin: hir::OpaqueTyOrigin,
     },
     /// Impl trait in type aliases.
-    TypeAliasesOpaqueTy {
-        /// Set of lifetimes that this opaque type can capture, if it uses
-        /// them. This includes lifetimes bound since we entered this context.
-        /// For example:
-        ///
-        /// ```
-        /// type A<'b> = impl for<'a> Trait<'a, Out = impl Sized + 'a>;
-        /// ```
-        ///
-        /// Here the inner opaque type captures `'a` because it uses it. It doesn't
-        /// need to capture `'b` because it already inherits the lifetime
-        /// parameter from `A`.
-        // FIXME(impl_trait): but `required_region_bounds` will ICE later
-        // anyway.
-        capturable_lifetimes: &'b mut FxHashSet<hir::LifetimeName>,
-    },
+    TypeAliasesOpaqueTy,
     /// `impl Trait` is not accepted in this position.
     Disallowed(ImplTraitPosition),
 }
@@ -273,12 +304,8 @@ impl<'a> ImplTraitContext<'_, 'a> {
         use self::ImplTraitContext::*;
         match self {
             Universal(params, parent) => Universal(params, *parent),
-            ReturnPositionOpaqueTy { fn_def_id, origin } => {
-                ReturnPositionOpaqueTy { fn_def_id: *fn_def_id, origin: *origin }
-            }
-            TypeAliasesOpaqueTy { capturable_lifetimes } => {
-                TypeAliasesOpaqueTy { capturable_lifetimes }
-            }
+            ReturnPositionOpaqueTy { origin } => ReturnPositionOpaqueTy { origin: *origin },
+            TypeAliasesOpaqueTy => TypeAliasesOpaqueTy,
             Disallowed(pos) => Disallowed(*pos),
         }
     }
@@ -453,56 +480,6 @@ enum ParenthesizedGenericArgs {
     Err,
 }
 
-/// What to do when we encounter an **anonymous** lifetime
-/// reference. Anonymous lifetime references come in two flavors. You
-/// have implicit, or fully elided, references to lifetimes, like the
-/// one in `&T` or `Ref<T>`, and you have `'_` lifetimes, like `&'_ T`
-/// or `Ref<'_, T>`. These often behave the same, but not always:
-///
-/// - certain usages of implicit references are deprecated, like
-///   `Ref<T>`, and we sometimes just give hard errors in those cases
-///   as well.
-/// - for object bounds there is a difference: `Box<dyn Foo>` is not
-///   the same as `Box<dyn Foo + '_>`.
-///
-/// We describe the effects of the various modes in terms of three cases:
-///
-/// - **Modern** -- includes all uses of `'_`, but also the lifetime arg
-///   of a `&` (e.g., the missing lifetime in something like `&T`)
-/// - **Dyn Bound** -- if you have something like `Box<dyn Foo>`,
-///   there is an elided lifetime bound (`Box<dyn Foo + 'X>`). These
-///   elided bounds follow special rules. Note that this only covers
-///   cases where *nothing* is written; the `'_` in `Box<dyn Foo +
-///   '_>` is a case of "modern" elision.
-/// - **Deprecated** -- this covers cases like `Ref<T>`, where the lifetime
-///   parameter to ref is completely elided. `Ref<'_, T>` would be the modern,
-///   non-deprecated equivalent.
-///
-/// Currently, the handling of lifetime elision is somewhat spread out
-/// between HIR lowering and -- as described below -- the
-/// `resolve_lifetime` module. Often we "fallthrough" to that code by generating
-/// an "elided" or "underscore" lifetime name. In the future, we probably want to move
-/// everything into HIR lowering.
-#[derive(Copy, Clone, Debug)]
-pub enum AnonymousLifetimeMode {
-    /// For **Modern** cases, create a new anonymous region parameter
-    /// and reference that.
-    ///
-    /// For **Dyn Bound** cases, pass responsibility to
-    /// `resolve_lifetime` code.
-    ///
-    /// For **Deprecated** cases, report an error.
-    CreateParameter,
-
-    /// Give a hard error when either `&` or `'_` is written. Used to
-    /// rule out things like `where T: Foo<'_>`. Does not imply an
-    /// error on default object bounds (e.g., `Box<dyn Foo>`).
-    ReportError,
-
-    /// Pass responsibility to `resolve_lifetime` code for all cases.
-    PassThrough,
-}
-
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn with_hir_id_owner(
         &mut self,
@@ -688,26 +665,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         )
     }
 
-    fn with_anonymous_lifetime_mode<R>(
-        &mut self,
-        anonymous_lifetime_mode: AnonymousLifetimeMode,
-        op: impl FnOnce(&mut Self) -> R,
-    ) -> R {
-        debug!(
-            "with_anonymous_lifetime_mode(anonymous_lifetime_mode={:?})",
-            anonymous_lifetime_mode,
-        );
-        let old_anonymous_lifetime_mode = self.anonymous_lifetime_mode;
-        self.anonymous_lifetime_mode = anonymous_lifetime_mode;
-        let result = op(self);
-        self.anonymous_lifetime_mode = old_anonymous_lifetime_mode;
-        debug!(
-            "with_anonymous_lifetime_mode: restoring anonymous_lifetime_mode={:?}",
-            old_anonymous_lifetime_mode
-        );
-        result
-    }
-
     /// Intercept all spans entering HIR.
     /// Mark a span as relative to the current owning item.
     fn lower_span(&self, span: Span) -> Span {
@@ -723,135 +680,60 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         Ident::new(ident.name, self.lower_span(ident.span))
     }
 
-    /// Creates a new `hir::GenericParam` for every new lifetime and
-    /// type parameter encountered while evaluating `f`. Definitions
-    /// are created with the parent provided. If no `parent_id` is
-    /// provided, no definitions will be returned.
-    ///
-    /// Presuming that in-band lifetimes are enabled, then
-    /// `self.anonymous_lifetime_mode` will be updated to match the
-    /// parameter while `f` is running (and restored afterwards).
-    fn collect_in_band_defs<T>(
-        &mut self,
-        parent_def_id: LocalDefId,
-        f: impl FnOnce(&mut Self) -> T,
-    ) -> (Vec<(Span, NodeId)>, T) {
-        let was_collecting =
-            std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, Some(parent_def_id));
-        let len = self.lifetimes_to_define.len();
-
-        let res = f(self);
-
-        let lifetimes_to_define = self.lifetimes_to_define.split_off(len);
-        self.is_collecting_anonymous_lifetimes = was_collecting;
-        (lifetimes_to_define, res)
-    }
-
     /// Converts a lifetime into a new generic parameter.
-    fn fresh_lifetime_to_generic_param(
+    fn lifetime_res_to_generic_param(
         &mut self,
-        span: Span,
+        ident: Ident,
         node_id: NodeId,
-    ) -> hir::GenericParam<'hir> {
+        res: LifetimeRes,
+    ) -> Option<hir::GenericParam<'hir>> {
+        let (name, kind) = match res {
+            LifetimeRes::Param { .. } => {
+                (hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
+            }
+            LifetimeRes::Fresh { param, .. } => {
+                (hir::ParamName::Fresh(param), hir::LifetimeParamKind::Elided)
+            }
+            LifetimeRes::Static | LifetimeRes::Error => return None,
+            res => panic!(
+                "Unexpected lifetime resolution {:?} for {:?} at {:?}",
+                res, ident, ident.span
+            ),
+        };
         let hir_id = self.lower_node_id(node_id);
-        let def_id = self.resolver.local_def_id(node_id);
-        hir::GenericParam {
+        Some(hir::GenericParam {
             hir_id,
-            name: hir::ParamName::Fresh(def_id),
+            name,
             bounds: &[],
-            span: self.lower_span(span),
+            span: self.lower_span(ident.span),
             pure_wrt_drop: false,
-            kind: hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided },
-        }
-    }
-
-    /// When we have either an elided or `'_` lifetime in an impl
-    /// header, we convert it to an in-band lifetime.
-    fn collect_fresh_anonymous_lifetime(&mut self, span: Span) -> ParamName {
-        let Some(parent_def_id) = self.is_collecting_anonymous_lifetimes else { panic!() };
-
-        let node_id = self.resolver.next_node_id();
-
-        // Add a definition for the in-band lifetime def.
-        let param_def_id = self.resolver.create_def(
-            parent_def_id,
-            node_id,
-            DefPathData::LifetimeNs(kw::UnderscoreLifetime),
-            ExpnId::root(),
-            span.with_parent(None),
-        );
-
-        let hir_name = ParamName::Fresh(param_def_id);
-        self.lifetimes_to_define.push((span, node_id));
-        hir_name
-    }
-
-    // Evaluates `f` with the lifetimes in `params` in-scope.
-    // This is used to track which lifetimes have already been defined, and
-    // which are new in-band lifetimes that need to have a definition created
-    // for them.
-    fn with_in_scope_lifetime_defs<T>(
-        &mut self,
-        params: &[GenericParam],
-        f: impl FnOnce(&mut Self) -> T,
-    ) -> T {
-        let old_len = self.in_scope_lifetimes.len();
-        let lt_def_names = params.iter().filter_map(|param| match param.kind {
-            GenericParamKind::Lifetime { .. } => {
-                Some(ParamName::Plain(param.ident.normalize_to_macros_2_0()))
-            }
-            _ => None,
-        });
-        self.in_scope_lifetimes.extend(lt_def_names);
-
-        let res = f(self);
-
-        self.in_scope_lifetimes.truncate(old_len);
-        res
+            kind: hir::GenericParamKind::Lifetime { kind },
+        })
     }
 
-    /// Appends in-band lifetime defs and argument-position `impl
-    /// Trait` defs to the existing set of generics.
-    ///
-    /// Presuming that in-band lifetimes are enabled, then
-    /// `self.anonymous_lifetime_mode` will be updated to match the
-    /// parameter while `f` is running (and restored afterwards).
-    fn add_in_band_defs<T>(
+    /// Creates a new `hir::GenericParam` for every new `Fresh` lifetime and
+    /// universal `impl Trait` type parameter encountered while evaluating `f`.
+    /// Definitions are created with the provided `parent_def_id`.
+    fn add_implicit_generics<T>(
         &mut self,
         generics: &Generics,
-        parent_def_id: LocalDefId,
-        anonymous_lifetime_mode: AnonymousLifetimeMode,
+        parent_node_id: NodeId,
         f: impl FnOnce(&mut Self, &mut Vec<hir::GenericParam<'hir>>) -> T,
     ) -> (hir::Generics<'hir>, T) {
-        let (lifetimes_to_define, (mut lowered_generics, impl_trait_defs, res)) = self
-            .collect_in_band_defs(parent_def_id, |this| {
-                this.with_anonymous_lifetime_mode(anonymous_lifetime_mode, |this| {
-                    this.with_in_scope_lifetime_defs(&generics.params, |this| {
-                        let mut impl_trait_defs = Vec::new();
-                        // Note: it is necessary to lower generics *before* calling `f`.
-                        // When lowering `async fn`, there's a final step when lowering
-                        // the return type that assumes that all in-scope lifetimes have
-                        // already been added to either `in_scope_lifetimes` or
-                        // `lifetimes_to_define`. If we swapped the order of these two,
-                        // in-band-lifetimes introduced by generics or where-clauses
-                        // wouldn't have been added yet.
-                        let generics = this.lower_generics_mut(
-                            generics,
-                            ImplTraitContext::Universal(
-                                &mut impl_trait_defs,
-                                this.current_hir_id_owner,
-                            ),
-                        );
-                        let res = f(this, &mut impl_trait_defs);
-                        (generics, impl_trait_defs, res)
-                    })
-                })
-            });
+        let mut impl_trait_defs = Vec::new();
+        let mut lowered_generics = self.lower_generics_mut(
+            generics,
+            ImplTraitContext::Universal(&mut impl_trait_defs, self.current_hir_id_owner),
+        );
+        let res = f(self, &mut impl_trait_defs);
 
+        let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
         lowered_generics.params.extend(
-            lifetimes_to_define
+            extra_lifetimes
                 .into_iter()
-                .map(|(span, node_id)| self.fresh_lifetime_to_generic_param(span, node_id))
+                .filter_map(|(ident, node_id, res)| {
+                    self.lifetime_res_to_generic_param(ident, node_id, res)
+                })
                 .chain(impl_trait_defs),
         );
 
@@ -859,6 +741,45 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         (lowered_generics, res)
     }
 
+    /// Setup lifetime capture for and impl-trait.
+    /// The captures will be added to `captures`.
+    fn while_capturing_lifetimes<T>(
+        &mut self,
+        parent_def_id: LocalDefId,
+        captures: &mut FxHashMap<LocalDefId, (Span, NodeId, ParamName, LifetimeRes)>,
+        f: impl FnOnce(&mut Self) -> T,
+    ) -> T {
+        let lifetime_stash = std::mem::replace(
+            &mut self.captured_lifetimes,
+            Some(LifetimeCaptureContext {
+                parent_def_id,
+                captures: std::mem::take(captures),
+                binders_to_ignore: Default::default(),
+            }),
+        );
+
+        let ret = f(self);
+
+        let ctxt = std::mem::replace(&mut self.captured_lifetimes, lifetime_stash).unwrap();
+        *captures = ctxt.captures;
+
+        ret
+    }
+
+    /// Register a binder to be ignored for lifetime capture.
+    #[tracing::instrument(level = "debug", skip(self, f))]
+    #[inline]
+    fn with_lifetime_binder<T>(&mut self, binder: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
+        if let Some(ctxt) = &mut self.captured_lifetimes {
+            ctxt.binders_to_ignore.insert(binder);
+        }
+        let ret = f(self);
+        if let Some(ctxt) = &mut self.captured_lifetimes {
+            ctxt.binders_to_ignore.remove(&binder);
+        }
+        ret
+    }
+
     fn with_dyn_type_scope<T>(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T {
         let was_in_dyn_type = self.is_in_dyn_type;
         self.is_in_dyn_type = in_scope;
@@ -1058,7 +979,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 hir::TypeBindingKind::Equality { term }
             }
             AssocConstraintKind::Bound { ref bounds } => {
-                let mut capturable_lifetimes;
                 let mut parent_def_id = self.current_hir_id_owner;
                 // Piggy-back on the `impl Trait` context to figure out the correct behavior.
                 let (desugar_to_impl_trait, itctx) = match itctx {
@@ -1091,13 +1011,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     //
                     // FIXME: this is only needed until `impl Trait` is allowed in type aliases.
                     ImplTraitContext::Disallowed(_) if self.is_in_dyn_type => {
-                        capturable_lifetimes = FxHashSet::default();
-                        (
-                            true,
-                            ImplTraitContext::TypeAliasesOpaqueTy {
-                                capturable_lifetimes: &mut capturable_lifetimes,
-                            },
-                        )
+                        (true, ImplTraitContext::TypeAliasesOpaqueTy)
                     }
 
                     // We are in the parameter position, but not within a dyn type:
@@ -1258,26 +1172,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             TyKind::Slice(ref ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
             TyKind::Ptr(ref mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
             TyKind::Rptr(ref region, ref mt) => {
-                let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
-                let lifetime = match *region {
-                    Some(ref lt) => self.lower_lifetime(lt),
-                    None => self.elided_ref_lifetime(span),
-                };
+                let region = region.unwrap_or_else(|| {
+                    let Some(LifetimeRes::ElidedAnchor { start, end }) = self.resolver.get_lifetime_res(t.id) else {
+                        panic!()
+                    };
+                    debug_assert_eq!(start.plus(1), end);
+                    let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
+                    Lifetime {
+                        ident: Ident::new(kw::UnderscoreLifetime, span),
+                        id: start,
+                    }
+                });
+                let lifetime = self.lower_lifetime(&region);
                 hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx))
             }
-            TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| {
-                this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
-                    hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
-                        generic_params: this.lower_generic_params(
-                            &f.generic_params,
-                            ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
-                        ),
-                        unsafety: this.lower_unsafety(f.unsafety),
-                        abi: this.lower_extern(f.ext),
-                        decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
-                        param_names: this.lower_fn_params_to_names(&f.decl),
-                    }))
-                })
+            TyKind::BareFn(ref f) => self.with_lifetime_binder(t.id, |this| {
+                hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
+                    generic_params: this.lower_generic_params(
+                        &f.generic_params,
+                        ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+                    ),
+                    unsafety: this.lower_unsafety(f.unsafety),
+                    abi: this.lower_extern(f.ext),
+                    decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
+                    param_names: this.lower_fn_params_to_names(&f.decl),
+                }))
             }),
             TyKind::Never => hir::TyKind::Never,
             TyKind::Tup(ref tys) => {
@@ -1342,28 +1261,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             TyKind::ImplTrait(def_node_id, ref bounds) => {
                 let span = t.span;
                 match itctx {
-                    ImplTraitContext::ReturnPositionOpaqueTy { fn_def_id, origin } => self
-                        .lower_opaque_impl_trait(
-                            span,
-                            Some(fn_def_id),
-                            origin,
-                            def_node_id,
-                            None,
-                            |this| this.lower_param_bounds(bounds, itctx),
-                        ),
-                    ImplTraitContext::TypeAliasesOpaqueTy { ref capturable_lifetimes } => {
-                        // Reset capturable lifetimes, any nested impl trait
-                        // types will inherit lifetimes from this opaque type,
-                        // so don't need to capture them again.
-                        let nested_itctx = ImplTraitContext::TypeAliasesOpaqueTy {
-                            capturable_lifetimes: &mut FxHashSet::default(),
-                        };
+                    ImplTraitContext::ReturnPositionOpaqueTy { origin } => self
+                        .lower_opaque_impl_trait(span, origin, def_node_id, |this| {
+                            this.lower_param_bounds(bounds, itctx)
+                        }),
+                    ImplTraitContext::TypeAliasesOpaqueTy => {
+                        let nested_itctx = ImplTraitContext::TypeAliasesOpaqueTy;
                         self.lower_opaque_impl_trait(
                             span,
-                            None,
                             hir::OpaqueTyOrigin::TyAlias,
                             def_node_id,
-                            Some(capturable_lifetimes),
                             |this| this.lower_param_bounds(bounds, nested_itctx),
                         )
                     }
@@ -1421,20 +1328,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) }
     }
 
+    #[tracing::instrument(level = "debug", skip(self, lower_bounds))]
     fn lower_opaque_impl_trait(
         &mut self,
         span: Span,
-        fn_def_id: Option<LocalDefId>,
         origin: hir::OpaqueTyOrigin,
         opaque_ty_node_id: NodeId,
-        capturable_lifetimes: Option<&FxHashSet<hir::LifetimeName>>,
         lower_bounds: impl FnOnce(&mut Self) -> hir::GenericBounds<'hir>,
     ) -> hir::TyKind<'hir> {
-        debug!(
-            "lower_opaque_impl_trait(fn_def_id={:?}, opaque_ty_node_id={:?}, span={:?})",
-            fn_def_id, opaque_ty_node_id, span,
-        );
-
         // Make sure we know that some funky desugaring has been going on here.
         // This is a first: there is code in other places like for loop
         // desugaring that explicitly states that we don't want to track that.
@@ -1444,48 +1345,40 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id);
 
-        let mut collected_lifetimes = Vec::new();
+        let mut collected_lifetimes = FxHashMap::default();
         self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
-            let hir_bounds = lower_bounds(lctx);
+            let hir_bounds = if origin == hir::OpaqueTyOrigin::TyAlias {
+                lower_bounds(lctx)
+            } else {
+                lctx.while_capturing_lifetimes(
+                    opaque_ty_def_id,
+                    &mut collected_lifetimes,
+                    lower_bounds,
+                )
+            };
+            debug!(?collected_lifetimes);
 
-            collected_lifetimes = lifetimes_from_impl_trait_bounds(
-                opaque_ty_node_id,
-                &hir_bounds,
-                capturable_lifetimes,
-            );
+            let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(
+                |(_, &(span, p_id, p_name, _))| {
+                    let hir_id = lctx.lower_node_id(p_id);
+                    debug_assert_ne!(lctx.resolver.opt_local_def_id(p_id), None);
 
-            let lifetime_defs =
-                lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(|&(name, span)| {
-                    let def_node_id = lctx.resolver.next_node_id();
-                    lctx.resolver.create_def(
-                        opaque_ty_def_id,
-                        def_node_id,
-                        DefPathData::LifetimeNs(name.ident().name),
-                        ExpnId::root(),
-                        span.with_parent(None),
-                    );
-                    let hir_id = lctx.lower_node_id(def_node_id);
-
-                    let (name, kind) = match name {
-                        hir::LifetimeName::Underscore => (
-                            hir::ParamName::Plain(Ident::with_dummy_span(kw::UnderscoreLifetime)),
-                            hir::LifetimeParamKind::Elided,
-                        ),
-                        hir::LifetimeName::Param(param_name) => {
-                            (param_name, hir::LifetimeParamKind::Explicit)
-                        }
-                        _ => panic!("expected `LifetimeName::Param` or `ParamName::Plain`"),
+                    let kind = if p_name.ident().name == kw::UnderscoreLifetime {
+                        hir::LifetimeParamKind::Elided
+                    } else {
+                        hir::LifetimeParamKind::Explicit
                     };
 
                     hir::GenericParam {
                         hir_id,
-                        name,
+                        name: p_name,
                         span,
                         pure_wrt_drop: false,
                         bounds: &[],
                         kind: hir::GenericParamKind::Lifetime { kind },
                     }
-                }));
+                },
+            ));
 
             debug!("lower_opaque_impl_trait: lifetime_defs={:#?}", lifetime_defs);
 
@@ -1503,10 +1396,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             lctx.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
         });
 
-        let lifetimes =
-            self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(|(name, span)| {
-                hir::GenericArg::Lifetime(hir::Lifetime { hir_id: self.next_id(), span, name })
-            }));
+        let lifetimes = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(
+            |(_, (span, _, p_name, res))| {
+                let id = self.resolver.next_node_id();
+                let ident = Ident::new(p_name.ident().name, span);
+                let l = self.new_named_lifetime_with_res(id, span, ident, res);
+                hir::GenericArg::Lifetime(l)
+            },
+        ));
 
         debug!("lower_opaque_impl_trait: lifetimes={:#?}", lifetimes);
 
@@ -1565,7 +1462,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn lower_fn_decl(
         &mut self,
         decl: &FnDecl,
-        mut in_band_ty_params: Option<(LocalDefId, &mut Vec<hir::GenericParam<'hir>>)>,
+        mut in_band_ty_params: Option<(NodeId, &mut Vec<hir::GenericParam<'hir>>)>,
         kind: FnDeclKind,
         make_ret_async: Option<NodeId>,
     ) -> &'hir hir::FnDecl<'hir> {
@@ -1577,50 +1474,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             make_ret_async: {:?})",
             decl, in_band_ty_params, kind, make_ret_async,
         );
-        let lt_mode = if make_ret_async.is_some() {
-            // In `async fn`, argument-position elided lifetimes
-            // must be transformed into fresh generic parameters so that
-            // they can be applied to the opaque `impl Trait` return type.
-            AnonymousLifetimeMode::CreateParameter
-        } else {
-            self.anonymous_lifetime_mode
-        };
 
         let c_variadic = decl.c_variadic();
 
-        // Remember how many lifetimes were already around so that we can
-        // only look at the lifetime parameters introduced by the arguments.
-        let inputs = self.with_anonymous_lifetime_mode(lt_mode, |this| {
-            // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
-            // as they are not explicit in HIR/Ty function signatures.
-            // (instead, the `c_variadic` flag is set to `true`)
-            let mut inputs = &decl.inputs[..];
-            if c_variadic {
-                inputs = &inputs[..inputs.len() - 1];
+        // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
+        // as they are not explicit in HIR/Ty function signatures.
+        // (instead, the `c_variadic` flag is set to `true`)
+        let mut inputs = &decl.inputs[..];
+        if c_variadic {
+            inputs = &inputs[..inputs.len() - 1];
+        }
+        let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
+            if let Some((_, ibty)) = &mut in_band_ty_params {
+                self.lower_ty_direct(
+                    &param.ty,
+                    ImplTraitContext::Universal(ibty, self.current_hir_id_owner),
+                )
+            } else {
+                self.lower_ty_direct(
+                    &param.ty,
+                    ImplTraitContext::Disallowed(match kind {
+                        FnDeclKind::Fn | FnDeclKind::Inherent => {
+                            unreachable!("fn should allow in-band lifetimes")
+                        }
+                        FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam,
+                        FnDeclKind::Closure => ImplTraitPosition::ClosureParam,
+                        FnDeclKind::Pointer => ImplTraitPosition::PointerParam,
+                        FnDeclKind::Trait => ImplTraitPosition::TraitParam,
+                        FnDeclKind::Impl => ImplTraitPosition::ImplParam,
+                    }),
+                )
             }
-            this.arena.alloc_from_iter(inputs.iter().map(|param| {
-                if let Some((_, ibty)) = &mut in_band_ty_params {
-                    this.lower_ty_direct(
-                        &param.ty,
-                        ImplTraitContext::Universal(ibty, this.current_hir_id_owner),
-                    )
-                } else {
-                    this.lower_ty_direct(
-                        &param.ty,
-                        ImplTraitContext::Disallowed(match kind {
-                            FnDeclKind::Fn | FnDeclKind::Inherent => {
-                                unreachable!("fn should allow in-band lifetimes")
-                            }
-                            FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam,
-                            FnDeclKind::Closure => ImplTraitPosition::ClosureParam,
-                            FnDeclKind::Pointer => ImplTraitPosition::PointerParam,
-                            FnDeclKind::Trait => ImplTraitPosition::TraitParam,
-                            FnDeclKind::Impl => ImplTraitPosition::ImplParam,
-                        }),
-                    )
-                }
-            }))
-        });
+        }));
 
         let output = if let Some(ret_id) = make_ret_async {
             self.lower_async_fn_ret_ty(
@@ -1632,10 +1517,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             match decl.output {
                 FnRetTy::Ty(ref ty) => {
                     let context = match in_band_ty_params {
-                        Some((def_id, _)) if kind.impl_trait_return_allowed() => {
+                        Some((node_id, _)) if kind.impl_trait_return_allowed() => {
+                            let fn_def_id = self.resolver.local_def_id(node_id);
                             ImplTraitContext::ReturnPositionOpaqueTy {
-                                fn_def_id: def_id,
-                                origin: hir::OpaqueTyOrigin::FnReturn(def_id),
+                                origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
                             }
                         }
                         _ => ImplTraitContext::Disallowed(match kind {
@@ -1696,25 +1581,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     // `fn_def_id`: `DefId` of the parent function (used to create child impl trait definition)
     // `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
     // `elided_lt_replacement`: replacement for elided lifetimes in the return type
+    #[tracing::instrument(level = "debug", skip(self))]
     fn lower_async_fn_ret_ty(
         &mut self,
         output: &FnRetTy,
-        fn_def_id: LocalDefId,
+        fn_node_id: NodeId,
         opaque_ty_node_id: NodeId,
     ) -> hir::FnRetTy<'hir> {
-        debug!(
-            "lower_async_fn_ret_ty(\
-             output={:?}, \
-             fn_def_id={:?}, \
-             opaque_ty_node_id={:?})",
-            output, fn_def_id, opaque_ty_node_id,
-        );
-
         let span = output.span();
 
         let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
 
         let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id);
+        let fn_def_id = self.resolver.local_def_id(fn_node_id);
 
         // When we create the opaque type for this async fn, it is going to have
         // to capture all the lifetimes involved in the signature (including in the
@@ -1754,92 +1633,85 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // should be figured out using the ordinary elision rules, and
         // this desugaring achieves that.
 
-        debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", self.in_scope_lifetimes);
-        debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", self.lifetimes_to_define);
-
         // Calculate all the lifetimes that should be captured
         // by the opaque type. This should include all in-scope
         // lifetime parameters, including those defined in-band.
-        //
-        // `lifetime_params` is a vector of tuple (span, parameter name, lifetime name).
-
-        // Input lifetime like `'a` or `'1`:
-        let mut lifetime_params: Vec<_> = self
-            .in_scope_lifetimes
-            .iter()
-            .cloned()
-            .map(|name| (name.ident().span, hir::LifetimeName::Param(name)))
-            .chain(self.lifetimes_to_define.iter().map(|&(span, node_id)| {
-                let def_id = self.resolver.local_def_id(node_id);
-                let name = hir::ParamName::Fresh(def_id);
-                (span, hir::LifetimeName::Param(name))
-            }))
-            .collect();
+
+        let mut captures = FxHashMap::default();
+
+        let extra_lifetime_params = self.resolver.take_extra_lifetime_params(opaque_ty_node_id);
+        debug!(?extra_lifetime_params);
+        for (ident, outer_node_id, outer_res) in extra_lifetime_params {
+            let Ident { name, span } = ident;
+            let outer_def_id = self.resolver.local_def_id(outer_node_id);
+            let inner_node_id = self.resolver.next_node_id();
+
+            // Add a definition for the in scope lifetime def.
+            self.resolver.create_def(
+                opaque_ty_def_id,
+                inner_node_id,
+                DefPathData::LifetimeNs(name),
+                ExpnId::root(),
+                span.with_parent(None),
+            );
+
+            let (p_name, inner_res) = match outer_res {
+                // Input lifetime like `'a`:
+                LifetimeRes::Param { param, .. } => {
+                    (hir::ParamName::Plain(ident), LifetimeRes::Param { param, binder: fn_node_id })
+                }
+                // Input lifetime like `'1`:
+                LifetimeRes::Fresh { param, .. } => (
+                    hir::ParamName::Fresh(outer_def_id),
+                    LifetimeRes::Fresh { param, binder: fn_node_id },
+                ),
+                LifetimeRes::Static | LifetimeRes::Error => continue,
+                res => {
+                    panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span)
+                }
+            };
+
+            captures.insert(outer_def_id, (span, inner_node_id, p_name, inner_res));
+        }
+
+        debug!(?captures);
 
         self.with_hir_id_owner(opaque_ty_node_id, |this| {
-            let mut generic_params: Vec<_> = lifetime_params
-                .iter()
-                .map(|&(span, name)| {
-                    // We can only get lifetime names from the outside.
-                    let hir::LifetimeName::Param(hir_name) = name else { panic!() };
-
-                    let node_id = this.resolver.next_node_id();
-
-                    // Add a definition for the in-band lifetime def.
-                    let def_id = this.resolver.create_def(
-                        opaque_ty_def_id,
-                        node_id,
-                        DefPathData::LifetimeNs(hir_name.ident().name),
-                        ExpnId::root(),
-                        span.with_parent(None),
-                    );
+            let future_bound =
+                this.while_capturing_lifetimes(opaque_ty_def_id, &mut captures, |this| {
+                    // We have to be careful to get elision right here. The
+                    // idea is that we create a lifetime parameter for each
+                    // lifetime in the return type.  So, given a return type
+                    // like `async fn foo(..) -> &[&u32]`, we lower to `impl
+                    // Future<Output = &'1 [ &'2 u32 ]>`.
+                    //
+                    // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
+                    // hence the elision takes place at the fn site.
+                    this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
+                });
+            debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
+            debug!("lower_async_fn_ret_ty: captures={:#?}", captures);
 
-                    let (kind, name) = match hir_name {
-                        ParamName::Plain(ident) => {
-                            (hir::LifetimeParamKind::Explicit, hir::ParamName::Plain(ident))
-                        }
-                        ParamName::Fresh(_) => {
-                            (hir::LifetimeParamKind::Elided, hir::ParamName::Fresh(def_id))
-                        }
-                        ParamName::Error => (hir::LifetimeParamKind::Error, hir::ParamName::Error),
+            let generic_params =
+                this.arena.alloc_from_iter(captures.iter().map(|(_, &(span, p_id, p_name, _))| {
+                    let hir_id = this.lower_node_id(p_id);
+                    debug_assert_ne!(this.resolver.opt_local_def_id(p_id), None);
+
+                    let kind = if p_name.ident().name == kw::UnderscoreLifetime {
+                        hir::LifetimeParamKind::Elided
+                    } else {
+                        hir::LifetimeParamKind::Explicit
                     };
 
                     hir::GenericParam {
-                        hir_id: this.lower_node_id(node_id),
-                        name,
-                        bounds: &[],
-                        span: this.lower_span(span),
+                        hir_id,
+                        name: p_name,
+                        span,
                         pure_wrt_drop: false,
+                        bounds: &[],
                         kind: hir::GenericParamKind::Lifetime { kind },
                     }
-                })
-                .collect();
-
-            // We have to be careful to get elision right here. The
-            // idea is that we create a lifetime parameter for each
-            // lifetime in the return type.  So, given a return type
-            // like `async fn foo(..) -> &[&u32]`, we lower to `impl
-            // Future<Output = &'1 [ &'2 u32 ]>`.
-            //
-            // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
-            // hence the elision takes place at the fn site.
-            let (lifetimes_to_define, future_bound) =
-                this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::CreateParameter, |this| {
-                    this.collect_in_band_defs(opaque_ty_def_id, |this| {
-                        this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
-                    })
-                });
-            debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
-            debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define);
-
-            // Output lifetime like `'_`:
-            for (span, node_id) in lifetimes_to_define {
-                let param = this.fresh_lifetime_to_generic_param(span, node_id);
-                lifetime_params.push((span, hir::LifetimeName::Implicit));
-                generic_params.push(param);
-            }
-            let generic_params = this.arena.alloc_from_iter(generic_params);
-            debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);
+                }));
             debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
 
             let opaque_ty_item = hir::OpaqueTy {
@@ -1856,8 +1728,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
         });
 
-        // As documented above on the variable
-        // `input_lifetimes_count`, we need to create the lifetime
+        // As documented above, we need to create the lifetime
         // arguments to our opaque type. Continuing with our example,
         // we're creating the type arguments for the return type:
         //
@@ -1873,12 +1744,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // For the "output" lifetime parameters, we just want to
         // generate `'_`.
         let generic_args =
-            self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, name)| {
-                GenericArg::Lifetime(hir::Lifetime {
-                    hir_id: self.next_id(),
-                    span: self.lower_span(span),
-                    name,
-                })
+            self.arena.alloc_from_iter(captures.into_iter().map(|(_, (span, _, p_name, res))| {
+                let id = self.resolver.next_node_id();
+                let ident = Ident::new(p_name.ident().name, span);
+                let l = self.new_named_lifetime_with_res(id, span, ident, res);
+                hir::GenericArg::Lifetime(l)
             }));
 
         // Create the `Foo<...>` reference itself. Note that the `type
@@ -1905,7 +1775,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 // `impl Future` opaque type that `async fn` implicitly
                 // generates.
                 let context = ImplTraitContext::ReturnPositionOpaqueTy {
-                    fn_def_id,
                     origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
                 };
                 self.lower_ty(ty, context)
@@ -1948,35 +1817,106 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
         let span = self.lower_span(l.ident.span);
-        match l.ident {
-            ident if ident.name == kw::StaticLifetime => {
-                self.new_named_lifetime(l.id, span, hir::LifetimeName::Static)
-            }
-            ident if ident.name == kw::UnderscoreLifetime => match self.anonymous_lifetime_mode {
-                AnonymousLifetimeMode::CreateParameter => {
-                    let fresh_name = self.collect_fresh_anonymous_lifetime(span);
-                    self.new_named_lifetime(l.id, span, hir::LifetimeName::Param(fresh_name))
-                }
-
-                AnonymousLifetimeMode::PassThrough => {
-                    self.new_named_lifetime(l.id, span, hir::LifetimeName::Underscore)
-                }
-
-                AnonymousLifetimeMode::ReportError => self.new_error_lifetime(Some(l.id), span),
-            },
-            ident => {
-                let param_name = ParamName::Plain(self.lower_ident(ident));
-                self.new_named_lifetime(l.id, span, hir::LifetimeName::Param(param_name))
-            }
-        }
+        let ident = self.lower_ident(l.ident);
+        let res = self
+            .resolver
+            .get_lifetime_res(l.id)
+            .unwrap_or_else(|| panic!("Missing resolution for lifetime {:?} at {:?}", l, span));
+        self.new_named_lifetime_with_res(l.id, span, ident, res)
     }
 
-    fn new_named_lifetime(
+    #[tracing::instrument(level = "debug", skip(self))]
+    fn new_named_lifetime_with_res(
         &mut self,
         id: NodeId,
         span: Span,
-        name: hir::LifetimeName,
+        ident: Ident,
+        res: LifetimeRes,
     ) -> hir::Lifetime {
+        debug!(?self.captured_lifetimes);
+        let name = match res {
+            LifetimeRes::Param { param, binder } => {
+                debug_assert_ne!(ident.name, kw::UnderscoreLifetime);
+                let p_name = ParamName::Plain(ident);
+                if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+                    &mut self.captured_lifetimes
+                    && !binders_to_ignore.contains(&binder)
+                {
+                    match captures.entry(param) {
+                        Entry::Occupied(_) => {}
+                        Entry::Vacant(v) => {
+                            let p_id = self.resolver.next_node_id();
+                            self.resolver.create_def(
+                                *parent_def_id,
+                                p_id,
+                                DefPathData::LifetimeNs(p_name.ident().name),
+                                ExpnId::root(),
+                                span.with_parent(None),
+                            );
+
+                            v.insert((span, p_id, p_name, res));
+                        }
+                    }
+                }
+                hir::LifetimeName::Param(p_name)
+            }
+            LifetimeRes::Fresh { mut param, binder } => {
+                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+                if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+                    &mut self.captured_lifetimes
+                    && !binders_to_ignore.contains(&binder)
+                {
+                    match captures.entry(param) {
+                        Entry::Occupied(o) => param = self.resolver.local_def_id(o.get().1),
+                        Entry::Vacant(v) => {
+                            let p_id = self.resolver.next_node_id();
+                            let p_def_id = self.resolver.create_def(
+                                *parent_def_id,
+                                p_id,
+                                DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+                                ExpnId::root(),
+                                span.with_parent(None),
+                            );
+
+                            let p_name = ParamName::Fresh(param);
+                            v.insert((span, p_id, p_name, res));
+                            param = p_def_id;
+                        }
+                    }
+                }
+                let p_name = ParamName::Fresh(param);
+                hir::LifetimeName::Param(p_name)
+            }
+            LifetimeRes::Anonymous { binder, elided } => {
+                let l_name = if elided {
+                    hir::LifetimeName::Implicit
+                } else {
+                    hir::LifetimeName::Underscore
+                };
+                if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+                    &mut self.captured_lifetimes
+                    && !binders_to_ignore.contains(&binder)
+                {
+                    let p_id = self.resolver.next_node_id();
+                    let p_def_id = self.resolver.create_def(
+                        *parent_def_id,
+                        p_id,
+                        DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+                        ExpnId::root(),
+                        span.with_parent(None),
+                    );
+                    let p_name = ParamName::Fresh(p_def_id);
+                    captures.insert(p_def_id, (span, p_id, p_name, res));
+                    hir::LifetimeName::Param(p_name)
+                } else {
+                    l_name
+                }
+            }
+            LifetimeRes::Static => hir::LifetimeName::Static,
+            LifetimeRes::Error => hir::LifetimeName::Error,
+            res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span),
+        };
+        debug!(?self.captured_lifetimes);
         hir::Lifetime { hir_id: self.lower_node_id(id), span: self.lower_span(span), name }
     }
 
@@ -2001,39 +1941,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         param: &GenericParam,
         mut itctx: ImplTraitContext<'_, 'hir>,
     ) -> hir::GenericParam<'hir> {
-        let bounds: Vec<_> = self
-            .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
-                this.lower_param_bounds_mut(&param.bounds, itctx.reborrow()).collect()
-            });
+        let bounds: Vec<_> = self.lower_param_bounds_mut(&param.bounds, itctx.reborrow()).collect();
 
         let (name, kind) = match param.kind {
             GenericParamKind::Lifetime => {
-                let was_collecting_in_band = self.is_collecting_anonymous_lifetimes;
-                self.is_collecting_anonymous_lifetimes = None;
-
-                let lt = self
-                    .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
-                        this.lower_lifetime(&Lifetime { id: param.id, ident: param.ident })
-                    });
-                let param_name = match lt.name {
-                    hir::LifetimeName::Param(param_name) => param_name,
-                    hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
-                        hir::ParamName::Plain(lt.name.ident())
-                    }
-                    hir::LifetimeName::ImplicitObjectLifetimeDefault => {
-                        self.sess.diagnostic().span_bug(
-                            param.ident.span,
-                            "object-lifetime-default should not occur here",
-                        );
-                    }
-                    hir::LifetimeName::Static | hir::LifetimeName::Error => ParamName::Error,
+                let param_name = if param.ident.name == kw::StaticLifetime
+                    || param.ident.name == kw::UnderscoreLifetime
+                {
+                    ParamName::Error
+                } else {
+                    let ident = self.lower_ident(param.ident);
+                    ParamName::Plain(ident)
                 };
-
                 let kind =
                     hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit };
 
-                self.is_collecting_anonymous_lifetimes = was_collecting_in_band;
-
                 (param_name, kind)
             }
             GenericParamKind::Type { ref default, .. } => {
@@ -2047,10 +1969,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 (hir::ParamName::Plain(self.lower_ident(param.ident)), kind)
             }
             GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
-                let ty =
-                    self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
-                        this.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type))
-                    });
+                let ty = self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
                 let default = default.as_ref().map(|def| self.lower_anon_const(def));
                 (
                     hir::ParamName::Plain(self.lower_ident(param.ident)),
@@ -2087,6 +2006,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) }
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
     fn lower_poly_trait_ref(
         &mut self,
         p: &PolyTraitRef,
@@ -2095,27 +2015,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let bound_generic_params =
             self.lower_generic_params(&p.bound_generic_params, itctx.reborrow());
 
-        let trait_ref = self.with_in_scope_lifetime_defs(&p.bound_generic_params, |this| {
-            // Any impl Trait types defined within this scope can capture
-            // lifetimes bound on this predicate.
-            let lt_def_names = p.bound_generic_params.iter().filter_map(|param| match param.kind {
-                GenericParamKind::Lifetime { .. } => Some(hir::LifetimeName::Param(
-                    ParamName::Plain(param.ident.normalize_to_macros_2_0()),
-                )),
-                _ => None,
-            });
-            if let ImplTraitContext::TypeAliasesOpaqueTy { ref mut capturable_lifetimes } = itctx {
-                capturable_lifetimes.extend(lt_def_names.clone());
-            }
-
-            let res = this.lower_trait_ref(&p.trait_ref, itctx.reborrow());
-
-            if let ImplTraitContext::TypeAliasesOpaqueTy { ref mut capturable_lifetimes } = itctx {
-                for param in lt_def_names {
-                    capturable_lifetimes.remove(&param);
-                }
-            }
-            res
+        let trait_ref = self.with_lifetime_binder(p.trait_ref.ref_id, |this| {
+            this.lower_trait_ref(&p.trait_ref, itctx.reborrow())
         });
 
         hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
@@ -2378,98 +2279,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         hir::Ty { hir_id, kind, span: self.lower_span(span) }
     }
 
-    /// Invoked to create the lifetime argument for a type `&T`
-    /// with no explicit lifetime.
-    fn elided_ref_lifetime(&mut self, span: Span) -> hir::Lifetime {
-        match self.anonymous_lifetime_mode {
-            // Intercept when we are in an impl header or async fn and introduce an in-band
-            // lifetime.
-            // Hence `impl Foo for &u32` becomes `impl<'f> Foo for &'f u32` for some fresh
-            // `'f`.
-            AnonymousLifetimeMode::CreateParameter => {
-                let fresh_name = self.collect_fresh_anonymous_lifetime(span);
-                hir::Lifetime {
-                    hir_id: self.next_id(),
-                    span: self.lower_span(span),
-                    name: hir::LifetimeName::Param(fresh_name),
-                }
-            }
-
-            AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
-
-            AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
-        }
-    }
-
-    /// Report an error on illegal use of `'_` or a `&T` with no explicit lifetime;
-    /// return an "error lifetime".
-    fn new_error_lifetime(&mut self, id: Option<NodeId>, span: Span) -> hir::Lifetime {
-        let id = id.unwrap_or_else(|| self.resolver.next_node_id());
-        self.new_named_lifetime(id, span, hir::LifetimeName::Error)
-    }
-
-    /// Invoked to create the lifetime argument(s) for a path like
-    /// `std::cell::Ref<T>`; note that implicit lifetimes in these
-    /// sorts of cases are deprecated. This may therefore report a warning or an
-    /// error, depending on the mode.
-    fn elided_path_lifetimes<'s>(
-        &'s mut self,
-        span: Span,
-        count: usize,
-    ) -> impl Iterator<Item = hir::Lifetime> + Captures<'a> + Captures<'s> + Captures<'hir> {
-        (0..count).map(move |_| self.elided_path_lifetime(span))
-    }
-
-    fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime {
-        match self.anonymous_lifetime_mode {
-            AnonymousLifetimeMode::CreateParameter => {
-                // We should have emitted E0726 when processing this path above
-                self.sess
-                    .delay_span_bug(span, "expected 'implicit elided lifetime not allowed' error");
-                let id = self.resolver.next_node_id();
-                self.new_named_lifetime(id, span, hir::LifetimeName::Error)
-            }
-            // `PassThrough` is the normal case.
-            // `new_error_lifetime`, which would usually be used in the case of `ReportError`,
-            // is unsuitable here, as these can occur from missing lifetime parameters in a
-            // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit
-            // lifetime. Instead, we simply create an implicit lifetime, which will be checked
-            // later, at which point a suitable error will be emitted.
-            AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
-                self.new_implicit_lifetime(span)
-            }
-        }
-    }
-
     /// Invoked to create the lifetime argument(s) for an elided trait object
     /// bound, like the bound in `Box<dyn Debug>`. This method is not invoked
     /// when the bound is written, even if it is written with `'_` like in
     /// `Box<dyn Debug + '_>`. In those cases, `lower_lifetime` is invoked.
     fn elided_dyn_bound(&mut self, span: Span) -> hir::Lifetime {
-        match self.anonymous_lifetime_mode {
-            // NB. We intentionally ignore the create-parameter mode here.
-            // and instead "pass through" to resolve-lifetimes, which will apply
-            // the object-lifetime-defaulting rules. Elided object lifetime defaults
-            // do not act like other elided lifetimes. In other words, given this:
-            //
-            //     impl Foo for Box<dyn Debug>
-            //
-            // we do not introduce a fresh `'_` to serve as the bound, but instead
-            // ultimately translate to the equivalent of:
-            //
-            //     impl Foo for Box<dyn Debug + 'static>
-            //
-            // `resolve_lifetime` has the code to make that happen.
-            AnonymousLifetimeMode::CreateParameter => {}
-
-            AnonymousLifetimeMode::ReportError => {
-                // ReportError applies to explicit use of `'_`.
-            }
-
-            // This is the normal case.
-            AnonymousLifetimeMode::PassThrough => {}
-        }
-
         let r = hir::Lifetime {
             hir_id: self.next_id(),
             span: self.lower_span(span),
@@ -2478,14 +2292,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         debug!("elided_dyn_bound: r={:?}", r);
         r
     }
-
-    fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
-        hir::Lifetime {
-            hir_id: self.next_id(),
-            span: self.lower_span(span),
-            name: hir::LifetimeName::Implicit,
-        }
-    }
 }
 
 /// Helper struct for delayed construction of GenericArgs.
@@ -2511,121 +2317,3 @@ impl<'hir> GenericArgsCtor<'hir> {
         this.arena.alloc(ga)
     }
 }
-
-#[tracing::instrument(level = "debug")]
-fn lifetimes_from_impl_trait_bounds(
-    opaque_ty_id: NodeId,
-    bounds: hir::GenericBounds<'_>,
-    lifetimes_to_include: Option<&FxHashSet<hir::LifetimeName>>,
-) -> Vec<(hir::LifetimeName, Span)> {
-    // This visitor walks over `impl Trait` bounds and creates defs for all lifetimes that
-    // appear in the bounds, excluding lifetimes that are created within the bounds.
-    // E.g., `'a`, `'b`, but not `'c` in `impl for<'c> SomeTrait<'a, 'b, 'c>`.
-    struct ImplTraitLifetimeCollector<'r> {
-        collect_elided_lifetimes: bool,
-        currently_bound_lifetimes: Vec<hir::LifetimeName>,
-        already_defined_lifetimes: FxHashSet<hir::LifetimeName>,
-        lifetimes: Vec<(hir::LifetimeName, Span)>,
-        lifetimes_to_include: Option<&'r FxHashSet<hir::LifetimeName>>,
-    }
-
-    impl<'r, 'v> intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r> {
-        fn visit_generic_args(&mut self, span: Span, parameters: &'v hir::GenericArgs<'v>) {
-            // Don't collect elided lifetimes used inside of `Fn()` syntax.
-            if parameters.parenthesized {
-                let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
-                self.collect_elided_lifetimes = false;
-                intravisit::walk_generic_args(self, span, parameters);
-                self.collect_elided_lifetimes = old_collect_elided_lifetimes;
-            } else {
-                intravisit::walk_generic_args(self, span, parameters);
-            }
-        }
-
-        fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
-            // Don't collect elided lifetimes used inside of `fn()` syntax.
-            if let hir::TyKind::BareFn(_) = t.kind {
-                let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
-                self.collect_elided_lifetimes = false;
-
-                // Record the "stack height" of `for<'a>` lifetime bindings
-                // to be able to later fully undo their introduction.
-                let old_len = self.currently_bound_lifetimes.len();
-                intravisit::walk_ty(self, t);
-                self.currently_bound_lifetimes.truncate(old_len);
-
-                self.collect_elided_lifetimes = old_collect_elided_lifetimes;
-            } else {
-                intravisit::walk_ty(self, t)
-            }
-        }
-
-        fn visit_poly_trait_ref(
-            &mut self,
-            trait_ref: &'v hir::PolyTraitRef<'v>,
-            modifier: hir::TraitBoundModifier,
-        ) {
-            // Record the "stack height" of `for<'a>` lifetime bindings
-            // to be able to later fully undo their introduction.
-            let old_len = self.currently_bound_lifetimes.len();
-            intravisit::walk_poly_trait_ref(self, trait_ref, modifier);
-            self.currently_bound_lifetimes.truncate(old_len);
-        }
-
-        fn visit_generic_param(&mut self, param: &'v hir::GenericParam<'v>) {
-            // Record the introduction of 'a in `for<'a> ...`.
-            if let hir::GenericParamKind::Lifetime { .. } = param.kind {
-                // Introduce lifetimes one at a time so that we can handle
-                // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>`.
-                let lt_name = hir::LifetimeName::Param(param.name);
-                self.currently_bound_lifetimes.push(lt_name);
-            }
-
-            intravisit::walk_generic_param(self, param);
-        }
-
-        fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
-            let name = match lifetime.name {
-                hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
-                    if self.collect_elided_lifetimes {
-                        // Use `'_` for both implicit and underscore lifetimes in
-                        // `type Foo<'_> = impl SomeTrait<'_>;`.
-                        hir::LifetimeName::Underscore
-                    } else {
-                        return;
-                    }
-                }
-                hir::LifetimeName::Param(_) => lifetime.name,
-
-                // Refers to some other lifetime that is "in
-                // scope" within the type.
-                hir::LifetimeName::ImplicitObjectLifetimeDefault => return,
-
-                hir::LifetimeName::Error | hir::LifetimeName::Static => return,
-            };
-
-            if !self.currently_bound_lifetimes.contains(&name)
-                && !self.already_defined_lifetimes.contains(&name)
-                && self.lifetimes_to_include.map_or(true, |lifetimes| lifetimes.contains(&name))
-            {
-                self.already_defined_lifetimes.insert(name);
-
-                self.lifetimes.push((name, lifetime.span));
-            }
-        }
-    }
-
-    let mut lifetime_collector = ImplTraitLifetimeCollector {
-        collect_elided_lifetimes: true,
-        currently_bound_lifetimes: Vec::new(),
-        already_defined_lifetimes: FxHashSet::default(),
-        lifetimes: Vec::new(),
-        lifetimes_to_include,
-    };
-
-    for bound in bounds {
-        intravisit::walk_param_bound(&mut lifetime_collector, &bound);
-    }
-
-    lifetime_collector.lifetimes
-}
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 8bf4b8fcc39..3c9399c1fdf 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -1,15 +1,14 @@
 use crate::ImplTraitPosition;
 
-use super::{AnonymousLifetimeMode, ImplTraitContext, LoweringContext, ParamMode};
-use super::{GenericArgsCtor, ParenthesizedGenericArgs};
+use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
+use super::{ImplTraitContext, LoweringContext, ParamMode};
 
 use rustc_ast::{self as ast, *};
 use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, PartialRes, Res};
-use rustc_hir::def_id::DefId;
 use rustc_hir::GenericArg;
-use rustc_span::symbol::Ident;
+use rustc_span::symbol::{kw, Ident};
 use rustc_span::{BytePos, Span, DUMMY_SP};
 
 use smallvec::smallvec;
@@ -47,30 +46,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         _ => param_mode,
                     };
 
-                    // Figure out if this is a type/trait segment,
-                    // which may need lifetime elision performed.
-                    let parent_def_id = |this: &mut Self, def_id: DefId| DefId {
-                        krate: def_id.krate,
-                        index: this.resolver.def_key(def_id).parent.expect("missing parent"),
-                    };
-                    let type_def_id = match partial_res.base_res() {
-                        Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
-                            Some(parent_def_id(self, def_id))
-                        }
-                        Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
-                            Some(parent_def_id(self, def_id))
-                        }
-                        Res::Def(DefKind::Struct, def_id)
-                        | Res::Def(DefKind::Union, def_id)
-                        | Res::Def(DefKind::Enum, def_id)
-                        | Res::Def(DefKind::TyAlias, def_id)
-                        | Res::Def(DefKind::Trait, def_id)
-                            if i + 1 == proj_start =>
-                        {
-                            Some(def_id)
-                        }
-                        _ => None,
-                    };
                     let parenthesized_generic_args = match partial_res.base_res() {
                         // `a::b::Trait(Args)`
                         Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
@@ -90,13 +65,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         _ => ParenthesizedGenericArgs::Err,
                     };
 
-                    let num_lifetimes = type_def_id
-                        .map_or(0, |def_id| self.resolver.item_generics_num_lifetimes(def_id));
                     self.lower_path_segment(
                         p.span,
                         segment,
                         param_mode,
-                        num_lifetimes,
                         parenthesized_generic_args,
                         itctx.reborrow(),
                     )
@@ -143,7 +115,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 p.span,
                 segment,
                 param_mode,
-                0,
                 ParenthesizedGenericArgs::Err,
                 itctx.reborrow(),
             ));
@@ -184,7 +155,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     p.span,
                     segment,
                     param_mode,
-                    0,
                     ParenthesizedGenericArgs::Err,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                 )
@@ -209,14 +179,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         path_span: Span,
         segment: &PathSegment,
         param_mode: ParamMode,
-        expected_lifetimes: usize,
         parenthesized_generic_args: ParenthesizedGenericArgs,
         itctx: ImplTraitContext<'_, 'hir>,
     ) -> hir::PathSegment<'hir> {
-        debug!(
-            "path_span: {:?}, lower_path_segment(segment: {:?}, expected_lifetimes: {:?})",
-            path_span, segment, expected_lifetimes
-        );
+        debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment,);
         let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
             let msg = "parenthesized type parameters may only be used with a `Fn` trait";
             match **generic_args {
@@ -224,7 +190,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
                 }
                 GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
-                    ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
+                    ParenthesizedGenericArgs::Ok => {
+                        self.lower_parenthesized_parameter_data(segment.id, data)
+                    }
                     ParenthesizedGenericArgs::Err => {
                         let mut err = struct_span_err!(self.sess, data.span, E0214, "{}", msg);
                         err.span_label(data.span, "only `Fn` traits may use parentheses");
@@ -274,33 +242,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         let has_lifetimes =
             generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
-        if !generic_args.parenthesized && !has_lifetimes && expected_lifetimes > 0 {
-            // Note: these spans are used for diagnostics when they can't be inferred.
-            // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
-            let elided_lifetime_span = if generic_args.span.is_empty() {
-                // If there are no brackets, use the identifier span.
-                // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
-                // originating from macros, since the segment's span might be from a macro arg.
-                segment.ident.span.find_ancestor_inside(path_span).unwrap_or(path_span)
-            } else if generic_args.is_empty() {
-                // If there are brackets, but not generic arguments, then use the opening bracket
-                generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
-            } else {
-                // Else use an empty span right after the opening bracket.
-                generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
-            };
-            generic_args.args = self
-                .elided_path_lifetimes(elided_lifetime_span, expected_lifetimes)
-                .map(GenericArg::Lifetime)
-                .chain(generic_args.args.into_iter())
-                .collect();
-            if let (ParamMode::Explicit, AnonymousLifetimeMode::CreateParameter) =
-                (param_mode, self.anonymous_lifetime_mode)
-            {
-                // Late resolver should have issued the error.
-                self.sess
-                    .delay_span_bug(elided_lifetime_span, "implicit lifetime not allowed here");
-            }
+        if !generic_args.parenthesized && !has_lifetimes {
+            self.maybe_insert_elided_lifetimes_in_path(
+                path_span,
+                segment.id,
+                segment.ident.span,
+                &mut generic_args,
+            );
         }
 
         let res = self.expect_full_res(segment.id);
@@ -323,6 +271,49 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
     }
 
+    fn maybe_insert_elided_lifetimes_in_path(
+        &mut self,
+        path_span: Span,
+        segment_id: NodeId,
+        segment_ident_span: Span,
+        generic_args: &mut GenericArgsCtor<'hir>,
+    ) {
+        let (start, end) = match self.resolver.get_lifetime_res(segment_id) {
+            Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end),
+            None => return,
+            Some(_) => panic!(),
+        };
+        let expected_lifetimes = end.as_usize() - start.as_usize();
+        debug!(expected_lifetimes);
+
+        // Note: these spans are used for diagnostics when they can't be inferred.
+        // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
+        let elided_lifetime_span = if generic_args.span.is_empty() {
+            // If there are no brackets, use the identifier span.
+            // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
+            // originating from macros, since the segment's span might be from a macro arg.
+            segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span)
+        } else if generic_args.is_empty() {
+            // If there are brackets, but not generic arguments, then use the opening bracket
+            generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
+        } else {
+            // Else use an empty span right after the opening bracket.
+            generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
+        };
+
+        generic_args.args.insert_many(
+            0,
+            (start.as_u32()..end.as_u32()).map(|i| {
+                let id = NodeId::from_u32(i);
+                let l = self.lower_lifetime(&Lifetime {
+                    id,
+                    ident: Ident::new(kw::UnderscoreLifetime, elided_lifetime_span),
+                });
+                GenericArg::Lifetime(l)
+            }),
+        );
+    }
+
     pub(crate) fn lower_angle_bracketed_parameter_data(
         &mut self,
         data: &AngleBracketedArgs,
@@ -354,6 +345,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_parenthesized_parameter_data(
         &mut self,
+        id: NodeId,
         data: &ParenthesizedArgs,
     ) -> (GenericArgsCtor<'hir>, bool) {
         // Switch to `PassThrough` mode for anonymous lifetimes; this
@@ -361,7 +353,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // a hidden lifetime parameter. This is needed for backwards
         // compatibility, even in contexts like an impl header where
         // we generally don't permit such things (see #51008).
-        self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
+        self.with_lifetime_binder(id, |this| {
             let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
             let inputs = this.arena.alloc_from_iter(inputs.iter().map(|ty| {
                 this.lower_ty_direct(
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 2d01673b61d..7d9203b70ab 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -93,8 +93,6 @@ pub enum LifetimeName {
     Param(ParamName),
 
     /// User wrote nothing (e.g., the lifetime in `&u32`).
-    ///
-    /// The bool indicates whether the user should have written something.
     Implicit,
 
     /// Implicit lifetime in a context like `dyn Foo`. This is
@@ -444,9 +442,6 @@ pub enum GenericBound<'hir> {
     Outlives(Lifetime),
 }
 
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(GenericBound<'_>, 48);
-
 impl GenericBound<'_> {
     pub fn trait_ref(&self) -> Option<&TraitRef<'_>> {
         match self {
@@ -3330,6 +3325,7 @@ mod size_asserts {
     rustc_data_structures::static_assert_size!(super::Pat<'static>, 88);
     rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
     rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
+    rustc_data_structures::static_assert_size!(super::GenericBound<'_>, 48);
 
     rustc_data_structures::static_assert_size!(super::Item<'static>, 160);
     rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 50055f8030c..9ea66c0b59d 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -14,13 +14,15 @@ use crate::{ResolutionError, Resolver, Segment, UseError};
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
 use rustc_ast::*;
-use rustc_ast_lowering::ResolverAstLowering;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_ast_lowering::{LifetimeRes, ResolverAstLowering};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_errors::DiagnosticId;
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS};
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
+use rustc_hir::definitions::DefPathData;
 use rustc_hir::{PrimTy, TraitCandidate};
+use rustc_index::vec::Idx;
 use rustc_middle::ty::DefIdTree;
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
@@ -195,7 +197,7 @@ enum LifetimeRibKind {
     Item,
 
     /// This rib declares generic parameters.
-    Generics { span: Span, kind: LifetimeBinderKind },
+    Generics { parent: NodeId, span: Span, kind: LifetimeBinderKind },
 
     /// For **Modern** cases, create a new anonymous region parameter
     /// and reference that.
@@ -204,7 +206,7 @@ enum LifetimeRibKind {
     /// `resolve_lifetime` code.
     ///
     /// For **Deprecated** cases, report an error.
-    AnonymousCreateParameter,
+    AnonymousCreateParameter(NodeId),
 
     /// Give a hard error when either `&` or `'_` is written. Used to
     /// rule out things like `where T: Foo<'_>`. Does not imply an
@@ -212,7 +214,7 @@ enum LifetimeRibKind {
     AnonymousReportError,
 
     /// Pass responsibility to `resolve_lifetime` code for all cases.
-    AnonymousPassThrough,
+    AnonymousPassThrough(NodeId),
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -242,7 +244,8 @@ impl LifetimeBinderKind {
 #[derive(Debug)]
 struct LifetimeRib {
     kind: LifetimeRibKind,
-    bindings: IdentMap<()>,
+    // We need to preserve insertion order for async fns.
+    bindings: FxIndexMap<Ident, (NodeId, LifetimeRes)>,
 }
 
 impl LifetimeRib {
@@ -581,12 +584,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                 self.with_generic_param_rib(
                     &bare_fn.generic_params,
                     NormalRibKind,
-                    LifetimeRibKind::Generics { kind: LifetimeBinderKind::BareFnType, span },
+                    LifetimeRibKind::Generics {
+                        parent: ty.id,
+                        kind: LifetimeBinderKind::BareFnType,
+                        span,
+                    },
                     |this| {
-                        this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
-                            this.visit_generic_param_vec(&bare_fn.generic_params, false);
-                            visit::walk_fn_decl(this, &bare_fn.decl);
-                        });
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::AnonymousPassThrough(ty.id),
+                            |this| {
+                                this.visit_generic_param_vec(&bare_fn.generic_params, false);
+                                visit::walk_fn_decl(this, &bare_fn.decl);
+                            },
+                        );
                     },
                 );
                 self.diagnostic_metadata.current_trait_object = prev;
@@ -604,7 +614,11 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
         self.with_generic_param_rib(
             &tref.bound_generic_params,
             NormalRibKind,
-            LifetimeRibKind::Generics { kind: LifetimeBinderKind::PolyTrait, span },
+            LifetimeRibKind::Generics {
+                parent: tref.trait_ref.ref_id,
+                kind: LifetimeBinderKind::PolyTrait,
+                span,
+            },
             |this| {
                 this.visit_generic_param_vec(&tref.bound_generic_params, false);
                 this.smart_resolve_path(
@@ -625,6 +639,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         &generics.params,
                         ItemRibKind(HasGenericParams::Yes),
                         LifetimeRibKind::Generics {
+                            parent: foreign_item.id,
                             kind: LifetimeBinderKind::Item,
                             span: generics.span,
                         },
@@ -638,6 +653,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         &generics.params,
                         ItemRibKind(HasGenericParams::Yes),
                         LifetimeRibKind::Generics {
+                            parent: foreign_item.id,
                             kind: LifetimeBinderKind::Function,
                             span: generics.span,
                         },
@@ -655,13 +671,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
             }
         }
     }
-    fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, _: NodeId) {
+    fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, fn_id: NodeId) {
         let rib_kind = match fn_kind {
             // Bail if the function is foreign, and thus cannot validly have
             // a body, or if there's no body for some other reason.
             FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _)
             | FnKind::Fn(_, _, sig, _, generics, None) => {
-                self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
+                self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
                     // We don't need to deal with patterns in parameters, because
                     // they are not possible for foreign or bodiless functions.
                     this.visit_fn_header(&sig.header);
@@ -691,20 +707,50 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                     this.visit_generics(generics);
                 }
 
-                if async_node_id.is_some() {
+                if let Some(async_node_id) = async_node_id {
                     // In `async fn`, argument-position elided lifetimes
                     // must be transformed into fresh generic parameters so that
                     // they can be applied to the opaque `impl Trait` return type.
-                    this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter, |this| {
-                        // Add each argument to the rib.
-                        this.resolve_params(&declaration.inputs)
-                    });
+                    this.with_lifetime_rib(
+                        LifetimeRibKind::AnonymousCreateParameter(fn_id),
+                        |this| {
+                            // Add each argument to the rib.
+                            this.resolve_params(&declaration.inputs)
+                        },
+                    );
 
-                    this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
-                        visit::walk_fn_ret_ty(this, &declaration.output)
-                    });
+                    // Construct the list of in-scope lifetime parameters for async lowering.
+                    // We include all lifetime parameters, either named or "Fresh".
+                    // The order of those parameters does not matter, as long as it is
+                    // deterministic.
+                    let mut extra_lifetime_params =
+                        this.r.extra_lifetime_params_map.get(&fn_id).cloned().unwrap_or_default();
+                    for rib in this.lifetime_ribs.iter().rev() {
+                        extra_lifetime_params.extend(
+                            rib.bindings
+                                .iter()
+                                .map(|(&ident, &(node_id, res))| (ident, node_id, res)),
+                        );
+                        match rib.kind {
+                            LifetimeRibKind::Item => break,
+                            LifetimeRibKind::AnonymousCreateParameter(id) => {
+                                if let Some(earlier_fresh) =
+                                    this.r.extra_lifetime_params_map.get(&id)
+                                {
+                                    extra_lifetime_params.extend(earlier_fresh);
+                                }
+                            }
+                            _ => {}
+                        }
+                    }
+                    this.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params);
+
+                    this.with_lifetime_rib(
+                        LifetimeRibKind::AnonymousPassThrough(async_node_id),
+                        |this| visit::walk_fn_ret_ty(this, &declaration.output),
+                    );
                 } else {
-                    this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
+                    this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
                         // Add each argument to the rib.
                         this.resolve_params(&declaration.inputs);
 
@@ -716,13 +762,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                 // Be sure not to set this until the function signature has been resolved.
                 let previous_state = replace(&mut this.in_func_body, true);
                 // Resolve the function body, potentially inside the body of an async closure
-                this.with_lifetime_rib(
-                    LifetimeRibKind::AnonymousPassThrough,
-                    |this| match fn_kind {
+                this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
+                    match fn_kind {
                         FnKind::Fn(.., body) => walk_list!(this, visit_block, body),
                         FnKind::Closure(_, body) => this.visit_expr(body),
-                    },
-                );
+                    }
+                });
 
                 debug!("(resolving function) leaving function");
                 this.in_func_body = previous_state;
@@ -801,10 +846,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
         if let Some(ref args) = path_segment.args {
             match &**args {
                 GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, path_span, args),
-                GenericArgs::Parenthesized(..) => self
-                    .with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
-                        visit::walk_generic_args(this, path_span, args)
-                    }),
+                GenericArgs::Parenthesized(..) => self.with_lifetime_rib(
+                    LifetimeRibKind::AnonymousPassThrough(path_segment.id),
+                    |this| visit::walk_generic_args(this, path_span, args),
+                ),
             }
         }
     }
@@ -830,7 +875,11 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                 this.with_generic_param_rib(
                     &bound_generic_params,
                     NormalRibKind,
-                    LifetimeRibKind::Generics { kind: LifetimeBinderKind::WhereBound, span },
+                    LifetimeRibKind::Generics {
+                        parent: bounded_ty.id,
+                        kind: LifetimeBinderKind::WhereBound,
+                        span,
+                    },
                     |this| {
                         this.visit_generic_param_vec(&bound_generic_params, false);
                         this.visit_ty(bounded_ty);
@@ -1092,6 +1141,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         let ident = lifetime.ident;
 
         if ident.name == kw::StaticLifetime {
+            self.record_lifetime_res(lifetime.id, LifetimeRes::Static);
             return;
         }
 
@@ -1103,7 +1153,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         for i in &mut indices {
             let rib = &self.lifetime_ribs[i];
             let normalized_ident = ident.normalize_to_macros_2_0();
-            if let Some(_) = rib.bindings.get_key_value(&normalized_ident) {
+            if let Some(&(_, region)) = rib.bindings.get(&normalized_ident) {
+                self.record_lifetime_res(lifetime.id, region);
                 return;
             }
 
@@ -1123,6 +1174,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         }
 
         self.emit_undeclared_lifetime_error(lifetime, outer_res);
+        self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
@@ -1132,6 +1184,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         for i in (0..self.lifetime_ribs.len()).rev() {
             let rib = &mut self.lifetime_ribs[i];
             match rib.kind {
+                LifetimeRibKind::AnonymousCreateParameter(item_node_id) => {
+                    self.create_fresh_lifetime(lifetime.id, lifetime.ident, item_node_id);
+                    return;
+                }
                 LifetimeRibKind::AnonymousReportError => {
                     let (msg, note) = if elided {
                         (
@@ -1151,24 +1207,65 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     .span_label(lifetime.ident.span, note)
                     .emit();
 
+                    self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
+                    return;
+                }
+                LifetimeRibKind::AnonymousPassThrough(node_id) => {
+                    self.record_lifetime_res(
+                        lifetime.id,
+                        LifetimeRes::Anonymous { binder: node_id, elided },
+                    );
                     return;
                 }
-                LifetimeRibKind::AnonymousCreateParameter
-                | LifetimeRibKind::AnonymousPassThrough
-                | LifetimeRibKind::Item => return,
+                LifetimeRibKind::Item => break,
                 _ => {}
             }
         }
+        // This resolution is wrong, it passes the work to HIR lifetime resolution.
+        // We cannot use `LifetimeRes::Error` because we do not emit a diagnostic.
+        self.record_lifetime_res(
+            lifetime.id,
+            LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided },
+        );
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
     fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
         let id = self.r.next_node_id();
+        self.record_lifetime_res(
+            anchor_id,
+            LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) },
+        );
+
         let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
         self.resolve_anonymous_lifetime(&lt, true);
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
+    fn create_fresh_lifetime(&mut self, id: NodeId, ident: Ident, item_node_id: NodeId) {
+        debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+        debug!(?ident.span);
+        let item_def_id = self.r.local_def_id(item_node_id);
+        let def_node_id = self.r.next_node_id();
+        let def_id = self.r.create_def(
+            item_def_id,
+            def_node_id,
+            DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+            self.parent_scope.expansion.to_expn_id(),
+            ident.span,
+        );
+        debug!(?def_id);
+
+        let region = LifetimeRes::Fresh { param: def_id, binder: item_node_id };
+        self.record_lifetime_res(id, region);
+        self.r.extra_lifetime_params_map.entry(item_node_id).or_insert_with(Vec::new).push((
+            ident,
+            def_node_id,
+            region,
+        ));
+    }
+
+    #[tracing::instrument(level = "debug", skip(self))]
     fn resolve_elided_lifetimes_in_path(
         &mut self,
         path_id: NodeId,
@@ -1231,7 +1328,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     //
                     //     impl Foo for std::cell::Ref<u32> // note lack of '_
                     //     async fn foo(_: std::cell::Ref<u32>) { ... }
-                    LifetimeRibKind::AnonymousCreateParameter => {
+                    LifetimeRibKind::AnonymousCreateParameter(_) => {
                         error = true;
                         break;
                     }
@@ -1241,13 +1338,29 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit
                     // lifetime. Instead, we simply create an implicit lifetime, which will be checked
                     // later, at which point a suitable error will be emitted.
-                    LifetimeRibKind::AnonymousPassThrough
+                    LifetimeRibKind::AnonymousPassThrough(..)
                     | LifetimeRibKind::AnonymousReportError
                     | LifetimeRibKind::Item => break,
                     _ => {}
                 }
             }
 
+            let res = if error {
+                LifetimeRes::Error
+            } else {
+                LifetimeRes::Anonymous { binder: segment_id, elided: true }
+            };
+
+            let node_ids = self.r.next_node_ids(expected_lifetimes);
+            self.record_lifetime_res(
+                segment_id,
+                LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end },
+            );
+            for i in 0..expected_lifetimes {
+                let id = node_ids.start.plus(i);
+                self.record_lifetime_res(id, res);
+            }
+
             if !missing {
                 continue;
             }
@@ -1296,6 +1409,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         }
     }
 
+    #[tracing::instrument(level = "debug", skip(self))]
+    fn record_lifetime_res(&mut self, id: NodeId, res: LifetimeRes) {
+        if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
+            panic!(
+                "lifetime {:?} resolved multiple times ({:?} before, {:?} now)",
+                id, prev_res, res
+            )
+        }
+    }
+
     /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
     /// label and reports an error if the label is not found or is unreachable.
     fn resolve_label(&self, mut label: Ident) -> Option<NodeId> {
@@ -1379,7 +1502,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             this.with_generic_param_rib(
                 &generics.params,
                 ItemRibKind(HasGenericParams::Yes),
-                LifetimeRibKind::Generics { kind: LifetimeBinderKind::Item, span: generics.span },
+                LifetimeRibKind::Generics {
+                    parent: item.id,
+                    kind: LifetimeBinderKind::Item,
+                    span: generics.span,
+                },
                 |this| {
                     let item_def_id = this.r.local_def_id(item.id).to_def_id();
                     this.with_self_rib(
@@ -1446,6 +1573,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     &generics.params,
                     ItemRibKind(HasGenericParams::Yes),
                     LifetimeRibKind::Generics {
+                        parent: item.id,
                         kind: LifetimeBinderKind::Item,
                         span: generics.span,
                     },
@@ -1458,6 +1586,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     &generics.params,
                     ItemRibKind(HasGenericParams::Yes),
                     LifetimeRibKind::Generics {
+                        parent: item.id,
                         kind: LifetimeBinderKind::Function,
                         span: generics.span,
                     },
@@ -1487,6 +1616,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     &generics.params,
                     ItemRibKind(HasGenericParams::Yes),
                     LifetimeRibKind::Generics {
+                        parent: item.id,
                         kind: LifetimeBinderKind::Item,
                         span: generics.span,
                     },
@@ -1506,7 +1636,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                         this.with_generic_param_rib(
                                             &generics.params,
                                             AssocItemRibKind,
-                                            LifetimeRibKind::Generics { span: generics.span, kind },
+                                            LifetimeRibKind::Generics {
+                                                parent: item.id,
+                                                span: generics.span,
+                                                kind,
+                                            },
                                             |this| {
                                                 visit::walk_assoc_item(this, item, AssocCtxt::Trait)
                                             },
@@ -1571,6 +1705,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     &generics.params,
                     ItemRibKind(HasGenericParams::Yes),
                     LifetimeRibKind::Generics {
+                        parent: item.id,
                         kind: LifetimeBinderKind::Item,
                         span: generics.span,
                     },
@@ -1708,7 +1843,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam),
                 GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam),
                 GenericParamKind::Lifetime => {
-                    function_lifetime_rib.bindings.insert(ident, ());
+                    let LifetimeRibKind::Generics { parent, .. } = lifetime_kind else { panic!() };
+                    let res = LifetimeRes::Param { param: def_id, binder: parent };
+                    self.record_lifetime_res(param.id, res);
+                    function_lifetime_rib.bindings.insert(ident, (param.id, res));
                     continue;
                 }
             };
@@ -1849,10 +1987,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     ) {
         debug!("resolve_implementation");
         // If applicable, create a rib for the type parameters.
-        self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::ImplBlock }, |this| {
+        self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, parent: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| {
             // Dummy self type for better errors if `Self` is used in the trait path.
             this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| {
-                this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter, |this| {
+                this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| {
                     // Resolve the trait reference, if necessary.
                     this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| {
                         let item_def_id = this.r.local_def_id(item_id);
@@ -1876,7 +2014,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                             this.visit_generics(generics);
 
                             // Resolve the items within the impl.
-                            this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough,
+                            this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id),
                                 |this| {
                                     this.with_current_self_type(self_type, |this| {
                                         this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
@@ -1921,7 +2059,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                                         this.with_generic_param_rib(
                                                             &generics.params,
                                                             AssocItemRibKind,
-                                                            LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::Function },
+                                                            LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Function },
                                                             |this| {
                                                                 // If this is a trait impl, ensure the method
                                                                 // exists in trait
@@ -1950,7 +2088,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                                         this.with_generic_param_rib(
                                                             &generics.params,
                                                             AssocItemRibKind,
-                                                            LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::Item },
+                                                            LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Item },
                                                             |this| {
                                                                 // If this is a trait impl, ensure the type
                                                                 // exists in trait
@@ -2655,9 +2793,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         if !matches!(source, PathSource::TraitItem(..)) {
             // Avoid recording definition of `A::B` in `<T as A>::B::C`.
             self.r.record_partial_res(id, partial_res);
+            self.resolve_elided_lifetimes_in_path(id, partial_res, path, source, finalize);
         }
 
-        self.resolve_elided_lifetimes_in_path(id, partial_res, path, source, finalize);
         partial_res
     }
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index d77cc917e2f..69f69d5f566 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1824,7 +1824,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
 
         for rib in self.lifetime_ribs.iter().rev() {
             match rib.kind {
-                LifetimeRibKind::Generics { span, kind } => {
+                LifetimeRibKind::Generics { parent: _, span, kind } => {
                     if !span.can_be_used_for_suggestions() && suggest_note {
                         suggest_note = false; // Avoid displaying the same help multiple times.
                         err.span_label(
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index cca1f102586..64e228a88d9 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -29,7 +29,7 @@ use rustc_arena::{DroplessArena, TypedArena};
 use rustc_ast::node_id::NodeMap;
 use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID};
 use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path};
-use rustc_ast_lowering::ResolverAstLowering;
+use rustc_ast_lowering::{LifetimeRes, ResolverAstLowering};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::Lrc;
@@ -901,6 +901,10 @@ pub struct Resolver<'a> {
     import_res_map: NodeMap<PerNS<Option<Res>>>,
     /// Resolutions for labels (node IDs of their corresponding blocks or loops).
     label_res_map: NodeMap<NodeId>,
+    /// Resolutions for lifetimes.
+    lifetimes_res_map: NodeMap<LifetimeRes>,
+    /// Lifetime parameters that lowering will have to introduce.
+    extra_lifetime_params_map: NodeMap<Vec<(Ident, NodeId, LifetimeRes)>>,
 
     /// `CrateNum` resolutions of `extern crate` items.
     extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
@@ -1154,6 +1158,14 @@ impl ResolverAstLowering for Resolver<'_> {
         self.label_res_map.get(&id).cloned()
     }
 
+    fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes> {
+        self.lifetimes_res_map.get(&id).copied()
+    }
+
+    fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> {
+        self.extra_lifetime_params_map.remove(&id).unwrap_or_default()
+    }
+
     fn create_stable_hashing_context(&self) -> StableHashingContext<'_> {
         StableHashingContext::new(self.session, &self.definitions, self.crate_loader.cstore())
     }
@@ -1302,6 +1314,8 @@ impl<'a> Resolver<'a> {
             partial_res_map: Default::default(),
             import_res_map: Default::default(),
             label_res_map: Default::default(),
+            lifetimes_res_map: Default::default(),
+            extra_lifetime_params_map: Default::default(),
             extern_crate_map: Default::default(),
             reexport_map: FxHashMap::default(),
             trait_map: NodeMap::default(),