about summary refs log tree commit diff
diff options
context:
space:
mode:
-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 a0a809123c0..230c22045c7 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 608d010394f..9d05b0ec699 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 0540eb0efd6..078923ab5be 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`.