From 6bf131f3f4c978b93e709533d8d7ff4cb22e4769 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sun, 2 Sep 2018 11:10:51 +0200 Subject: [PATCH] Added help message for impl trait static constraint. --- .../nice_region_error/static_impl_trait.rs | 2 +- src/librustc/ty/context.rs | 13 +++-- .../nll/region_infer/error_reporting/mod.rs | 57 ++++++++++++++++--- ...t_outlive_least_region_or_bound.nll.stderr | 8 +++ .../static-return-lifetime-infered.nll.stderr | 8 +++ 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 2f7c6adfd218..29ba23b58bc0 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 c1ae7bba50a2..ae840f93c20e 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 { 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 0b9b9b33b3f1..2d8f782f748e 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>, outlived_fr: RegionVid, + outlived_fr_region: Option>, category: ConstraintCategory, span: Span, errors_buffer: &mut Vec, @@ -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>, outlived_fr: RegionVid, outlived_fr_is_local: bool, + outlived_fr_region: Option>, category: ConstraintCategory, span: Span, errors_buffer: &mut Vec, @@ -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> { + 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 1e3e997b111a..e372bd33aa2d 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 d75de81fc1c7..d6b19acb86b2 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 { | - 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 + '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 { | -- 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 + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors