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`
This commit is contained in:
bors 2024-10-31 03:36:06 +00:00
commit c8b83785dc
27 changed files with 807 additions and 45 deletions

View File

@ -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> {

View File

@ -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::*;

View File

@ -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};

View File

@ -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(()),
}
}
}

View File

@ -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, .. }

View File

@ -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,
},

View File

@ -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)

View File

@ -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>(

View File

@ -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);

View File

@ -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;
};

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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)]

View File

@ -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>,

View File

@ -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>

View File

@ -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
}
}

View File

@ -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,

View File

@ -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())

View File

@ -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()
}

View File

@ -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(&[]);

View File

@ -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 { .. }

View File

@ -0,0 +1,6 @@
//@ edition: 2024
//@ compile-flags: -Zunstable-options
use std::fmt::Display;
pub fn hello(x: &Vec<i32>) -> impl Display { 0 }

View File

@ -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
}

View File

@ -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`.

View File

@ -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() {}

View File

@ -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`.