Note about object lifetime defaults in does not live long enough error

This is a aspect of Rust that frequently trips up people who are not
aware of it yet. This diagnostic attempts to explain what's happening
and why the lifetime constraint, that was never mentioned in the source,
arose.
This commit is contained in:
Nilstrieb 2023-11-12 13:48:47 +01:00
parent ed086d86b8
commit e5c330ac48
8 changed files with 132 additions and 11 deletions

View file

@ -5,12 +5,13 @@
use rustc_hir::intravisit::Visitor;
use rustc_index::IndexSlice;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
use rustc_middle::mir::{
Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location,
Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind,
};
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::{self, RegionVid, TyCtxt};
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, DesugaringKind, Span};
use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
@ -290,12 +291,69 @@ pub(crate) fn add_explanation_to_diagnostic(
}
}
if let ConstraintCategory::Cast { unsize_to: Some(unsize_ty) } = category {
self.add_object_lifetime_default_note(tcx, err, unsize_ty);
}
self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
}
_ => {}
}
}
fn add_object_lifetime_default_note(
&self,
tcx: TyCtxt<'tcx>,
err: &mut Diagnostic,
unsize_ty: Ty<'tcx>,
) {
if let ty::Adt(def, args) = unsize_ty.kind() {
// We try to elaborate the object lifetime defaults and present those to the user. This should
// make it clear where the region constraint is coming from.
let generics = tcx.generics_of(def.did());
let mut has_dyn = false;
let mut failed = false;
let elaborated_args = std::iter::zip(*args, &generics.params).map(|(arg, param)| {
if let Some(ty::Dynamic(obj, _, ty::DynKind::Dyn)) = arg.as_type().map(Ty::kind) {
let default = tcx.object_lifetime_default(param.def_id);
let re_static = tcx.lifetimes.re_static;
let implied_region = match default {
// This is not entirely precise.
ObjectLifetimeDefault::Empty => re_static,
ObjectLifetimeDefault::Ambiguous => {
failed = true;
re_static
}
ObjectLifetimeDefault::Param(param_def_id) => {
let index = generics.param_def_id_to_index[&param_def_id] as usize;
args.get(index).and_then(|arg| arg.as_region()).unwrap_or_else(|| {
failed = true;
re_static
})
}
ObjectLifetimeDefault::Static => re_static,
};
has_dyn = true;
Ty::new_dynamic(tcx, obj, implied_region, ty::DynKind::Dyn).into()
} else {
arg
}
});
let elaborated_ty = Ty::new_adt(tcx, *def, tcx.mk_args_from_iter(elaborated_args));
if has_dyn && !failed {
err.note(format!(
"due to object lifetime defaults, `{unsize_ty}` actually means `{elaborated_ty}`"
));
}
}
}
fn add_lifetime_bound_suggestion_to_diagnostic(
&self,
err: &mut Diagnostic,

View file

@ -53,7 +53,7 @@ fn description(&self) -> &'static str {
ConstraintCategory::Yield => "yielding this value ",
ConstraintCategory::UseAsConst => "using this value as a constant ",
ConstraintCategory::UseAsStatic => "using this value as a static ",
ConstraintCategory::Cast => "cast ",
ConstraintCategory::Cast { .. } => "cast ",
ConstraintCategory::CallArgument(_) => "argument ",
ConstraintCategory::TypeAnnotation => "type annotation ",
ConstraintCategory::ClosureBounds => "closure body ",

View file

@ -1934,7 +1934,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
*ty,
ty_fn_ptr_from,
location.to_locations(),
ConstraintCategory::Cast,
ConstraintCategory::Cast { unsize_to: None },
) {
span_mirbug!(
self,
@ -1959,7 +1959,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
*ty,
ty_fn_ptr_from,
location.to_locations(),
ConstraintCategory::Cast,
ConstraintCategory::Cast { unsize_to: None },
) {
span_mirbug!(
self,
@ -1988,7 +1988,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
*ty,
ty_fn_ptr_from,
location.to_locations(),
ConstraintCategory::Cast,
ConstraintCategory::Cast { unsize_to: None },
) {
span_mirbug!(
self,
@ -2013,7 +2013,15 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
self.prove_trait_ref(
trait_ref,
location.to_locations(),
ConstraintCategory::Cast,
ConstraintCategory::Cast {
unsize_to: Some(tcx.fold_regions(ty, |r, _| {
if let ty::ReVar(_) = r.kind() {
tcx.lifetimes.re_erased
} else {
r
}
})),
},
);
}
@ -2033,7 +2041,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
.iter()
.map(|predicate| predicate.with_self_ty(tcx, self_ty)),
location.to_locations(),
ConstraintCategory::Cast,
ConstraintCategory::Cast { unsize_to: None },
);
let outlives_predicate = tcx.mk_predicate(Binder::dummy(
@ -2044,7 +2052,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
self.prove_predicate(
outlives_predicate,
location.to_locations(),
ConstraintCategory::Cast,
ConstraintCategory::Cast { unsize_to: None },
);
}
@ -2065,7 +2073,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
*ty_from,
*ty_to,
location.to_locations(),
ConstraintCategory::Cast,
ConstraintCategory::Cast { unsize_to: None },
) {
span_mirbug!(
self,
@ -2131,7 +2139,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
*ty_elem,
*ty_to,
location.to_locations(),
ConstraintCategory::Cast,
ConstraintCategory::Cast { unsize_to: None },
) {
span_mirbug!(
self,

View file

@ -341,7 +341,11 @@ pub enum ConstraintCategory<'tcx> {
UseAsConst,
UseAsStatic,
TypeAnnotation,
Cast,
Cast {
/// Whether this is an unsizing cast and if yes, this contains the target type.
/// Region variables are erased to ReErased.
unsize_to: Option<Ty<'tcx>>,
},
/// A constraint that came from checking the body of a closure.
///

View file

@ -77,6 +77,8 @@ LL | reg.register_univ(Box::new(CapturePass::new(&reg.sess_mut)));
| | | immutable borrow occurs here
| | cast requires that `reg.sess_mut` is borrowed for `'a`
| mutable borrow occurs here
|
= note: due to object lifetime defaults, `Box<dyn for<'b> LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>`
error[E0502]: cannot borrow `*reg` as mutable because it is also borrowed as immutable
--> $DIR/two-phase-surprise-no-conflict.rs:144:5
@ -119,6 +121,8 @@ LL | reg.register_univ(Box::new(CapturePass::new_mut(&mut reg.sess_mut)));
| | | first mutable borrow occurs here
| | cast requires that `reg.sess_mut` is borrowed for `'a`
| second mutable borrow occurs here
|
= note: due to object lifetime defaults, `Box<dyn for<'b> LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>`
error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time
--> $DIR/two-phase-surprise-no-conflict.rs:158:53

View file

@ -8,6 +8,8 @@ LL | o1.set0(&o2);
...
LL | }
| - `o2` dropped here while still borrowed
|
= note: due to object lifetime defaults, `Box<dyn Obj<'_>>` actually means `Box<(dyn Obj<'_> + 'static)>`
error[E0597]: `o3` does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:112:13
@ -20,6 +22,8 @@ LL | o1.set1(&o3);
...
LL | }
| - `o3` dropped here while still borrowed
|
= note: due to object lifetime defaults, `Box<dyn Obj<'_>>` actually means `Box<(dyn Obj<'_> + 'static)>`
error[E0597]: `o2` does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:113:13
@ -32,6 +36,8 @@ LL | o2.set0(&o2);
...
LL | }
| - `o2` dropped here while still borrowed
|
= note: due to object lifetime defaults, `Box<dyn Obj<'_>>` actually means `Box<(dyn Obj<'_> + 'static)>`
error[E0597]: `o3` does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:114:13
@ -44,6 +50,8 @@ LL | o2.set1(&o3);
...
LL | }
| - `o3` dropped here while still borrowed
|
= note: due to object lifetime defaults, `Box<dyn Obj<'_>>` actually means `Box<(dyn Obj<'_> + 'static)>`
error[E0597]: `o1` does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:115:13
@ -56,6 +64,8 @@ LL | o3.set0(&o1);
LL | o3.set1(&o2);
LL | }
| - `o1` dropped here while still borrowed
|
= note: due to object lifetime defaults, `Box<dyn Obj<'_>>` actually means `Box<(dyn Obj<'_> + 'static)>`
error[E0597]: `o2` does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:116:13
@ -67,6 +77,8 @@ LL | o3.set1(&o2);
| ^^^ borrowed value does not live long enough
LL | }
| - `o2` dropped here while still borrowed
|
= note: due to object lifetime defaults, `Box<dyn Obj<'_>>` actually means `Box<(dyn Obj<'_> + 'static)>`
error: aborting due to 6 previous errors

View file

@ -0,0 +1,16 @@
trait A {}
impl<T> A for T {}
fn main() {
let local = 0; //~ NOTE binding `local` declared here
let r = &local; //~ ERROR `local` does not live long enough
//~| NOTE borrowed value does not live long enough
//~| NOTE due to object lifetime defaults, `Box<dyn A>` actually means `Box<(dyn A + 'static)>`
require_box(Box::new(r));
//~^ NOTE cast requires that `local` is borrowed for `'static`
let _ = 0;
} //~ NOTE `local` dropped here while still borrowed
fn require_box(_a: Box<dyn A>) {}

View file

@ -0,0 +1,19 @@
error[E0597]: `local` does not live long enough
--> $DIR/trait-object-lifetime-default-note.rs:7:13
|
LL | let local = 0;
| ----- binding `local` declared here
LL | let r = &local;
| ^^^^^^ borrowed value does not live long enough
...
LL | require_box(Box::new(r));
| ----------- cast requires that `local` is borrowed for `'static`
...
LL | }
| - `local` dropped here while still borrowed
|
= note: due to object lifetime defaults, `Box<dyn A>` actually means `Box<(dyn A + 'static)>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0597`.