From a076eae0d27915f352d92db1b63661e62750d7ae Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Wed, 3 Apr 2024 19:58:50 -0400
Subject: [PATCH 01/11] Parsing , pre-lowering support for precise captures

---
 compiler/rustc_ast/src/ast.rs                 |  2 +-
 compiler/rustc_ast/src/mut_visit.rs           |  5 +++-
 compiler/rustc_ast/src/visit.rs               |  3 ++-
 compiler/rustc_ast_lowering/src/lib.rs        |  3 ++-
 .../rustc_ast_passes/src/ast_validation.rs    |  2 +-
 compiler/rustc_ast_passes/src/feature_gate.rs |  1 +
 compiler/rustc_ast_pretty/src/pprust/state.rs |  3 ++-
 compiler/rustc_feature/src/unstable.rs        |  2 ++
 compiler/rustc_lint/src/unused.rs             |  2 +-
 compiler/rustc_parse/src/parser/generics.rs   |  2 +-
 compiler/rustc_parse/src/parser/item.rs       |  2 +-
 compiler/rustc_parse/src/parser/ty.rs         | 24 +++++++++++++++----
 compiler/rustc_resolve/src/late.rs            |  2 +-
 .../rustc_resolve/src/late/diagnostics.rs     |  2 +-
 compiler/rustc_span/src/symbol.rs             |  1 +
 15 files changed, 41 insertions(+), 15 deletions(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 5b708cf4e1a..55a934c48a8 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2132,7 +2132,7 @@ pub enum TyKind {
     /// The `NodeId` exists to prevent lowering from having to
     /// generate `NodeId`s on the fly, which would complicate
     /// the generation of opaque `type Foo = impl Trait` items significantly.
-    ImplTrait(NodeId, GenericBounds),
+    ImplTrait(NodeId, GenericBounds, Option<P<GenericArgs>>),
     /// No-op; kept solely so that we can pretty-print faithfully.
     Paren(P<Ty>),
     /// Unused for now.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index da57def263d..b8a5eeebf3a 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -518,9 +518,12 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
         TyKind::TraitObject(bounds, _syntax) => {
             visit_vec(bounds, |bound| vis.visit_param_bound(bound))
         }
-        TyKind::ImplTrait(id, bounds) => {
+        TyKind::ImplTrait(id, bounds, precise_capturing) => {
             vis.visit_id(id);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound));
+            visit_opt(precise_capturing, |precise_capturing| {
+                vis.visit_generic_args(precise_capturing);
+            });
         }
         TyKind::MacCall(mac) => vis.visit_mac_call(mac),
         TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 9e9ae52962d..91740021727 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -457,8 +457,9 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
         TyKind::TraitObject(bounds, ..) => {
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject);
         }
-        TyKind::ImplTrait(_, bounds) => {
+        TyKind::ImplTrait(_, bounds, precise_capturing) => {
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
+            visit_opt!(visitor, visit_generic_args, precise_capturing);
         }
         TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)),
         TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {}
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 5005c22d4cc..a4470622972 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1398,7 +1398,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 });
                 hir::TyKind::TraitObject(bounds, lifetime_bound, *kind)
             }
-            TyKind::ImplTrait(def_node_id, bounds) => {
+            TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => {
+                assert!(precise_capturing.is_none(), "precise captures not supported yet!");
                 let span = t.span;
                 match itctx {
                     ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait(
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index cb4dcf3ae75..495e90e967b 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -737,7 +737,7 @@ impl<'a> AstValidator<'a> {
                     }
                 }
             }
-            TyKind::ImplTrait(_, bounds) => {
+            TyKind::ImplTrait(_, bounds, _) => {
                 if self.is_impl_trait_banned {
                     self.dcx().emit_err(errors::ImplTraitPath { span: ty.span });
                 }
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index d7cd3efe408..70a3ccb0f44 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -569,6 +569,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
     gate_all!(postfix_match, "postfix match is experimental");
     gate_all!(mut_ref, "mutable by-reference bindings are experimental");
+    gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental");
 
     if !visitor.features.never_patterns {
         if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 51ccfe89fbd..6553ed72532 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1150,7 +1150,8 @@ impl<'a> State<'a> {
                 }
                 self.print_type_bounds(bounds);
             }
-            ast::TyKind::ImplTrait(_, bounds) => {
+            ast::TyKind::ImplTrait(_, bounds, _precise_capturing) => {
+                // TODO(precise_capturing):
                 self.word_nbsp("impl");
                 self.print_type_bounds(bounds);
             }
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index e6b19817de3..792cc1066f5 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -535,6 +535,8 @@ declare_features! (
     (unstable, must_not_suspend, "1.57.0", Some(83310)),
     /// Allows `mut ref` and `mut ref mut` identifier patterns.
     (incomplete, mut_ref, "CURRENT_RUSTC_VERSION", Some(123076)),
+    /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args.
+    (incomplete, precise_capturing, "CURRENT_RUSTC_VERSION", Some(123432)),
     /// Allows using `#[naked]` on functions.
     (unstable, naked_functions, "1.9.0", Some(90957)),
     /// Allows specifying the as-needed link modifier
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 503caa35358..3fe1f21d56a 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens {
                     ast::TyKind::TraitObject(..) => {}
                     ast::TyKind::BareFn(b)
                         if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
-                    ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
+                    ast::TyKind::ImplTrait(_, bounds, _) if bounds.len() > 1 => {}
                     _ => {
                         let spans = if !ty.span.from_expansion() {
                             r.span
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index fde16ac957d..93a15c938ec 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -62,7 +62,7 @@ impl<'a> Parser<'a> {
                 let snapshot = self.create_snapshot_for_diagnostic();
                 match self.parse_ty() {
                     Ok(p) => {
-                        if let TyKind::ImplTrait(_, bounds) = &p.kind {
+                        if let TyKind::ImplTrait(_, bounds, None) = &p.kind {
                             let span = impl_span.to(self.token.span.shrink_to_lo());
                             let mut err = self.dcx().struct_span_err(
                                 span,
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index d54eb8dc4c9..fd72dca6e21 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -625,7 +625,7 @@ impl<'a> Parser<'a> {
                     // This notably includes paths passed through `ty` macro fragments (#46438).
                     TyKind::Path(None, path) => path,
                     other => {
-                        if let TyKind::ImplTrait(_, bounds) = other
+                        if let TyKind::ImplTrait(_, bounds, None) = other
                             && let [bound] = bounds.as_slice()
                         {
                             // Suggest removing extra `impl` keyword:
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 1cea32cb90f..5d6c4f39ae1 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -316,7 +316,7 @@ impl<'a> Parser<'a> {
                             TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
                         }
                         (TyKind::TraitObject(bounds, _), kw::Impl) => {
-                            TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
+                            TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, None)
                         }
                         _ => return Err(err),
                     };
@@ -655,7 +655,6 @@ impl<'a> Parser<'a> {
 
     /// Parses an `impl B0 + ... + Bn` type.
     fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
-        // Always parse bounds greedily for better error recovery.
         if self.token.is_lifetime() {
             self.look_ahead(1, |t| {
                 if let token::Ident(sym, _) = t.kind {
@@ -669,9 +668,26 @@ impl<'a> Parser<'a> {
                 }
             })
         }
+
+        // parse precise captures, if any.
+        let precise_capturing = if self.eat_keyword(kw::Use) {
+            self.expect_lt()?;
+            let use_span = self.prev_token.span;
+            self.psess.gated_spans.gate(sym::precise_capturing, use_span);
+            let lo = self.token.span;
+            let args = self.parse_angle_args(None)?;
+            self.expect_gt()?;
+            Some(ast::AngleBracketedArgs { args, span: lo.to(self.prev_token.span) }.into())
+        } else {
+            None
+        };
+
+        // Always parse bounds greedily for better error recovery.
         let bounds = self.parse_generic_bounds()?;
+
         *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus);
-        Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
+
+        Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing))
     }
 
     /// Is a `dyn B0 + ... + Bn` type allowed here?
@@ -957,7 +973,7 @@ impl<'a> Parser<'a> {
                             Applicability::MaybeIncorrect,
                         )
                     }
-                    TyKind::ImplTrait(_, bounds)
+                    TyKind::ImplTrait(_, bounds, None)
                         if let [GenericBound::Trait(tr, ..), ..] = bounds.as_slice() =>
                     {
                         (
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 33c9c7fcc62..9e881532311 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -793,7 +793,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
                 self.r.record_partial_res(ty.id, PartialRes::new(res));
                 visit::walk_ty(self, ty)
             }
-            TyKind::ImplTrait(node_id, _) => {
+            TyKind::ImplTrait(node_id, _, _) => {
                 let candidates = self.lifetime_elision_candidates.take();
                 visit::walk_ty(self, ty);
                 self.record_lifetime_params_for_impl_trait(*node_id);
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index bb4294fbcfb..d79c638fa07 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -3121,7 +3121,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                                 .inputs
                                 .iter()
                                 .filter_map(|param| match &param.ty.kind {
-                                    TyKind::ImplTrait(_, bounds) => Some(bounds),
+                                    TyKind::ImplTrait(_, bounds, _) => Some(bounds),
                                     _ => None,
                                 })
                                 .flat_map(|bounds| bounds.into_iter())
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index bfd0f77c237..19c3fc58943 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1375,6 +1375,7 @@ symbols! {
         powif32,
         powif64,
         pre_dash_lto: "pre-lto",
+        precise_capturing,
         precise_pointer_size_matching,
         pref_align_of,
         prefetch_read_data,

From 647b672f16f6db2f156b69668ca963ec28016464 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Wed, 3 Apr 2024 20:54:23 -0400
Subject: [PATCH 02/11] Begin AST lowering for precise captures

---
 compiler/rustc_ast_lowering/src/lib.rs        | 79 ++++++++++++-------
 .../precise-capturing/higher-ranked.rs        | 18 +++++
 .../precise-capturing/higher-ranked.stderr    | 11 +++
 .../impl-trait/precise-capturing/outlives.rs  | 16 ++++
 .../precise-capturing/outlives.stderr         | 11 +++
 5 files changed, 106 insertions(+), 29 deletions(-)
 create mode 100644 tests/ui/impl-trait/precise-capturing/higher-ranked.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/higher-ranked.stderr
 create mode 100644 tests/ui/impl-trait/precise-capturing/outlives.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/outlives.stderr

diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index a4470622972..32b2f7c86ca 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1399,7 +1399,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 hir::TyKind::TraitObject(bounds, lifetime_bound, *kind)
             }
             TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => {
-                assert!(precise_capturing.is_none(), "precise captures not supported yet!");
                 let span = t.span;
                 match itctx {
                     ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait(
@@ -1409,8 +1408,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         bounds,
                         fn_kind,
                         itctx,
+                        precise_capturing.as_deref(),
                     ),
                     ImplTraitContext::Universal => {
+                        assert!(
+                            precise_capturing.is_none(),
+                            "TODO: precise captures not supported on universals!"
+                        );
                         let span = t.span;
 
                         // HACK: pprust breaks strings with newlines when the type
@@ -1521,6 +1525,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         bounds: &GenericBounds,
         fn_kind: Option<FnDeclKind>,
         itctx: ImplTraitContext,
+        precise_capturing: Option<&ast::GenericArgs>,
     ) -> hir::TyKind<'hir> {
         // 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
@@ -1529,40 +1534,56 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // frequently opened issues show.
         let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
 
-        let captured_lifetimes_to_duplicate = match origin {
-            hir::OpaqueTyOrigin::TyAlias { .. } => {
-                // type alias impl trait and associated type position impl trait were
-                // decided to capture all in-scope lifetimes, which we collect for
-                // all opaques during resolution.
-                self.resolver
-                    .take_extra_lifetime_params(opaque_ty_node_id)
-                    .into_iter()
-                    .map(|(ident, id, _)| Lifetime { id, ident })
-                    .collect()
-            }
-            hir::OpaqueTyOrigin::FnReturn(..) => {
-                if matches!(
-                    fn_kind.expect("expected RPITs to be lowered with a FnKind"),
-                    FnDeclKind::Impl | FnDeclKind::Trait
-                ) || self.tcx.features().lifetime_capture_rules_2024
-                    || span.at_least_rust_2024()
-                {
-                    // return-position impl trait in trait was decided to capture all
-                    // in-scope lifetimes, which we collect for all opaques during resolution.
+        let captured_lifetimes_to_duplicate = if let Some(precise_capturing) = precise_capturing {
+            let ast::GenericArgs::AngleBracketed(precise_capturing) = precise_capturing else {
+                panic!("we only parse angle-bracketed args")
+            };
+            // We'll actually validate these later on; all we need is the list of
+            // lifetimes to duplicate during this portion of lowering.
+            precise_capturing
+                .args
+                .iter()
+                .filter_map(|arg| match arg {
+                    ast::AngleBracketedArg::Arg(ast::GenericArg::Lifetime(lt)) => Some(*lt),
+                    _ => None,
+                })
+                .collect()
+        } else {
+            match origin {
+                hir::OpaqueTyOrigin::TyAlias { .. } => {
+                    // type alias impl trait and associated type position impl trait were
+                    // decided to capture all in-scope lifetimes, which we collect for
+                    // all opaques during resolution.
                     self.resolver
                         .take_extra_lifetime_params(opaque_ty_node_id)
                         .into_iter()
                         .map(|(ident, id, _)| Lifetime { id, ident })
                         .collect()
-                } else {
-                    // in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
-                    // example, we only need to duplicate lifetimes that appear in the
-                    // bounds, since those are the only ones that are captured by the opaque.
-                    lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
                 }
-            }
-            hir::OpaqueTyOrigin::AsyncFn(..) => {
-                unreachable!("should be using `lower_async_fn_ret_ty`")
+                hir::OpaqueTyOrigin::FnReturn(..) => {
+                    if matches!(
+                        fn_kind.expect("expected RPITs to be lowered with a FnKind"),
+                        FnDeclKind::Impl | FnDeclKind::Trait
+                    ) || self.tcx.features().lifetime_capture_rules_2024
+                        || span.at_least_rust_2024()
+                    {
+                        // return-position impl trait in trait was decided to capture all
+                        // in-scope lifetimes, which we collect for all opaques during resolution.
+                        self.resolver
+                            .take_extra_lifetime_params(opaque_ty_node_id)
+                            .into_iter()
+                            .map(|(ident, id, _)| Lifetime { id, ident })
+                            .collect()
+                    } else {
+                        // in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
+                        // example, we only need to duplicate lifetimes that appear in the
+                        // bounds, since those are the only ones that are captured by the opaque.
+                        lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
+                    }
+                }
+                hir::OpaqueTyOrigin::AsyncFn(..) => {
+                    unreachable!("should be using `lower_async_fn_ret_ty`")
+                }
             }
         };
         debug!(?captured_lifetimes_to_duplicate);
diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs
new file mode 100644
index 00000000000..c9faaaed968
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs
@@ -0,0 +1,18 @@
+//@ check-pass
+
+// Show how precise captures allow us to skip capturing a higher-ranked lifetime
+
+#![feature(lifetime_capture_rules_2024, precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+trait Trait<'a> {
+    type Item;
+}
+
+impl Trait<'_> for () {
+    type Item = Vec<()>;
+}
+
+fn hello() -> impl for<'a> Trait<'a, Item = impl use<> IntoIterator> {}
+
+fn main() {}
\ No newline at end of file
diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr b/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr
new file mode 100644
index 00000000000..e48d6d42af0
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr
@@ -0,0 +1,11 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/higher-ranked.rs:5:41
+   |
+LL | #![feature(lifetime_capture_rules_2024, precise_capturing)]
+   |                                         ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/outlives.rs b/tests/ui/impl-trait/precise-capturing/outlives.rs
new file mode 100644
index 00000000000..71e6333934e
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/outlives.rs
@@ -0,0 +1,16 @@
+//@ check-pass
+
+// Show that precise captures allow us to skip a lifetime param for outlives
+
+#![feature(lifetime_capture_rules_2024, precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn hello<'a: 'a, 'b: 'b>() -> impl use<'a> Sized { }
+
+fn outlives<'a, T: 'a>(_: T) {}
+
+fn test<'a, 'b>() {
+    outlives::<'a, _>(hello::<'a, 'b>());
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/outlives.stderr b/tests/ui/impl-trait/precise-capturing/outlives.stderr
new file mode 100644
index 00000000000..405c09cccd9
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/outlives.stderr
@@ -0,0 +1,11 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/outlives.rs:5:41
+   |
+LL | #![feature(lifetime_capture_rules_2024, precise_capturing)]
+   |                                         ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+

From fc9e344874ce718c951016ba29f7fcabb36f26c3 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Wed, 3 Apr 2024 21:47:02 -0400
Subject: [PATCH 03/11] Use dedicated PreciseCapturingArg for representing what
 goes in use<>

---
 compiler/rustc_ast/src/ast.rs          | 10 ++-
 compiler/rustc_ast/src/mut_visit.rs    | 31 ++++++++-
 compiler/rustc_ast/src/visit.rs        | 32 ++++++++-
 compiler/rustc_ast_lowering/src/lib.rs | 89 +++++++++++++-------------
 compiler/rustc_parse/src/parser/ty.rs  | 30 +++++++--
 compiler/rustc_resolve/src/late.rs     | 10 +++
 6 files changed, 146 insertions(+), 56 deletions(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 55a934c48a8..623a6437c53 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2132,7 +2132,7 @@ pub enum TyKind {
     /// The `NodeId` exists to prevent lowering from having to
     /// generate `NodeId`s on the fly, which would complicate
     /// the generation of opaque `type Foo = impl Trait` items significantly.
-    ImplTrait(NodeId, GenericBounds, Option<P<GenericArgs>>),
+    ImplTrait(NodeId, GenericBounds, Option<ThinVec<PreciseCapturingArg>>),
     /// No-op; kept solely so that we can pretty-print faithfully.
     Paren(P<Ty>),
     /// Unused for now.
@@ -2188,6 +2188,14 @@ pub enum TraitObjectSyntax {
     None,
 }
 
+#[derive(Clone, PartialEq, Encodable, Decodable, Debug)]
+pub enum PreciseCapturingArg {
+    /// Lifetime parameter
+    Lifetime(Lifetime),
+    /// Type or const parameter
+    Arg(Ident, NodeId),
+}
+
 /// Inline assembly operand explicit register or register class.
 ///
 /// E.g., `"eax"` as in `asm!("mov eax, 2", out("eax") result)`.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index b8a5eeebf3a..cb625873937 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -259,6 +259,14 @@ pub trait MutVisitor: Sized {
         noop_visit_param_bound(tpb, self);
     }
 
+    fn visit_precise_capturing_args(&mut self, args: &mut ThinVec<PreciseCapturingArg>) {
+        noop_visit_precise_capturing_args(args, self);
+    }
+
+    fn visit_precise_capturing_arg(&mut self, arg: &mut PreciseCapturingArg) {
+        noop_visit_precise_capturing_arg(arg, self);
+    }
+
     fn visit_mt(&mut self, mt: &mut MutTy) {
         noop_visit_mt(mt, self);
     }
@@ -522,7 +530,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
             vis.visit_id(id);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound));
             visit_opt(precise_capturing, |precise_capturing| {
-                vis.visit_generic_args(precise_capturing);
+                vis.visit_precise_capturing_args(precise_capturing);
             });
         }
         TyKind::MacCall(mac) => vis.visit_mac_call(mac),
@@ -917,6 +925,27 @@ pub fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T)
     }
 }
 
+pub fn noop_visit_precise_capturing_args<T: MutVisitor>(
+    args: &mut ThinVec<PreciseCapturingArg>,
+    vis: &mut T,
+) {
+    for arg in args {
+        vis.visit_precise_capturing_arg(arg);
+    }
+}
+
+pub fn noop_visit_precise_capturing_arg<T: MutVisitor>(arg: &mut PreciseCapturingArg, vis: &mut T) {
+    match arg {
+        PreciseCapturingArg::Lifetime(lt) => {
+            vis.visit_lifetime(lt);
+        }
+        PreciseCapturingArg::Arg(ident, id) => {
+            vis.visit_ident(ident);
+            vis.visit_id(id);
+        }
+    }
+}
+
 pub fn noop_flat_map_generic_param<T: MutVisitor>(
     mut param: GenericParam,
     vis: &mut T,
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 91740021727..b7e4d31edd7 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -20,6 +20,7 @@ use rustc_span::Span;
 
 pub use rustc_ast_ir::visit::VisitorResult;
 pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list};
+use thin_vec::ThinVec;
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum AssocCtxt {
@@ -184,6 +185,12 @@ pub trait Visitor<'ast>: Sized {
     fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) -> Self::Result {
         walk_param_bound(self, bounds)
     }
+    fn visit_precise_capturing_args(&mut self, args: &'ast ThinVec<PreciseCapturingArg>) {
+        walk_precise_capturing_args(self, args);
+    }
+    fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) {
+        walk_precise_capturing_arg(self, arg);
+    }
     fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) -> Self::Result {
         walk_poly_trait_ref(self, t)
     }
@@ -459,7 +466,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
         }
         TyKind::ImplTrait(_, bounds, precise_capturing) => {
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
-            visit_opt!(visitor, visit_generic_args, precise_capturing);
+            visit_opt!(visitor, visit_precise_capturing_args, precise_capturing);
         }
         TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)),
         TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {}
@@ -638,6 +645,29 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB
     }
 }
 
+pub fn walk_precise_capturing_args<'a, V: Visitor<'a>>(
+    visitor: &mut V,
+    args: &'a ThinVec<PreciseCapturingArg>,
+) {
+    for arg in args {
+        visitor.visit_precise_capturing_arg(arg);
+    }
+}
+
+pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>(
+    visitor: &mut V,
+    arg: &'a PreciseCapturingArg,
+) {
+    match arg {
+        PreciseCapturingArg::Lifetime(lt) => {
+            visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg);
+        }
+        PreciseCapturingArg::Arg(ident, _) => {
+            visitor.visit_ident(*ident);
+        }
+    }
+}
+
 pub fn walk_generic_param<'a, V: Visitor<'a>>(
     visitor: &mut V,
     param: &'a GenericParam,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 32b2f7c86ca..731dda39e9a 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1525,7 +1525,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         bounds: &GenericBounds,
         fn_kind: Option<FnDeclKind>,
         itctx: ImplTraitContext,
-        precise_capturing: Option<&ast::GenericArgs>,
+        precise_capturing: Option<&[ast::PreciseCapturingArg]>,
     ) -> hir::TyKind<'hir> {
         // 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
@@ -1534,58 +1534,55 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // frequently opened issues show.
         let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
 
-        let captured_lifetimes_to_duplicate = if let Some(precise_capturing) = precise_capturing {
-            let ast::GenericArgs::AngleBracketed(precise_capturing) = precise_capturing else {
-                panic!("we only parse angle-bracketed args")
-            };
-            // We'll actually validate these later on; all we need is the list of
-            // lifetimes to duplicate during this portion of lowering.
-            precise_capturing
-                .args
-                .iter()
-                .filter_map(|arg| match arg {
-                    ast::AngleBracketedArg::Arg(ast::GenericArg::Lifetime(lt)) => Some(*lt),
-                    _ => None,
-                })
-                .collect()
-        } else {
-            match origin {
-                hir::OpaqueTyOrigin::TyAlias { .. } => {
-                    // type alias impl trait and associated type position impl trait were
-                    // decided to capture all in-scope lifetimes, which we collect for
-                    // all opaques during resolution.
-                    self.resolver
-                        .take_extra_lifetime_params(opaque_ty_node_id)
-                        .into_iter()
-                        .map(|(ident, id, _)| Lifetime { id, ident })
-                        .collect()
-                }
-                hir::OpaqueTyOrigin::FnReturn(..) => {
-                    if matches!(
-                        fn_kind.expect("expected RPITs to be lowered with a FnKind"),
-                        FnDeclKind::Impl | FnDeclKind::Trait
-                    ) || self.tcx.features().lifetime_capture_rules_2024
-                        || span.at_least_rust_2024()
-                    {
-                        // return-position impl trait in trait was decided to capture all
-                        // in-scope lifetimes, which we collect for all opaques during resolution.
+        let captured_lifetimes_to_duplicate =
+            if let Some(precise_capturing) = precise_capturing_args {
+                // We'll actually validate these later on; all we need is the list of
+                // lifetimes to duplicate during this portion of lowering.
+                precise_capturing
+                    .iter()
+                    .filter_map(|arg| match arg {
+                        ast::PreciseCapturingArg::Lifetime(lt) => Some(*lt),
+                        ast::PreciseCapturingArg::Arg(..) => None,
+                    })
+                    .collect()
+            } else {
+                match origin {
+                    hir::OpaqueTyOrigin::TyAlias { .. } => {
+                        // type alias impl trait and associated type position impl trait were
+                        // decided to capture all in-scope lifetimes, which we collect for
+                        // all opaques during resolution.
                         self.resolver
                             .take_extra_lifetime_params(opaque_ty_node_id)
                             .into_iter()
                             .map(|(ident, id, _)| Lifetime { id, ident })
                             .collect()
-                    } else {
-                        // in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
-                        // example, we only need to duplicate lifetimes that appear in the
-                        // bounds, since those are the only ones that are captured by the opaque.
-                        lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
+                    }
+                    hir::OpaqueTyOrigin::FnReturn(..) => {
+                        if matches!(
+                            fn_kind.expect("expected RPITs to be lowered with a FnKind"),
+                            FnDeclKind::Impl | FnDeclKind::Trait
+                        ) || self.tcx.features().lifetime_capture_rules_2024
+                            || span.at_least_rust_2024()
+                        {
+                            // return-position impl trait in trait was decided to capture all
+                            // in-scope lifetimes, which we collect for all opaques during resolution.
+                            self.resolver
+                                .take_extra_lifetime_params(opaque_ty_node_id)
+                                .into_iter()
+                                .map(|(ident, id, _)| Lifetime { id, ident })
+                                .collect()
+                        } else {
+                            // in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
+                            // example, we only need to duplicate lifetimes that appear in the
+                            // bounds, since those are the only ones that are captured by the opaque.
+                            lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
+                        }
+                    }
+                    hir::OpaqueTyOrigin::AsyncFn(..) => {
+                        unreachable!("should be using `lower_async_fn_ret_ty`")
                     }
                 }
-                hir::OpaqueTyOrigin::AsyncFn(..) => {
-                    unreachable!("should be using `lower_async_fn_ret_ty`")
-                }
-            }
-        };
+            };
         debug!(?captured_lifetimes_to_duplicate);
 
         self.lower_opaque_inner(
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 5d6c4f39ae1..62f0602e1fa 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -1,4 +1,4 @@
-use super::{Parser, PathStyle, TokenType, Trailing};
+use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
 
 use crate::errors::{
     self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
@@ -14,7 +14,7 @@ use rustc_ast::util::case::Case;
 use rustc_ast::{
     self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound,
     GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef,
-    TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID,
+    PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID,
 };
 use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::{kw, sym, Ident};
@@ -671,13 +671,10 @@ impl<'a> Parser<'a> {
 
         // parse precise captures, if any.
         let precise_capturing = if self.eat_keyword(kw::Use) {
-            self.expect_lt()?;
             let use_span = self.prev_token.span;
             self.psess.gated_spans.gate(sym::precise_capturing, use_span);
-            let lo = self.token.span;
-            let args = self.parse_angle_args(None)?;
-            self.expect_gt()?;
-            Some(ast::AngleBracketedArgs { args, span: lo.to(self.prev_token.span) }.into())
+            let args = self.parse_precise_capturing_args()?;
+            Some(args)
         } else {
             None
         };
@@ -690,6 +687,25 @@ impl<'a> Parser<'a> {
         Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing))
     }
 
+    fn parse_precise_capturing_args(&mut self) -> PResult<'a, ThinVec<PreciseCapturingArg>> {
+        Ok(self
+            .parse_unspanned_seq(
+                &TokenKind::Lt,
+                &TokenKind::Gt,
+                SeqSep::trailing_allowed(token::Comma),
+                |self_| {
+                    if self_.check_ident() {
+                        Ok(PreciseCapturingArg::Arg(self_.parse_ident().unwrap(), DUMMY_NODE_ID))
+                    } else if self_.check_lifetime() {
+                        Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime()))
+                    } else {
+                        self_.unexpected_any()
+                    }
+                },
+            )?
+            .0)
+    }
+
     /// Is a `dyn B0 + ... + Bn` type allowed here?
     fn is_explicit_dyn_type(&mut self) -> bool {
         self.check_keyword(kw::Dyn)
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 9e881532311..e4bcdc96f44 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1047,10 +1047,20 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
         });
         self.diag_metadata.current_function = previous_value;
     }
+
     fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) {
         self.resolve_lifetime(lifetime, use_ctxt)
     }
 
+    fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) {
+        match arg {
+            PreciseCapturingArg::Lifetime(_) => visit::walk_precise_capturing_arg(self, arg),
+            PreciseCapturingArg::Arg(ident, _) => {
+                todo!("cannot resolve args yet: {ident}");
+            }
+        }
+    }
+
     fn visit_generics(&mut self, generics: &'ast Generics) {
         self.visit_generic_params(&generics.params, self.diag_metadata.current_self_item.is_some());
         for p in &generics.where_clause.predicates {

From c897092654bae65d5695ef3b1a5850c15513ed5d Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Wed, 3 Apr 2024 22:34:53 -0400
Subject: [PATCH 04/11] Implement resolution, parse use<Self>

---
 compiler/rustc_parse/src/parser/ty.rs |  8 +++++++-
 compiler/rustc_resolve/src/late.rs    | 24 ++++++++++++++++++++++--
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 62f0602e1fa..a24ecf938ba 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -694,7 +694,13 @@ impl<'a> Parser<'a> {
                 &TokenKind::Gt,
                 SeqSep::trailing_allowed(token::Comma),
                 |self_| {
-                    if self_.check_ident() {
+                    if self_.check_keyword(kw::SelfUpper) {
+                        self_.bump();
+                        Ok(PreciseCapturingArg::Arg(
+                            self_.prev_token.ident().unwrap().0,
+                            DUMMY_NODE_ID,
+                        ))
+                    } else if self_.check_ident() {
                         Ok(PreciseCapturingArg::Arg(self_.parse_ident().unwrap(), DUMMY_NODE_ID))
                     } else if self_.check_lifetime() {
                         Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime()))
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index e4bcdc96f44..c7e12284b50 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1055,8 +1055,28 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
     fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) {
         match arg {
             PreciseCapturingArg::Lifetime(_) => visit::walk_precise_capturing_arg(self, arg),
-            PreciseCapturingArg::Arg(ident, _) => {
-                todo!("cannot resolve args yet: {ident}");
+            PreciseCapturingArg::Arg(ident, node_id) => {
+                let ident = ident.normalize_to_macros_2_0();
+                'found: {
+                    for (rib_t, rib_v) in
+                        std::iter::zip(&self.ribs.type_ns, &self.ribs.value_ns).rev()
+                    {
+                        if let Some(res) = rib_t.bindings.get(&ident).or(rib_v.bindings.get(&ident))
+                        {
+                            self.r.record_partial_res(*node_id, PartialRes::new(*res));
+                            break 'found;
+                        }
+                    }
+                    self.report_error(
+                        ident.span,
+                        ResolutionError::FailedToResolve {
+                            segment: Some(ident.name),
+                            label: "could not find type or const parameter".to_string(),
+                            suggestion: None,
+                            module: None,
+                        },
+                    );
+                }
             }
         }
     }

From 41cf87b71b792b40155cd79f96234a89ae7dc27f Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 4 Apr 2024 10:48:47 -0400
Subject: [PATCH 05/11] Lower and resolve precise captures in HIR

---
 compiler/rustc_ast/src/ast.rs                 |  2 +-
 compiler/rustc_ast/src/mut_visit.rs           | 17 ++-----
 compiler/rustc_ast/src/visit.rs               | 19 ++------
 compiler/rustc_ast_lowering/src/lib.rs        | 47 ++++++++++++++++---
 .../src/lifetime_collector.rs                 | 15 +++---
 compiler/rustc_hir/src/hir.rs                 |  9 ++++
 compiler/rustc_hir/src/intravisit.rs          | 20 +++++++-
 .../src/collect/resolve_bound_vars.rs         | 25 ++++++++++
 8 files changed, 108 insertions(+), 46 deletions(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 623a6437c53..ec445bc5ed3 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -63,7 +63,7 @@ impl fmt::Debug for Label {
 
 /// A "Lifetime" is an annotation of the scope in which variable
 /// can be used, e.g. `'a` in `&'a i32`.
-#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq)]
+#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq, Hash)]
 pub struct Lifetime {
     pub id: NodeId,
     pub ident: Ident,
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index cb625873937..5c9e1313b5b 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -259,10 +259,6 @@ pub trait MutVisitor: Sized {
         noop_visit_param_bound(tpb, self);
     }
 
-    fn visit_precise_capturing_args(&mut self, args: &mut ThinVec<PreciseCapturingArg>) {
-        noop_visit_precise_capturing_args(args, self);
-    }
-
     fn visit_precise_capturing_arg(&mut self, arg: &mut PreciseCapturingArg) {
         noop_visit_precise_capturing_arg(arg, self);
     }
@@ -530,7 +526,9 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
             vis.visit_id(id);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound));
             visit_opt(precise_capturing, |precise_capturing| {
-                vis.visit_precise_capturing_args(precise_capturing);
+                for arg in precise_capturing {
+                    vis.visit_precise_capturing_arg(arg);
+                }
             });
         }
         TyKind::MacCall(mac) => vis.visit_mac_call(mac),
@@ -925,15 +923,6 @@ pub fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T)
     }
 }
 
-pub fn noop_visit_precise_capturing_args<T: MutVisitor>(
-    args: &mut ThinVec<PreciseCapturingArg>,
-    vis: &mut T,
-) {
-    for arg in args {
-        vis.visit_precise_capturing_arg(arg);
-    }
-}
-
 pub fn noop_visit_precise_capturing_arg<T: MutVisitor>(arg: &mut PreciseCapturingArg, vis: &mut T) {
     match arg {
         PreciseCapturingArg::Lifetime(lt) => {
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index b7e4d31edd7..055afc422ed 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -20,7 +20,6 @@ use rustc_span::Span;
 
 pub use rustc_ast_ir::visit::VisitorResult;
 pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list};
-use thin_vec::ThinVec;
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum AssocCtxt {
@@ -185,9 +184,6 @@ pub trait Visitor<'ast>: Sized {
     fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) -> Self::Result {
         walk_param_bound(self, bounds)
     }
-    fn visit_precise_capturing_args(&mut self, args: &'ast ThinVec<PreciseCapturingArg>) {
-        walk_precise_capturing_args(self, args);
-    }
     fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) {
         walk_precise_capturing_arg(self, arg);
     }
@@ -466,7 +462,11 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
         }
         TyKind::ImplTrait(_, bounds, precise_capturing) => {
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
-            visit_opt!(visitor, visit_precise_capturing_args, precise_capturing);
+            if let Some(precise_capturing) = precise_capturing {
+                for arg in precise_capturing {
+                    try_visit!(visitor.visit_precise_capturing_arg(arg));
+                }
+            }
         }
         TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)),
         TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {}
@@ -645,15 +645,6 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB
     }
 }
 
-pub fn walk_precise_capturing_args<'a, V: Visitor<'a>>(
-    visitor: &mut V,
-    args: &'a ThinVec<PreciseCapturingArg>,
-) {
-    for arg in args {
-        visitor.visit_precise_capturing_arg(arg);
-    }
-}
-
 pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>(
     visitor: &mut V,
     arg: &'a PreciseCapturingArg,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 731dda39e9a..2a220dd4df6 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -48,6 +48,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::FxIndexSet;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
@@ -1525,7 +1526,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         bounds: &GenericBounds,
         fn_kind: Option<FnDeclKind>,
         itctx: ImplTraitContext,
-        precise_capturing: Option<&[ast::PreciseCapturingArg]>,
+        precise_capturing_args: Option<&[PreciseCapturingArg]>,
     ) -> hir::TyKind<'hir> {
         // 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
@@ -1541,9 +1542,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 precise_capturing
                     .iter()
                     .filter_map(|arg| match arg {
-                        ast::PreciseCapturingArg::Lifetime(lt) => Some(*lt),
-                        ast::PreciseCapturingArg::Arg(..) => None,
+                        PreciseCapturingArg::Lifetime(lt) => Some(*lt),
+                        PreciseCapturingArg::Arg(..) => None,
                     })
+                    // Add in all the lifetimes mentioned in the bounds. We will error
+                    // them out later, but capturing them here is important to make sure
+                    // they actually get resolved in resolve_bound_vars.
+                    .chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds))
                     .collect()
             } else {
                 match origin {
@@ -1592,6 +1597,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             captured_lifetimes_to_duplicate,
             span,
             opaque_ty_span,
+            precise_capturing_args,
             |this| this.lower_param_bounds(bounds, itctx),
         )
     }
@@ -1601,9 +1607,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         opaque_ty_node_id: NodeId,
         origin: hir::OpaqueTyOrigin,
         in_trait: bool,
-        captured_lifetimes_to_duplicate: Vec<Lifetime>,
+        captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
         span: Span,
         opaque_ty_span: Span,
+        precise_capturing_args: Option<&[PreciseCapturingArg]>,
         lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
     ) -> hir::TyKind<'hir> {
         let opaque_ty_def_id = self.create_def(
@@ -1690,8 +1697,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // Install the remapping from old to new (if any). This makes sure that
             // any lifetimes that would have resolved to the def-id of captured
             // lifetimes are remapped to the new *synthetic* lifetimes of the opaque.
-            let bounds = this
-                .with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this));
+            let (bounds, precise_capturing_args) =
+                this.with_remapping(captured_to_synthesized_mapping, |this| {
+                    (
+                        lower_item_bounds(this),
+                        precise_capturing_args.map(|precise_capturing| {
+                            this.lower_precise_capturing_args(precise_capturing)
+                        }),
+                    )
+                });
 
             let generic_params =
                 this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map(
@@ -1736,6 +1750,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 origin,
                 lifetime_mapping,
                 in_trait,
+                precise_capturing_args,
             };
 
             // Generate an `type Foo = impl Trait;` declaration.
@@ -1768,6 +1783,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         )
     }
 
+    fn lower_precise_capturing_args(
+        &mut self,
+        precise_capturing_args: &[PreciseCapturingArg],
+    ) -> &'hir [hir::PreciseCapturingArg<'hir>] {
+        self.arena.alloc_from_iter(precise_capturing_args.iter().map(|arg| match arg {
+            PreciseCapturingArg::Lifetime(lt) => {
+                hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt))
+            }
+            PreciseCapturingArg::Arg(_, node_id) => {
+                let res = self.resolver.get_partial_res(*node_id).map_or(Res::Err, |partial_res| {
+                    partial_res.full_res().expect("no partial res expected for precise capture arg")
+                });
+                hir::PreciseCapturingArg::Param(self.lower_res(res), self.lower_node_id(*node_id))
+            }
+        }))
+    }
+
     fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
         self.arena.alloc_from_iter(decl.inputs.iter().map(|param| match param.pat.kind {
             PatKind::Ident(_, ident, _) => self.lower_ident(ident),
@@ -1908,7 +1940,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let opaque_ty_span =
             self.mark_span_with_reason(DesugaringKind::Async, span, allowed_features);
 
-        let captured_lifetimes: Vec<_> = self
+        let captured_lifetimes = self
             .resolver
             .take_extra_lifetime_params(opaque_ty_node_id)
             .into_iter()
@@ -1922,6 +1954,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             captured_lifetimes,
             span,
             opaque_ty_span,
+            None,
             |this| {
                 let bound = this.lower_coroutine_fn_output_type_to_bound(
                     output,
diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs
index 4b1c057cdbf..5456abd489b 100644
--- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs
+++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs
@@ -1,6 +1,7 @@
 use super::ResolverAstLoweringExt;
 use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor};
 use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind};
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::def::{DefKind, LifetimeRes, Res};
 use rustc_middle::span_bug;
 use rustc_middle::ty::ResolverAstLowering;
@@ -10,27 +11,23 @@ use rustc_span::Span;
 struct LifetimeCollectVisitor<'ast> {
     resolver: &'ast ResolverAstLowering,
     current_binders: Vec<NodeId>,
-    collected_lifetimes: Vec<Lifetime>,
+    collected_lifetimes: FxIndexSet<Lifetime>,
 }
 
 impl<'ast> LifetimeCollectVisitor<'ast> {
     fn new(resolver: &'ast ResolverAstLowering) -> Self {
-        Self { resolver, current_binders: Vec::new(), collected_lifetimes: Vec::new() }
+        Self { resolver, current_binders: Vec::new(), collected_lifetimes: FxIndexSet::default() }
     }
 
     fn record_lifetime_use(&mut self, lifetime: Lifetime) {
         match self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error) {
             LifetimeRes::Param { binder, .. } | LifetimeRes::Fresh { binder, .. } => {
                 if !self.current_binders.contains(&binder) {
-                    if !self.collected_lifetimes.contains(&lifetime) {
-                        self.collected_lifetimes.push(lifetime);
-                    }
+                    self.collected_lifetimes.insert(lifetime);
                 }
             }
             LifetimeRes::Static | LifetimeRes::Error => {
-                if !self.collected_lifetimes.contains(&lifetime) {
-                    self.collected_lifetimes.push(lifetime);
-                }
+                self.collected_lifetimes.insert(lifetime);
             }
             LifetimeRes::Infer => {}
             res => {
@@ -111,7 +108,7 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
 pub(crate) fn lifetimes_in_bounds(
     resolver: &ResolverAstLowering,
     bounds: &GenericBounds,
-) -> Vec<Lifetime> {
+) -> FxIndexSet<Lifetime> {
     let mut visitor = LifetimeCollectVisitor::new(resolver);
     for bound in bounds {
         visitor.visit_param_bound(bound, BoundKind::Bound);
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index c6e3ad31f01..dc76deb1815 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2557,6 +2557,15 @@ pub struct OpaqueTy<'hir> {
     /// originating from a trait method. This makes it so that the opaque is
     /// lowered as an associated type.
     pub in_trait: bool,
+    /// List of arguments captured via `impl use<'a, P, ...> Trait` syntax.
+    pub precise_capturing_args: Option<&'hir [PreciseCapturingArg<'hir>]>,
+}
+
+#[derive(Debug, Clone, Copy, HashStable_Generic)]
+pub enum PreciseCapturingArg<'hir> {
+    Lifetime(&'hir Lifetime),
+    /// Non-lifetime argument (type or const)
+    Param(Res, HirId),
 }
 
 /// From whence the opaque type came.
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 5da9d4444da..d3ede3a904f 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -413,6 +413,9 @@ pub trait Visitor<'v>: Sized {
     fn visit_param_bound(&mut self, bounds: &'v GenericBound<'v>) -> Self::Result {
         walk_param_bound(self, bounds)
     }
+    fn visit_precise_capturing_arg(&mut self, arg: &'v PreciseCapturingArg<'v>) -> Self::Result {
+        walk_precise_capturing_arg(self, arg)
+    }
     fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef<'v>) -> Self::Result {
         walk_poly_trait_ref(self, t)
     }
@@ -526,10 +529,15 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
             try_visit!(visitor.visit_ty(ty));
             try_visit!(visitor.visit_generics(generics));
         }
-        ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => {
+        ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, precise_capturing_args, .. }) => {
             try_visit!(visitor.visit_id(item.hir_id()));
             try_visit!(walk_generics(visitor, generics));
             walk_list!(visitor, visit_param_bound, bounds);
+            if let Some(precise_capturing_args) = precise_capturing_args {
+                for arg in precise_capturing_args {
+                    try_visit!(visitor.visit_precise_capturing_arg(arg));
+                }
+            }
         }
         ItemKind::Enum(ref enum_definition, ref generics) => {
             try_visit!(visitor.visit_generics(generics));
@@ -1137,6 +1145,16 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>(
     }
 }
 
+pub fn walk_precise_capturing_arg<'v, V: Visitor<'v>>(
+    visitor: &mut V,
+    arg: &'v PreciseCapturingArg<'v>,
+) -> V::Result {
+    match *arg {
+        PreciseCapturingArg::Lifetime(lt) => visitor.visit_lifetime(lt),
+        PreciseCapturingArg::Param(_, hir_id) => visitor.visit_id(hir_id),
+    }
+}
+
 pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>(
     visitor: &mut V,
     trait_ref: &'v PolyTraitRef<'v>,
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 3d16f1420d9..8e29269e8fd 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -557,6 +557,31 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
         }
     }
 
+    fn visit_precise_capturing_arg(
+        &mut self,
+        arg: &'tcx hir::PreciseCapturingArg<'tcx>,
+    ) -> Self::Result {
+        match *arg {
+            hir::PreciseCapturingArg::Lifetime(lt) => match lt.res {
+                LifetimeName::Param(def_id) => {
+                    self.resolve_lifetime_ref(def_id, lt);
+                }
+                LifetimeName::Error => {}
+                LifetimeName::ImplicitObjectLifetimeDefault
+                | LifetimeName::Infer
+                | LifetimeName::Static => todo!("TODO: Error on invalid lifetime"),
+            },
+            hir::PreciseCapturingArg::Param(res, hir_id) => match res {
+                Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id)
+                | Res::SelfTyParam { trait_: def_id } => {
+                    self.resolve_type_ref(def_id.expect_local(), hir_id);
+                }
+                Res::Err => {}
+                _ => todo!("TODO: Error on invalid param res"),
+            },
+        }
+    }
+
     fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
         match item.kind {
             hir::ForeignItemKind::Fn(_, _, generics) => {

From 42ba57c0133306ff510bf641e9d90b33419d7ac7 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 4 Apr 2024 12:54:56 -0400
Subject: [PATCH 06/11] Validation and other things

---
 compiler/rustc_ast/src/ast.rs                 |  2 +-
 compiler/rustc_ast/src/mut_visit.rs           |  4 +-
 compiler/rustc_ast/src/visit.rs               |  2 +-
 compiler/rustc_ast_lowering/messages.ftl      |  2 +
 compiler/rustc_ast_lowering/src/errors.rs     |  7 ++
 compiler/rustc_ast_lowering/src/lib.rs        |  9 +-
 compiler/rustc_ast_pretty/src/pprust/state.rs | 12 ++-
 compiler/rustc_feature/src/unstable.rs        |  4 +-
 compiler/rustc_hir_analysis/messages.ftl      | 10 ++
 .../rustc_hir_analysis/src/check/check.rs     | 95 ++++++++++++++++++-
 .../src/collect/resolve_bound_vars.rs         | 13 ++-
 compiler/rustc_hir_analysis/src/errors.rs     |  3 +
 .../src/errors/precise_captures.rs            | 33 +++++++
 compiler/rustc_parse/src/parser/ty.rs         |  2 +-
 compiler/rustc_resolve/src/late.rs            | 35 +++++++
 .../precise-capturing/bad-lifetimes.rs        | 14 +++
 .../precise-capturing/bad-lifetimes.stderr    | 45 +++++++++
 .../precise-capturing/bad-params.rs           | 16 ++++
 .../precise-capturing/bad-params.stderr       | 30 ++++++
 .../forgot-to-capture-const.rs                |  7 ++
 .../forgot-to-capture-const.stderr            | 19 ++++
 .../forgot-to-capture-lifetime.rs             |  7 ++
 .../forgot-to-capture-lifetime.stderr         | 19 ++++
 .../forgot-to-capture-type.rs                 | 10 ++
 .../forgot-to-capture-type.stderr             | 35 +++++++
 .../precise-capturing/higher-ranked.rs        |  2 +-
 26 files changed, 418 insertions(+), 19 deletions(-)
 create mode 100644 compiler/rustc_hir_analysis/src/errors/precise_captures.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr
 create mode 100644 tests/ui/impl-trait/precise-capturing/bad-params.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/bad-params.stderr
 create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
 create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
 create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index ec445bc5ed3..ace20a005ee 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2132,7 +2132,7 @@ pub enum TyKind {
     /// The `NodeId` exists to prevent lowering from having to
     /// generate `NodeId`s on the fly, which would complicate
     /// the generation of opaque `type Foo = impl Trait` items significantly.
-    ImplTrait(NodeId, GenericBounds, Option<ThinVec<PreciseCapturingArg>>),
+    ImplTrait(NodeId, GenericBounds, Option<P<(ThinVec<PreciseCapturingArg>, Span)>>),
     /// No-op; kept solely so that we can pretty-print faithfully.
     Paren(P<Ty>),
     /// Unused for now.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 5c9e1313b5b..204f33cab34 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -525,11 +525,11 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
         TyKind::ImplTrait(id, bounds, precise_capturing) => {
             vis.visit_id(id);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound));
-            visit_opt(precise_capturing, |precise_capturing| {
+            if let Some((precise_capturing, _span)) = precise_capturing.as_deref_mut() {
                 for arg in precise_capturing {
                     vis.visit_precise_capturing_arg(arg);
                 }
-            });
+            }
         }
         TyKind::MacCall(mac) => vis.visit_mac_call(mac),
         TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 055afc422ed..a9c29e077d1 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -462,7 +462,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
         }
         TyKind::ImplTrait(_, bounds, precise_capturing) => {
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
-            if let Some(precise_capturing) = precise_capturing {
+            if let Some((precise_capturing, _span)) = precise_capturing.as_deref() {
                 for arg in precise_capturing {
                     try_visit!(visitor.visit_precise_capturing_arg(arg));
                 }
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index d91d65497e1..a23e714ef01 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -127,6 +127,8 @@ ast_lowering_never_pattern_with_guard =
     a guard on a never pattern will never be run
     .suggestion = remove this guard
 
+ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed on argument-position `impl Trait`
+
 ast_lowering_previously_used_here = previously used here
 
 ast_lowering_register1 = register `{$reg1_name}`
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 6fd980ed3ca..ca0821e2c9e 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -414,3 +414,10 @@ pub(crate) struct AsyncBoundOnlyForFnTraits {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_lowering_no_precise_captures_on_apit)]
+pub(crate) struct NoPreciseCapturesOnApit {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 2a220dd4df6..b586d2a1bf5 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1409,13 +1409,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         bounds,
                         fn_kind,
                         itctx,
-                        precise_capturing.as_deref(),
+                        precise_capturing.as_deref().map(|(args, _)| args.as_slice()),
                     ),
                     ImplTraitContext::Universal => {
-                        assert!(
-                            precise_capturing.is_none(),
-                            "TODO: precise captures not supported on universals!"
-                        );
+                        if let Some(&(_, span)) = precise_capturing.as_deref() {
+                            self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span });
+                        };
                         let span = t.span;
 
                         // HACK: pprust breaks strings with newlines when the type
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 6553ed72532..db9633bb1cb 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1150,9 +1150,17 @@ impl<'a> State<'a> {
                 }
                 self.print_type_bounds(bounds);
             }
-            ast::TyKind::ImplTrait(_, bounds, _precise_capturing) => {
-                // TODO(precise_capturing):
+            ast::TyKind::ImplTrait(_, bounds, precise_capturing_args) => {
                 self.word_nbsp("impl");
+                if let Some((precise_capturing_args, ..)) = precise_capturing_args.as_deref() {
+                    self.word("use");
+                    self.word("<");
+                    self.commasep(Inconsistent, precise_capturing_args, |s, arg| match arg {
+                        ast::PreciseCapturingArg::Arg(a, _) => s.print_ident(*a),
+                        ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt),
+                    });
+                    self.word(">")
+                }
                 self.print_type_bounds(bounds);
             }
             ast::TyKind::Array(ty, length) => {
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 792cc1066f5..7986add68b7 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -535,8 +535,6 @@ declare_features! (
     (unstable, must_not_suspend, "1.57.0", Some(83310)),
     /// Allows `mut ref` and `mut ref mut` identifier patterns.
     (incomplete, mut_ref, "CURRENT_RUSTC_VERSION", Some(123076)),
-    /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args.
-    (incomplete, precise_capturing, "CURRENT_RUSTC_VERSION", Some(123432)),
     /// Allows using `#[naked]` on functions.
     (unstable, naked_functions, "1.9.0", Some(90957)),
     /// Allows specifying the as-needed link modifier
@@ -569,6 +567,8 @@ declare_features! (
     (unstable, optimize_attribute, "1.34.0", Some(54882)),
     /// Allows postfix match `expr.match { ... }`
     (unstable, postfix_match, "CURRENT_RUSTC_VERSION", Some(121618)),
+    /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args.
+    (incomplete, precise_capturing, "CURRENT_RUSTC_VERSION", Some(123432)),
     /// Allows macro attributes on expressions, statements and non-inline modules.
     (unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
     /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 86b8b6d6b2b..81cf3067a1e 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -37,6 +37,8 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh
     .label = deref recursion limit reached
     .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`)
 
+hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found}
+
 hir_analysis_cannot_capture_late_bound_const =
     cannot capture late-bound const parameter in {$what}
     .label = parameter defined here
@@ -214,6 +216,10 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim
 hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl
     .label = type parameter declared here
 
+hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+    .label = lifetime captured due to being mentioned in the bounds of the `impl Trait`
+    .param_label = this lifetime parameter is captured
+
 hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
     lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
     .label = lifetimes do not match {$item_kind} in trait
@@ -339,6 +345,10 @@ hir_analysis_param_in_ty_of_assoc_const_binding =
         *[normal] the {$param_def_kind} `{$param_name}` is defined here
     }
 
+hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope
+    .label = {$kind} parameter is implicitly captured by this `impl Trait`
+    .note = currently, all {$kind} parameters are required to be mentioned in the precise captures list
+
 hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
     .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 216b89fd4f1..f4d443c324a 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1,10 +1,10 @@
 use crate::check::intrinsicck::InlineAsmCtxt;
-use crate::errors::LinkageType;
 
 use super::compare_impl_item::check_type_bounds;
 use super::compare_impl_item::{compare_impl_method, compare_impl_ty};
 use super::*;
 use rustc_attr as attr;
+use rustc_data_structures::unord::UnordSet;
 use rustc_errors::{codes::*, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind};
@@ -12,6 +12,7 @@ use rustc_hir::Node;
 use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::{Obligation, TraitEngineExt as _};
 use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
+use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::fold::BottomUpFolder;
@@ -474,6 +475,94 @@ fn sanity_check_found_hidden_type<'tcx>(
     }
 }
 
+fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) {
+    let hir::OpaqueTy { precise_capturing_args, .. } =
+        *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
+    let Some(precise_capturing_args) = precise_capturing_args else {
+        // No precise capturing args; nothing to validate
+        return;
+    };
+
+    let mut expected_captures = UnordSet::default();
+    for arg in precise_capturing_args {
+        match *arg {
+            hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, .. })
+            | hir::PreciseCapturingArg::Param(_, hir_id) => match tcx.named_bound_var(hir_id) {
+                Some(ResolvedArg::EarlyBound(def_id)) => {
+                    expected_captures.insert(def_id);
+                }
+                _ => {
+                    tcx.dcx().span_delayed_bug(
+                        tcx.hir().span(hir_id),
+                        "parameter should have been resolved",
+                    );
+                }
+            },
+        }
+    }
+
+    let variances = tcx.variances_of(opaque_def_id);
+    let mut def_id = Some(opaque_def_id.to_def_id());
+    while let Some(generics) = def_id {
+        let generics = tcx.generics_of(generics);
+        def_id = generics.parent;
+
+        for param in &generics.params {
+            if expected_captures.contains(&param.def_id) {
+                assert_eq!(
+                    variances[param.index as usize],
+                    ty::Invariant,
+                    "precise captured param should be invariant"
+                );
+                continue;
+            }
+
+            match param.kind {
+                ty::GenericParamDefKind::Lifetime => {
+                    // Check if the lifetime param was captured but isn't named in the precise captures list.
+                    if variances[param.index as usize] == ty::Invariant {
+                        let param_span =
+                            if let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
+                            | ty::ReLateParam(ty::LateParamRegion {
+                                bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
+                                ..
+                            }) = *tcx
+                                .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
+                            {
+                                Some(tcx.def_span(def_id))
+                            } else {
+                                None
+                            };
+                        // FIXME(precise_capturing): Structured suggestion for this would be useful
+                        tcx.dcx().emit_err(errors::LifetimeNotCaptured {
+                            use_span: tcx.def_span(param.def_id),
+                            param_span,
+                            opaque_span: tcx.def_span(opaque_def_id),
+                        });
+                        continue;
+                    }
+                }
+                ty::GenericParamDefKind::Type { .. } => {
+                    // FIXME(precise_capturing): Structured suggestion for this would be useful
+                    tcx.dcx().emit_err(errors::ParamNotCaptured {
+                        param_span: tcx.def_span(param.def_id),
+                        opaque_span: tcx.def_span(opaque_def_id),
+                        kind: "type",
+                    });
+                }
+                ty::GenericParamDefKind::Const { .. } => {
+                    // FIXME(precise_capturing): Structured suggestion for this would be useful
+                    tcx.dcx().emit_err(errors::ParamNotCaptured {
+                        param_span: tcx.def_span(param.def_id),
+                        opaque_span: tcx.def_span(opaque_def_id),
+                        kind: "const",
+                    });
+                }
+            }
+        }
+    }
+}
+
 fn is_enum_of_nonnullable_ptr<'tcx>(
     tcx: TyCtxt<'tcx>,
     adt_def: AdtDef<'tcx>,
@@ -499,7 +588,7 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             ty::Adt(adt_def, args) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *args),
             _ => true,
         } {
-            tcx.dcx().emit_err(LinkageType { span: tcx.def_span(def_id) });
+            tcx.dcx().emit_err(errors::LinkageType { span: tcx.def_span(def_id) });
         }
     }
 }
@@ -566,6 +655,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             check_union(tcx, def_id);
         }
         DefKind::OpaqueTy => {
+            check_opaque_precise_captures(tcx, def_id);
+
             let origin = tcx.opaque_type_origin(def_id);
             if let hir::OpaqueTyOrigin::FnReturn(fn_def_id)
             | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 8e29269e8fd..8d5f4c13721 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -569,7 +569,13 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                 LifetimeName::Error => {}
                 LifetimeName::ImplicitObjectLifetimeDefault
                 | LifetimeName::Infer
-                | LifetimeName::Static => todo!("TODO: Error on invalid lifetime"),
+                | LifetimeName::Static => {
+                    self.tcx.dcx().emit_err(errors::BadPreciseCapture {
+                        span: lt.ident.span,
+                        kind: "lifetime",
+                        found: format!("`{}`", lt.ident.name),
+                    });
+                }
             },
             hir::PreciseCapturingArg::Param(res, hir_id) => match res {
                 Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id)
@@ -577,7 +583,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     self.resolve_type_ref(def_id.expect_local(), hir_id);
                 }
                 Res::Err => {}
-                _ => todo!("TODO: Error on invalid param res"),
+                _ => {
+                    // This is handled in resolve
+                    self.tcx.dcx().delayed_bug(format!("parameter should have been resolved"));
+                }
             },
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index d129614e0e1..867ee772a30 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -10,6 +10,9 @@ use rustc_span::{symbol::Ident, Span, Symbol};
 mod pattern_types;
 pub use pattern_types::*;
 
+mod precise_captures;
+pub(crate) use precise_captures::*;
+
 #[derive(Diagnostic)]
 #[diag(hir_analysis_ambiguous_assoc_item)]
 pub struct AmbiguousAssocItem<'a> {
diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
new file mode 100644
index 00000000000..3b22437abb2
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
@@ -0,0 +1,33 @@
+use rustc_macros::Diagnostic;
+use rustc_span::Span;
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_param_not_captured)]
+#[note]
+pub struct ParamNotCaptured {
+    #[primary_span]
+    pub param_span: Span,
+    #[label]
+    pub opaque_span: Span,
+    pub kind: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_lifetime_not_captured)]
+pub struct LifetimeNotCaptured {
+    #[primary_span]
+    pub use_span: Span,
+    #[label(hir_analysis_param_label)]
+    pub param_span: Option<Span>,
+    #[label]
+    pub opaque_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_bad_precise_capture)]
+pub struct BadPreciseCapture {
+    #[primary_span]
+    pub span: Span,
+    pub kind: &'static str,
+    pub found: String,
+}
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index a24ecf938ba..91f4823f65d 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -674,7 +674,7 @@ impl<'a> Parser<'a> {
             let use_span = self.prev_token.span;
             self.psess.gated_spans.gate(sym::precise_capturing, use_span);
             let args = self.parse_precise_capturing_args()?;
-            Some(args)
+            Some(P((args, use_span)))
         } else {
             None
         };
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index c7e12284b50..7203adab135 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1054,7 +1054,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
 
     fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) {
         match arg {
+            // Lower the lifetime regularly; we'll resolve the lifetime and check
+            // it's a parameter later on in HIR lowering.
             PreciseCapturingArg::Lifetime(_) => visit::walk_precise_capturing_arg(self, arg),
+
             PreciseCapturingArg::Arg(ident, node_id) => {
                 let ident = ident.normalize_to_macros_2_0();
                 'found: {
@@ -1064,6 +1067,38 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
                         if let Some(res) = rib_t.bindings.get(&ident).or(rib_v.bindings.get(&ident))
                         {
                             self.r.record_partial_res(*node_id, PartialRes::new(*res));
+
+                            // Validate that this is a parameter
+                            match res {
+                                Res::Def(DefKind::TyParam | DefKind::ConstParam, _)
+                                | Res::SelfTyParam { .. } => {}
+                                Res::SelfTyAlias { .. } => {
+                                    self.report_error(
+                                        ident.span,
+                                        ResolutionError::FailedToResolve {
+                                            segment: Some(ident.name),
+                                            label: "`Self` cannot be captured because it is not a type parameter".to_string(),
+                                            suggestion: None,
+                                            module: None,
+                                        },
+                                    );
+                                }
+                                _ => {
+                                    self.report_error(
+                                        ident.span,
+                                        ResolutionError::FailedToResolve {
+                                            segment: Some(ident.name),
+                                            label: format!(
+                                                "expected type or const parameter, found {}",
+                                                res.descr()
+                                            ),
+                                            suggestion: None,
+                                            module: None,
+                                        },
+                                    );
+                                }
+                            }
+
                             break 'found;
                         }
                     }
diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs
new file mode 100644
index 00000000000..623063a8f50
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs
@@ -0,0 +1,14 @@
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn no_elided_lt() -> impl use<'_> Sized {}
+//~^ ERROR missing lifetime specifier
+//~| ERROR expected lifetime parameter in `use<...>` precise captures list, found `'_`
+
+fn static_lt() -> impl use<'static> Sized {}
+//~^ ERROR expected lifetime parameter in `use<...>` precise captures list, found `'static`
+
+fn missing_lt() -> impl use<'missing> Sized {}
+//~^ ERROR use of undeclared lifetime name `'missing`
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr
new file mode 100644
index 00000000000..a926362c50c
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr
@@ -0,0 +1,45 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/bad-lifetimes.rs:4:31
+   |
+LL | fn no_elided_lt() -> impl use<'_> Sized {}
+   |                               ^^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
+   |
+LL | fn no_elided_lt() -> impl use<'static> Sized {}
+   |                               ~~~~~~~
+
+error[E0261]: use of undeclared lifetime name `'missing`
+  --> $DIR/bad-lifetimes.rs:11:29
+   |
+LL | fn missing_lt() -> impl use<'missing> Sized {}
+   |              -              ^^^^^^^^ undeclared lifetime
+   |              |
+   |              help: consider introducing lifetime `'missing` here: `<'missing>`
+
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/bad-lifetimes.rs:1:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: expected lifetime parameter in `use<...>` precise captures list, found `'_`
+  --> $DIR/bad-lifetimes.rs:4:31
+   |
+LL | fn no_elided_lt() -> impl use<'_> Sized {}
+   |                               ^^
+
+error: expected lifetime parameter in `use<...>` precise captures list, found `'static`
+  --> $DIR/bad-lifetimes.rs:8:28
+   |
+LL | fn static_lt() -> impl use<'static> Sized {}
+   |                            ^^^^^^^
+
+error: aborting due to 4 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0106, E0261.
+For more information about an error, try `rustc --explain E0106`.
diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.rs b/tests/ui/impl-trait/precise-capturing/bad-params.rs
new file mode 100644
index 00000000000..c600fa96f79
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/bad-params.rs
@@ -0,0 +1,16 @@
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn missing() -> impl use<T> Sized {}
+//~^ ERROR could not find type or const parameter
+
+fn missing_self() -> impl use<Self> Sized {}
+//~^ ERROR could not find type or const parameter
+
+struct MyType;
+impl MyType {
+    fn self_is_not_param() -> impl use<Self> Sized {}
+    //~^ ERROR `Self` cannot be captured because it is not a type parameter
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.stderr b/tests/ui/impl-trait/precise-capturing/bad-params.stderr
new file mode 100644
index 00000000000..bb5fe76ae3e
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/bad-params.stderr
@@ -0,0 +1,30 @@
+error[E0433]: failed to resolve: could not find type or const parameter
+  --> $DIR/bad-params.rs:4:26
+   |
+LL | fn missing() -> impl use<T> Sized {}
+   |                          ^ could not find type or const parameter
+
+error[E0433]: failed to resolve: could not find type or const parameter
+  --> $DIR/bad-params.rs:7:31
+   |
+LL | fn missing_self() -> impl use<Self> Sized {}
+   |                               ^^^^ could not find type or const parameter
+
+error[E0433]: failed to resolve: `Self` cannot be captured because it is not a type parameter
+  --> $DIR/bad-params.rs:12:40
+   |
+LL |     fn self_is_not_param() -> impl use<Self> Sized {}
+   |                                        ^^^^ `Self` cannot be captured because it is not a type parameter
+
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/bad-params.rs:1:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs
new file mode 100644
index 00000000000..1b604e6c358
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs
@@ -0,0 +1,7 @@
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn constant<const C: usize>() -> impl use<> Sized {}
+//~^ ERROR `impl Trait` must mention all const parameters in scope
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
new file mode 100644
index 00000000000..9c99f2b711e
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
@@ -0,0 +1,19 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/forgot-to-capture-const.rs:1:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: `impl Trait` must mention all const parameters in scope
+  --> $DIR/forgot-to-capture-const.rs:4:13
+   |
+LL | fn constant<const C: usize>() -> impl use<> Sized {}
+   |             ^^^^^^^^^^^^^^       ---------------- const parameter is implicitly captured by this `impl Trait`
+   |
+   = note: currently, all const parameters are required to be mentioned in the precise captures list
+
+error: aborting due to 1 previous error; 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
new file mode 100644
index 00000000000..7e856ba9517
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
@@ -0,0 +1,7 @@
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn type_param<T>() -> impl use<> Sized {}
+//~^ ERROR `impl Trait` must mention all type parameters in scope
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
new file mode 100644
index 00000000000..1ef46a36e46
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
@@ -0,0 +1,19 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/forgot-to-capture-lifetime.rs:1:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: `impl Trait` must mention all type parameters in scope
+  --> $DIR/forgot-to-capture-lifetime.rs:4:15
+   |
+LL | fn type_param<T>() -> impl use<> Sized {}
+   |               ^       ---------------- type parameter is implicitly captured by this `impl Trait`
+   |
+   = note: currently, all type parameters are required to be mentioned in the precise captures list
+
+error: aborting due to 1 previous error; 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
new file mode 100644
index 00000000000..cc86bf83107
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
@@ -0,0 +1,10 @@
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x }
+//~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+
+fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x }
+//~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
new file mode 100644
index 00000000000..58f284ec6bc
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
@@ -0,0 +1,35 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/forgot-to-capture-type.rs:1:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+  --> $DIR/forgot-to-capture-type.rs:4:58
+   |
+LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x }
+   |                       --                -----------------^^----
+   |                       |                 |
+   |                       |                 lifetime captured due to being mentioned in the bounds of the `impl Trait`
+   |                       this lifetime parameter is captured
+
+error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
+  --> $DIR/forgot-to-capture-type.rs:7:60
+   |
+LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x }
+   |                       --                ----------------   ^
+   |                       |                 |
+   |                       |                 opaque type defined here
+   |                       hidden type `&'a ()` captures the lifetime `'a` as defined here
+   |
+help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound
+   |
+LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized + 'a { x }
+   |                                                          ++++
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs
index c9faaaed968..28fb1fa4b9e 100644
--- a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs
+++ b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs
@@ -15,4 +15,4 @@ impl Trait<'_> for () {
 
 fn hello() -> impl for<'a> Trait<'a, Item = impl use<> IntoIterator> {}
 
-fn main() {}
\ No newline at end of file
+fn main() {}

From 02d7317af218343818dcb107cda80750e9604a3a Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 4 Apr 2024 14:46:26 -0400
Subject: [PATCH 07/11] Add hir::Node::PreciseCapturingNonLifetimeArg

---
 compiler/rustc_ast_lowering/src/index.rs      | 17 ++++++++++
 compiler/rustc_ast_lowering/src/lib.rs        |  8 +++--
 compiler/rustc_hir/src/hir.rs                 | 11 ++++++-
 compiler/rustc_hir/src/intravisit.rs          |  2 +-
 .../rustc_hir_analysis/src/check/check.rs     |  4 ++-
 .../src/collect/resolve_bound_vars.rs         |  4 +--
 compiler/rustc_hir_pretty/src/lib.rs          |  1 +
 compiler/rustc_middle/src/hir/map/mod.rs      |  2 ++
 .../forgot-to-capture-lifetime.rs             |  7 ++--
 .../forgot-to-capture-lifetime.stderr         | 28 ++++++++++++----
 .../forgot-to-capture-type.rs                 | 10 +++---
 .../forgot-to-capture-type.stderr             | 33 ++++++++-----------
 .../precise-capturing/self-capture.rs         | 10 ++++++
 .../precise-capturing/self-capture.stderr     | 11 +++++++
 14 files changed, 110 insertions(+), 38 deletions(-)
 create mode 100644 tests/ui/impl-trait/precise-capturing/self-capture.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/self-capture.stderr

diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 4c552289a81..93be9b9b8cf 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -385,4 +385,21 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
     fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) {
         self.visit_pat(p)
     }
+
+    fn visit_precise_capturing_arg(
+        &mut self,
+        arg: &'hir PreciseCapturingArg<'hir>,
+    ) -> Self::Result {
+        match arg {
+            PreciseCapturingArg::Lifetime(_) => {
+                // This is represented as a `Node::Lifetime`, intravisit will get to it below.
+            }
+            PreciseCapturingArg::Param(param) => self.insert(
+                param.ident.span,
+                param.hir_id,
+                Node::PreciseCapturingNonLifetimeArg(param),
+            ),
+        }
+        intravisit::walk_precise_capturing_arg(self, arg);
+    }
 }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index b586d2a1bf5..84776f920c4 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1790,11 +1790,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             PreciseCapturingArg::Lifetime(lt) => {
                 hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt))
             }
-            PreciseCapturingArg::Arg(_, node_id) => {
+            PreciseCapturingArg::Arg(ident, node_id) => {
                 let res = self.resolver.get_partial_res(*node_id).map_or(Res::Err, |partial_res| {
                     partial_res.full_res().expect("no partial res expected for precise capture arg")
                 });
-                hir::PreciseCapturingArg::Param(self.lower_res(res), self.lower_node_id(*node_id))
+                hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg {
+                    hir_id: self.lower_node_id(*node_id),
+                    ident: self.lower_ident(*ident),
+                    res: self.lower_res(res),
+                })
             }
         }))
     }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index dc76deb1815..00b1ea061cc 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2565,7 +2565,14 @@ pub struct OpaqueTy<'hir> {
 pub enum PreciseCapturingArg<'hir> {
     Lifetime(&'hir Lifetime),
     /// Non-lifetime argument (type or const)
-    Param(Res, HirId),
+    Param(PreciseCapturingNonLifetimeArg),
+}
+
+#[derive(Debug, Clone, Copy, HashStable_Generic)]
+pub struct PreciseCapturingNonLifetimeArg {
+    pub hir_id: HirId,
+    pub ident: Ident,
+    pub res: Res,
 }
 
 /// From whence the opaque type came.
@@ -3544,6 +3551,7 @@ pub enum Node<'hir> {
     WhereBoundPredicate(&'hir WhereBoundPredicate<'hir>),
     // FIXME: Merge into `Node::Infer`.
     ArrayLenInfer(&'hir InferArg),
+    PreciseCapturingNonLifetimeArg(&'hir PreciseCapturingNonLifetimeArg),
     // Created by query feeding
     Synthetic,
     // Span by reference to minimize `Node`'s size
@@ -3580,6 +3588,7 @@ impl<'hir> Node<'hir> {
             Node::TypeBinding(b) => Some(b.ident),
             Node::PatField(f) => Some(f.ident),
             Node::ExprField(f) => Some(f.ident),
+            Node::PreciseCapturingNonLifetimeArg(a) => Some(a.ident),
             Node::Param(..)
             | Node::AnonConst(..)
             | Node::ConstBlock(..)
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index d3ede3a904f..cd9f9ff9109 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -1151,7 +1151,7 @@ pub fn walk_precise_capturing_arg<'v, V: Visitor<'v>>(
 ) -> V::Result {
     match *arg {
         PreciseCapturingArg::Lifetime(lt) => visitor.visit_lifetime(lt),
-        PreciseCapturingArg::Param(_, hir_id) => visitor.visit_id(hir_id),
+        PreciseCapturingArg::Param(param) => visitor.visit_id(param.hir_id),
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index f4d443c324a..592a8648f14 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -487,7 +487,9 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
     for arg in precise_capturing_args {
         match *arg {
             hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, .. })
-            | hir::PreciseCapturingArg::Param(_, hir_id) => match tcx.named_bound_var(hir_id) {
+            | hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg {
+                hir_id, ..
+            }) => match tcx.named_bound_var(hir_id) {
                 Some(ResolvedArg::EarlyBound(def_id)) => {
                     expected_captures.insert(def_id);
                 }
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 8d5f4c13721..fb3713140ff 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -577,10 +577,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     });
                 }
             },
-            hir::PreciseCapturingArg::Param(res, hir_id) => match res {
+            hir::PreciseCapturingArg::Param(param) => match param.res {
                 Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id)
                 | Res::SelfTyParam { trait_: def_id } => {
-                    self.resolve_type_ref(def_id.expect_local(), hir_id);
+                    self.resolve_type_ref(def_id.expect_local(), param.hir_id);
                 }
                 Res::Err => {}
                 _ => {
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 39312614c1b..3d3b16baf69 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -99,6 +99,7 @@ impl<'a> State<'a> {
             Node::PatField(a) => self.print_patfield(a),
             Node::Arm(a) => self.print_arm(a),
             Node::Infer(_) => self.word("_"),
+            Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident),
             Node::Block(a) => {
                 // Containing cbox, will be closed by print-block at `}`.
                 self.cbox(INDENT_UNIT);
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 72f849b534a..88de834c436 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -909,6 +909,7 @@ impl<'hir> Map<'hir> {
             Node::Crate(item) => item.spans.inner_span,
             Node::WhereBoundPredicate(pred) => pred.span,
             Node::ArrayLenInfer(inf) => inf.span,
+            Node::PreciseCapturingNonLifetimeArg(param) => param.ident.span,
             Node::Synthetic => unreachable!(),
             Node::Err(span) => *span,
         }
@@ -1176,6 +1177,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
         Node::ArrayLenInfer(_) => node_str("array len infer"),
         Node::Synthetic => unreachable!(),
         Node::Err(_) => node_str("error"),
+        Node::PreciseCapturingNonLifetimeArg(_param) => node_str("parameter"),
     }
 }
 
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
index 7e856ba9517..cc86bf83107 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
@@ -1,7 +1,10 @@
 #![feature(precise_capturing)]
 //~^ WARN the feature `precise_capturing` is incomplete
 
-fn type_param<T>() -> impl use<> Sized {}
-//~^ ERROR `impl Trait` must mention all type parameters in scope
+fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x }
+//~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+
+fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x }
+//~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
index 1ef46a36e46..e472c898050 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
@@ -7,13 +7,29 @@ LL | #![feature(precise_capturing)]
    = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
-error: `impl Trait` must mention all type parameters in scope
-  --> $DIR/forgot-to-capture-lifetime.rs:4:15
+error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+  --> $DIR/forgot-to-capture-lifetime.rs:4:58
    |
-LL | fn type_param<T>() -> impl use<> Sized {}
-   |               ^       ---------------- type parameter is implicitly captured by this `impl Trait`
+LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x }
+   |                       --                -----------------^^----
+   |                       |                 |
+   |                       |                 lifetime captured due to being mentioned in the bounds of the `impl Trait`
+   |                       this lifetime parameter is captured
+
+error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
+  --> $DIR/forgot-to-capture-lifetime.rs:7:60
    |
-   = note: currently, all type parameters are required to be mentioned in the precise captures list
+LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x }
+   |                       --                ----------------   ^
+   |                       |                 |
+   |                       |                 opaque type defined here
+   |                       hidden type `&'a ()` captures the lifetime `'a` as defined here
+   |
+help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound
+   |
+LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized + 'a { x }
+   |                                                          ++++
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 2 previous errors; 1 warning emitted
 
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
index cc86bf83107..6eaff01183d 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
@@ -1,10 +1,12 @@
 #![feature(precise_capturing)]
 //~^ WARN the feature `precise_capturing` is incomplete
 
-fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x }
-//~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
+fn type_param<T>() -> impl use<> Sized {}
+//~^ ERROR `impl Trait` must mention all type parameters in scope
 
-fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x }
-//~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds
+trait Foo {
+//~^ ERROR `impl Trait` must mention all type parameters in scope
+    fn bar() -> impl use<> Sized;
+}
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
index 58f284ec6bc..a8eb4547dcd 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
@@ -7,29 +7,24 @@ LL | #![feature(precise_capturing)]
    = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
-error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
-  --> $DIR/forgot-to-capture-type.rs:4:58
+error: `impl Trait` must mention all type parameters in scope
+  --> $DIR/forgot-to-capture-type.rs:4:15
    |
-LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x }
-   |                       --                -----------------^^----
-   |                       |                 |
-   |                       |                 lifetime captured due to being mentioned in the bounds of the `impl Trait`
-   |                       this lifetime parameter is captured
+LL | fn type_param<T>() -> impl use<> Sized {}
+   |               ^       ---------------- type parameter is implicitly captured by this `impl Trait`
+   |
+   = note: currently, all type parameters are required to be mentioned in the precise captures list
 
-error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
-  --> $DIR/forgot-to-capture-type.rs:7:60
+error: `impl Trait` must mention all type parameters in scope
+  --> $DIR/forgot-to-capture-type.rs:7:1
    |
-LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x }
-   |                       --                ----------------   ^
-   |                       |                 |
-   |                       |                 opaque type defined here
-   |                       hidden type `&'a ()` captures the lifetime `'a` as defined here
+LL | trait Foo {
+   | ^^^^^^^^^
+LL |
+LL |     fn bar() -> impl use<> Sized;
+   |                 ---------------- type parameter is implicitly captured by this `impl Trait`
    |
-help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound
-   |
-LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized + 'a { x }
-   |                                                          ++++
+   = note: currently, all type parameters are required to be mentioned in the precise captures list
 
 error: aborting due to 2 previous errors; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.rs b/tests/ui/impl-trait/precise-capturing/self-capture.rs
new file mode 100644
index 00000000000..ecbc388e27b
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/self-capture.rs
@@ -0,0 +1,10 @@
+//@ check-pass
+
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+trait Foo {
+    fn bar<'a>() -> impl use<Self> Sized;
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.stderr b/tests/ui/impl-trait/precise-capturing/self-capture.stderr
new file mode 100644
index 00000000000..5a058c6826d
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/self-capture.stderr
@@ -0,0 +1,11 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/self-capture.rs:3:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+

From ce8961039e244b1e4e0fa02fc10d59a22abc9ea3 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 4 Apr 2024 15:06:30 -0400
Subject: [PATCH 08/11] Some ordering and duplication checks

---
 compiler/rustc_hir_analysis/messages.ftl      |  6 ++
 .../rustc_hir_analysis/src/check/check.rs     | 57 ++++++++++++++-----
 .../src/errors/precise_captures.rs            | 22 ++++++-
 .../feature-gate-precise-capturing.rs         |  4 ++
 .../feature-gate-precise-capturing.stderr     | 13 +++++
 .../impl-trait/precise-capturing/ordering.rs  | 16 ++++++
 .../precise-capturing/ordering.stderr         | 37 ++++++++++++
 7 files changed, 140 insertions(+), 15 deletions(-)
 create mode 100644 tests/ui/feature-gates/feature-gate-precise-capturing.rs
 create mode 100644 tests/ui/feature-gates/feature-gate-precise-capturing.stderr
 create mode 100644 tests/ui/impl-trait/precise-capturing/ordering.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/ordering.stderr

diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 81cf3067a1e..4ac2965bd5f 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -113,6 +113,9 @@ hir_analysis_drop_impl_on_wrong_item =
 
 hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported
 
+hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice
+    .label = parameter captured again here
+
 hir_analysis_empty_specialization = specialization impl does not specialize any associated items
     .note = impl is a specialization of this impl
 
@@ -216,6 +219,9 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim
 hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl
     .label = type parameter declared here
 
+hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters
+    .label = move the lifetime before this parameter
+
 hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
     .label = lifetime captured due to being mentioned in the bounds of the `impl Trait`
     .param_label = this lifetime parameter is captured
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 592a8648f14..1e8cd50ca0d 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -4,7 +4,7 @@ use super::compare_impl_item::check_type_bounds;
 use super::compare_impl_item::{compare_impl_method, compare_impl_ty};
 use super::*;
 use rustc_attr as attr;
-use rustc_data_structures::unord::UnordSet;
+use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::{codes::*, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind};
@@ -484,22 +484,51 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
     };
 
     let mut expected_captures = UnordSet::default();
+    let mut seen_params = UnordMap::default();
+    let mut prev_non_lifetime_param = None;
     for arg in precise_capturing_args {
-        match *arg {
-            hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, .. })
-            | hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg {
-                hir_id, ..
-            }) => match tcx.named_bound_var(hir_id) {
-                Some(ResolvedArg::EarlyBound(def_id)) => {
-                    expected_captures.insert(def_id);
+        let (hir_id, ident) = match *arg {
+            hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg {
+                hir_id,
+                ident,
+                ..
+            }) => {
+                if prev_non_lifetime_param.is_none() {
+                    prev_non_lifetime_param = Some(ident);
                 }
-                _ => {
-                    tcx.dcx().span_delayed_bug(
-                        tcx.hir().span(hir_id),
-                        "parameter should have been resolved",
-                    );
+                (hir_id, ident)
+            }
+            hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, ident, .. }) => {
+                if let Some(prev_non_lifetime_param) = prev_non_lifetime_param {
+                    tcx.dcx().emit_err(errors::LifetimesMustBeFirst {
+                        lifetime_span: ident.span,
+                        name: ident.name,
+                        other_span: prev_non_lifetime_param.span,
+                    });
                 }
-            },
+                (hir_id, ident)
+            }
+        };
+
+        let ident = ident.normalize_to_macros_2_0();
+        if let Some(span) = seen_params.insert(ident, ident.span) {
+            tcx.dcx().emit_err(errors::DuplicatePreciseCapture {
+                name: ident.name,
+                first_span: span,
+                second_span: ident.span,
+            });
+        }
+
+        match tcx.named_bound_var(hir_id) {
+            Some(ResolvedArg::EarlyBound(def_id)) => {
+                expected_captures.insert(def_id);
+            }
+            _ => {
+                tcx.dcx().span_delayed_bug(
+                    tcx.hir().span(hir_id),
+                    "parameter should have been resolved",
+                );
+            }
         }
     }
 
diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
index 3b22437abb2..e2eb9c72bf2 100644
--- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
+++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
@@ -1,5 +1,5 @@
 use rustc_macros::Diagnostic;
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
 
 #[derive(Diagnostic)]
 #[diag(hir_analysis_param_not_captured)]
@@ -31,3 +31,23 @@ pub struct BadPreciseCapture {
     pub kind: &'static str,
     pub found: String,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_duplicate_precise_capture)]
+pub struct DuplicatePreciseCapture {
+    #[primary_span]
+    pub first_span: Span,
+    pub name: Symbol,
+    #[label]
+    pub second_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_lifetime_must_be_first)]
+pub struct LifetimesMustBeFirst {
+    #[primary_span]
+    pub lifetime_span: Span,
+    pub name: Symbol,
+    #[label]
+    pub other_span: Span,
+}
diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.rs b/tests/ui/feature-gates/feature-gate-precise-capturing.rs
new file mode 100644
index 00000000000..0c3b4977623
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-precise-capturing.rs
@@ -0,0 +1,4 @@
+fn hello() -> impl use<> Sized {}
+//~^ ERROR precise captures on `impl Trait` are experimental
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.stderr b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr
new file mode 100644
index 00000000000..102b39148f9
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr
@@ -0,0 +1,13 @@
+error[E0658]: precise captures on `impl Trait` are experimental
+  --> $DIR/feature-gate-precise-capturing.rs:1:20
+   |
+LL | fn hello() -> impl use<> Sized {}
+   |                    ^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = help: add `#![feature(precise_capturing)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/impl-trait/precise-capturing/ordering.rs b/tests/ui/impl-trait/precise-capturing/ordering.rs
new file mode 100644
index 00000000000..2bace798c57
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/ordering.rs
@@ -0,0 +1,16 @@
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn lt<'a>() -> impl use<'a, 'a> Sized {}
+//~^ ERROR cannot capture parameter `'a` twice
+
+fn ty<T>() -> impl use<T, T> Sized {}
+//~^ ERROR cannot capture parameter `T` twice
+
+fn ct<const N: usize>() -> impl use<N, N> Sized {}
+//~^ ERROR cannot capture parameter `N` twice
+
+fn ordering<'a, T>() -> impl use<T, 'a> Sized {}
+//~^ ERROR lifetime parameter `'a` must be listed before non-lifetime parameters
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/ordering.stderr b/tests/ui/impl-trait/precise-capturing/ordering.stderr
new file mode 100644
index 00000000000..3f545108df5
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/ordering.stderr
@@ -0,0 +1,37 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/ordering.rs:1:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: cannot capture parameter `'a` twice
+  --> $DIR/ordering.rs:4:25
+   |
+LL | fn lt<'a>() -> impl use<'a, 'a> Sized {}
+   |                         ^^  -- parameter captured again here
+
+error: cannot capture parameter `T` twice
+  --> $DIR/ordering.rs:7:24
+   |
+LL | fn ty<T>() -> impl use<T, T> Sized {}
+   |                        ^  - parameter captured again here
+
+error: cannot capture parameter `N` twice
+  --> $DIR/ordering.rs:10:37
+   |
+LL | fn ct<const N: usize>() -> impl use<N, N> Sized {}
+   |                                     ^  - parameter captured again here
+
+error: lifetime parameter `'a` must be listed before non-lifetime parameters
+  --> $DIR/ordering.rs:13:37
+   |
+LL | fn ordering<'a, T>() -> impl use<T, 'a> Sized {}
+   |                                  -  ^^
+   |                                  |
+   |                                  move the lifetime before this parameter
+
+error: aborting due to 4 previous errors; 1 warning emitted
+

From 52c6b101ea18ed6f09367bf459ac55ffe473cd9c Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 4 Apr 2024 20:23:52 -0400
Subject: [PATCH 09/11] Use a path instead of an ident (and stop manually
 resolving)

---
 compiler/rustc_ast/src/ast.rs                 |  4 +-
 compiler/rustc_ast/src/mut_visit.rs           |  4 +-
 compiler/rustc_ast/src/visit.rs               |  4 +-
 compiler/rustc_ast_lowering/src/lib.rs        | 11 ++--
 compiler/rustc_ast_pretty/src/pprust/state.rs |  2 +-
 compiler/rustc_hir_analysis/messages.ftl      |  3 +
 .../src/collect/resolve_bound_vars.rs         | 16 ++++-
 .../src/errors/precise_captures.rs            | 10 +++
 compiler/rustc_parse/src/parser/ty.rs         |  7 +-
 compiler/rustc_resolve/src/late.rs            | 66 ++++---------------
 .../precise-capturing/bad-params.rs           |  6 +-
 .../precise-capturing/bad-params.stderr       | 32 +++++----
 12 files changed, 81 insertions(+), 84 deletions(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index ace20a005ee..0cf96797028 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2188,12 +2188,12 @@ pub enum TraitObjectSyntax {
     None,
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug)]
 pub enum PreciseCapturingArg {
     /// Lifetime parameter
     Lifetime(Lifetime),
     /// Type or const parameter
-    Arg(Ident, NodeId),
+    Arg(Path, NodeId),
 }
 
 /// Inline assembly operand explicit register or register class.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 204f33cab34..c4e49d7dbea 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -928,8 +928,8 @@ pub fn noop_visit_precise_capturing_arg<T: MutVisitor>(arg: &mut PreciseCapturin
         PreciseCapturingArg::Lifetime(lt) => {
             vis.visit_lifetime(lt);
         }
-        PreciseCapturingArg::Arg(ident, id) => {
-            vis.visit_ident(ident);
+        PreciseCapturingArg::Arg(path, id) => {
+            vis.visit_path(path);
             vis.visit_id(id);
         }
     }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index a9c29e077d1..968d10ad487 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -653,8 +653,8 @@ pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>(
         PreciseCapturingArg::Lifetime(lt) => {
             visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg);
         }
-        PreciseCapturingArg::Arg(ident, _) => {
-            visitor.visit_ident(*ident);
+        PreciseCapturingArg::Arg(path, id) => {
+            visitor.visit_path(path, *id);
         }
     }
 }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 84776f920c4..a21d6019cf1 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1790,13 +1790,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             PreciseCapturingArg::Lifetime(lt) => {
                 hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt))
             }
-            PreciseCapturingArg::Arg(ident, node_id) => {
-                let res = self.resolver.get_partial_res(*node_id).map_or(Res::Err, |partial_res| {
+            PreciseCapturingArg::Arg(path, id) => {
+                let [segment] = path.segments.as_slice() else {
+                    panic!();
+                };
+                let res = self.resolver.get_partial_res(*id).map_or(Res::Err, |partial_res| {
                     partial_res.full_res().expect("no partial res expected for precise capture arg")
                 });
                 hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg {
-                    hir_id: self.lower_node_id(*node_id),
-                    ident: self.lower_ident(*ident),
+                    hir_id: self.lower_node_id(*id),
+                    ident: self.lower_ident(segment.ident),
                     res: self.lower_res(res),
                 })
             }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index db9633bb1cb..96909940972 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1156,7 +1156,7 @@ impl<'a> State<'a> {
                     self.word("use");
                     self.word("<");
                     self.commasep(Inconsistent, precise_capturing_args, |s, arg| match arg {
-                        ast::PreciseCapturingArg::Arg(a, _) => s.print_ident(*a),
+                        ast::PreciseCapturingArg::Arg(p, _) => s.print_path(p, false, 0),
                         ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt),
                     });
                     self.word(">")
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 4ac2965bd5f..bc6583444c3 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -39,6 +39,9 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh
 
 hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found}
 
+hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias
+    .label = `Self` is not a generic argument, but an alias to the type of the {$what}
+
 hir_analysis_cannot_capture_late_bound_const =
     cannot capture late-bound const parameter in {$what}
     .label = parameter defined here
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index fb3713140ff..78c2665fd1f 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -583,9 +583,19 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     self.resolve_type_ref(def_id.expect_local(), param.hir_id);
                 }
                 Res::Err => {}
-                _ => {
-                    // This is handled in resolve
-                    self.tcx.dcx().delayed_bug(format!("parameter should have been resolved"));
+                Res::SelfTyAlias { alias_to, .. } => {
+                    self.tcx.dcx().emit_err(errors::PreciseCaptureSelfAlias {
+                        span: param.ident.span,
+                        self_span: self.tcx.def_span(alias_to),
+                        what: self.tcx.def_descr(alias_to),
+                    });
+                }
+                res => {
+                    self.tcx.dcx().emit_err(errors::BadPreciseCapture {
+                        span: param.ident.span,
+                        kind: "type or const",
+                        found: res.descr().to_string(),
+                    });
                 }
             },
         }
diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
index e2eb9c72bf2..520bf1d9f40 100644
--- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
+++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
@@ -32,6 +32,16 @@ pub struct BadPreciseCapture {
     pub found: String,
 }
 
+#[derive(Diagnostic)]
+#[diag(hir_analysis_precise_capture_self_alias)]
+pub struct PreciseCaptureSelfAlias {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub self_span: Span,
+    pub what: &'static str,
+}
+
 #[derive(Diagnostic)]
 #[diag(hir_analysis_duplicate_precise_capture)]
 pub struct DuplicatePreciseCapture {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 91f4823f65d..f78a039394b 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -697,11 +697,14 @@ impl<'a> Parser<'a> {
                     if self_.check_keyword(kw::SelfUpper) {
                         self_.bump();
                         Ok(PreciseCapturingArg::Arg(
-                            self_.prev_token.ident().unwrap().0,
+                            ast::Path::from_ident(self_.prev_token.ident().unwrap().0),
                             DUMMY_NODE_ID,
                         ))
                     } else if self_.check_ident() {
-                        Ok(PreciseCapturingArg::Arg(self_.parse_ident().unwrap(), DUMMY_NODE_ID))
+                        Ok(PreciseCapturingArg::Arg(
+                            ast::Path::from_ident(self_.parse_ident()?),
+                            DUMMY_NODE_ID,
+                        ))
                     } else if self_.check_lifetime() {
                         Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime()))
                     } else {
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 7203adab135..64434412886 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1056,64 +1056,22 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
         match arg {
             // Lower the lifetime regularly; we'll resolve the lifetime and check
             // it's a parameter later on in HIR lowering.
-            PreciseCapturingArg::Lifetime(_) => visit::walk_precise_capturing_arg(self, arg),
+            PreciseCapturingArg::Lifetime(_) => {}
 
-            PreciseCapturingArg::Arg(ident, node_id) => {
-                let ident = ident.normalize_to_macros_2_0();
-                'found: {
-                    for (rib_t, rib_v) in
-                        std::iter::zip(&self.ribs.type_ns, &self.ribs.value_ns).rev()
-                    {
-                        if let Some(res) = rib_t.bindings.get(&ident).or(rib_v.bindings.get(&ident))
-                        {
-                            self.r.record_partial_res(*node_id, PartialRes::new(*res));
-
-                            // Validate that this is a parameter
-                            match res {
-                                Res::Def(DefKind::TyParam | DefKind::ConstParam, _)
-                                | Res::SelfTyParam { .. } => {}
-                                Res::SelfTyAlias { .. } => {
-                                    self.report_error(
-                                        ident.span,
-                                        ResolutionError::FailedToResolve {
-                                            segment: Some(ident.name),
-                                            label: "`Self` cannot be captured because it is not a type parameter".to_string(),
-                                            suggestion: None,
-                                            module: None,
-                                        },
-                                    );
-                                }
-                                _ => {
-                                    self.report_error(
-                                        ident.span,
-                                        ResolutionError::FailedToResolve {
-                                            segment: Some(ident.name),
-                                            label: format!(
-                                                "expected type or const parameter, found {}",
-                                                res.descr()
-                                            ),
-                                            suggestion: None,
-                                            module: None,
-                                        },
-                                    );
-                                }
-                            }
-
-                            break 'found;
-                        }
-                    }
-                    self.report_error(
-                        ident.span,
-                        ResolutionError::FailedToResolve {
-                            segment: Some(ident.name),
-                            label: "could not find type or const parameter".to_string(),
-                            suggestion: None,
-                            module: None,
-                        },
-                    );
+            PreciseCapturingArg::Arg(path, id) => {
+                let mut check_ns = |ns| {
+                    self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns).is_some()
+                };
+                // Like `Ty::Param`, we try resolving this as both a const and a type.
+                if !check_ns(TypeNS) && check_ns(ValueNS) {
+                    self.smart_resolve_path(*id, &None, path, PathSource::Expr(None));
+                } else {
+                    self.smart_resolve_path(*id, &None, path, PathSource::Type);
                 }
             }
         }
+
+        visit::walk_precise_capturing_arg(self, arg)
     }
 
     fn visit_generics(&mut self, generics: &'ast Generics) {
diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.rs b/tests/ui/impl-trait/precise-capturing/bad-params.rs
index c600fa96f79..195bd1b67ae 100644
--- a/tests/ui/impl-trait/precise-capturing/bad-params.rs
+++ b/tests/ui/impl-trait/precise-capturing/bad-params.rs
@@ -2,15 +2,15 @@
 //~^ WARN the feature `precise_capturing` is incomplete
 
 fn missing() -> impl use<T> Sized {}
-//~^ ERROR could not find type or const parameter
+//~^ ERROR cannot find type `T` in this scope
 
 fn missing_self() -> impl use<Self> Sized {}
-//~^ ERROR could not find type or const parameter
+//~^ ERROR cannot find type `Self` in this scope
 
 struct MyType;
 impl MyType {
     fn self_is_not_param() -> impl use<Self> Sized {}
-    //~^ ERROR `Self` cannot be captured because it is not a type parameter
+    //~^ ERROR `Self` can't be captured in `use<...>` precise captures list, since it is an alias
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.stderr b/tests/ui/impl-trait/precise-capturing/bad-params.stderr
index bb5fe76ae3e..09694a800c4 100644
--- a/tests/ui/impl-trait/precise-capturing/bad-params.stderr
+++ b/tests/ui/impl-trait/precise-capturing/bad-params.stderr
@@ -1,20 +1,21 @@
-error[E0433]: failed to resolve: could not find type or const parameter
+error[E0412]: cannot find type `T` in this scope
   --> $DIR/bad-params.rs:4:26
    |
 LL | fn missing() -> impl use<T> Sized {}
-   |                          ^ could not find type or const parameter
+   |                          ^ not found in this scope
+   |
+help: you might be missing a type parameter
+   |
+LL | fn missing<T>() -> impl use<T> Sized {}
+   |           +++
 
-error[E0433]: failed to resolve: could not find type or const parameter
+error[E0411]: cannot find type `Self` in this scope
   --> $DIR/bad-params.rs:7:31
    |
 LL | fn missing_self() -> impl use<Self> Sized {}
-   |                               ^^^^ could not find type or const parameter
-
-error[E0433]: failed to resolve: `Self` cannot be captured because it is not a type parameter
-  --> $DIR/bad-params.rs:12:40
-   |
-LL |     fn self_is_not_param() -> impl use<Self> Sized {}
-   |                                        ^^^^ `Self` cannot be captured because it is not a type parameter
+   |    ------------               ^^^^ `Self` is only available in impls, traits, and type definitions
+   |    |
+   |    `Self` not allowed in a function
 
 warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
   --> $DIR/bad-params.rs:1:12
@@ -25,6 +26,15 @@ LL | #![feature(precise_capturing)]
    = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
+error: `Self` can't be captured in `use<...>` precise captures list, since it is an alias
+  --> $DIR/bad-params.rs:12:40
+   |
+LL | impl MyType {
+   | ----------- `Self` is not a generic argument, but an alias to the type of the implementation
+LL |     fn self_is_not_param() -> impl use<Self> Sized {}
+   |                                        ^^^^
+
 error: aborting due to 3 previous errors; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0433`.
+Some errors have detailed explanations: E0411, E0412.
+For more information about an error, try `rustc --explain E0411`.

From ac7651ccaf7447e695bf505916463927b78e433f Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 4 Apr 2024 20:36:18 -0400
Subject: [PATCH 10/11] More polishing

---
 compiler/rustc_hir/src/hir.rs                   |  5 +++++
 compiler/rustc_hir_analysis/messages.ftl        |  6 +++---
 compiler/rustc_hir_analysis/src/check/check.rs  |  8 ++++++++
 .../nice_region_error/static_impl_trait.rs      |  1 +
 compiler/rustc_parse/src/parser/ty.rs           |  4 +++-
 compiler/rustc_resolve/src/late.rs              |  6 ++++++
 tests/ui/impl-trait/precise-capturing/apit.rs   |  7 +++++++
 .../ui/impl-trait/precise-capturing/apit.stderr | 17 +++++++++++++++++
 .../impl-trait/precise-capturing/bad-params.rs  |  3 +++
 .../precise-capturing/bad-params.stderr         |  8 +++++++-
 tests/ui/impl-trait/precise-capturing/elided.rs |  8 ++++++++
 .../impl-trait/precise-capturing/elided.stderr  | 11 +++++++++++
 12 files changed, 79 insertions(+), 5 deletions(-)
 create mode 100644 tests/ui/impl-trait/precise-capturing/apit.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/apit.stderr
 create mode 100644 tests/ui/impl-trait/precise-capturing/elided.rs
 create mode 100644 tests/ui/impl-trait/precise-capturing/elided.stderr

diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 00b1ea061cc..b39056d8690 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2568,6 +2568,11 @@ pub enum PreciseCapturingArg<'hir> {
     Param(PreciseCapturingNonLifetimeArg),
 }
 
+/// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param
+/// resolution to. Lifetimes don't have this problem, and for them, it's actually
+/// kind of detrimental to use a custom node type versus just using [`Lifetime`],
+/// since resolve_bound_vars operates on `Lifetime`s.
+// FIXME(precise_capturing): Investigate storing this as a path instead?
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct PreciseCapturingNonLifetimeArg {
     pub hir_id: HirId,
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index bc6583444c3..0ff78ebff99 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -39,9 +39,6 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh
 
 hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found}
 
-hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias
-    .label = `Self` is not a generic argument, but an alias to the type of the {$what}
-
 hir_analysis_cannot_capture_late_bound_const =
     cannot capture late-bound const parameter in {$what}
     .label = parameter defined here
@@ -374,6 +371,9 @@ hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pa
 hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
     .label = not allowed in type signatures
 
+hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias
+    .label = `Self` is not a generic argument, but an alias to the type of the {$what}
+
 hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}`
     .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}`
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 1e8cd50ca0d..8c85d13650b 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -475,6 +475,14 @@ fn sanity_check_found_hidden_type<'tcx>(
     }
 }
 
+/// Check that the opaque's precise captures list is valid (if present).
+/// We check this for regular `impl Trait`s and also RPITITs, even though the latter
+/// are technically GATs.
+///
+/// This function is responsible for:
+/// 1. Checking that all type/const params are mention in the captures list.
+/// 2. Checking that all lifetimes that are implicitly captured are mentioned.
+/// 3. Asserting that all parameters mentioned in the captures list are invariant.
 fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) {
     let hir::OpaqueTy { precise_capturing_args, .. } =
         *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index fe70b631cdb..0bbabefaf95 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -283,6 +283,7 @@ pub fn suggest_new_region_bound(
             continue;
         }
         match fn_return.kind {
+            // FIXME(precise_captures): Suggest adding to `use<...>` list instead.
             TyKind::OpaqueDef(item_id, _, _) => {
                 let item = tcx.hir().item(item_id);
                 let ItemKind::OpaqueTy(opaque) = &item.kind else {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index f78a039394b..a6195fa7693 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -669,7 +669,9 @@ impl<'a> Parser<'a> {
             })
         }
 
-        // parse precise captures, if any.
+        // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of
+        // lifetimes and ident params (including SelfUpper). These are validated later
+        // for order, duplication, and whether they actually reference params.
         let precise_capturing = if self.eat_keyword(kw::Use) {
             let use_span = self.prev_token.span;
             self.psess.gated_spans.gate(sym::precise_capturing, use_span);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 64434412886..ba1391bc378 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1059,6 +1059,12 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
             PreciseCapturingArg::Lifetime(_) => {}
 
             PreciseCapturingArg::Arg(path, id) => {
+                // we want `impl use<C>` to try to resolve `C` as both a type parameter or
+                // a const parameter. Since the resolver specifically doesn't allow having
+                // two generic params with the same name, even if they're a different namespace,
+                // it doesn't really matter which we try resolving first, but just like
+                // `Ty::Param` we just fall back to the value namespace only if it's missing
+                // from the type namespace.
                 let mut check_ns = |ns| {
                     self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns).is_some()
                 };
diff --git a/tests/ui/impl-trait/precise-capturing/apit.rs b/tests/ui/impl-trait/precise-capturing/apit.rs
new file mode 100644
index 00000000000..efcac9ebb0b
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/apit.rs
@@ -0,0 +1,7 @@
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn hello(_: impl use<> Sized) {}
+//~^ ERROR `use<...>` precise capturing syntax not allowed on argument-position `impl Trait`
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/apit.stderr b/tests/ui/impl-trait/precise-capturing/apit.stderr
new file mode 100644
index 00000000000..36bf80d9e2f
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/apit.stderr
@@ -0,0 +1,17 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/apit.rs:1:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: `use<...>` precise capturing syntax not allowed on argument-position `impl Trait`
+  --> $DIR/apit.rs:4:18
+   |
+LL | fn hello(_: impl use<> Sized) {}
+   |                  ^^^
+
+error: aborting due to 1 previous error; 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.rs b/tests/ui/impl-trait/precise-capturing/bad-params.rs
index 195bd1b67ae..7970d49bf7c 100644
--- a/tests/ui/impl-trait/precise-capturing/bad-params.rs
+++ b/tests/ui/impl-trait/precise-capturing/bad-params.rs
@@ -13,4 +13,7 @@ impl MyType {
     //~^ ERROR `Self` can't be captured in `use<...>` precise captures list, since it is an alias
 }
 
+fn hello() -> impl use<hello> Sized {}
+//~^ ERROR expected type or const parameter in `use<...>` precise captures list, found function
+
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.stderr b/tests/ui/impl-trait/precise-capturing/bad-params.stderr
index 09694a800c4..27bf05302f9 100644
--- a/tests/ui/impl-trait/precise-capturing/bad-params.stderr
+++ b/tests/ui/impl-trait/precise-capturing/bad-params.stderr
@@ -34,7 +34,13 @@ LL | impl MyType {
 LL |     fn self_is_not_param() -> impl use<Self> Sized {}
    |                                        ^^^^
 
-error: aborting due to 3 previous errors; 1 warning emitted
+error: expected type or const parameter in `use<...>` precise captures list, found function
+  --> $DIR/bad-params.rs:16:24
+   |
+LL | fn hello() -> impl use<hello> Sized {}
+   |                        ^^^^^
+
+error: aborting due to 4 previous errors; 1 warning emitted
 
 Some errors have detailed explanations: E0411, E0412.
 For more information about an error, try `rustc --explain E0411`.
diff --git a/tests/ui/impl-trait/precise-capturing/elided.rs b/tests/ui/impl-trait/precise-capturing/elided.rs
new file mode 100644
index 00000000000..de80e8a5d58
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/elided.rs
@@ -0,0 +1,8 @@
+//@ check-pass
+
+#![feature(precise_capturing)]
+//~^ WARN the feature `precise_capturing` is incomplete
+
+fn elided(x: &()) -> impl use<'_> Sized { x }
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/elided.stderr b/tests/ui/impl-trait/precise-capturing/elided.stderr
new file mode 100644
index 00000000000..38da0828de9
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/elided.stderr
@@ -0,0 +1,11 @@
+warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/elided.rs:3:12
+   |
+LL | #![feature(precise_capturing)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+

From 8ddd2805de6ad8200868befc6ae703243fac3a46 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 4 Apr 2024 20:52:56 -0400
Subject: [PATCH 11/11] Rustfmt, clippy

---
 src/tools/clippy/clippy_utils/src/ast_utils.rs | 11 ++++++++++-
 src/tools/rustfmt/src/types.rs                 |  9 +++++++--
 2 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index f594a40ff59..9f0bd4ea7e2 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -709,7 +709,8 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
         (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
         (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
         (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
-        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
+        (ImplTrait(_, lg, lc), ImplTrait(_, rg, rc)) =>
+            over(lg, rg, eq_generic_bound) && both(lc, rc, |lc, rc| over(lc.0.as_slice(), rc.0.as_slice(), eq_precise_capture)),
         (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
         _ => false,
@@ -770,6 +771,14 @@ pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
     }
 }
 
+pub fn eq_precise_capture(l: &PreciseCapturingArg, r: &PreciseCapturingArg) -> bool {
+    match (l, r) {
+        (PreciseCapturingArg::Lifetime(l), PreciseCapturingArg::Lifetime(r)) => l.ident == r.ident,
+        (PreciseCapturingArg::Arg(l, _), PreciseCapturingArg::Arg(r, _)) => l.segments[0].ident == r.segments[0].ident,
+        _ => false,
+    }
+}
+
 fn eq_term(l: &Term, r: &Term) -> bool {
     match (l, r) {
         (Term::Ty(l), Term::Ty(r)) => eq_ty(l, r),
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 10a87f6e698..fe2d28ae1b9 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -843,7 +843,11 @@ impl Rewrite for ast::Ty {
                 rewrite_macro(mac, None, context, shape, MacroPosition::Expression)
             }
             ast::TyKind::ImplicitSelf => Some(String::from("")),
-            ast::TyKind::ImplTrait(_, ref it) => {
+            ast::TyKind::ImplTrait(_, ref it, ref captures) => {
+                // FIXME(precise_capturing): Implement formatting.
+                if captures.is_some() {
+                    return None;
+                }
                 // Empty trait is not a parser error.
                 if it.is_empty() {
                     return Some("impl".to_owned());
@@ -1106,7 +1110,8 @@ fn join_bounds_inner(
 
 pub(crate) fn opaque_ty(ty: &Option<ptr::P<ast::Ty>>) -> Option<&ast::GenericBounds> {
     ty.as_ref().and_then(|t| match &t.kind {
-        ast::TyKind::ImplTrait(_, bounds) => Some(bounds),
+        // FIXME(precise_capturing): Implement support here
+        ast::TyKind::ImplTrait(_, bounds, _) => Some(bounds),
         _ => None,
     })
 }