about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-06-05 16:18:52 -0400
committerMichael Goulet <michael@errs.io>2024-06-17 22:35:25 -0400
commitb1efe1ab5d10c5dc0462445273ca4e42dea3c5e3 (patch)
tree88d5d68eaba69cf8e65e87f38b6dcd04bd9c3f46 /compiler
parent68bd001c00e37a6c6854482138d27a76428efdff (diff)
downloadrust-b1efe1ab5d10c5dc0462445273ca4e42dea3c5e3.tar.gz
rust-b1efe1ab5d10c5dc0462445273ca4e42dea3c5e3.zip
Rework precise capturing syntax
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs5
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs12
-rw-r--r--compiler/rustc_ast/src/util/classify.rs6
-rw-r--r--compiler/rustc_ast/src/visit.rs21
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs132
-rw-r--r--compiler/rustc_ast_passes/messages.ftl2
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs180
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs8
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs20
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs4
-rw-r--r--compiler/rustc_hir/src/intravisit.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs3
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs16
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs18
-rw-r--r--compiler/rustc_lint/src/unused.rs2
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs2
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs27
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs4
-rw-r--r--compiler/rustc_resolve/src/late.rs2
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs4
23 files changed, 273 insertions, 217 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 9cb193b4a67..fe888d2004f 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -307,6 +307,8 @@ impl TraitBoundModifiers {
 pub enum GenericBound {
     Trait(PolyTraitRef, TraitBoundModifiers),
     Outlives(Lifetime),
+    /// Precise capturing syntax: `impl Sized + use<'a>`
+    Use(ThinVec<PreciseCapturingArg>, Span),
 }
 
 impl GenericBound {
@@ -314,6 +316,7 @@ impl GenericBound {
         match self {
             GenericBound::Trait(t, ..) => t.span,
             GenericBound::Outlives(l) => l.ident.span,
+            GenericBound::Use(_, span) => *span,
         }
     }
 }
@@ -2162,7 +2165,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<(ThinVec<PreciseCapturingArg>, Span)>>),
+    ImplTrait(NodeId, GenericBounds),
     /// 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 cc33ce2cb56..182ad7359af 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -523,14 +523,9 @@ 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, precise_capturing) => {
+        TyKind::ImplTrait(id, bounds) => {
             vis.visit_id(id);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound));
-            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) => {
@@ -923,6 +918,11 @@ fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T) {
     match pb {
         GenericBound::Trait(ty, _modifier) => vis.visit_poly_trait_ref(ty),
         GenericBound::Outlives(lifetime) => noop_visit_lifetime(lifetime, vis),
+        GenericBound::Use(args, _) => {
+            for arg in args {
+                vis.visit_precise_capturing_arg(arg);
+            }
+        }
     }
 }
 
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 382c903625f..4b2544ac47e 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -184,7 +184,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
                 None => break None,
             },
 
-            ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => {
+            ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
                 match bounds.last() {
                     Some(ast::GenericBound::Trait(bound, _)) => {
                         match path_return_type(&bound.trait_ref.path) {
@@ -192,7 +192,9 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
                             None => break None,
                         }
                     }
-                    Some(ast::GenericBound::Outlives(_)) | None => break None,
+                    Some(ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..)) | None => {
+                        break None;
+                    }
                 }
             }
 
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index fa97c8db326..104a401cd53 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -52,6 +52,16 @@ pub enum BoundKind {
     /// E.g., `trait A: B`
     SuperTraits,
 }
+impl BoundKind {
+    pub fn descr(self) -> &'static str {
+        match self {
+            BoundKind::Bound => "bounds",
+            BoundKind::Impl => "`impl Trait`",
+            BoundKind::TraitObject => "`dyn` trait object bounds",
+            BoundKind::SuperTraits => "supertrait bounds",
+        }
+    }
+}
 
 #[derive(Copy, Clone, Debug)]
 pub enum FnKind<'a> {
@@ -497,13 +507,8 @@ 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, precise_capturing) => {
+        TyKind::ImplTrait(_, bounds) => {
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
-            if let Some((precise_capturing, _span)) = precise_capturing.as_deref() {
-                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(_) => {}
@@ -688,6 +693,10 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB
     match bound {
         GenericBound::Trait(typ, _modifier) => visitor.visit_poly_trait_ref(typ),
         GenericBound::Outlives(lifetime) => visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound),
+        GenericBound::Use(args, _) => {
+            walk_list!(visitor, visit_precise_capturing_arg, args);
+            V::Result::output()
+        }
     }
 }
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 88f6e6c3b78..1fc6a969915 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -51,9 +51,9 @@ use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{DiagArgFromDisplay, DiagCtxt, StashKey};
-use rustc_hir as hir;
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::{self as hir};
 use rustc_hir::{
     ConstArg, GenericArg, HirId, ItemLocalMap, MissingLifetimeKind, ParamName, TraitCandidate,
 };
@@ -1384,6 +1384,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 }
                                 None
                             }
+                            // Ignore `use` syntax since that is not valid in objects.
+                            GenericBound::Use(..) => None,
                         }));
                     let lifetime_bound =
                         lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span));
@@ -1391,7 +1393,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 });
                 hir::TyKind::TraitObject(bounds, lifetime_bound, *kind)
             }
-            TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => {
+            TyKind::ImplTrait(def_node_id, bounds) => {
                 let span = t.span;
                 match itctx {
                     ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait(
@@ -1401,12 +1403,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         bounds,
                         fn_kind,
                         itctx,
-                        precise_capturing.as_deref().map(|(args, span)| (args.as_slice(), *span)),
                     ),
                     ImplTraitContext::Universal => {
-                        if let Some(&(_, span)) = precise_capturing.as_deref() {
+                        if let Some(span) = bounds.iter().find_map(|bound| match *bound {
+                            ast::GenericBound::Use(_, span) => Some(span),
+                            _ => None,
+                        }) {
                             self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span });
-                        };
+                        }
+
                         let span = t.span;
 
                         // HACK: pprust breaks strings with newlines when the type
@@ -1517,7 +1522,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         bounds: &GenericBounds,
         fn_kind: Option<FnDeclKind>,
         itctx: ImplTraitContext,
-        precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
     ) -> 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
@@ -1526,59 +1530,61 @@ 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_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 {
-                        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 {
-                    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.
+        let captured_lifetimes_to_duplicate = if let Some(args) =
+            bounds.iter().find_map(|bound| match bound {
+                ast::GenericBound::Use(a, _) => Some(a),
+                _ => None,
+            }) {
+            // We'll actually validate these later on; all we need is the list of
+            // lifetimes to duplicate during this portion of lowering.
+            args.iter()
+                .filter_map(|arg| match arg {
+                    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 {
+                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.
                         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.
-                            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`")
+                    } 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);
 
         self.lower_opaque_inner(
@@ -1588,7 +1594,6 @@ 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,7 +1606,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
         span: Span,
         opaque_ty_span: Span,
-        precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
         lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
     ) -> hir::TyKind<'hir> {
         let opaque_ty_def_id = self.create_def(
@@ -1688,18 +1692,8 @@ 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, precise_capturing_args) =
-                this.with_remapping(captured_to_synthesized_mapping, |this| {
-                    (
-                        lower_item_bounds(this),
-                        precise_capturing_args.map(|(precise_capturing, span)| {
-                            (
-                                this.lower_precise_capturing_args(precise_capturing),
-                                this.lower_span(span),
-                            )
-                        }),
-                    )
-                });
+            let bounds = this
+                .with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this));
 
             let generic_params =
                 this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map(
@@ -1744,7 +1738,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 origin,
                 lifetime_mapping,
                 in_trait,
-                precise_capturing_args,
             };
 
             // Generate an `type Foo = impl Trait;` declaration.
@@ -1955,7 +1948,6 @@ 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,
@@ -2038,6 +2030,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             GenericBound::Outlives(lifetime) => {
                 hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
             }
+            GenericBound::Use(args, span) => hir::GenericBound::Use(
+                self.lower_precise_capturing_args(args),
+                self.lower_span(*span),
+            ),
         }
     }
 
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 9a8689e27c0..af27429b508 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -14,6 +14,8 @@ ast_passes_assoc_type_without_body =
     associated type in `impl` without body
     .suggestion = provide a definition for the type
 
+ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax is not allowed in {$loc}
+
 ast_passes_at_least_one_trait = at least one trait must be specified
 
 ast_passes_auto_generic = auto traits cannot have generic parameters
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 0fbb288cc96..04c06e76e9c 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -751,7 +751,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 });
                 }
@@ -1304,6 +1304,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                                     }
                                 }
                                 GenericBound::Outlives(_) => {}
+                                GenericBound::Use(..) => {}
                             }
                         }
                     }
@@ -1322,95 +1323,110 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     }
 
     fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
-        if let GenericBound::Trait(poly, modifiers) = bound {
-            match (ctxt, modifiers.constness, modifiers.polarity) {
-                (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
-                    self.dcx().emit_err(errors::OptionalTraitSupertrait {
-                        span: poly.span,
-                        path_str: pprust::path_to_string(&poly.trait_ref.path),
-                    });
-                }
-                (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
-                    self.dcx().emit_err(errors::OptionalTraitObject { span: poly.span });
-                }
-                (BoundKind::TraitObject, BoundConstness::Always(_), BoundPolarity::Positive) => {
-                    self.dcx().emit_err(errors::ConstBoundTraitObject { span: poly.span });
+        match bound {
+            GenericBound::Trait(trait_ref, modifiers) => {
+                match (ctxt, modifiers.constness, modifiers.polarity) {
+                    (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
+                        self.dcx().emit_err(errors::OptionalTraitSupertrait {
+                            span: trait_ref.span,
+                            path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
+                        });
+                    }
+                    (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
+                        self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span });
+                    }
+                    (
+                        BoundKind::TraitObject,
+                        BoundConstness::Always(_),
+                        BoundPolarity::Positive,
+                    ) => {
+                        self.dcx().emit_err(errors::ConstBoundTraitObject { span: trait_ref.span });
+                    }
+                    (_, BoundConstness::Maybe(span), BoundPolarity::Positive)
+                        if let Some(reason) = &self.disallow_tilde_const =>
+                    {
+                        let reason = match reason {
+                            DisallowTildeConstContext::Fn(FnKind::Closure(..)) => {
+                                errors::TildeConstReason::Closure
+                            }
+                            DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => {
+                                errors::TildeConstReason::Function { ident: ident.span }
+                            }
+                            &DisallowTildeConstContext::Trait(span) => {
+                                errors::TildeConstReason::Trait { span }
+                            }
+                            &DisallowTildeConstContext::TraitImpl(span) => {
+                                errors::TildeConstReason::TraitImpl { span }
+                            }
+                            &DisallowTildeConstContext::Impl(span) => {
+                                // FIXME(effects): Consider providing a help message or even a structured
+                                // suggestion for moving such bounds to the assoc const fns if available.
+                                errors::TildeConstReason::Impl { span }
+                            }
+                            &DisallowTildeConstContext::TraitAssocTy(span) => {
+                                errors::TildeConstReason::TraitAssocTy { span }
+                            }
+                            &DisallowTildeConstContext::TraitImplAssocTy(span) => {
+                                errors::TildeConstReason::TraitImplAssocTy { span }
+                            }
+                            &DisallowTildeConstContext::InherentAssocTy(span) => {
+                                errors::TildeConstReason::InherentAssocTy { span }
+                            }
+                            DisallowTildeConstContext::TraitObject => {
+                                errors::TildeConstReason::TraitObject
+                            }
+                            DisallowTildeConstContext::Item => errors::TildeConstReason::Item,
+                        };
+                        self.dcx().emit_err(errors::TildeConstDisallowed { span, reason });
+                    }
+                    (
+                        _,
+                        BoundConstness::Always(_) | BoundConstness::Maybe(_),
+                        BoundPolarity::Negative(_) | BoundPolarity::Maybe(_),
+                    ) => {
+                        self.dcx().emit_err(errors::IncompatibleTraitBoundModifiers {
+                            span: bound.span(),
+                            left: modifiers.constness.as_str(),
+                            right: modifiers.polarity.as_str(),
+                        });
+                    }
+                    _ => {}
                 }
-                (_, BoundConstness::Maybe(span), BoundPolarity::Positive)
-                    if let Some(reason) = &self.disallow_tilde_const =>
+
+                // Negative trait bounds are not allowed to have associated constraints
+                if let BoundPolarity::Negative(_) = modifiers.polarity
+                    && let Some(segment) = trait_ref.trait_ref.path.segments.last()
                 {
-                    let reason = match reason {
-                        DisallowTildeConstContext::Fn(FnKind::Closure(..)) => {
-                            errors::TildeConstReason::Closure
-                        }
-                        DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => {
-                            errors::TildeConstReason::Function { ident: ident.span }
-                        }
-                        &DisallowTildeConstContext::Trait(span) => {
-                            errors::TildeConstReason::Trait { span }
-                        }
-                        &DisallowTildeConstContext::TraitImpl(span) => {
-                            errors::TildeConstReason::TraitImpl { span }
-                        }
-                        &DisallowTildeConstContext::Impl(span) => {
-                            // FIXME(effects): Consider providing a help message or even a structured
-                            // suggestion for moving such bounds to the assoc const fns if available.
-                            errors::TildeConstReason::Impl { span }
-                        }
-                        &DisallowTildeConstContext::TraitAssocTy(span) => {
-                            errors::TildeConstReason::TraitAssocTy { span }
-                        }
-                        &DisallowTildeConstContext::TraitImplAssocTy(span) => {
-                            errors::TildeConstReason::TraitImplAssocTy { span }
-                        }
-                        &DisallowTildeConstContext::InherentAssocTy(span) => {
-                            errors::TildeConstReason::InherentAssocTy { span }
-                        }
-                        DisallowTildeConstContext::TraitObject => {
-                            errors::TildeConstReason::TraitObject
+                    match segment.args.as_deref() {
+                        Some(ast::GenericArgs::AngleBracketed(args)) => {
+                            for arg in &args.args {
+                                if let ast::AngleBracketedArg::Constraint(constraint) = arg {
+                                    self.dcx().emit_err(errors::ConstraintOnNegativeBound {
+                                        span: constraint.span,
+                                    });
+                                }
+                            }
                         }
-                        DisallowTildeConstContext::Item => errors::TildeConstReason::Item,
-                    };
-                    self.dcx().emit_err(errors::TildeConstDisallowed { span, reason });
-                }
-                (
-                    _,
-                    BoundConstness::Always(_) | BoundConstness::Maybe(_),
-                    BoundPolarity::Negative(_) | BoundPolarity::Maybe(_),
-                ) => {
-                    self.dcx().emit_err(errors::IncompatibleTraitBoundModifiers {
-                        span: bound.span(),
-                        left: modifiers.constness.as_str(),
-                        right: modifiers.polarity.as_str(),
-                    });
-                }
-                _ => {}
-            }
-        }
-
-        // Negative trait bounds are not allowed to have associated constraints
-        if let GenericBound::Trait(trait_ref, modifiers) = bound
-            && let BoundPolarity::Negative(_) = modifiers.polarity
-            && let Some(segment) = trait_ref.trait_ref.path.segments.last()
-        {
-            match segment.args.as_deref() {
-                Some(ast::GenericArgs::AngleBracketed(args)) => {
-                    for arg in &args.args {
-                        if let ast::AngleBracketedArg::Constraint(constraint) = arg {
-                            self.dcx().emit_err(errors::ConstraintOnNegativeBound {
-                                span: constraint.span,
+                        // The lowered form of parenthesized generic args contains an associated type binding.
+                        Some(ast::GenericArgs::Parenthesized(args)) => {
+                            self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation {
+                                span: args.span,
                             });
                         }
+                        None => {}
                     }
                 }
-                // The lowered form of parenthesized generic args contains an associated type binding.
-                Some(ast::GenericArgs::Parenthesized(args)) => {
-                    self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation {
-                        span: args.span,
-                    });
-                }
-                None => {}
             }
+            GenericBound::Outlives(_) => {}
+            GenericBound::Use(_, span) => match ctxt {
+                BoundKind::Impl => {}
+                BoundKind::Bound | BoundKind::TraitObject | BoundKind::SuperTraits => {
+                    self.dcx().emit_err(errors::PreciseCapturingNotAllowedHere {
+                        loc: ctxt.descr(),
+                        span: *span,
+                    })
+                }
+            },
         }
 
         visit::walk_param_bound(self, bound)
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 260c182bd9e..cb95b7bce91 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -844,3 +844,11 @@ pub struct MatchArmWithNoBody {
     #[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
     pub suggestion: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_precise_capturing_not_allowed_here)]
+pub struct PreciseCapturingNotAllowedHere {
+    #[primary_span]
+    pub span: Span,
+    pub loc: &'static str,
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 4eb2a103fd8..0225c95dca8 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1187,17 +1187,8 @@ impl<'a> State<'a> {
                 }
                 self.print_type_bounds(bounds);
             }
-            ast::TyKind::ImplTrait(_, bounds, precise_capturing_args) => {
+            ast::TyKind::ImplTrait(_, bounds) => {
                 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(p, _) => s.print_path(p, false, 0),
-                        ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt),
-                    });
-                    self.word(">")
-                }
                 self.print_type_bounds(bounds);
             }
             ast::TyKind::Array(ty, length) => {
@@ -1800,6 +1791,15 @@ impl<'a> State<'a> {
                     self.print_poly_trait_ref(tref);
                 }
                 GenericBound::Outlives(lt) => self.print_lifetime(*lt),
+                GenericBound::Use(args, _) => {
+                    self.word("use");
+                    self.word("<");
+                    self.commasep(Inconsistent, args, |s, arg| match arg {
+                        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_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 58832cb1087..5cb72a2ba38 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -567,7 +567,7 @@ declare_features! (
     (unstable, optimize_attribute, "1.34.0", Some(54882)),
     /// Allows postfix match `expr.match { ... }`
     (unstable, postfix_match, "1.79.0", Some(121618)),
-    /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args.
+    /// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args.
     (incomplete, precise_capturing, "1.79.0", Some(123432)),
     /// Allows macro attributes on expressions, statements and non-inline modules.
     (unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index d4a22c4c31f..22a6c06bba3 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -463,6 +463,7 @@ pub enum TraitBoundModifier {
 pub enum GenericBound<'hir> {
     Trait(PolyTraitRef<'hir>, TraitBoundModifier),
     Outlives(&'hir Lifetime),
+    Use(&'hir [PreciseCapturingArg<'hir>], Span),
 }
 
 impl GenericBound<'_> {
@@ -477,6 +478,7 @@ impl GenericBound<'_> {
         match self {
             GenericBound::Trait(t, ..) => t.span,
             GenericBound::Outlives(l) => l.ident.span,
+            GenericBound::Use(_, span) => *span,
         }
     }
 }
@@ -2689,8 +2691,6 @@ 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>], Span)>,
 }
 
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 5a16f266dab..065ecc5d7b7 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -532,15 +532,10 @@ 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, precise_capturing_args, .. }) => {
+        ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => {
             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));
@@ -1147,6 +1142,10 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>(
     match *bound {
         GenericBound::Trait(ref typ, _modifier) => visitor.visit_poly_trait_ref(typ),
         GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
+        GenericBound::Use(args, _) => {
+            walk_list!(visitor, visit_precise_capturing_arg, args);
+            V::Result::output()
+        }
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 72e5995e892..3b53c253195 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -481,9 +481,12 @@ fn sanity_check_found_hidden_type<'tcx>(
 /// 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, .. } =
+    let hir::OpaqueTy { bounds, .. } =
         *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
-    let Some((precise_capturing_args, _)) = precise_capturing_args else {
+    let Some(precise_capturing_args) = bounds.iter().find_map(|bound| match *bound {
+        hir::GenericBound::Use(bounds, ..) => Some(bounds),
+        _ => None,
+    }) else {
         // No precise capturing args; nothing to validate
         return;
     };
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 9a6bc8a7b72..c7699b0b310 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -178,6 +178,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         lifetime.ident.span,
                     );
                 }
+                hir::GenericBound::Use(..) => {
+                    // We don't actually lower `use` into the type layer.
+                }
             }
         }
     }
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index d32d0183c4e..b21f1eadfb7 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -13,7 +13,7 @@ use rustc_ast_pretty::pprust::{Comments, PrintState};
 use rustc_hir as hir;
 use rustc_hir::{
     BindingMode, ByRef, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId,
-    LifetimeParamKind, Node, PatKind, RangeEnd, Term, TraitBoundModifier,
+    LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, TraitBoundModifier,
 };
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, Ident, Symbol};
@@ -2100,10 +2100,24 @@ impl<'a> State<'a> {
                 GenericBound::Outlives(lt) => {
                     self.print_lifetime(lt);
                 }
+                GenericBound::Use(args, _) => {
+                    self.word("use <");
+
+                    self.commasep(Inconsistent, args, |s, arg| s.print_precise_capturing_arg(*arg));
+
+                    self.word(">");
+                }
             }
         }
     }
 
+    fn print_precise_capturing_arg(&mut self, arg: PreciseCapturingArg<'_>) {
+        match arg {
+            PreciseCapturingArg::Lifetime(lt) => self.print_lifetime(lt),
+            PreciseCapturingArg::Param(arg) => self.print_ident(arg.ident),
+        }
+    }
+
     fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
         if !generic_params.is_empty() {
             self.word("<");
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index a311c274a6b..d4f6d388d9f 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{sym, Span};
 
 use crate::fluent_generated as fluent;
 use crate::{LateContext, LateLintPass};
@@ -53,7 +53,7 @@ declare_lint! {
     /// while the `impl Display` is live.
     ///
     /// To fix this, we can explicitly state that the `impl Display` doesn't
-    /// capture any lifetimes, using `impl use<> Display`.
+    /// capture any lifetimes, using `impl Display + use<>`.
     pub IMPL_TRAIT_OVERCAPTURES,
     Allow,
     "`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
@@ -79,7 +79,7 @@ declare_lint! {
     /// # #![feature(precise_capturing, lifetime_capture_rules_2024)]
     /// # #![allow(incomplete_features)]
     /// # #![deny(impl_trait_redundant_captures)]
-    /// fn test<'a>(x: &'a i32) -> impl use<'a> Sized { x }
+    /// fn test<'a>(x: &'a i32) -> impl Sized + use<'a> { x }
     /// ```
     ///
     /// {{produces}}
@@ -249,7 +249,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
             // If we have uncaptured args, and if the opaque doesn't already have
             // `use<>` syntax on it, and we're < edition 2024, then warn the user.
             if !new_capture_rules
-                && opaque.precise_capturing_args.is_none()
+                && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..)))
                 && !uncaptured_spans.is_empty()
             {
                 let suggestion = if let Ok(snippet) =
@@ -268,8 +268,8 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
                     // Make sure that we're not trying to name any APITs
                     if generics.iter().all(|name| !name.starts_with("impl ")) {
                         Some((
-                            format!(" use<{}>", generics.join(", ")),
-                            opaque_span.with_lo(opaque_span.lo() + BytePos(4)).shrink_to_lo(),
+                            format!(" + use<{}>", generics.join(", ")),
+                            opaque_span.shrink_to_hi(),
                         ))
                     } else {
                         None
@@ -294,7 +294,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
             // have no uncaptured args, then we should warn to the user that
             // it's redundant to capture all args explicitly.
             else if new_capture_rules
-                && let Some((captured_args, capturing_span)) = opaque.precise_capturing_args
+                && let Some((captured_args, capturing_span)) =
+                    opaque.bounds.iter().find_map(|bound| match *bound {
+                        hir::GenericBound::Use(a, s) => Some((a, s)),
+                        _ => None,
+                    })
             {
                 let mut explicitly_captured = UnordSet::default();
                 for arg in captured_args {
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 7a1aa4043be..195a0f72475 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1268,7 +1268,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 93a15c938ec..fde16ac957d 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, None) = &p.kind {
+                        if let TyKind::ImplTrait(_, bounds) = &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 3f5a4afdad8..3d2eee247b8 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -633,7 +633,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, None) = other
+                        if let TyKind::ImplTrait(_, bounds) = 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 bdb08709f27..fcd623b477f 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, None)
+                            TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
                         }
                         _ => return Err(err),
                     };
@@ -670,24 +670,12 @@ impl<'a> Parser<'a> {
             })
         }
 
-        // 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);
-            let (args, args_span) = self.parse_precise_capturing_args()?;
-            Some(P((args, use_span.to(args_span))))
-        } 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, precise_capturing))
+        Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
     }
 
     fn parse_precise_capturing_args(
@@ -834,6 +822,7 @@ impl<'a> Parser<'a> {
             || self.check(&token::OpenDelim(Delimiter::Parenthesis))
             || self.check_keyword(kw::Const)
             || self.check_keyword(kw::Async)
+            || self.check_keyword(kw::Use)
     }
 
     /// Parses a bound according to the grammar:
@@ -850,6 +839,14 @@ impl<'a> Parser<'a> {
         let bound = if self.token.is_lifetime() {
             self.error_lt_bound_with_modifiers(modifiers);
             self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
+        } else if self.eat_keyword(kw::Use) {
+            // 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 use_span = self.prev_token.span;
+            self.psess.gated_spans.gate(sym::precise_capturing, use_span);
+            let (args, args_span) = self.parse_precise_capturing_args()?;
+            GenericBound::Use(args, use_span.to(args_span))
         } else {
             self.parse_generic_ty_bound(lo, has_parens, modifiers, &leading_token)?
         };
@@ -1009,7 +1006,7 @@ impl<'a> Parser<'a> {
                             Applicability::MaybeIncorrect,
                         )
                     }
-                    TyKind::ImplTrait(_, bounds, None)
+                    TyKind::ImplTrait(_, bounds)
                         if let [GenericBound::Trait(tr, ..), ..] = bounds.as_slice() =>
                     {
                         (
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index d49298781a2..0ba61f8e8b4 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -429,7 +429,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
         record_variants!(
             (self, b, b, Id::None, hir, GenericBound, GenericBound),
-            [Trait, Outlives]
+            [Trait, Outlives, Use]
         );
         hir_visit::walk_param_bound(self, b)
     }
@@ -659,7 +659,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
         record_variants!(
             (self, b, b, Id::None, ast, GenericBound, GenericBound),
-            [Trait, Outlives]
+            [Trait, Outlives, Use]
         );
         ast_visit::walk_param_bound(self, b)
     }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index c1e83c59f98..5ab6ba23a7d 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -799,7 +799,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 be24755d4c5..75a1aff4fc5 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -829,7 +829,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                         auto-traits; structs and enums can't be bound in that way",
                 );
                 if bounds.iter().all(|bound| match bound {
-                    ast::GenericBound::Outlives(_) => true,
+                    ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true,
                     ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
                 }) {
                     let mut sugg = vec![];
@@ -3210,7 +3210,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())