about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs2
-rw-r--r--src/librustc/ty/context.rs13
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs57
-rw-r--r--src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr8
-rw-r--r--src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr8
5 files changed, 73 insertions, 15 deletions
diff --git a/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs
index 2f7c6adfd21..29ba23b58bc 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -29,7 +29,7 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
                 ) => {
                     let anon_reg_sup = self.tcx.is_suitable_region(sup_r)?;
                     if sub_r == &RegionKind::ReStatic &&
-                        self.tcx.is_return_type_impl_trait(anon_reg_sup.def_id)
+                        self.tcx.return_type_impl_trait(anon_reg_sup.def_id).is_some()
                     {
                         let sp = var_origin.span();
                         let return_sp = sub_origin.span();
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index c1ae7bba50a..ae840f93c20 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1587,20 +1587,23 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         });
     }
 
-    pub fn is_return_type_impl_trait(
+    pub fn return_type_impl_trait(
         &self,
         scope_def_id: DefId,
-    ) -> bool {
+    ) -> Option<Ty> {
         let ret_ty = self.type_of(scope_def_id);
         match ret_ty.sty {
             ty::FnDef(_, _) => {
                 let sig = ret_ty.fn_sig(*self);
                 let output = self.erase_late_bound_regions(&sig.output());
-                return output.is_impl_trait();
+                if output.is_impl_trait() {
+                    Some(output)
+                } else {
+                    None
+                }
             }
-            _ => {}
+            _ => None
         }
-        false
     }
 
     // Here we check if the bound region is in Impl Item.
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
index 0b9b9b33b3f..2d8f782f748 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
@@ -15,7 +15,7 @@ use rustc::hir::def_id::DefId;
 use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
 use rustc::infer::InferCtxt;
 use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
-use rustc::ty::{TyCtxt, RegionVid};
+use rustc::ty::{TyCtxt, Ty, TyS, TyKind, Region, RegionKind, RegionVid};
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_errors::Diagnostic;
 use std::collections::VecDeque;
@@ -344,7 +344,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         );
 
         // Check if we can use one of the "nice region errors".
-        if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
+        let fr_region = self.to_error_region(fr);
+        let outlived_fr_region = self.to_error_region(outlived_fr);
+        if let (Some(f), Some(o)) = (fr_region, outlived_fr_region) {
             let tables = infcx.tcx.typeck_tables_of(mir_def_id);
             let nice = NiceRegionError::new_from_span(infcx.tcx, span, o, f, Some(tables));
             if let Some(_error_reported) = nice.try_report_from_nll() {
@@ -356,17 +358,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             self.universal_regions.is_local_free_region(fr),
             self.universal_regions.is_local_free_region(outlived_fr),
         );
-        debug!("report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
-               fr_is_local, outlived_fr_is_local, category);
 
+        debug!("report_error: fr_is_local={:?} outlived_fr_is_local={:?} fr_region={:?} \
+                outlived_fr_region={:?} category={:?}",
+               fr_is_local, outlived_fr_is_local, fr_region, outlived_fr_region, category);
         match (category, fr_is_local, outlived_fr_is_local) {
             (ConstraintCategory::Assignment, true, false) |
             (ConstraintCategory::CallArgument, true, false) =>
-                self.report_escaping_data_error(mir, infcx, mir_def_id, fr, outlived_fr,
-                                                category, span, errors_buffer),
+                self.report_escaping_data_error(mir, infcx, mir_def_id, fr, fr_region, outlived_fr,
+                                                outlived_fr_region, category, span, errors_buffer),
             _ =>
-                self.report_general_error(mir, infcx, mir_def_id, fr, fr_is_local,
-                                          outlived_fr, outlived_fr_is_local,
+                self.report_general_error(mir, infcx, mir_def_id, fr, fr_is_local, fr_region,
+                                          outlived_fr, outlived_fr_is_local, outlived_fr_region,
                                           category, span, errors_buffer),
         };
     }
@@ -377,7 +380,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         infcx: &InferCtxt<'_, '_, 'tcx>,
         mir_def_id: DefId,
         fr: RegionVid,
+        fr_region: Option<Region<'tcx>>,
         outlived_fr: RegionVid,
+        outlived_fr_region: Option<Region<'tcx>>,
         category: ConstraintCategory,
         span: Span,
         errors_buffer: &mut Vec<Diagnostic>,
@@ -390,7 +395,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
             return self.report_general_error(mir, infcx, mir_def_id,
-                                             fr, true, outlived_fr, false,
+                                             fr, true, fr_region,
+                                             outlived_fr, false, outlived_fr_region,
                                              category, span, errors_buffer);
         }
 
@@ -430,8 +436,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir_def_id: DefId,
         fr: RegionVid,
         fr_is_local: bool,
+        fr_region: Option<Region<'tcx>>,
         outlived_fr: RegionVid,
         outlived_fr_is_local: bool,
+        outlived_fr_region: Option<Region<'tcx>>,
         category: ConstraintCategory,
         span: Span,
         errors_buffer: &mut Vec<Diagnostic>,
@@ -465,6 +473,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             },
         }
 
+        if let (Some(f), Some(RegionKind::ReStatic)) = (fr_region, outlived_fr_region) {
+            if let Some(TyS {
+                sty: TyKind::Anon(did, _),
+                ..
+            }) = self.return_type_impl_trait(infcx, f) {
+                let span = infcx.tcx.def_span(*did);
+                if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
+                    diag.span_suggestion(
+                        span,
+                        &format!(
+                            "you can add a constraint to the return type to make it last \
+                             less than `'static` and match {}",
+                            fr_name,
+                        ),
+                        format!("{} + {}", snippet, fr_name),
+                    );
+                }
+            }
+        }
+
         diag.buffer(errors_buffer);
     }
 
@@ -490,4 +518,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let (_, span, _) = self.best_blame_constraint(mir, tcx, fr1, |r| r == fr2);
         span
     }
+
+    fn return_type_impl_trait<'cx>(
+        &self,
+        infcx: &'cx InferCtxt<'_, '_, 'tcx>,
+        outlived_fr_region: Region<'tcx>,
+    ) -> Option<Ty<'cx>> {
+        infcx.tcx.is_suitable_region(outlived_fr_region)
+            .map(|r| r.def_id)
+            .map(|id| infcx.tcx.return_type_impl_trait(id))
+            .unwrap_or(None)
+    }
 }
diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
index 1e3e997b111..e372bd33aa2 100644
--- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
+++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
@@ -11,12 +11,20 @@ error: unsatisfied lifetime constraints
    |
 LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x }
    |             -- lifetime `'a` defined here  ^ returning this value requires that `'a` must outlive `'static`
+help: you can add a constraint to the return type to make it last less than `'static` and match 'a
+   |
+LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x }
+   |                                ^^^^^^^^^^^^^^
 
 error: unsatisfied lifetime constraints
   --> $DIR/must_outlive_least_region_or_bound.rs:22:69
    |
 LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
    |               -- lifetime `'a` defined here                         ^ returning this value requires that `'a` must outlive `'static`
+help: you can add a constraint to the return type to make it last less than `'static` and match 'a
+   |
+LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x }
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unsatisfied lifetime constraints
   --> $DIR/must_outlive_least_region_or_bound.rs:29:5
diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr
index d75de81fc1c..d6b19acb86b 100644
--- a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr
+++ b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr
@@ -5,6 +5,10 @@ LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
    |                         - let's call the lifetime of this reference `'1`
 LL |         self.x.iter().map(|a| a.0)
    |         ^^^^^^ cast requires that `'1` must outlive `'static`
+help: you can add a constraint to the return type to make it last less than `'static` and match '1
+   |
+LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> + '1 {
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unsatisfied lifetime constraints
   --> $DIR/static-return-lifetime-infered.rs:21:9
@@ -13,6 +17,10 @@ LL |     fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> {
    |                    -- lifetime `'a` defined here
 LL |         self.x.iter().map(|a| a.0)
    |         ^^^^^^ cast requires that `'a` must outlive `'static`
+help: you can add a constraint to the return type to make it last less than `'static` and match 'a
+   |
+LL |     fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> + 'a {
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors