Skip to content

Commit 7a89e93

Browse files
committed
Emit appropriate suggestion when there's already 'static bound on the return type.
1 parent 65e2539 commit 7a89e93

File tree

4 files changed

+104
-51
lines changed

4 files changed

+104
-51
lines changed

src/librustc/ty/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1590,7 +1590,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
15901590
pub fn return_type_impl_trait(
15911591
&self,
15921592
scope_def_id: DefId,
1593-
) -> Option<Ty> {
1593+
) -> Option<Ty<'tcx>> {
15941594
let ret_ty = self.type_of(scope_def_id);
15951595
match ret_ty.sty {
15961596
ty::FnDef(_, _) => {

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs

+60-30
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc::hir::def_id::DefId;
1515
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
1616
use rustc::infer::InferCtxt;
1717
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
18-
use rustc::ty::{TyCtxt, Ty, TyS, TyKind, Region, RegionKind, RegionVid};
18+
use rustc::ty::{self, TyCtxt, Region, RegionKind, RegionVid};
1919
use rustc_data_structures::indexed_vec::IndexVec;
2020
use rustc_errors::{Diagnostic, DiagnosticBuilder};
2121
use std::collections::VecDeque;
@@ -491,26 +491,67 @@ impl<'tcx> RegionInferenceContext<'tcx> {
491491
fr_region: Option<Region<'tcx>>,
492492
outlived_fr_region: Option<Region<'tcx>>,
493493
) {
494-
if let (Some(f), Some(RegionKind::ReStatic)) = (fr_region, outlived_fr_region) {
495-
if let Some(TyS {
496-
sty: TyKind::Anon(did, _),
494+
if let (Some(f), Some(ty::RegionKind::ReStatic)) = (fr_region, outlived_fr_region) {
495+
if let Some(ty::TyS {
496+
sty: ty::TyKind::Anon(did, substs),
497497
..
498-
}) = self.return_type_impl_trait(infcx, f) {
498+
}) = infcx.tcx.is_suitable_region(f)
499+
.map(|r| r.def_id)
500+
.map(|id| infcx.tcx.return_type_impl_trait(id))
501+
.unwrap_or(None)
502+
{
503+
let has_static_predicate = {
504+
let predicates_of = infcx.tcx.predicates_of(*did);
505+
let bounds = predicates_of.instantiate(infcx.tcx, substs);
506+
507+
let mut found = false;
508+
for predicate in bounds.predicates {
509+
if let ty::Predicate::TypeOutlives(binder) = predicate {
510+
if let ty::OutlivesPredicate(
511+
_,
512+
RegionKind::ReStatic
513+
) = binder.skip_binder() {
514+
found = true;
515+
break;
516+
}
517+
}
518+
}
519+
520+
found
521+
};
522+
523+
debug!("add_static_impl_trait_suggestion: has_static_predicate={:?}",
524+
has_static_predicate);
499525
let static_str = keywords::StaticLifetime.name();
500-
let span = infcx.tcx.def_span(*did);
501-
if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
502-
diag.span_suggestion(
503-
span,
504-
&format!(
505-
"you can add a constraint to the return type to make it last \
506-
less than `{}` and match `{}`",
507-
static_str, fr_name,
508-
),
509-
match fr_name {
510-
RegionName::Named(name) => format!("{} + {}", snippet, name),
511-
RegionName::Synthesized(_) => format!("{} + '_", snippet),
512-
},
513-
);
526+
if has_static_predicate {
527+
let span = self.get_span_of_named_region(infcx.tcx, f, &fr_name);
528+
if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
529+
diag.span_suggestion(
530+
span,
531+
&format!(
532+
"you can add a constraint to the definition of `{}` to require it \
533+
outlive `{}`",
534+
fr_name, static_str,
535+
),
536+
format!("{}: {}", snippet, static_str),
537+
);
538+
}
539+
} else {
540+
let span = infcx.tcx.def_span(*did);
541+
if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
542+
diag.span_suggestion(
543+
span,
544+
&format!(
545+
"you can add a constraint to the return type to make it last \
546+
less than `{}` and match `{}`",
547+
static_str, fr_name,
548+
),
549+
match fr_name {
550+
RegionName::Named(name) => format!("{} + {}", snippet, name),
551+
RegionName::Synthesized(_) => format!("{} + '_", snippet),
552+
},
553+
);
554+
}
514555
}
515556
}
516557
}
@@ -538,15 +579,4 @@ impl<'tcx> RegionInferenceContext<'tcx> {
538579
let (_, span, _) = self.best_blame_constraint(mir, tcx, fr1, |r| r == fr2);
539580
span
540581
}
541-
542-
fn return_type_impl_trait<'cx>(
543-
&self,
544-
infcx: &'cx InferCtxt<'_, '_, 'tcx>,
545-
outlived_fr_region: Region<'tcx>,
546-
) -> Option<Ty<'cx>> {
547-
infcx.tcx.is_suitable_region(outlived_fr_region)
548-
.map(|r| r.def_id)
549-
.map(|id| infcx.tcx.return_type_impl_trait(id))
550-
.unwrap_or(None)
551-
}
552582
}

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs

+40-17
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc::util::ppaux::with_highlight_region;
2222
use rustc_errors::DiagnosticBuilder;
2323
use syntax::ast::{Name, DUMMY_NODE_ID};
2424
use syntax::symbol::keywords;
25+
use syntax_pos::Span;
2526
use syntax_pos::symbol::InternedString;
2627

2728
/// Name of a region used in error reporting. Variants denote the source of the region name -
@@ -33,6 +34,14 @@ pub(crate) enum RegionName {
3334
Synthesized(InternedString),
3435
}
3536

37+
impl RegionName {
38+
fn as_interned_string(&self) -> &InternedString {
39+
match self {
40+
RegionName::Named(name) | RegionName::Synthesized(name) => name,
41+
}
42+
}
43+
}
44+
3645
impl Display for RegionName {
3746
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3847
match self {
@@ -121,8 +130,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
121130
match error_region {
122131
ty::ReEarlyBound(ebr) => {
123132
if ebr.has_name() {
124-
self.highlight_named_span(tcx, error_region, &ebr.name, diag);
125-
Some(RegionName::Named(ebr.name))
133+
let name = RegionName::Named(ebr.name);
134+
self.highlight_named_span(tcx, error_region, &name, diag);
135+
Some(name)
126136
} else {
127137
None
128138
}
@@ -134,8 +144,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
134144

135145
ty::ReFree(free_region) => match free_region.bound_region {
136146
ty::BoundRegion::BrNamed(_, name) => {
147+
let name = RegionName::Named(name);
137148
self.highlight_named_span(tcx, error_region, &name, diag);
138-
Some(RegionName::Named(name))
149+
Some(name)
139150
},
140151

141152
ty::BoundRegion::BrEnv => {
@@ -198,6 +209,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
198209
}
199210
}
200211

212+
/// Get the span of a named region.
213+
pub(super) fn get_span_of_named_region(
214+
&self,
215+
tcx: TyCtxt<'_, '_, 'tcx>,
216+
error_region: &RegionKind,
217+
name: &RegionName,
218+
) -> Span {
219+
let scope = error_region.free_region_binding_scope(tcx);
220+
let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
221+
222+
let span = tcx.sess.source_map().def_span(tcx.hir.span(node));
223+
if let Some(param) = tcx.hir.get_generics(scope).and_then(|generics| {
224+
generics.get_named(name.as_interned_string())
225+
}) {
226+
param.span
227+
} else {
228+
span
229+
}
230+
}
231+
201232
/// Highlight a named span to provide context for error messages that
202233
/// mention that span, for example:
203234
///
@@ -216,23 +247,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
216247
&self,
217248
tcx: TyCtxt<'_, '_, 'tcx>,
218249
error_region: &RegionKind,
219-
name: &InternedString,
250+
name: &RegionName,
220251
diag: &mut DiagnosticBuilder<'_>,
221252
) {
222-
let cm = tcx.sess.source_map();
223-
224-
let scope = error_region.free_region_binding_scope(tcx);
225-
let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
226-
227-
let mut sp = cm.def_span(tcx.hir.span(node));
228-
if let Some(param) = tcx.hir
229-
.get_generics(scope)
230-
.and_then(|generics| generics.get_named(name))
231-
{
232-
sp = param.span;
233-
}
253+
let span = self.get_span_of_named_region(tcx, error_region, name);
234254

235-
diag.span_label(sp, format!("lifetime `{}` defined here", name));
255+
diag.span_label(
256+
span,
257+
format!("lifetime `{}` defined here", name),
258+
);
236259
}
237260

238261
/// Find an argument that contains `fr` and label it with a fully

src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ error: unsatisfied lifetime constraints
2121
|
2222
LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
2323
| -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static`
24-
help: you can add a constraint to the return type to make it last less than `'static` and match `'a`
24+
help: you can add a constraint to the definition of `'a` to require it outlive `'static`
2525
|
26-
LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x }
27-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
LL | fn with_bound<'a: 'static>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
27+
| ^^^^^^^^^^^
2828

2929
error: unsatisfied lifetime constraints
3030
--> $DIR/must_outlive_least_region_or_bound.rs:29:5

0 commit comments

Comments
 (0)