From 7a89e93519ce0880fe10c37c2ca8f63686fc210b Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 4 Sep 2018 18:56:14 +0200 Subject: [PATCH] Emit appropriate suggestion when there's already 'static bound on the return type. --- src/librustc/ty/context.rs | 2 +- .../nll/region_infer/error_reporting/mod.rs | 90 ++++++++++++------- .../error_reporting/region_name.rs | 57 ++++++++---- ...t_outlive_least_region_or_bound.nll.stderr | 6 +- 4 files changed, 104 insertions(+), 51 deletions(-) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index ae840f93c20e..03c99180d5e5 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1590,7 +1590,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn return_type_impl_trait( &self, scope_def_id: DefId, - ) -> Option { + ) -> Option> { let ret_ty = self.type_of(scope_def_id); match ret_ty.sty { ty::FnDef(_, _) => { 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 bf3b5e2a17c6..8690212804e2 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, Ty, TyS, TyKind, Region, RegionKind, RegionVid}; +use rustc::ty::{self, TyCtxt, Region, RegionKind, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_errors::{Diagnostic, DiagnosticBuilder}; use std::collections::VecDeque; @@ -491,26 +491,67 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr_region: Option>, outlived_fr_region: Option>, ) { - if let (Some(f), Some(RegionKind::ReStatic)) = (fr_region, outlived_fr_region) { - if let Some(TyS { - sty: TyKind::Anon(did, _), + if let (Some(f), Some(ty::RegionKind::ReStatic)) = (fr_region, outlived_fr_region) { + if let Some(ty::TyS { + sty: ty::TyKind::Anon(did, substs), .. - }) = self.return_type_impl_trait(infcx, f) { + }) = infcx.tcx.is_suitable_region(f) + .map(|r| r.def_id) + .map(|id| infcx.tcx.return_type_impl_trait(id)) + .unwrap_or(None) + { + let has_static_predicate = { + let predicates_of = infcx.tcx.predicates_of(*did); + let bounds = predicates_of.instantiate(infcx.tcx, substs); + + let mut found = false; + for predicate in bounds.predicates { + if let ty::Predicate::TypeOutlives(binder) = predicate { + if let ty::OutlivesPredicate( + _, + RegionKind::ReStatic + ) = binder.skip_binder() { + found = true; + break; + } + } + } + + found + }; + + debug!("add_static_impl_trait_suggestion: has_static_predicate={:?}", + has_static_predicate); let static_str = keywords::StaticLifetime.name(); - 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 `{}` and match `{}`", - static_str, fr_name, - ), - match fr_name { - RegionName::Named(name) => format!("{} + {}", snippet, name), - RegionName::Synthesized(_) => format!("{} + '_", snippet), - }, - ); + if has_static_predicate { + let span = self.get_span_of_named_region(infcx.tcx, f, &fr_name); + 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 definition of `{}` to require it \ + outlive `{}`", + fr_name, static_str, + ), + format!("{}: {}", snippet, static_str), + ); + } + } else { + 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 `{}` and match `{}`", + static_str, fr_name, + ), + match fr_name { + RegionName::Named(name) => format!("{} + {}", snippet, name), + RegionName::Synthesized(_) => format!("{} + '_", snippet), + }, + ); + } } } } @@ -538,15 +579,4 @@ 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/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs index 136f660fe8da..857f1dae7ab5 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs @@ -22,6 +22,7 @@ use rustc::util::ppaux::with_highlight_region; use rustc_errors::DiagnosticBuilder; use syntax::ast::{Name, DUMMY_NODE_ID}; use syntax::symbol::keywords; +use syntax_pos::Span; use syntax_pos::symbol::InternedString; /// Name of a region used in error reporting. Variants denote the source of the region name - @@ -33,6 +34,14 @@ pub(crate) enum RegionName { Synthesized(InternedString), } +impl RegionName { + fn as_interned_string(&self) -> &InternedString { + match self { + RegionName::Named(name) | RegionName::Synthesized(name) => name, + } + } +} + impl Display for RegionName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -121,8 +130,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { match error_region { ty::ReEarlyBound(ebr) => { if ebr.has_name() { - self.highlight_named_span(tcx, error_region, &ebr.name, diag); - Some(RegionName::Named(ebr.name)) + let name = RegionName::Named(ebr.name); + self.highlight_named_span(tcx, error_region, &name, diag); + Some(name) } else { None } @@ -134,8 +144,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { ty::ReFree(free_region) => match free_region.bound_region { ty::BoundRegion::BrNamed(_, name) => { + let name = RegionName::Named(name); self.highlight_named_span(tcx, error_region, &name, diag); - Some(RegionName::Named(name)) + Some(name) }, ty::BoundRegion::BrEnv => { @@ -198,6 +209,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + /// Get the span of a named region. + pub(super) fn get_span_of_named_region( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + error_region: &RegionKind, + name: &RegionName, + ) -> Span { + let scope = error_region.free_region_binding_scope(tcx); + let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID); + + let span = tcx.sess.source_map().def_span(tcx.hir.span(node)); + if let Some(param) = tcx.hir.get_generics(scope).and_then(|generics| { + generics.get_named(name.as_interned_string()) + }) { + param.span + } else { + span + } + } + /// Highlight a named span to provide context for error messages that /// mention that span, for example: /// @@ -216,23 +247,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, tcx: TyCtxt<'_, '_, 'tcx>, error_region: &RegionKind, - name: &InternedString, + name: &RegionName, diag: &mut DiagnosticBuilder<'_>, ) { - let cm = tcx.sess.source_map(); + let span = self.get_span_of_named_region(tcx, error_region, name); - let scope = error_region.free_region_binding_scope(tcx); - let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID); - - let mut sp = cm.def_span(tcx.hir.span(node)); - if let Some(param) = tcx.hir - .get_generics(scope) - .and_then(|generics| generics.get_named(name)) - { - sp = param.span; - } - - diag.span_label(sp, format!("lifetime `{}` defined here", name)); + diag.span_label( + span, + format!("lifetime `{}` defined here", name), + ); } /// Find an argument that contains `fr` and label it with a fully 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 e95fc7663710..1e527140127a 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 @@ -21,10 +21,10 @@ error: unsatisfied lifetime constraints | 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` +help: you can add a constraint to the definition of `'a` to require it outlive `'static` | -LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn with_bound<'a: 'static>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } + | ^^^^^^^^^^^ error: unsatisfied lifetime constraints --> $DIR/must_outlive_least_region_or_bound.rs:29:5