From e5c330ac48b9524e42d872c5ca0e231622d42bee Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 12 Nov 2023 13:48:47 +0100 Subject: [PATCH] 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. --- .../src/diagnostics/explain_borrow.rs | 60 ++++++++++++++++++- .../src/diagnostics/region_errors.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 24 +++++--- compiler/rustc_middle/src/mir/query.rs | 6 +- .../two-phase-surprise-no-conflict.stderr | 4 ++ .../dropck/dropck_trait_cycle_checked.stderr | 12 ++++ .../trait-object-lifetime-default-note.rs | 16 +++++ .../trait-object-lifetime-default-note.stderr | 19 ++++++ 8 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 tests/ui/traits/trait-object-lifetime-default-note.rs create mode 100644 tests/ui/traits/trait-object-lifetime-default-note.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 8a930ca59a334..78e9e104bdae8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -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, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index a0a809123c0f9..230c22045c7ab 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -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 ", diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 608d010394f66..9d05b0ec699e1 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -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, diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 0540eb0efd6c9..078923ab5be5d 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -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>, + }, /// A constraint that came from checking the body of a closure. /// diff --git a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr index 9f9d4bd8d6c05..b3bf2f924fc3e 100644 --- a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr +++ b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr @@ -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 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 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 diff --git a/tests/ui/dropck/dropck_trait_cycle_checked.stderr b/tests/ui/dropck/dropck_trait_cycle_checked.stderr index 4d4f7b9df1179..63fd07a91633d 100644 --- a/tests/ui/dropck/dropck_trait_cycle_checked.stderr +++ b/tests/ui/dropck/dropck_trait_cycle_checked.stderr @@ -8,6 +8,8 @@ LL | o1.set0(&o2); ... LL | } | - `o2` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` 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>` 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>` 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>` 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>` 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>` actually means `Box<(dyn Obj<'_> + 'static)>` error: aborting due to 6 previous errors diff --git a/tests/ui/traits/trait-object-lifetime-default-note.rs b/tests/ui/traits/trait-object-lifetime-default-note.rs new file mode 100644 index 0000000000000..31e3eb4ba4155 --- /dev/null +++ b/tests/ui/traits/trait-object-lifetime-default-note.rs @@ -0,0 +1,16 @@ +trait A {} + +impl 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` 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) {} diff --git a/tests/ui/traits/trait-object-lifetime-default-note.stderr b/tests/ui/traits/trait-object-lifetime-default-note.stderr new file mode 100644 index 0000000000000..b6dd67538993c --- /dev/null +++ b/tests/ui/traits/trait-object-lifetime-default-note.stderr @@ -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` actually means `Box<(dyn A + 'static)>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`.