about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-10-31 03:36:06 +0000
committerbors <bors@rust-lang.org>2024-10-31 03:36:06 +0000
commitc8b83785dccdd22345d193fed7431ce44074007b (patch)
tree3a35f3900814528be97d735217174f04e463ad66
parent75eff9a5749411ba5a0b37cc3299116c4e263075 (diff)
parentc1457798db03ffb7f207eaf208d8d4d77ba01563 (diff)
Auto merge of #131186 - compiler-errors:precise-capturing-borrowck, r=estebank
Try to point out when edition 2024 lifetime capture rules cause borrowck issues

Lifetime capture rules in 2024 are modified to capture more lifetimes, which sometimes lead to some non-local borrowck errors. This PR attempts to link these back together with a useful note pointing out the capture rule changes.

This is not a blocking concern, but I'd appreciate feedback (though, again, I'd like to stress that I don't want to block this PR on this): I'm worried about this note drowning in the sea of other diagnostics that borrowck emits. I was tempted to change the level of the note to `.span_warn` just so it would show up in a different color. Thoughts?

Fixes #130545

Opening as a draft first since it's stacked on #131183.
r? `@ghost`
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs8
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs1
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs224
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs2
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/mod.rs5
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs5
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs6
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs2
-rw-r--r--compiler/rustc_middle/src/query/erase.rs1
-rw-r--r--compiler/rustc_middle/src/query/mod.rs5
-rw-r--r--compiler/rustc_middle/src/ty/context.rs10
-rw-r--r--compiler/rustc_middle/src/ty/parameterized.rs1
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs5
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs2
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs4
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs2
-rw-r--r--tests/ui/impl-trait/precise-capturing/auxiliary/foreign.rs6
-rw-r--r--tests/ui/impl-trait/precise-capturing/foreign-2021.rs15
-rw-r--r--tests/ui/impl-trait/precise-capturing/foreign-2021.stderr26
-rw-r--r--tests/ui/impl-trait/precise-capturing/migration-note.rs190
-rw-r--r--tests/ui/impl-trait/precise-capturing/migration-note.stderr284
27 files changed, 807 insertions, 45 deletions
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index e1ee3d0af49..5a0e9e8aec0 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -272,7 +272,7 @@ enum ImplTraitContext {
     /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually
     /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
     ///
-    OpaqueTy { origin: hir::OpaqueTyOrigin },
+    OpaqueTy { origin: hir::OpaqueTyOrigin<LocalDefId> },
     /// `impl Trait` is unstably accepted in this position.
     FeatureGated(ImplTraitPosition, Symbol),
     /// `impl Trait` is not accepted in this position.
@@ -1416,7 +1416,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn lower_opaque_impl_trait(
         &mut self,
         span: Span,
-        origin: hir::OpaqueTyOrigin,
+        origin: hir::OpaqueTyOrigin<LocalDefId>,
         opaque_ty_node_id: NodeId,
         bounds: &GenericBounds,
         itctx: ImplTraitContext,
@@ -1458,7 +1458,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn lower_opaque_inner(
         &mut self,
         opaque_ty_node_id: NodeId,
-        origin: hir::OpaqueTyOrigin,
+        origin: hir::OpaqueTyOrigin<LocalDefId>,
         opaque_ty_span: Span,
         lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
     ) -> hir::TyKind<'hir> {
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index c687be69b1a..1bfb54a7bb7 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1489,6 +1489,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             &borrow_msg,
             &value_msg,
         );
+        self.note_due_to_edition_2024_opaque_capture_rules(borrow, &mut err);
 
         borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
 
@@ -1561,6 +1562,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             borrow_span,
             &self.describe_any_place(borrow.borrowed_place.as_ref()),
         );
+        self.note_due_to_edition_2024_opaque_capture_rules(borrow, &mut err);
+
         borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
             use crate::session_diagnostics::CaptureVarCause::*;
             let place = &borrow.borrowed_place;
@@ -1820,6 +1823,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 unreachable!()
             }
         };
+        self.note_due_to_edition_2024_opaque_capture_rules(issued_borrow, &mut err);
 
         if issued_spans == borrow_spans {
             borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| {
@@ -2860,7 +2864,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         debug!(?place_desc, ?explanation);
 
-        let err = match (place_desc, explanation) {
+        let mut err = match (place_desc, explanation) {
             // If the outlives constraint comes from inside the closure,
             // for example:
             //
@@ -2939,6 +2943,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 explanation,
             ),
         };
+        self.note_due_to_edition_2024_opaque_capture_rules(borrow, &mut err);
 
         self.buffer_error(err);
     }
@@ -3777,6 +3782,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
 
         let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
+        self.note_due_to_edition_2024_opaque_capture_rules(loan, &mut err);
 
         loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
             use crate::session_diagnostics::CaptureVarCause::*;
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 801c7af2de7..3b600716036 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -48,6 +48,7 @@ mod conflict_errors;
 mod explain_borrow;
 mod move_errors;
 mod mutability_errors;
+mod opaque_suggestions;
 mod region_errors;
 
 pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};
diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs
new file mode 100644
index 00000000000..bfd7e83501c
--- /dev/null
+++ b/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs
@@ -0,0 +1,224 @@
+#![allow(rustc::diagnostic_outside_of_impl)]
+#![allow(rustc::untranslatable_diagnostic)]
+
+use std::ops::ControlFlow;
+
+use either::Either;
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{Applicability, Diag};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::mir::{self, ConstraintCategory, Location};
+use rustc_middle::ty::{
+    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+};
+use rustc_span::Symbol;
+
+use crate::MirBorrowckCtxt;
+use crate::borrow_set::BorrowData;
+use crate::consumers::RegionInferenceContext;
+use crate::type_check::Locations;
+
+impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
+    /// Try to note when an opaque is involved in a borrowck error and that
+    /// opaque captures lifetimes due to edition 2024.
+    // FIXME: This code is otherwise somewhat general, and could easily be adapted
+    // to explain why other things overcapture... like async fn and RPITITs.
+    pub(crate) fn note_due_to_edition_2024_opaque_capture_rules(
+        &self,
+        borrow: &BorrowData<'tcx>,
+        diag: &mut Diag<'_>,
+    ) {
+        // We look at all the locals. Why locals? Because it's the best thing
+        // I could think of that's correlated with the *instantiated* higer-ranked
+        // binder for calls, since we don't really store those anywhere else.
+        for ty in self.body.local_decls.iter().map(|local| local.ty) {
+            if !ty.has_opaque_types() {
+                continue;
+            }
+
+            let tcx = self.infcx.tcx;
+            let ControlFlow::Break((opaque_def_id, offending_region_idx, location)) = ty
+                .visit_with(&mut FindOpaqueRegion {
+                    regioncx: &self.regioncx,
+                    tcx,
+                    borrow_region: borrow.region,
+                })
+            else {
+                continue;
+            };
+
+            // If an opaque explicitly captures a lifetime, then no need to point it out.
+            // FIXME: We should be using a better heuristic for `use<>`.
+            if tcx.rendered_precise_capturing_args(opaque_def_id).is_some() {
+                continue;
+            }
+
+            // If one of the opaque's bounds mentions the region, then no need to
+            // point it out, since it would've been captured on edition 2021 as well.
+            //
+            // Also, while we're at it, collect all the lifetimes that the opaque
+            // *does* mention. We'll use that for the `+ use<'a>` suggestion below.
+            let mut visitor = CheckExplicitRegionMentionAndCollectGenerics {
+                tcx,
+                offending_region_idx,
+                seen_opaques: [opaque_def_id].into_iter().collect(),
+                seen_lifetimes: Default::default(),
+            };
+            if tcx
+                .explicit_item_bounds(opaque_def_id)
+                .skip_binder()
+                .visit_with(&mut visitor)
+                .is_break()
+            {
+                continue;
+            }
+
+            // If we successfully located a terminator, then point it out
+            // and provide a suggestion if it's local.
+            match self.body.stmt_at(location) {
+                Either::Right(mir::Terminator { source_info, .. }) => {
+                    diag.span_note(
+                        source_info.span,
+                        "this call may capture more lifetimes than intended, \
+                        because Rust 2024 has adjusted the `impl Trait` lifetime capture rules",
+                    );
+                    let mut seen_generics: Vec<_> =
+                        visitor.seen_lifetimes.iter().map(ToString::to_string).collect();
+                    // Capture all in-scope ty/const params.
+                    seen_generics.extend(
+                        ty::GenericArgs::identity_for_item(tcx, opaque_def_id)
+                            .iter()
+                            .filter(|arg| {
+                                matches!(
+                                    arg.unpack(),
+                                    ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_)
+                                )
+                            })
+                            .map(|arg| arg.to_string()),
+                    );
+                    if opaque_def_id.is_local() {
+                        diag.span_suggestion_verbose(
+                            tcx.def_span(opaque_def_id).shrink_to_hi(),
+                            "add a precise capturing bound to avoid overcapturing",
+                            format!(" + use<{}>", seen_generics.join(", ")),
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        diag.span_help(
+                            tcx.def_span(opaque_def_id),
+                            format!(
+                                "if you can modify this crate, add a precise \
+                                capturing bound to avoid overcapturing: `+ use<{}>`",
+                                seen_generics.join(", ")
+                            ),
+                        );
+                    }
+                    return;
+                }
+                Either::Left(_) => {}
+            }
+        }
+    }
+}
+
+/// This visitor contains the bulk of the logic for this lint.
+struct FindOpaqueRegion<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    regioncx: &'a RegionInferenceContext<'tcx>,
+    borrow_region: ty::RegionVid,
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
+    type Result = ControlFlow<(DefId, usize, Location), ()>;
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+        // If we find an opaque in a local ty, then for each of its captured regions,
+        // try to find a path between that captured regions and our borrow region...
+        if let ty::Alias(ty::Opaque, opaque) = *ty.kind()
+            && let hir::OpaqueTyOrigin::FnReturn { parent, in_trait_or_impl: None } =
+                self.tcx.opaque_ty_origin(opaque.def_id)
+        {
+            let variances = self.tcx.variances_of(opaque.def_id);
+            for (idx, (arg, variance)) in std::iter::zip(opaque.args, variances).enumerate() {
+                // Skip uncaptured args.
+                if *variance == ty::Bivariant {
+                    continue;
+                }
+                // We only care about regions.
+                let Some(opaque_region) = arg.as_region() else {
+                    continue;
+                };
+                // Don't try to convert a late-bound region, which shouldn't exist anyways (yet).
+                if opaque_region.is_bound() {
+                    continue;
+                }
+                let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
+
+                // Find a path between the borrow region and our opaque capture.
+                if let Some((path, _)) =
+                    self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
+                        r == opaque_region_vid
+                    })
+                {
+                    for constraint in path {
+                        // If we find a call in this path, then check if it defines the opaque.
+                        if let ConstraintCategory::CallArgument(Some(call_ty)) = constraint.category
+                            && let ty::FnDef(call_def_id, _) = *call_ty.kind()
+                            // This function defines the opaque :D
+                            && call_def_id == parent
+                            && let Locations::Single(location) = constraint.locations
+                        {
+                            return ControlFlow::Break((opaque.def_id, idx, location));
+                        }
+                    }
+                }
+            }
+        }
+
+        ty.super_visit_with(self)
+    }
+}
+
+struct CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    offending_region_idx: usize,
+    seen_opaques: FxIndexSet<DefId>,
+    seen_lifetimes: FxIndexSet<Symbol>,
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
+    type Result = ControlFlow<(), ()>;
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+        match *ty.kind() {
+            ty::Alias(ty::Opaque, opaque) => {
+                if self.seen_opaques.insert(opaque.def_id) {
+                    for (bound, _) in self
+                        .tcx
+                        .explicit_item_bounds(opaque.def_id)
+                        .iter_instantiated_copied(self.tcx, opaque.args)
+                    {
+                        bound.visit_with(self)?;
+                    }
+                }
+                ControlFlow::Continue(())
+            }
+            _ => ty.super_visit_with(self),
+        }
+    }
+
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
+        match r.kind() {
+            ty::ReEarlyParam(param) => {
+                if param.index as usize == self.offending_region_idx {
+                    ControlFlow::Break(())
+                } else {
+                    self.seen_lifetimes.insert(param.name);
+                    ControlFlow::Continue(())
+                }
+            }
+            _ => ControlFlow::Continue(()),
+        }
+    }
+}
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 741dac9e763..3a2f5c35c72 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -502,7 +502,7 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> {
         }
 
         let &Self { tcx, def_id, .. } = self;
-        let origin = tcx.opaque_type_origin(def_id);
+        let origin = tcx.local_opaque_ty_origin(def_id);
         let parent = match origin {
             hir::OpaqueTyOrigin::FnReturn { parent, .. }
             | hir::OpaqueTyOrigin::AsyncFn { parent, .. }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 12b01266a93..fa76f8652ea 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2746,7 +2746,7 @@ pub struct OpaqueTy<'hir> {
     pub hir_id: HirId,
     pub def_id: LocalDefId,
     pub bounds: GenericBounds<'hir>,
-    pub origin: OpaqueTyOrigin,
+    pub origin: OpaqueTyOrigin<LocalDefId>,
     pub span: Span,
 }
 
@@ -2784,33 +2784,35 @@ pub struct PreciseCapturingNonLifetimeArg {
     pub res: Res,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
 pub enum RpitContext {
     Trait,
     TraitImpl,
 }
 
 /// From whence the opaque type came.
-#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)]
-pub enum OpaqueTyOrigin {
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
+pub enum OpaqueTyOrigin<D> {
     /// `-> impl Trait`
     FnReturn {
         /// The defining function.
-        parent: LocalDefId,
+        parent: D,
         // Whether this is an RPITIT (return position impl trait in trait)
         in_trait_or_impl: Option<RpitContext>,
     },
     /// `async fn`
     AsyncFn {
         /// The defining function.
-        parent: LocalDefId,
+        parent: D,
         // Whether this is an AFIT (async fn in trait)
         in_trait_or_impl: Option<RpitContext>,
     },
     /// type aliases: `type Foo = impl Trait;`
     TyAlias {
         /// The type alias or associated type parent of the TAIT/ATPIT
-        parent: LocalDefId,
+        parent: D,
         /// associated types in impl blocks for traits.
         in_assoc_ty: bool,
     },
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 79e84e507bc..476814c261e 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -268,7 +268,7 @@ fn check_opaque_meets_bounds<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: LocalDefId,
     span: Span,
-    origin: &hir::OpaqueTyOrigin,
+    origin: &hir::OpaqueTyOrigin<LocalDefId>,
 ) -> Result<(), ErrorGuaranteed> {
     let defining_use_anchor = match *origin {
         hir::OpaqueTyOrigin::FnReturn { parent, .. }
@@ -677,7 +677,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
         DefKind::OpaqueTy => {
             check_opaque_precise_captures(tcx, def_id);
 
-            let origin = tcx.opaque_type_origin(def_id);
+            let origin = tcx.local_opaque_ty_origin(def_id);
             if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. }
             | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = origin
                 && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id)
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index c41117d213f..3f6198dbd31 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -86,7 +86,7 @@ pub fn provide(providers: &mut Providers) {
         impl_trait_header,
         coroutine_kind,
         coroutine_for_closure,
-        is_type_alias_impl_trait,
+        opaque_ty_origin,
         rendered_precise_capturing_args,
         ..*providers
     };
@@ -1759,9 +1759,18 @@ fn coroutine_for_closure(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DefId {
     def_id.to_def_id()
 }
 
-fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
-    let opaque = tcx.hir().expect_opaque_ty(def_id);
-    matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias { .. })
+fn opaque_ty_origin<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> hir::OpaqueTyOrigin<DefId> {
+    match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
+        hir::OpaqueTyOrigin::FnReturn { parent, in_trait_or_impl } => {
+            hir::OpaqueTyOrigin::FnReturn { parent: parent.to_def_id(), in_trait_or_impl }
+        }
+        hir::OpaqueTyOrigin::AsyncFn { parent, in_trait_or_impl } => {
+            hir::OpaqueTyOrigin::AsyncFn { parent: parent.to_def_id(), in_trait_or_impl }
+        }
+        hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty } => {
+            hir::OpaqueTyOrigin::TyAlias { parent: parent.to_def_id(), in_assoc_ty }
+        }
+    }
 }
 
 fn rendered_precise_capturing_args<'tcx>(
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 2bd8a5223aa..d70e4d8345a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -20,6 +20,7 @@ pub mod errors;
 pub mod generics;
 mod lint;
 
+use std::assert_matches::assert_matches;
 use std::slice;
 
 use rustc_ast::TraitObjectSyntax;
@@ -1811,7 +1812,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         match path.res {
             Res::Def(DefKind::OpaqueTy, did) => {
                 // Check for desugared `impl Trait`.
-                assert!(tcx.is_type_alias_impl_trait(did));
+                assert_matches!(tcx.opaque_ty_origin(did), hir::OpaqueTyOrigin::TyAlias { .. });
                 let item_segment = path.segments.split_last().unwrap();
                 let _ = self
                     .prohibit_generic_args(item_segment.1.iter(), GenericsArgsErrExtend::OpaqueTy);
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 3372cae7a51..1774772b50b 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -601,7 +601,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => return None,
         };
         let hir::OpaqueTyOrigin::FnReturn { parent: parent_def_id, .. } =
-            self.tcx.opaque_type_origin(def_id)
+            self.tcx.local_opaque_ty_origin(def_id)
         else {
             return None;
         };
diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
index 498d25aea64..2bc006c37da 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
@@ -155,7 +155,10 @@ impl<'tcx> InferCtxt<'tcx> {
                     // however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`, where
                     // it is of no concern, so we only check for TAITs.
                     if self.can_define_opaque_ty(b_def_id)
-                        && self.tcx.is_type_alias_impl_trait(b_def_id)
+                        && matches!(
+                            self.tcx.opaque_ty_origin(b_def_id),
+                            hir::OpaqueTyOrigin::TyAlias { .. }
+                        )
                     {
                         self.dcx().emit_err(OpaqueHiddenTypeDiag {
                             span,
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 926eb4f6210..f06f2fdc5e5 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -316,10 +316,7 @@ provide! { tcx, def_id, other, cdata,
             })
             .unwrap_or_default()
     }
-    is_type_alias_impl_trait => {
-        debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy);
-        cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index)
-    }
+    opaque_ty_origin => { table }
     assumed_wf_types_for_rpitit => { table }
     collect_return_position_impl_trait_in_trait_tys => {
         Ok(cdata
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index b4b48570453..7277039bb7d 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1188,7 +1188,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
         | DefKind::SyntheticCoroutineBody => true,
 
         DefKind::OpaqueTy => {
-            let origin = tcx.opaque_type_origin(def_id);
+            let origin = tcx.local_opaque_ty_origin(def_id);
             if let hir::OpaqueTyOrigin::FnReturn { parent, .. }
             | hir::OpaqueTyOrigin::AsyncFn { parent, .. } = origin
                 && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(parent)
@@ -1530,9 +1530,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             if let DefKind::OpaqueTy = def_kind {
                 self.encode_explicit_item_bounds(def_id);
                 self.encode_explicit_item_super_predicates(def_id);
-                self.tables
-                    .is_type_alias_impl_trait
-                    .set(def_id.index, self.tcx.is_type_alias_impl_trait(def_id));
+                record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id));
                 self.encode_precise_capturing_args(def_id);
             }
             if tcx.impl_method_has_trait_impl_trait_tys(def_id)
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 927c9fa47a1..ab878760c00 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -378,7 +378,6 @@ define_tables! {
 - defaulted:
     intrinsic: Table<DefIndex, Option<LazyValue<ty::IntrinsicDef>>>,
     is_macro_rules: Table<DefIndex, bool>,
-    is_type_alias_impl_trait: Table<DefIndex, bool>,
     type_alias_is_lazy: Table<DefIndex, bool>,
     attr_flags: Table<DefIndex, AttrFlags>,
     // The u64 is the crate-local part of the DefPathHash. All hashes in this crate have the same
@@ -468,6 +467,7 @@ define_tables! {
     doc_link_resolutions: Table<DefIndex, LazyValue<DocLinkResMap>>,
     doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>,
     assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>,
+    opaque_ty_origin: Table<DefIndex, LazyValue<hir::OpaqueTyOrigin<DefId>>>,
 }
 
 #[derive(TyEncodable, TyDecodable)]
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 5f8427bd707..8cfc7f1e33e 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -280,6 +280,7 @@ trivial! {
     rustc_hir::IsAsync,
     rustc_hir::ItemLocalId,
     rustc_hir::LangItem,
+    rustc_hir::OpaqueTyOrigin<rustc_hir::def_id::DefId>,
     rustc_hir::OwnerId,
     rustc_hir::Upvar,
     rustc_index::bit_set::FiniteBitSet<u32>,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index d7f58963101..088b5d4ec96 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -260,11 +260,10 @@ rustc_queries! {
         separate_provide_extern
     }
 
-    query is_type_alias_impl_trait(key: DefId) -> bool
+    query opaque_ty_origin(key: DefId) -> hir::OpaqueTyOrigin<DefId>
     {
-        desc { "determine whether the opaque is a type-alias impl trait" }
+        desc { "determine where the opaque originates from" }
         separate_provide_extern
-        feedable
     }
 
     query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::BitSet<u32>
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index ac36b940725..9abad6d1a68 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -55,7 +55,7 @@ use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
 pub use rustc_type_ir::lift::Lift;
 use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo, search_graph};
-use tracing::{debug, trace};
+use tracing::{debug, instrument};
 
 use crate::arena::Arena;
 use crate::dep_graph::{DepGraph, DepKindStruct};
@@ -2103,11 +2103,9 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// Returns the origin of the opaque type `def_id`.
-    #[track_caller]
-    pub fn opaque_type_origin(self, def_id: LocalDefId) -> hir::OpaqueTyOrigin {
-        let origin = self.hir().expect_opaque_ty(def_id).origin;
-        trace!("opaque_type_origin({def_id:?}) => {origin:?}");
-        origin
+    #[instrument(skip(self), level = "trace", ret)]
+    pub fn local_opaque_ty_origin(self, def_id: LocalDefId) -> hir::OpaqueTyOrigin<LocalDefId> {
+        self.hir().expect_opaque_ty(def_id).origin
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index 43bdce5b576..7c280bc8b49 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -94,6 +94,7 @@ trivially_parameterized_over_tcx! {
     rustc_hir::def_id::DefId,
     rustc_hir::def_id::DefIndex,
     rustc_hir::definitions::DefKey,
+    rustc_hir::OpaqueTyOrigin<rustc_hir::def_id::DefId>,
     rustc_index::bit_set::BitSet<u32>,
     rustc_index::bit_set::FiniteBitSet<u32>,
     rustc_session::cstore::ForeignModule,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
index 0cf7c43beb5..b97f3dc303b 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
@@ -384,7 +384,10 @@ impl<T> Trait<T> for X {
                                     | DefKind::AssocFn
                                     | DefKind::AssocConst
                             )
-                            && tcx.is_type_alias_impl_trait(opaque_ty.def_id)
+                            && matches!(
+                                tcx.opaque_ty_origin(opaque_ty.def_id),
+                                hir::OpaqueTyOrigin::TyAlias { .. }
+                            )
                             && !tcx
                                 .opaque_types_defined_by(body_owner_def_id.expect_local())
                                 .contains(&opaque_ty.def_id.expect_local())
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 612e92ea784..7aa558cfd3f 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -2648,7 +2648,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         def_id: DefId,
     ) -> ErrorGuaranteed {
-        let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
+        let name = match self.tcx.local_opaque_ty_origin(def_id.expect_local()) {
             hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } => {
                 "opaque type".to_string()
             }
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index 161bed9d3a6..3655fa5ae0e 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -244,7 +244,7 @@ fn associated_type_for_impl_trait_in_trait(
 ) -> LocalDefId {
     let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. }
     | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. }) =
-        tcx.opaque_type_origin(opaque_ty_def_id)
+        tcx.local_opaque_ty_origin(opaque_ty_def_id)
     else {
         bug!("expected opaque for {opaque_ty_def_id:?}");
     };
@@ -281,8 +281,6 @@ fn associated_type_for_impl_trait_in_trait(
     // Copy defaultness of the containing function.
     trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id));
 
-    trait_assoc_ty.is_type_alias_impl_trait(false);
-
     // There are no inferred outlives for the synthesized associated type.
     trait_assoc_ty.inferred_outlives_of(&[]);
 
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 5e2232ff47d..34f461aac58 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -139,7 +139,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
         }
 
         // TAITs outside their defining scopes are ignored.
-        let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
+        let origin = self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local());
         trace!(?origin);
         match origin {
             rustc_hir::OpaqueTyOrigin::FnReturn { .. }
diff --git a/tests/ui/impl-trait/precise-capturing/auxiliary/foreign.rs b/tests/ui/impl-trait/precise-capturing/auxiliary/foreign.rs
new file mode 100644
index 00000000000..49015bc48ba
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/auxiliary/foreign.rs
@@ -0,0 +1,6 @@
+//@ edition: 2024
+//@ compile-flags: -Zunstable-options
+
+use std::fmt::Display;
+
+pub fn hello(x: &Vec<i32>) -> impl Display { 0 }
diff --git a/tests/ui/impl-trait/precise-capturing/foreign-2021.rs b/tests/ui/impl-trait/precise-capturing/foreign-2021.rs
new file mode 100644
index 00000000000..aee412ff7f1
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/foreign-2021.rs
@@ -0,0 +1,15 @@
+//@ aux-build: foreign.rs
+
+extern crate foreign;
+
+fn main() {
+    let mut x = vec![];
+    let h = foreign::hello(&x);
+    //~^ NOTE this call may capture more lifetimes than intended
+    //~| NOTE immutable borrow occurs here
+    x.push(0);
+    //~^ ERROR cannot borrow `x` as mutable
+    //~| NOTE mutable borrow occurs here
+    println!("{h}");
+    //~^ NOTE immutable borrow later used here
+}
diff --git a/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr b/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
new file mode 100644
index 00000000000..2a17ef72912
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
@@ -0,0 +1,26 @@
+error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
+  --> $DIR/foreign-2021.rs:10:5
+   |
+LL |     let h = foreign::hello(&x);
+   |                            -- immutable borrow occurs here
+...
+LL |     x.push(0);
+   |     ^^^^^^^^^ mutable borrow occurs here
+...
+LL |     println!("{h}");
+   |               --- immutable borrow later used here
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/foreign-2021.rs:7:13
+   |
+LL |     let h = foreign::hello(&x);
+   |             ^^^^^^^^^^^^^^^^^^
+help: if you can modify this crate, add a precise capturing bound to avoid overcapturing: `+ use<>`
+  --> $DIR/auxiliary/foreign.rs:6:31
+   |
+LL | pub fn hello(x: &Vec<i32>) -> impl Display { 0 }
+   |                               ^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0502`.
diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.rs b/tests/ui/impl-trait/precise-capturing/migration-note.rs
new file mode 100644
index 00000000000..a5bade4ddc5
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/migration-note.rs
@@ -0,0 +1,190 @@
+//@ edition: 2024
+//@ compile-flags: -Zunstable-options
+
+use std::fmt::Display;
+
+fn display_len<T>(x: &Vec<T>) -> impl Display {
+    //~^ NOTE in this expansion of desugaring of `impl Trait`
+    //~| NOTE in this expansion of desugaring of `impl Trait`
+    //~| NOTE in this expansion of desugaring of `impl Trait`
+    //~| NOTE in this expansion of desugaring of `impl Trait`
+    //~| NOTE in this expansion of desugaring of `impl Trait`
+    x.len()
+}
+
+fn conflicting_borrow() {
+    let mut x = vec![];
+    let a = display_len(&x);
+    //~^ NOTE this call may capture more lifetimes than intended
+    //~| NOTE immutable borrow occurs here
+    x.push(1);
+    //~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
+    //~| NOTE mutable borrow occurs here
+    println!("{a}");
+    //~^ NOTE immutable borrow later used here
+}
+
+fn needs_static() {
+    let x = vec![1];
+    //~^ NOTE binding `x` declared here
+    let a = display_len(&x);
+    //~^ ERROR `x` does not live long enough
+    //~| NOTE this call may capture more lifetimes than intended
+    //~| NOTE argument requires that `x` is borrowed for `'static`
+    //~| NOTE borrowed value does not live long enoug
+
+    fn needs_static(_: impl Sized + 'static) {}
+    needs_static(a);
+}
+//~^ NOTE `x` dropped here while still borrowed
+
+fn is_moved() {
+    let x = vec![1];
+    //~^ NOTE binding `x` declared here
+    let a = display_len(&x);
+    //~^ NOTE this call may capture more lifetimes than intended
+    //~| NOTE borrow of `x` occurs here
+
+    fn mv(_: impl Sized) {}
+    mv(x);
+    //~^ ERROR cannot move out of `x` because it is borrowed
+    //~| NOTE move out of `x` occurs here
+}
+//~^ NOTE borrow might be used here, when `a` is dropped
+
+fn display_len_mut<T>(x: &mut Vec<T>) -> impl Display {
+    //~^ NOTE in this expansion of desugaring of `impl Trait`
+    //~| NOTE in this expansion of desugaring of `impl Trait`
+    //~| NOTE in this expansion of desugaring of `impl Trait`
+    x.len()
+}
+
+fn conflicting_borrow_mut() {
+    let mut x = vec![];
+    let a = display_len_mut(&mut x);
+    //~^ NOTE this call may capture more lifetimes than intended
+    //~| NOTE first mutable borrow occurs here
+    x.push(1);
+    //~^ ERROR cannot borrow `x` as mutable more than once
+    //~| NOTE second mutable borrow occurs here
+    println!("{a}");
+    //~^ NOTE first borrow later used here
+}
+
+fn needs_static_mut() {
+    let mut x = vec![1];
+    //~^ NOTE binding `x` declared here
+    let a = display_len_mut(&mut x);
+    //~^ ERROR `x` does not live long enough
+    //~| NOTE this call may capture more lifetimes than intended
+    //~| NOTE argument requires that `x` is borrowed for `'static`
+    //~| NOTE borrowed value does not live long enough
+
+    fn needs_static(_: impl Sized + 'static) {}
+    needs_static(a);
+}
+//~^ NOTE `x` dropped here while still borrowed
+
+fn is_move_mut() {
+    let mut x = vec![1];
+    //~^ NOTE binding `x` declared here
+    let a = display_len_mut(&mut x);
+    //~^ NOTE this call may capture more lifetimes than intended
+    //~| NOTE borrow of `x` occurs here
+
+    fn mv(_: impl Sized) {}
+    mv(x);
+    //~^ ERROR cannot move out of `x` because it is borrowed
+    //~| NOTE move out of `x` occurs here
+}
+//~^ NOTE borrow might be used here, when `a` is dropped
+
+struct S { f: i32 }
+
+fn display_field<T: Copy + Display>(t: &T) -> impl Display {
+    //~^ NOTE in this expansion of desugaring of `impl Trait`
+    //~| NOTE in this expansion of desugaring of `impl Trait`
+    //~| NOTE in this expansion of desugaring of `impl Trait`
+    *t
+}
+
+fn conflicting_borrow_field() {
+    let mut s = S { f: 0 };
+    let a = display_field(&s.f);
+    //~^ NOTE this call may capture more lifetimes than intended
+    //~| NOTE `s.f` is borrowed here
+    s.f = 1;
+    //~^ ERROR cannot assign to `s.f` because it is borrowed
+    //~| NOTE `s.f` is assigned to here but it was already borrowed
+    println!("{a}");
+    //~^ NOTE borrow later used here
+}
+
+fn display_field_mut<T: Copy + Display>(t: &mut T) -> impl Display {
+    *t
+}
+
+fn conflicting_borrow_field_mut() {
+    let mut s = S { f: 0 };
+    let a = display_field(&mut s.f);
+    //~^ NOTE this call may capture more lifetimes than intended
+    //~| NOTE `s.f` is borrowed here
+    s.f = 1;
+    //~^ ERROR cannot assign to `s.f` because it is borrowed
+    //~| NOTE `s.f` is assigned to here but it was already borrowed
+    println!("{a}");
+    //~^ NOTE borrow later used here
+}
+
+fn field_move() {
+    let mut s = S { f: 0 };
+    let a = display_field(&mut s.f);
+    //~^ NOTE this call may capture more lifetimes than intended
+    //~| NOTE `s.f` is borrowed here
+    s.f;
+    //~^ ERROR cannot use `s.f` because it was mutably borrowed
+    //~| NOTE use of borrowed `s.f`
+    println!("{a}");
+    //~^ NOTE borrow later used here
+}
+
+struct Z {
+    f: Vec<i32>,
+}
+
+fn live_long() {
+    let x;
+    {
+        let z = Z { f: vec![1] };
+        //~^ NOTE binding `z` declared here
+        x = display_len(&z.f);
+        //~^ ERROR `z.f` does not live long enough
+        //~| NOTE this call may capture more lifetimes than intended
+        //~| NOTE values in a scope are dropped in the opposite order they are defined
+        //~| NOTE borrowed value does not live long enough
+    }
+    //~^ NOTE `z.f` dropped here while still borrowed
+}
+//~^ NOTE borrow might be used here, when `x` is dropped
+
+fn temp() {
+    let x = { let x = display_len(&mut vec![0]); x };
+    //~^ ERROR temporary value dropped while borrowed
+    //~| NOTE this call may capture more lifetimes than intended
+    //~| NOTE consider using a `let` binding to create a longer lived value
+    //~| NOTE borrow later used here
+    //~| NOTE temporary value is freed at the end of this statement
+}
+
+// FIXME: This doesn't display a useful Rust 2024 suggestion :(
+fn returned() -> impl Sized {
+    let x = vec![0];
+    //~^ NOTE binding `x` declared here
+    display_len(&x)
+    //~^ ERROR `x` does not live long enough
+    //~| NOTE borrowed value does not live long enough
+    //~| NOTE argument requires that `x` is borrowed for `'static`
+}
+//~^ NOTE `x` dropped here while still borrowed
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
new file mode 100644
index 00000000000..3ac47ed1bcd
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
@@ -0,0 +1,284 @@
+error[E0597]: `x` does not live long enough
+  --> $DIR/migration-note.rs:183:17
+   |
+LL |     let x = vec![0];
+   |         - binding `x` declared here
+LL |
+LL |     display_len(&x)
+   |     ------------^^-
+   |     |           |
+   |     |           borrowed value does not live long enough
+   |     argument requires that `x` is borrowed for `'static`
+...
+LL | }
+   | - `x` dropped here while still borrowed
+
+error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
+  --> $DIR/migration-note.rs:20:5
+   |
+LL |     let a = display_len(&x);
+   |                         -- immutable borrow occurs here
+...
+LL |     x.push(1);
+   |     ^^^^^^^^^ mutable borrow occurs here
+...
+LL |     println!("{a}");
+   |               --- immutable borrow later used here
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:17:13
+   |
+LL |     let a = display_len(&x);
+   |             ^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_len<T>(x: &Vec<T>) -> impl Display + use<T> {
+   |                                               ++++++++
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/migration-note.rs:30:25
+   |
+LL |     let x = vec![1];
+   |         - binding `x` declared here
+LL |
+LL |     let a = display_len(&x);
+   |             ------------^^-
+   |             |           |
+   |             |           borrowed value does not live long enough
+   |             argument requires that `x` is borrowed for `'static`
+...
+LL | }
+   | - `x` dropped here while still borrowed
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:30:13
+   |
+LL |     let a = display_len(&x);
+   |             ^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_len<T>(x: &Vec<T>) -> impl Display + use<T> {
+   |                                               ++++++++
+
+error[E0505]: cannot move out of `x` because it is borrowed
+  --> $DIR/migration-note.rs:49:8
+   |
+LL |     let x = vec![1];
+   |         - binding `x` declared here
+LL |
+LL |     let a = display_len(&x);
+   |                         -- borrow of `x` occurs here
+...
+LL |     mv(x);
+   |        ^ move out of `x` occurs here
+...
+LL | }
+   | - borrow might be used here, when `a` is dropped and runs the destructor for type `impl std::fmt::Display`
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:44:13
+   |
+LL |     let a = display_len(&x);
+   |             ^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_len<T>(x: &Vec<T>) -> impl Display + use<T> {
+   |                                               ++++++++
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let a = display_len(&x.clone());
+   |                           ++++++++
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time
+  --> $DIR/migration-note.rs:67:5
+   |
+LL |     let a = display_len_mut(&mut x);
+   |                             ------ first mutable borrow occurs here
+...
+LL |     x.push(1);
+   |     ^ second mutable borrow occurs here
+...
+LL |     println!("{a}");
+   |               --- first borrow later used here
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:64:13
+   |
+LL |     let a = display_len_mut(&mut x);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_len_mut<T>(x: &mut Vec<T>) -> impl Display + use<T> {
+   |                                                       ++++++++
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/migration-note.rs:77:29
+   |
+LL |     let mut x = vec![1];
+   |         ----- binding `x` declared here
+LL |
+LL |     let a = display_len_mut(&mut x);
+   |             ----------------^^^^^^-
+   |             |               |
+   |             |               borrowed value does not live long enough
+   |             argument requires that `x` is borrowed for `'static`
+...
+LL | }
+   | - `x` dropped here while still borrowed
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:77:13
+   |
+LL |     let a = display_len_mut(&mut x);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_len_mut<T>(x: &mut Vec<T>) -> impl Display + use<T> {
+   |                                                       ++++++++
+
+error[E0505]: cannot move out of `x` because it is borrowed
+  --> $DIR/migration-note.rs:96:8
+   |
+LL |     let mut x = vec![1];
+   |         ----- binding `x` declared here
+LL |
+LL |     let a = display_len_mut(&mut x);
+   |                             ------ borrow of `x` occurs here
+...
+LL |     mv(x);
+   |        ^ move out of `x` occurs here
+...
+LL | }
+   | - borrow might be used here, when `a` is dropped and runs the destructor for type `impl std::fmt::Display`
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:91:13
+   |
+LL |     let a = display_len_mut(&mut x);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_len_mut<T>(x: &mut Vec<T>) -> impl Display + use<T> {
+   |                                                       ++++++++
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let a = display_len_mut(&mut x.clone());
+   |                                   ++++++++
+
+error[E0506]: cannot assign to `s.f` because it is borrowed
+  --> $DIR/migration-note.rs:116:5
+   |
+LL |     let a = display_field(&s.f);
+   |                           ---- `s.f` is borrowed here
+...
+LL |     s.f = 1;
+   |     ^^^^^^^ `s.f` is assigned to here but it was already borrowed
+...
+LL |     println!("{a}");
+   |               --- borrow later used here
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:113:13
+   |
+LL |     let a = display_field(&s.f);
+   |             ^^^^^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_field<T: Copy + Display>(t: &T) -> impl Display + use<T> {
+   |                                                            ++++++++
+
+error[E0506]: cannot assign to `s.f` because it is borrowed
+  --> $DIR/migration-note.rs:132:5
+   |
+LL |     let a = display_field(&mut s.f);
+   |                           -------- `s.f` is borrowed here
+...
+LL |     s.f = 1;
+   |     ^^^^^^^ `s.f` is assigned to here but it was already borrowed
+...
+LL |     println!("{a}");
+   |               --- borrow later used here
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:129:13
+   |
+LL |     let a = display_field(&mut s.f);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_field<T: Copy + Display>(t: &T) -> impl Display + use<T> {
+   |                                                            ++++++++
+
+error[E0503]: cannot use `s.f` because it was mutably borrowed
+  --> $DIR/migration-note.rs:144:5
+   |
+LL |     let a = display_field(&mut s.f);
+   |                           -------- `s.f` is borrowed here
+...
+LL |     s.f;
+   |     ^^^ use of borrowed `s.f`
+...
+LL |     println!("{a}");
+   |               --- borrow later used here
+   |
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:141:13
+   |
+LL |     let a = display_field(&mut s.f);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_field<T: Copy + Display>(t: &T) -> impl Display + use<T> {
+   |                                                            ++++++++
+
+error[E0597]: `z.f` does not live long enough
+  --> $DIR/migration-note.rs:160:25
+   |
+LL |         let z = Z { f: vec![1] };
+   |             - binding `z` declared here
+LL |
+LL |         x = display_len(&z.f);
+   |                         ^^^^ borrowed value does not live long enough
+...
+LL |     }
+   |     - `z.f` dropped here while still borrowed
+LL |
+LL | }
+   | - borrow might be used here, when `x` is dropped and runs the destructor for type `impl std::fmt::Display`
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:160:13
+   |
+LL |         x = display_len(&z.f);
+   |             ^^^^^^^^^^^^^^^^^
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_len<T>(x: &Vec<T>) -> impl Display + use<T> {
+   |                                               ++++++++
+
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/migration-note.rs:171:40
+   |
+LL |     let x = { let x = display_len(&mut vec![0]); x };
+   |                                        ^^^^^^^ - - borrow later used here
+   |                                        |       |
+   |                                        |       temporary value is freed at the end of this statement
+   |                                        creates a temporary value which is freed while still in use
+   |
+   = note: consider using a `let` binding to create a longer lived value
+note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
+  --> $DIR/migration-note.rs:171:23
+   |
+LL |     let x = { let x = display_len(&mut vec![0]); x };
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: add a precise capturing bound to avoid overcapturing
+   |
+LL | fn display_len<T>(x: &Vec<T>) -> impl Display + use<T> {
+   |                                               ++++++++
+
+error: aborting due to 12 previous errors
+
+Some errors have detailed explanations: E0499, E0502, E0503, E0505, E0506, E0597, E0716.
+For more information about an error, try `rustc --explain E0499`.