about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-11-20 20:56:40 +0100
committerGitHub <noreply@github.com>2023-11-20 20:56:40 +0100
commit87540879f451bd402bba072b4c5144db2031bc2f (patch)
tree768767608f267a7510a222245ac1ba981f82a757
parentda5eebf9422cc0590257236d920452beaa4d6586 (diff)
parente5c330ac48b9524e42d872c5ca0e231622d42bee (diff)
downloadrust-87540879f451bd402bba072b4c5144db2031bc2f.tar.gz
rust-87540879f451bd402bba072b4c5144db2031bc2f.zip
Rollup merge of #117835 - Nilstrieb:note-object-lifetime-defaults, r=compiler-errors
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.

The implementation feels a bit questionable, I'm not sure whether there are better ways to do this. There probably are.

fixes #117835

r? types
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs60
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs24
-rw-r--r--compiler/rustc_middle/src/mir/query.rs6
-rw-r--r--tests/ui/borrowck/two-phase-surprise-no-conflict.stderr4
-rw-r--r--tests/ui/dropck/dropck_trait_cycle_checked.stderr12
-rw-r--r--tests/ui/traits/trait-object-lifetime-default-note.rs16
-rw-r--r--tests/ui/traits/trait-object-lifetime-default-note.stderr19
8 files changed, 132 insertions, 11 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 8a930ca59a3..78e9e104bda 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[&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,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 76c5a06c80d..da1d1c70f0f 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 5d9fd2647f2..bb9ce5daba2 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 d609965f36f..b5dd3010d3a 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<Ty<'tcx>>,
+    },
 
     /// 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 9f9d4bd8d6c..b3bf2f924fc 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(&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
diff --git a/tests/ui/dropck/dropck_trait_cycle_checked.stderr b/tests/ui/dropck/dropck_trait_cycle_checked.stderr
index 4d4f7b9df11..63fd07a9163 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<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
 
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 00000000000..31e3eb4ba41
--- /dev/null
+++ b/tests/ui/traits/trait-object-lifetime-default-note.rs
@@ -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>) {}
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 00000000000..b6dd6753899
--- /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<dyn A>` actually means `Box<(dyn A + 'static)>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.