about summary refs log tree commit diff
diff options
context:
space:
mode:
-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.rs102
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs1003
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs176
-rw-r--r--compiler/rustc_hir/src/hir.rs4
-rw-r--r--compiler/rustc_resolve/src/late.rs202
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs9
9 files changed, 708 insertions, 798 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 5eab21bf79a..5a7ecb47dbe 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 a8bd8c92a41..80503bd066b 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1,5 +1,5 @@
-use super::{AnonymousLifetimeMode, LoweringContext, ParamMode};
 use super::{AstOwner, ImplTraitContext, ImplTraitPosition, ResolverAstLowering};
+use super::{LoweringContext, ParamMode};
 use crate::{Arena, FnDeclKind};
 
 use rustc_ast::ptr::P;
@@ -81,13 +81,13 @@ 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(),
+            lifetimes_to_define: Default::default(),
             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,12 +143,12 @@ 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 {
+            match parent_hir.node().expect_item().kind {
                 hir::ItemKind::Impl(hir::Impl { ref of_trait, ref generics, .. }) => {
                     lctx.is_in_trait_impl = of_trait.is_some();
                     lctx.in_scope_lifetimes = generics
@@ -157,7 +157,12 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
                         .filter(|param| {
                             matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
                         })
-                        .map(|param| param.name)
+                        .map(|param| {
+                            let def_id =
+                                parent_hir.nodes.local_id_to_def_id[&param.hir_id.local_id];
+                            let name = param.name;
+                            (name, def_id)
+                        })
                         .collect();
                 }
                 hir::ItemKind::Trait(_, _, ref generics, ..) => {
@@ -167,7 +172,12 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
                         .filter(|param| {
                             matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
                         })
-                        .map(|param| param.name)
+                        .map(|param| {
+                            let def_id =
+                                parent_hir.nodes.local_id_to_def_id[&param.hir_id.local_id];
+                            let name = param.name;
+                            (name, def_id)
+                        })
                         .collect();
                 }
                 _ => {}
@@ -288,20 +298,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_in_band_defs(generics, fn_def_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),
@@ -420,11 +421,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // 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_in_band_defs(ast_generics, lowered_trait_def_id, |this, _| {
                         let trait_ref = trait_ref.as_ref().map(|trait_ref| {
                             this.lower_trait_ref(
                                 trait_ref,
@@ -436,8 +434,7 @@ 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| {
@@ -750,18 +747,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_in_band_defs(generics, def_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)
                 }
@@ -868,13 +861,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), .. }) => {
@@ -884,7 +872,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(),
                 );
@@ -958,8 +946,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));
@@ -976,7 +962,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(),
                 );
@@ -1363,17 +1349,15 @@ 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 fn_def_id = self.resolver.local_def_id(id);
         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_in_band_defs(generics, fn_def_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) })
     }
 
@@ -1498,14 +1482,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> {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 9cb205074e7..c91e93c6a76 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -44,7 +44,7 @@ use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
@@ -53,7 +53,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 +121,38 @@ 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)>,
+    lifetimes_to_define: FxIndexMap<NodeId, Span>,
 
     /// 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>,
+    in_scope_lifetimes: Vec<(ParamName, LocalDefId)>,
+
+    /// Used to handle lifetimes appearing in impl-traits.  When we lower a lifetime,
+    /// it is inserted in the `FxHashMap`, and the resolution is modified so to point
+    /// to the lifetime parameter impl-trait will generate.
+    /// When traversing `for<...>` binders, they are inserted in the `FxHashSet` so
+    /// we know *not* to rebind the introduced lifetimes.
+    captured_lifetimes: Option<(
+        LocalDefId, // parent def_id for new definitions
+        FxHashMap<
+            LocalDefId, // original parameter id
+            (
+                Span,        // Span
+                NodeId,      // synthetized parameter id
+                ParamName,   // parameter name
+                LifetimeRes, // original resolution
+            ),
+        >,
+        FxHashSet<NodeId>, // traversed binders, to ignore
+    )>,
 
     current_hir_id_owner: LocalDefId,
     item_local_id_counter: hir::ItemLocalId,
@@ -157,6 +167,45 @@ 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.
+        binder: NodeId,
+    },
+    /// Created a generic parameter for an anonymous lifetime.
+    Fresh {
+        /// Id of the generic parameter that introduced it.
+        param: LocalDefId,
+        /// Id to create the HirId.
+        introducer: Option<NodeId>,
+        /// 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 },
+}
+
 pub trait ResolverAstLowering {
     fn def_key(&self, id: DefId) -> DefKey;
 
@@ -175,6 +224,9 @@ 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>;
+
     fn create_stable_hashing_context(&self) -> StableHashingContext<'_>;
 
     fn definitions(&self) -> &Definitions;
@@ -238,7 +290,7 @@ enum ImplTraitContext<'b, 'a> {
         /// parameter from `A`.
         // FIXME(impl_trait): but `required_region_bounds` will ICE later
         // anyway.
-        capturable_lifetimes: &'b mut FxHashSet<hir::LifetimeName>,
+        capturable_lifetimes: &'b mut FxHashSet<hir::ParamName>,
     },
     /// `impl Trait` is not accepted in this position.
     Disallowed(ImplTraitPosition),
@@ -453,56 +505,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 +690,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 {
@@ -727,23 +709,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     /// 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) {
+    ) -> (FxIndexMap<NodeId, Span>, T) {
+        let lifetime_stash = std::mem::take(&mut self.lifetimes_to_define);
         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;
+        let lifetimes_to_define = std::mem::replace(&mut self.lifetimes_to_define, lifetime_stash);
+
         (lifetimes_to_define, res)
     }
 
@@ -765,31 +744,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
     }
 
-    /// 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.
+    /// 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],
@@ -798,7 +756,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         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()))
+                let def_id = self.resolver.local_def_id(param.id);
+                let name = ParamName::Plain(param.ident);
+                Some((name, def_id))
             }
             _ => None,
         });
@@ -812,46 +772,39 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     /// 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>(
         &mut self,
         generics: &Generics,
         parent_def_id: LocalDefId,
-        anonymous_lifetime_mode: AnonymousLifetimeMode,
         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)
-                    })
+                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)
                 })
             });
 
         lowered_generics.params.extend(
             lifetimes_to_define
                 .into_iter()
-                .map(|(span, node_id)| self.fresh_lifetime_to_generic_param(span, node_id))
+                .map(|(node_id, span)| self.fresh_lifetime_to_generic_param(span, node_id))
                 .chain(impl_trait_defs),
         );
 
@@ -1258,26 +1211,40 @@ 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),
-                    }))
-                })
+                if let Some((_, _, binders)) = &mut this.captured_lifetimes {
+                    binders.insert(t.id);
+                }
+
+                let ret = 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),
+                }));
+
+                if let Some((_, _, binders)) = &mut this.captured_lifetimes {
+                    binders.remove(&t.id);
+                }
+                ret
             }),
             TyKind::Never => hir::TyKind::Never,
             TyKind::Tup(ref tys) => {
@@ -1421,20 +1388,16 @@ 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>>,
+        capturable_lifetimes: Option<&FxHashSet<hir::ParamName>>,
         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 +1407,44 @@ 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 lifetime_stash = std::mem::replace(
+                &mut lctx.captured_lifetimes,
+                Some((opaque_ty_def_id, FxHashMap::default(), FxHashSet::default())),
+            );
             let hir_bounds = lower_bounds(lctx);
+            collected_lifetimes =
+                std::mem::replace(&mut lctx.captured_lifetimes, lifetime_stash).unwrap().1;
 
-            collected_lifetimes = lifetimes_from_impl_trait_bounds(
-                opaque_ty_node_id,
-                &hir_bounds,
-                capturable_lifetimes,
-            );
+            if let Some(capturable_lifetimes) = capturable_lifetimes {
+                collected_lifetimes.retain(|_, (_, _, p_name, _)| {
+                    capturable_lifetimes.contains(&p_name.normalize_to_macros_2_0())
+                });
+            }
+            debug!(?collected_lifetimes);
 
-            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 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 (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 +1462,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 +1528,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 +1540,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 +1583,11 @@ 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),
+                                fn_def_id,
+                                origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
                             }
                         }
                         _ => ImplTraitContext::Disallowed(match kind {
@@ -1696,25 +1648,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
@@ -1761,59 +1707,68 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // 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();
 
-        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),
-                    );
+        // Input lifetime like `'a`:
+        let mut captures = FxHashMap::default();
+        for &(p_name, def_id) in &self.in_scope_lifetimes {
+            let Ident { name, span } = p_name.ident();
+            let node_id = self.resolver.next_node_id();
+
+            // Add a definition for the in-band lifetime def.
+            self.resolver.create_def(
+                opaque_ty_def_id,
+                node_id,
+                DefPathData::LifetimeNs(name),
+                ExpnId::root(),
+                span.with_parent(None),
+            );
 
-                    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 res = match p_name {
+                hir::ParamName::Plain(_) => {
+                    LifetimeRes::Param { param: def_id, binder: fn_node_id }
+                }
+                hir::ParamName::Fresh(_) => {
+                    LifetimeRes::Fresh { param: def_id, introducer: None, binder: fn_node_id }
+                }
+                hir::ParamName::Error => LifetimeRes::Error,
+            };
 
-                    hir::GenericParam {
-                        hir_id: this.lower_node_id(node_id),
-                        name,
-                        bounds: &[],
-                        span: this.lower_span(span),
-                        pure_wrt_drop: false,
-                        kind: hir::GenericParamKind::Lifetime { kind },
-                    }
-                })
-                .collect();
+            captures.insert(def_id, (span, node_id, p_name, res));
+        }
+
+        // Input lifetime like `'1`:
+        for (&node_id, &span) in &self.lifetimes_to_define {
+            let def_id = self.resolver.local_def_id(node_id);
+            let new_node_id = self.resolver.next_node_id();
+
+            // Add a definition for the in-band lifetime def.
+            let new_def_id = self.resolver.create_def(
+                opaque_ty_def_id,
+                new_node_id,
+                DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+                ExpnId::root(),
+                span.with_parent(None),
+            );
+
+            captures.insert(
+                def_id,
+                (
+                    span,
+                    new_node_id,
+                    hir::ParamName::Fresh(new_def_id),
+                    LifetimeRes::Fresh { param: def_id, introducer: None, binder: fn_node_id },
+                ),
+            );
+        }
+
+        debug!(?captures);
+
+        self.with_hir_id_owner(opaque_ty_node_id, |this| {
+            let lifetime_stash = std::mem::replace(
+                &mut this.captured_lifetimes,
+                Some((opaque_ty_def_id, std::mem::take(&mut captures), FxHashSet::default())),
+            );
+            debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", this.lifetimes_to_define);
 
             // We have to be careful to get elision right here. The
             // idea is that we create a lifetime parameter for each
@@ -1823,23 +1778,32 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             //
             // 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)
-                    })
-                });
+            let future_bound =
+                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);
+            captures = std::mem::replace(&mut this.captured_lifetimes, lifetime_stash).unwrap().1;
+            debug!("lower_async_fn_ret_ty: captures={:#?}", captures);
 
-            // 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);
+            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,
+                        name: p_name,
+                        span,
+                        pure_wrt_drop: false,
+                        bounds: &[],
+                        kind: hir::GenericParamKind::Lifetime { kind },
+                    }
+                }));
             debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
 
             let opaque_ty_item = hir::OpaqueTy {
@@ -1856,8 +1820,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 +1836,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
@@ -1948,35 +1910,115 @@ 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((parent_def_id, captures, binders)) = &mut self.captured_lifetimes {
+                    match captures.entry(param) {
+                        Entry::Occupied(_) => {}
+                        Entry::Vacant(v) => {
+                            if !binders.contains(&binder) {
+                                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, introducer, binder } => {
+                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+                // Only items are allowed to introduce fresh lifetimes,
+                // so we know `binder` has a `LocalDefId`.
+                let binder_def_id = self.resolver.local_def_id(binder);
+                if let Some((parent_def_id, captures, binders)) = &mut self.captured_lifetimes {
+                    match captures.entry(param) {
+                        Entry::Occupied(o) => param = self.resolver.local_def_id(o.get().1),
+                        Entry::Vacant(v) => {
+                            if !binders.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(param);
+                                v.insert((span, p_id, p_name, res));
+                                param = p_def_id;
+                            }
+                        }
+                    }
+                } else if let Some(introducer) = introducer {
+                    if self.is_collecting_anonymous_lifetimes == Some(binder_def_id)
+                        && self.resolver.opt_local_def_id(introducer) == Some(param)
+                    {
+                        debug!(
+                            "lifetime_to_define += id={:?} span={:?} res={:?}",
+                            introducer, span, res
+                        );
+                        self.lifetimes_to_define.insert(introducer, span);
+                    }
+                }
+                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
+                };
+                match &mut self.captured_lifetimes {
+                    Some((parent_def_id, captures, binders)) if !binders.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)
+                    }
+                    _ => 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 +2043,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 +2071,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 +2108,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,
@@ -2099,23 +2121,29 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // 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()),
-                )),
+                GenericParamKind::Lifetime { .. } => {
+                    Some(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());
             }
+            if let Some((_, _, binders)) = &mut this.captured_lifetimes {
+                binders.insert(p.trait_ref.ref_id);
+            }
 
-            let res = this.lower_trait_ref(&p.trait_ref, itctx.reborrow());
+            let trait_ref = 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
+            if let Some((_, _, binders)) = &mut this.captured_lifetimes {
+                binders.remove(&p.trait_ref.ref_id);
+            }
+            trait_ref
         });
 
         hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
@@ -2378,98 +2406,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 +2419,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 +2444,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..315e2289b7a 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,31 +353,33 @@ 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| {
-            let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
-            let inputs = this.arena.alloc_from_iter(inputs.iter().map(|ty| {
-                this.lower_ty_direct(
-                    ty,
-                    ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam),
-                )
-            }));
-            let output_ty = match output {
-                FnRetTy::Ty(ty) => this
-                    .lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)),
-                FnRetTy::Default(_) => this.arena.alloc(this.ty_tup(*span, &[])),
-            };
-            let args = smallvec![GenericArg::Type(this.ty_tup(*inputs_span, inputs))];
-            let binding = this.output_ty_binding(output_ty.span, output_ty);
-            (
-                GenericArgsCtor {
-                    args,
-                    bindings: arena_vec![this; binding],
-                    parenthesized: true,
-                    span: data.inputs_span,
-                },
-                false,
-            )
-        })
+        if let Some((_, _, binders)) = &mut self.captured_lifetimes {
+            binders.insert(id);
+        }
+        let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
+        let inputs = self.arena.alloc_from_iter(inputs.iter().map(|ty| {
+            self.lower_ty_direct(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam))
+        }));
+        let output_ty = match output {
+            FnRetTy::Ty(ty) => {
+                self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn))
+            }
+            FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
+        };
+        let args = smallvec![GenericArg::Type(self.ty_tup(*inputs_span, inputs))];
+        let binding = self.output_ty_binding(output_ty.span, output_ty);
+        if let Some((_, _, binders)) = &mut self.captured_lifetimes {
+            binders.remove(&id);
+        }
+        (
+            GenericArgsCtor {
+                args,
+                bindings: arena_vec![self; binding],
+                parenthesized: true,
+                span: data.inputs_span,
+            },
+            false,
+        )
     }
 
     /// An associated type binding `Output = $ty`.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index bbfabe01da1..432883a1103 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -442,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 {
@@ -3347,6 +3344,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>, 184);
     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 67a1557b0ea..0d940c05169 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, FnCtxt, FnKind, Visitor};
 use rustc_ast::*;
-use rustc_ast_lowering::ResolverAstLowering;
+use rustc_ast_lowering::{LifetimeRes, ResolverAstLowering};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 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,7 @@ impl LifetimeBinderKind {
 #[derive(Debug)]
 struct LifetimeRib {
     kind: LifetimeRibKind,
-    bindings: IdentMap<()>,
+    bindings: IdentMap<LifetimeRes>,
 }
 
 impl LifetimeRib {
@@ -581,12 +583,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 +613,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 +638,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 +652,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 +670,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 +706,24 @@ 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)
-                    });
+                    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 +735,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 +819,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 +848,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 +1114,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 +1126,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 +1147,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 +1157,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 +1180,64 @@ 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::AnonymousCreateParameter
-                | LifetimeRibKind::AnonymousPassThrough
-                | LifetimeRibKind::Item => return,
+                LifetimeRibKind::AnonymousPassThrough(node_id) => {
+                    self.record_lifetime_res(
+                        lifetime.id,
+                        LifetimeRes::Anonymous { binder: node_id, elided },
+                    );
+                    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,
+            introducer: Some(def_node_id),
+            binder: item_node_id,
+        };
+        self.record_lifetime_res(id, region);
+    }
+
+    #[tracing::instrument(level = "debug", skip(self))]
     fn resolve_elided_lifetimes_in_path(
         &mut self,
         path_id: NodeId,
@@ -1231,7 +1300,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 +1310,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 +1381,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 +1474,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 +1545,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 +1558,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 +1588,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 +1608,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 +1677,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 +1815,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, res);
                     continue;
                 }
             };
@@ -1849,10 +1959,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 +1986,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 +2031,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 +2060,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
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 4dfb7aef86f..180198326bb 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,8 @@ 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>,
 
     /// `CrateNum` resolutions of `extern crate` items.
     extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
@@ -1153,6 +1155,10 @@ 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 create_stable_hashing_context(&self) -> StableHashingContext<'_> {
         StableHashingContext::new(self.session, &self.definitions, self.crate_loader.cstore())
     }
@@ -1301,6 +1307,7 @@ impl<'a> Resolver<'a> {
             partial_res_map: Default::default(),
             import_res_map: Default::default(),
             label_res_map: Default::default(),
+            lifetimes_res_map: Default::default(),
             extern_crate_map: Default::default(),
             reexport_map: FxHashMap::default(),
             trait_map: NodeMap::default(),