diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 3b75c69132c20..26ee353a24baa 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -598,8 +598,10 @@ declare_features! (
     (incomplete, pin_ergonomics, "1.83.0", Some(130494)),
     /// Allows postfix match `expr.match { ... }`
     (unstable, postfix_match, "1.79.0", Some(121618)),
-    /// Allows `use<..>` precise capturign on impl Trait in traits.
+    /// Allows `use<..>` precise capturing on impl Trait in traits.
     (unstable, precise_capturing_in_traits, "1.83.0", Some(130044)),
+    /// Allows `use<..>` precise capturing to omit type and const parameters.
+    (incomplete, precise_capturing_of_types, "1.83.0", Some(130043)),
     /// Allows macro attributes on expressions, statements and non-inline modules.
     (unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
     /// Allows the use of raw-dylibs on ELF platforms
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 194f2cd04e468..fc6789eb70378 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -284,16 +284,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_implicitly_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
-    .param_label = all lifetime parameters originating from a trait are captured implicitly
-
 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
-
 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
@@ -412,6 +405,9 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur
     .label = `impl Trait` implicitly captures all lifetimes in scope
     .note = lifetime declared here
 
+hir_analysis_param_implicitly_captured = `impl Trait` captures {$kind} parameter, but it is not mentioned in `use<...>` precise captures list
+    .param_label = all parameters originating from a trait are captured implicitly
+
 hir_analysis_param_in_ty_of_assoc_const_binding =
     the type of the associated constant `{$assoc_const}` must not depend on {$param_category ->
         [self] `Self`
@@ -428,7 +424,11 @@ 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 in `use<...>`
+hir_analysis_param_not_captured = `impl Trait` captures {$kind} parameter, but it is not mentioned in `use<...>` precise captures list
+    .label = {$kind} captured due to being mentioned in the bounds of the `impl Trait`
+    .param_label = this {$kind} parameter is captured
+
+hir_analysis_param_not_captured_forced = `impl Trait` must mention all {$kind} parameters in scope in `use<...>`
     .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
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 07b5837bd871d..2589c282d04c1 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -609,44 +609,26 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
             }
 
             match param.kind {
-                ty::GenericParamDefKind::Lifetime => {
-                    let use_span = tcx.def_span(param.def_id);
-                    let opaque_span = tcx.def_span(opaque_def_id);
-                    // Check if the lifetime param was captured but isn't named in the precise captures list.
-                    if variances[param.index as usize] == ty::Invariant {
-                        if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id))
-                            && let Some(def_id) = tcx
-                                .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
-                                .opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id()))
-                        {
-                            tcx.dcx().emit_err(errors::LifetimeNotCaptured {
-                                opaque_span,
-                                use_span,
-                                param_span: tcx.def_span(def_id),
-                            });
-                        } else {
-                            if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait {
-                                tcx.dcx().emit_err(errors::LifetimeImplicitlyCaptured {
-                                    opaque_span,
-                                    param_span: tcx.def_span(param.def_id),
-                                });
-                            } else {
-                                // If the `use_span` is actually just the param itself, then we must
-                                // have not duplicated the lifetime but captured the original.
-                                // The "effective" `use_span` will be the span of the opaque itself,
-                                // and the param span will be the def span of the param.
-                                tcx.dcx().emit_err(errors::LifetimeNotCaptured {
-                                    opaque_span,
-                                    use_span: opaque_span,
-                                    param_span: use_span,
-                                });
-                            }
-                        }
-                        continue;
-                    }
-                }
+                ty::GenericParamDefKind::Lifetime => check_captured_arg_is_mentioned(
+                    tcx,
+                    opaque_def_id,
+                    variances,
+                    param,
+                    "lifetime",
+                ),
                 ty::GenericParamDefKind::Type { .. } => {
-                    if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) {
+                    if tcx.features().precise_capturing_of_types() {
+                        check_captured_arg_is_mentioned(
+                            tcx,
+                            opaque_def_id,
+                            variances,
+                            param,
+                            "type",
+                        )
+                    } else if matches!(
+                        tcx.def_kind(param.def_id),
+                        DefKind::Trait | DefKind::TraitAlias
+                    ) {
                         // FIXME(precise_capturing): Structured suggestion for this would be useful
                         tcx.dcx().emit_err(errors::SelfTyNotCaptured {
                             trait_span: tcx.def_span(param.def_id),
@@ -654,7 +636,7 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
                         });
                     } else {
                         // FIXME(precise_capturing): Structured suggestion for this would be useful
-                        tcx.dcx().emit_err(errors::ParamNotCaptured {
+                        tcx.dcx().emit_err(errors::ParamNotCapturedForced {
                             param_span: tcx.def_span(param.def_id),
                             opaque_span: tcx.def_span(opaque_def_id),
                             kind: "type",
@@ -662,18 +644,73 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
                     }
                 }
                 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",
-                    });
+                    if tcx.features().precise_capturing_of_types() {
+                        check_captured_arg_is_mentioned(
+                            tcx,
+                            opaque_def_id,
+                            variances,
+                            param,
+                            "const",
+                        )
+                    } else {
+                        // FIXME(precise_capturing): Structured suggestion for this would be useful
+                        tcx.dcx().emit_err(errors::ParamNotCapturedForced {
+                            param_span: tcx.def_span(param.def_id),
+                            opaque_span: tcx.def_span(opaque_def_id),
+                            kind: "const",
+                        });
+                    }
                 }
             }
         }
     }
 }
 
+fn check_captured_arg_is_mentioned<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    opaque_def_id: LocalDefId,
+    variances: &[ty::Variance],
+    param: &ty::GenericParamDef,
+    kind: &'static str,
+) {
+    let use_span = tcx.def_span(param.def_id);
+    let opaque_span = tcx.def_span(opaque_def_id);
+    // Check if the lifetime param was captured but isn't named in the precise captures list.
+    if variances[param.index as usize] == ty::Invariant {
+        if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id))
+            && let Some(def_id) = tcx
+                .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
+                .opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id()))
+        {
+            tcx.dcx().emit_err(errors::ParamNotCaptured {
+                opaque_span,
+                use_span,
+                param_span: tcx.def_span(def_id),
+                kind,
+            });
+        } else {
+            if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait {
+                tcx.dcx().emit_err(errors::ParamImplicitlyCaptured {
+                    opaque_span,
+                    param_span: tcx.def_span(param.def_id),
+                    kind,
+                });
+            } else {
+                // If the `use_span` is actually just the param itself, then we must
+                // have not duplicated the lifetime but captured the original.
+                // The "effective" `use_span` will be the span of the opaque itself,
+                // and the param span will be the def span of the param.
+                tcx.dcx().emit_err(errors::ParamNotCaptured {
+                    opaque_span,
+                    use_span: opaque_span,
+                    param_span: use_span,
+                    kind,
+                });
+            }
+        }
+    }
+}
+
 fn is_enum_of_nonnullable_ptr<'tcx>(
     tcx: TyCtxt<'tcx>,
     adt_def: AdtDef<'tcx>,
diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
index 8a83866b7fa46..12eb0246860f1 100644
--- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
+++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs
@@ -3,9 +3,9 @@ use rustc_macros::Diagnostic;
 use rustc_span::{Span, Symbol};
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_param_not_captured)]
+#[diag(hir_analysis_param_not_captured_forced)]
 #[note]
-pub(crate) struct ParamNotCaptured {
+pub(crate) struct ParamNotCapturedForced {
     #[primary_span]
     pub opaque_span: Span,
     #[label]
@@ -24,23 +24,25 @@ pub(crate) struct SelfTyNotCaptured {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_lifetime_not_captured)]
-pub(crate) struct LifetimeNotCaptured {
+#[diag(hir_analysis_param_not_captured)]
+pub(crate) struct ParamNotCaptured {
     #[primary_span]
     pub use_span: Span,
     #[label(hir_analysis_param_label)]
     pub param_span: Span,
     #[label]
     pub opaque_span: Span,
+    pub kind: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_lifetime_implicitly_captured)]
-pub(crate) struct LifetimeImplicitlyCaptured {
+#[diag(hir_analysis_param_implicitly_captured)]
+pub(crate) struct ParamImplicitlyCaptured {
     #[primary_span]
     pub opaque_span: Span,
     #[label(hir_analysis_param_label)]
     pub param_span: Span,
+    pub kind: &'static str,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 0800d99e9452e..6921ae02e49cb 100644
--- a/compiler/rustc_hir_analysis/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
@@ -63,7 +63,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
         }
         DefKind::AssocTy => match tcx.opt_rpitit_info(item_def_id.to_def_id()) {
             Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
-                return variance_of_opaque(
+                return variances_of_opaque(
                     tcx,
                     opaque_def_id.expect_local(),
                     ForceCaptureTraitArgs::Yes,
@@ -83,7 +83,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
                 ForceCaptureTraitArgs::No
             };
 
-            return variance_of_opaque(tcx, item_def_id, force_capture_trait_args);
+            return variances_of_opaque(tcx, item_def_id, force_capture_trait_args);
         }
         _ => {}
     }
@@ -99,7 +99,7 @@ enum ForceCaptureTraitArgs {
 }
 
 #[instrument(level = "trace", skip(tcx), ret)]
-fn variance_of_opaque(
+fn variances_of_opaque(
     tcx: TyCtxt<'_>,
     item_def_id: LocalDefId,
     force_capture_trait_args: ForceCaptureTraitArgs,
@@ -177,7 +177,11 @@ fn variance_of_opaque(
                         variances[param.index as usize] = ty::Bivariant;
                     }
                     ty::GenericParamDefKind::Type { .. }
-                    | ty::GenericParamDefKind::Const { .. } => {}
+                    | ty::GenericParamDefKind::Const { .. } => {
+                        if tcx.features().precise_capturing_of_types() {
+                            variances[param.index as usize] = ty::Bivariant;
+                        }
+                    }
                 }
             }
         }
diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl
index 0b3c0be1a4e1a..c7a54fa439d32 100644
--- a/compiler/rustc_middle/messages.ftl
+++ b/compiler/rustc_middle/messages.ftl
@@ -46,9 +46,6 @@ middle_consider_type_length_limit =
 middle_const_eval_non_int =
     constant evaluation of enum discriminant resulted in non-integer
 
-middle_const_not_used_in_type_alias =
-    const parameter `{$ct}` is part of concrete type but not used in parameter list for the `impl Trait` type alias
-
 middle_deprecated = use of deprecated {$kind} `{$path}`{$has_note ->
         [true] : {$note}
         *[other] {""}
diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs
index bd315577efb5e..f0352480845f2 100644
--- a/compiler/rustc_middle/src/error.rs
+++ b/compiler/rustc_middle/src/error.rs
@@ -99,14 +99,6 @@ pub(crate) struct RequiresLangItem {
     pub name: Symbol,
 }
 
-#[derive(Diagnostic)]
-#[diag(middle_const_not_used_in_type_alias)]
-pub(super) struct ConstNotUsedTraitAlias {
-    pub ct: String,
-    #[primary_span]
-    pub span: Span,
-}
-
 pub struct CustomSubdiagnostic<'a> {
     pub msg: fn() -> DiagMessage,
     pub add_args: Box<dyn FnOnce(&mut dyn FnMut(DiagArgName, DiagArgValue)) + 'a>,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index f54dd2b0040ae..58a384fd7e578 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -771,6 +771,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
     fn associated_const_equality(self) -> bool {
         self.associated_const_equality()
     }
+
+    fn precise_capturing_of_types(self) -> bool {
+        self.precise_capturing_of_types()
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Span<TyCtxt<'tcx>> for Span {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index a508487c796bf..0c93aa852f015 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -823,16 +823,32 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
         let id_args = GenericArgs::identity_for_item(tcx, def_id);
         debug!(?id_args);
 
-        // This zip may have several times the same lifetime in `args` paired with a different
-        // lifetime from `id_args`. Simply `collect`ing the iterator is the correct behaviour:
-        // it will pick the last one, which is the one we introduced in the impl-trait desugaring.
-        let map = args.iter().zip(id_args).collect();
-        debug!("map = {:#?}", map);
+        let mut mapping = FxHashMap::default();
+        let mut uncaptured_args = FxHashSet::default();
+        for ((arg, identity_arg), &variance) in
+            args.iter().zip(id_args).zip(tcx.variances_of(def_id))
+        {
+            match variance {
+                ty::Invariant => {
+                    mapping.insert(arg, identity_arg);
+                }
+                ty::Bivariant => {
+                    uncaptured_args.insert(arg);
+                }
+                _ => unreachable!(),
+            }
+        }
 
         // Convert the type from the function into a type valid outside
         // the function, by replacing invalid regions with 'static,
         // after producing an error for each of them.
-        self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span, ignore_errors))
+        self.fold_with(&mut opaque_types::ReverseMapper::new(
+            tcx,
+            mapping,
+            uncaptured_args,
+            self.span,
+            ignore_errors,
+        ))
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs
index 56c44c8a84c04..adf8237f8c5c9 100644
--- a/compiler/rustc_middle/src/ty/opaque_types.rs
+++ b/compiler/rustc_middle/src/ty/opaque_types.rs
@@ -1,9 +1,8 @@
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_span::Span;
 use rustc_span::def_id::DefId;
 use tracing::{debug, instrument, trace};
 
-use crate::error::ConstNotUsedTraitAlias;
 use crate::ty::{
     self, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
 };
@@ -15,7 +14,12 @@ pub type OpaqueTypeKey<'tcx> = rustc_type_ir::OpaqueTypeKey<TyCtxt<'tcx>>;
 /// list to the opaque type's own generics.
 pub(super) struct ReverseMapper<'tcx> {
     tcx: TyCtxt<'tcx>,
-    map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
+
+    mapping: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
+
+    /// List of uncaptured args (which are bivariant)
+    uncaptured_args: FxHashSet<GenericArg<'tcx>>,
+
     /// see call sites to fold_kind_no_missing_regions_error
     /// for an explanation of this field.
     do_not_error: bool,
@@ -33,11 +37,12 @@ pub(super) struct ReverseMapper<'tcx> {
 impl<'tcx> ReverseMapper<'tcx> {
     pub(super) fn new(
         tcx: TyCtxt<'tcx>,
-        map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
+        mapping: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
+        uncaptured_args: FxHashSet<GenericArg<'tcx>>,
         span: Span,
         ignore_errors: bool,
     ) -> Self {
-        Self { tcx, map, do_not_error: false, ignore_errors, span }
+        Self { tcx, mapping, uncaptured_args, do_not_error: false, ignore_errors, span }
     }
 
     fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
@@ -127,25 +132,29 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
             }
         }
 
-        match self.map.get(&r.into()).map(|k| k.unpack()) {
+        match self.mapping.get(&r.into()).map(|k| k.unpack()) {
             Some(GenericArgKind::Lifetime(r1)) => r1,
             Some(u) => panic!("region mapped to unexpected kind: {u:?}"),
-            None if self.do_not_error => self.tcx.lifetimes.re_static,
             None => {
-                let e = self
-                    .tcx
-                    .dcx()
-                    .struct_span_err(self.span, "non-defining opaque type use in defining scope")
-                    .with_span_label(
+                let guar = if self.uncaptured_args.contains(&r.into()) {
+                    // FIXME(precise_capturing_of_types): Mention `use<>` list
+                    // and add an structured suggestion.
+                    self.tcx.dcx().struct_span_err(
+                        self.span,
+                        format!("hidden type mentions uncaptured lifetime parameter `{r}`"),
+                    )
+                } else {
+                    self.tcx.dcx().struct_span_err(
                         self.span,
                         format!(
-                            "lifetime `{r}` is part of concrete type but not used in \
-                             parameter list of the `impl Trait` type alias"
+                            "lifetime `{r}` is mentioned in hidden type of type alias impl \
+                            trait, but is not declared in its generic args"
                         ),
                     )
-                    .emit();
+                }
+                .emit_unless(self.do_not_error);
 
-                ty::Region::new_error(self.cx(), e)
+                ty::Region::new_error(self.cx(), guar)
             }
         }
     }
@@ -169,27 +178,33 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
 
             ty::Param(param) => {
                 // Look it up in the generic parameters list.
-                match self.map.get(&ty.into()).map(|k| k.unpack()) {
+                match self.mapping.get(&ty.into()).map(|k| k.unpack()) {
                     // Found it in the generic parameters list; replace with the parameter from the
                     // opaque type.
                     Some(GenericArgKind::Type(t1)) => t1,
                     Some(u) => panic!("type mapped to unexpected kind: {u:?}"),
                     None => {
-                        debug!(?param, ?self.map);
-                        if !self.ignore_errors {
-                            self.tcx
-                                .dcx()
-                                .struct_span_err(
-                                    self.span,
-                                    format!(
-                                        "type parameter `{ty}` is part of concrete type but not \
-                                          used in parameter list for the `impl Trait` type alias"
-                                    ),
-                                )
-                                .emit();
+                        debug!(?param, ?self.mapping);
+                        let guar = if self.uncaptured_args.contains(&ty.into()) {
+                            // FIXME(precise_capturing_of_types): Mention `use<>` list
+                            // and add an structured suggestion.
+                            self.tcx.dcx().struct_span_err(
+                                self.span,
+                                format!("hidden type mentions uncaptured type parameter `{ty}`"),
+                            )
+                        } else {
+                            self.tcx.dcx().struct_span_err(
+                                self.span,
+                                format!(
+                                    "type parameter `{ty}` is mentioned in hidden type of \
+                                    type alias impl trait, but is not declared in its generic \
+                                    args"
+                                ),
+                            )
                         }
+                        .emit_unless(self.ignore_errors);
 
-                        Ty::new_misc_error(self.tcx)
+                        Ty::new_error(self.tcx, guar)
                     }
                 }
             }
@@ -204,20 +219,30 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
         match ct.kind() {
             ty::ConstKind::Param(..) => {
                 // Look it up in the generic parameters list.
-                match self.map.get(&ct.into()).map(|k| k.unpack()) {
+                match self.mapping.get(&ct.into()).map(|k| k.unpack()) {
                     // Found it in the generic parameters list, replace with the parameter from the
                     // opaque type.
                     Some(GenericArgKind::Const(c1)) => c1,
                     Some(u) => panic!("const mapped to unexpected kind: {u:?}"),
                     None => {
-                        let guar = self
-                            .tcx
-                            .dcx()
-                            .create_err(ConstNotUsedTraitAlias {
-                                ct: ct.to_string(),
-                                span: self.span,
-                            })
-                            .emit_unless(self.ignore_errors);
+                        let guar = if self.uncaptured_args.contains(&ct.into()) {
+                            // FIXME(precise_capturing_of_types): Mention `use<>` list
+                            // and add an structured suggestion.
+                            self.tcx.dcx().struct_span_err(
+                                self.span,
+                                format!("hidden type mentions uncaptured const parameter `{ct}`"),
+                            )
+                        } else {
+                            self.tcx.dcx().struct_span_err(
+                                self.span,
+                                format!(
+                                    "const parameter `{ct}` is mentioned in hidden type of \
+                                    type alias impl trait, but is not declared in its generic \
+                                    args"
+                                ),
+                            )
+                        }
+                        .emit_unless(self.ignore_errors);
 
                         ty::Const::new_error(self.tcx, guar)
                     }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8a8bec35d8194..cb68469091569 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1570,6 +1570,7 @@ symbols! {
         pre_dash_lto: "pre-lto",
         precise_capturing,
         precise_capturing_in_traits,
+        precise_capturing_of_types,
         precise_pointer_size_matching,
         pref_align_of,
         prefetch_read_data,
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index d4134bdf3a782..e0ea4397edd21 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -575,6 +575,8 @@ pub trait Features<I: Interner>: Copy {
     fn coroutine_clone(self) -> bool;
 
     fn associated_const_equality(self) -> bool;
+
+    fn precise_capturing_of_types(self) -> bool;
 }
 
 pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs
index 416d355fd03bd..645159e6c6144 100644
--- a/compiler/rustc_type_ir/src/opaque_ty.rs
+++ b/compiler/rustc_type_ir/src/opaque_ty.rs
@@ -20,9 +20,11 @@ pub struct OpaqueTypeKey<I: Interner> {
 impl<I: Interner> OpaqueTypeKey<I> {
     pub fn iter_captured_args(self, cx: I) -> impl Iterator<Item = (usize, I::GenericArg)> {
         let variances = cx.variances_of(self.def_id.into());
+        let precise_capturing_of_types = cx.features().precise_capturing_of_types();
         std::iter::zip(self.args.iter(), variances.iter()).enumerate().filter_map(
-            |(i, (arg, v))| match (arg.kind(), v) {
+            move |(i, (arg, v))| match (arg.kind(), v) {
                 (_, ty::Invariant) => Some((i, arg)),
+                (_, ty::Bivariant) if precise_capturing_of_types => None,
                 (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None,
                 _ => panic!("unexpected opaque type arg variance"),
             },
diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.rs b/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.rs
new file mode 100644
index 0000000000000..5c0a1e3c8292b
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.rs
@@ -0,0 +1,4 @@
+fn foo<T>() -> impl Sized + use<> {}
+//~^ ERROR `impl Trait` must mention all type parameters in scope in `use<...>`
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.stderr b/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.stderr
new file mode 100644
index 0000000000000..7a04a777f3d59
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.stderr
@@ -0,0 +1,12 @@
+error: `impl Trait` must mention all type parameters in scope in `use<...>`
+  --> $DIR/feature-gate-precise-capturing-of-types.rs:1:16
+   |
+LL | fn foo<T>() -> impl Sized + use<> {}
+   |        -       ^^^^^^^^^^^^^^^^^^
+   |        |
+   |        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
+
diff --git a/tests/ui/impl-trait/issue-55872-2.rs b/tests/ui/impl-trait/issue-55872-2.rs
index caca5c69a4aea..0fb00133163c8 100644
--- a/tests/ui/impl-trait/issue-55872-2.rs
+++ b/tests/ui/impl-trait/issue-55872-2.rs
@@ -12,8 +12,8 @@ impl<S> Bar for S {
     type E = impl std::marker::Send;
     fn foo<T>() -> Self::E {
         async {}
-        //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
-        //~| ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+        //~^ ERROR type parameter `T` is mentioned
+        //~| ERROR type parameter `T` is mentioned
     }
 }
 
diff --git a/tests/ui/impl-trait/issue-55872-2.stderr b/tests/ui/impl-trait/issue-55872-2.stderr
index b5b7f293a40b2..12d0a4493e32f 100644
--- a/tests/ui/impl-trait/issue-55872-2.stderr
+++ b/tests/ui/impl-trait/issue-55872-2.stderr
@@ -1,10 +1,10 @@
-error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+error: type parameter `T` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args
   --> $DIR/issue-55872-2.rs:14:9
    |
 LL |         async {}
    |         ^^^^^^^^
 
-error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+error: type parameter `T` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args
   --> $DIR/issue-55872-2.rs:14:9
    |
 LL |         async {}
diff --git a/tests/ui/impl-trait/issue-55872.rs b/tests/ui/impl-trait/issue-55872.rs
index 10850f0a9335e..d8311ff58a036 100644
--- a/tests/ui/impl-trait/issue-55872.rs
+++ b/tests/ui/impl-trait/issue-55872.rs
@@ -11,7 +11,7 @@ impl<S> Bar for S {
 
     fn foo<T>() -> Self::E {
         || ()
-        //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+        //~^ ERROR type parameter `T` is mentioned
     }
 }
 
diff --git a/tests/ui/impl-trait/issue-55872.stderr b/tests/ui/impl-trait/issue-55872.stderr
index 4ff8527bbe9d1..12e8e356b3875 100644
--- a/tests/ui/impl-trait/issue-55872.stderr
+++ b/tests/ui/impl-trait/issue-55872.stderr
@@ -1,4 +1,4 @@
-error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+error: type parameter `T` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args
   --> $DIR/issue-55872.rs:13:9
    |
 LL |         || ()
diff --git a/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.rs b/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.rs
new file mode 100644
index 0000000000000..36d4f952ce901
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.rs
@@ -0,0 +1,11 @@
+//@ check-pass
+
+#![feature(precise_capturing_of_types)]
+//~^ WARN the feature `precise_capturing_of_types` is incomplete
+
+fn uncaptured<T>() -> impl Sized + use<> {}
+
+fn main() {
+    let mut x = uncaptured::<i32>();
+    x = uncaptured::<u32>();
+}
diff --git a/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.stderr b/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.stderr
new file mode 100644
index 0000000000000..10a2e5c2122e2
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.stderr
@@ -0,0 +1,11 @@
+warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/of-types-eq-uncaptured.rs:3:12
+   |
+LL | #![feature(precise_capturing_of_types)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #130043 <https://github.com/rust-lang/rust/issues/130043> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.rs b/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.rs
new file mode 100644
index 0000000000000..bc739c6ec0cd5
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.rs
@@ -0,0 +1,9 @@
+#![feature(precise_capturing_of_types)]
+//~^ WARN the feature `precise_capturing_of_types` is incomplete
+
+fn foo<T>(x: T) -> impl Sized + use<> {
+    x
+    //~^ ERROR hidden type mentions uncaptured type parameter `T`
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.stderr b/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.stderr
new file mode 100644
index 0000000000000..42972423b66d8
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.stderr
@@ -0,0 +1,17 @@
+warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/of-types-hidden-capture.rs:1:12
+   |
+LL | #![feature(precise_capturing_of_types)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #130043 <https://github.com/rust-lang/rust/issues/130043> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: hidden type mentions uncaptured type parameter `T`
+  --> $DIR/of-types-hidden-capture.rs:5:5
+   |
+LL |     x
+   |     ^
+
+error: aborting due to 1 previous error; 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/of-types-outlives.rs b/tests/ui/impl-trait/precise-capturing/of-types-outlives.rs
new file mode 100644
index 0000000000000..44e0ac0a41c38
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/of-types-outlives.rs
@@ -0,0 +1,14 @@
+//@ check-pass
+
+#![feature(precise_capturing_of_types)]
+//~^ WARN the feature `precise_capturing_of_types` is incomplete
+
+fn uncaptured<T>() -> impl Sized + use<> {}
+
+fn outlives_static<T: 'static>(_: T) {}
+
+fn foo<'a>() {
+    outlives_static(uncaptured::<&'a ()>());
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/of-types-outlives.stderr b/tests/ui/impl-trait/precise-capturing/of-types-outlives.stderr
new file mode 100644
index 0000000000000..e3ee0d93bb8a8
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/of-types-outlives.stderr
@@ -0,0 +1,11 @@
+warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/of-types-outlives.rs:3:12
+   |
+LL | #![feature(precise_capturing_of_types)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #130043 <https://github.com/rust-lang/rust/issues/130043> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/of-types.rs b/tests/ui/impl-trait/precise-capturing/of-types.rs
new file mode 100644
index 0000000000000..44707c2e3a97b
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/of-types.rs
@@ -0,0 +1,18 @@
+//@ check-pass
+
+#![feature(precise_capturing_of_types)]
+//~^ WARN the feature `precise_capturing_of_types` is incomplete
+
+use std::fmt::Display;
+use std::ops::Deref;
+
+fn len<T: Deref<Target: Deref<Target = [u8]>>>(x: T) -> impl Display + use<> {
+    x.len()
+}
+
+fn main() {
+    let x = vec![1, 2, 3];
+    let len = len(&x);
+    drop(x);
+    println!("len = {len}");
+}
diff --git a/tests/ui/impl-trait/precise-capturing/of-types.stderr b/tests/ui/impl-trait/precise-capturing/of-types.stderr
new file mode 100644
index 0000000000000..045054a0f6f3c
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/of-types.stderr
@@ -0,0 +1,11 @@
+warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/of-types.rs:3:12
+   |
+LL | #![feature(precise_capturing_of_types)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #130043 <https://github.com/rust-lang/rust/issues/130043> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.stderr b/tests/ui/impl-trait/precise-capturing/rpitit.stderr
index 498eae54a1c68..f204ab83c1fda 100644
--- a/tests/ui/impl-trait/precise-capturing/rpitit.stderr
+++ b/tests/ui/impl-trait/precise-capturing/rpitit.stderr
@@ -2,7 +2,7 @@ error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use
   --> $DIR/rpitit.rs:11:19
    |
 LL | trait TraitLt<'a: 'a> {
-   |               -- all lifetime parameters originating from a trait are captured implicitly
+   |               -- all parameters originating from a trait are captured implicitly
 LL |     fn hello() -> impl Sized + use<Self>;
    |                   ^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/tests/ui/type-alias-impl-trait/generic_not_used.rs b/tests/ui/type-alias-impl-trait/generic_not_used.rs
index 6042cdd30a940..ba715af8f5ed3 100644
--- a/tests/ui/type-alias-impl-trait/generic_not_used.rs
+++ b/tests/ui/type-alias-impl-trait/generic_not_used.rs
@@ -8,5 +8,5 @@ type WrongGeneric<T: 'static> = impl 'static;
 #[define_opaque(WrongGeneric)]
 fn wrong_generic<U: 'static, V: 'static>(_: U, v: V) -> WrongGeneric<U> {
     v
-    //~^ ERROR type parameter `V` is part of concrete type but not used in parameter list
+    //~^ ERROR type parameter `V` is mentioned
 }
diff --git a/tests/ui/type-alias-impl-trait/generic_not_used.stderr b/tests/ui/type-alias-impl-trait/generic_not_used.stderr
index 5fe2fefcecfd0..526b5b09a6421 100644
--- a/tests/ui/type-alias-impl-trait/generic_not_used.stderr
+++ b/tests/ui/type-alias-impl-trait/generic_not_used.stderr
@@ -4,7 +4,7 @@ error: at least one trait must be specified
 LL | type WrongGeneric<T: 'static> = impl 'static;
    |                                 ^^^^^^^^^^^^
 
-error: type parameter `V` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+error: type parameter `V` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args
   --> $DIR/generic_not_used.rs:10:5
    |
 LL |     v
diff --git a/tests/ui/type-alias-impl-trait/issue-53598.rs b/tests/ui/type-alias-impl-trait/issue-53598.rs
index e3e2787b66bb4..2d9605e7e8bc9 100644
--- a/tests/ui/type-alias-impl-trait/issue-53598.rs
+++ b/tests/ui/type-alias-impl-trait/issue-53598.rs
@@ -18,7 +18,7 @@ impl Foo for S2 {
 
     fn foo<T: Debug>(_: T) -> Self::Item {
         S::<T>(Default::default())
-        //~^ Error type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+        //~^ Error type parameter `T` is mentioned
     }
 }
 
diff --git a/tests/ui/type-alias-impl-trait/issue-53598.stderr b/tests/ui/type-alias-impl-trait/issue-53598.stderr
index a31aabedba542..0ddae22ef4cdb 100644
--- a/tests/ui/type-alias-impl-trait/issue-53598.stderr
+++ b/tests/ui/type-alias-impl-trait/issue-53598.stderr
@@ -1,4 +1,4 @@
-error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+error: type parameter `T` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args
   --> $DIR/issue-53598.rs:20:9
    |
 LL |         S::<T>(Default::default())