From e093b82a41906c8c228643314e2f799568b37ee9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Oct 2024 23:16:31 -0400 Subject: [PATCH 1/2] Encode cross-crate opaque type origin --- compiler/rustc_ast_lowering/src/lib.rs | 6 +++--- .../src/region_infer/opaque_types.rs | 2 +- compiler/rustc_hir/src/hir.rs | 16 +++++++++------- compiler/rustc_hir_analysis/src/check/check.rs | 4 ++-- compiler/rustc_hir_analysis/src/collect.rs | 17 +++++++++++++---- .../src/hir_ty_lowering/mod.rs | 3 ++- compiler/rustc_hir_typeck/src/_match.rs | 2 +- .../rustc_infer/src/infer/opaque_types/mod.rs | 5 ++++- .../src/rmeta/decoder/cstore_impl.rs | 5 +---- compiler/rustc_metadata/src/rmeta/encoder.rs | 6 ++---- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_middle/src/query/erase.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 5 ++--- compiler/rustc_middle/src/ty/context.rs | 8 +++----- compiler/rustc_middle/src/ty/parameterized.rs | 1 + .../error_reporting/infer/note_and_explain.rs | 5 ++++- .../traits/fulfillment_errors.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 4 +--- compiler/rustc_ty_utils/src/opaque_types.rs | 2 +- 19 files changed, 53 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e1ee3d0af4910..5a0e9e8aec0c3 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 }, /// `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, 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, 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/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 741dac9e7639d..3a2f5c35c722f 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 12b01266a9317..fa76f8652ea1f 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, 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 { /// `-> 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, }, /// `async fn` AsyncFn { /// The defining function. - parent: LocalDefId, + parent: D, // Whether this is an AFIT (async fn in trait) in_trait_or_impl: Option, }, /// 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 f830108a02f4f..29555ebff50ff 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, ) -> 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 c41117d213f2f..3f6198dbd3196 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 { + 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 2bd8a5223aa3f..d70e4d8345a11 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 3372cae7a5107..1774772b50b4c 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 498d25aea640b..2bc006c37dac9 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 { 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 926eb4f6210bd..f06f2fdc5e56d 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 b5ac302c597de..4dafe36f5cd88 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 f1844045677ed..082dac5f8e7a7 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>>, is_macro_rules: Table, - is_type_alias_impl_trait: Table, type_alias_is_lazy: Table, attr_flags: Table, // The u64 is the crate-local part of the DefPathHash. All hashes in this crate have the same @@ -469,6 +468,7 @@ define_tables! { doc_link_resolutions: Table>, doc_link_traits_in_scope: Table>, assumed_wf_types_for_rpitit: Table, Span)>>, + opaque_ty_origin: Table>>, } #[derive(TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 5f8427bd707aa..8cfc7f1e33ecf 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::OwnerId, rustc_hir::Upvar, rustc_index::bit_set::FiniteBitSet, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d7f5896310103..088b5d4ec965f 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 { - 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 diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ac36b940725ac..9f1ffabc4f807 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -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 { + 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 43bdce5b576dd..7c280bc8b49e9 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_index::bit_set::BitSet, rustc_index::bit_set::FiniteBitSet, 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 0cf7c43beb541..b97f3dc303ba1 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 Trait 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 612e92ea7843d..7aa558cfd3ffe 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 f177b60948576..88d81cfa37780 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -246,7 +246,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:?}"); }; @@ -284,8 +284,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 5e2232ff47d73..34f461aac58a3 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 { .. } From c1457798db03ffb7f207eaf208d8d4d77ba01563 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 3 Oct 2024 00:44:14 -0400 Subject: [PATCH 2/2] Try to point out when edition 2024 lifetime capture rules cause borrowck issues --- .../src/diagnostics/conflict_errors.rs | 8 +- .../rustc_borrowck/src/diagnostics/mod.rs | 1 + .../src/diagnostics/opaque_suggestions.rs | 224 ++++++++++++++ compiler/rustc_middle/src/ty/context.rs | 2 +- .../precise-capturing/auxiliary/foreign.rs | 6 + .../precise-capturing/foreign-2021.rs | 15 + .../precise-capturing/foreign-2021.stderr | 26 ++ .../precise-capturing/migration-note.rs | 190 ++++++++++++ .../precise-capturing/migration-note.stderr | 284 ++++++++++++++++++ 9 files changed, 754 insertions(+), 2 deletions(-) create mode 100644 compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs create mode 100644 tests/ui/impl-trait/precise-capturing/auxiliary/foreign.rs create mode 100644 tests/ui/impl-trait/precise-capturing/foreign-2021.rs create mode 100644 tests/ui/impl-trait/precise-capturing/foreign-2021.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/migration-note.rs create mode 100644 tests/ui/impl-trait/precise-capturing/migration-note.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c687be69b1a65..1bfb54a7bb70e 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 801c7af2de701..3b60071603619 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 0000000000000..bfd7e83501c7d --- /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> 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, + seen_lifetimes: FxIndexSet, +} + +impl<'tcx> TypeVisitor> 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_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9f1ffabc4f807..9abad6d1a680b 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}; 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 0000000000000..49015bc48baf2 --- /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) -> 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 0000000000000..aee412ff7f13b --- /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 0000000000000..2a17ef72912a0 --- /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) -> 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 0000000000000..a5bade4ddc53a --- /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(x: &Vec) -> 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(x: &mut Vec) -> 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: &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: &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, +} + +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 0000000000000..3ac47ed1bcd38 --- /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(x: &Vec) -> impl Display + use { + | ++++++++ + +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(x: &Vec) -> impl Display + use { + | ++++++++ + +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(x: &Vec) -> impl Display + use { + | ++++++++ +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(x: &mut Vec) -> impl Display + use { + | ++++++++ + +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(x: &mut Vec) -> impl Display + use { + | ++++++++ + +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(x: &mut Vec) -> impl Display + use { + | ++++++++ +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: &T) -> impl Display + use { + | ++++++++ + +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: &T) -> impl Display + use { + | ++++++++ + +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: &T) -> impl Display + use { + | ++++++++ + +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(x: &Vec) -> impl Display + use { + | ++++++++ + +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(x: &Vec) -> impl Display + use { + | ++++++++ + +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`.