mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
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:
parent
ed086d86b8
commit
e5c330ac48
@ -5,12 +5,13 @@ use rustc_hir as hir;
|
||||
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 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
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[¶m_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,
|
||||
|
@ -53,7 +53,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
|
||||
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 ",
|
||||
|
@ -1934,7 +1934,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty,
|
||||
ty_fn_ptr_from,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -1959,7 +1959,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty,
|
||||
ty_fn_ptr_from,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -1988,7 +1988,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty,
|
||||
ty_fn_ptr_from,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -2013,7 +2013,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
.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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
self.prove_predicate(
|
||||
outlives_predicate,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
);
|
||||
}
|
||||
|
||||
@ -2065,7 +2073,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty_from,
|
||||
*ty_to,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
@ -2131,7 +2139,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
*ty_elem,
|
||||
*ty_to,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
ConstraintCategory::Cast { unsize_to: None },
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -77,6 +77,8 @@ LL | reg.register_univ(Box::new(CapturePass::new(®.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
|
||||
|
@ -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
|
||||
|
||||
|
16
tests/ui/traits/trait-object-lifetime-default-note.rs
Normal file
16
tests/ui/traits/trait-object-lifetime-default-note.rs
Normal 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>) {}
|
19
tests/ui/traits/trait-object-lifetime-default-note.stderr
Normal file
19
tests/ui/traits/trait-object-lifetime-default-note.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user