Skip to content

Commit a23b4f5

Browse files
authored
Rollup merge of rust-lang#72804 - estebank:opaque-missing-lts-in-fn-2, r=nikomatsakis
Further tweak lifetime errors involving `dyn Trait` and `impl Trait` in return position * Suggest substituting `'static` lifetime in impl/dyn `Trait + 'static` instead of `Trait + 'static + '_` * When `'static` is explicit, also suggest constraining argument with it * Reduce verbosity of suggestion message and mention lifetime in label * Tweak output for overlapping required/captured spans * Give these errors an error code Follow up to rust-lang#72543. r? @nikomatsakis
2 parents 6dbc959 + bfe1434 commit a23b4f5

25 files changed

+611
-205
lines changed

src/librustc_error_codes/error_codes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ E0752: include_str!("./error_codes/E0752.md"),
439439
E0753: include_str!("./error_codes/E0753.md"),
440440
E0754: include_str!("./error_codes/E0754.md"),
441441
E0758: include_str!("./error_codes/E0758.md"),
442+
E0759: include_str!("./error_codes/E0759.md"),
442443
E0760: include_str!("./error_codes/E0760.md"),
443444
E0761: include_str!("./error_codes/E0761.md"),
444445
E0762: include_str!("./error_codes/E0762.md"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
A `'static` requirement in a return type involving a trait is not fulfilled.
2+
3+
Erroneous code examples:
4+
5+
```compile_fail,E0759
6+
use std::fmt::Debug;
7+
8+
fn foo(x: &i32) -> impl Debug {
9+
x
10+
}
11+
```
12+
13+
```compile_fail,E0759
14+
# use std::fmt::Debug;
15+
fn bar(x: &i32) -> Box<dyn Debug> {
16+
Box::new(x)
17+
}
18+
```
19+
20+
These examples have the same semantics as the following:
21+
22+
```compile_fail,E0759
23+
# use std::fmt::Debug;
24+
fn foo(x: &i32) -> impl Debug + 'static {
25+
x
26+
}
27+
```
28+
29+
```compile_fail,E0759
30+
# use std::fmt::Debug;
31+
fn bar(x: &i32) -> Box<dyn Debug + 'static> {
32+
Box::new(x)
33+
}
34+
```
35+
36+
Both [`dyn Trait`] and [`impl Trait`] in return types have a an implicit
37+
`'static` requirement, meaning that the value implementing them that is being
38+
returned has to be either a `'static` borrow or an owned value.
39+
40+
In order to change the requirement from `'static` to be a lifetime derived from
41+
its arguments, you can add an explicit bound, either to an anonymous lifetime
42+
`'_` or some appropriate named lifetime.
43+
44+
```
45+
# use std::fmt::Debug;
46+
fn foo(x: &i32) -> impl Debug + '_ {
47+
x
48+
}
49+
fn bar(x: &i32) -> Box<dyn Debug + '_> {
50+
Box::new(x)
51+
}
52+
```
53+
54+
These are equivalent to the following explicit lifetime annotations:
55+
56+
```
57+
# use std::fmt::Debug;
58+
fn foo<'a>(x: &'a i32) -> impl Debug + 'a {
59+
x
60+
}
61+
fn bar<'a>(x: &'a i32) -> Box<dyn Debug + 'a> {
62+
Box::new(x)
63+
}
64+
```
65+
66+
[`dyn Trait`]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types
67+
[`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits

src/librustc_infer/infer/error_reporting/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -2035,8 +2035,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
20352035
self.tcx.sess,
20362036
var_origin.span(),
20372037
E0495,
2038-
"cannot infer an appropriate lifetime{} \
2039-
due to conflicting requirements",
2038+
"cannot infer an appropriate lifetime{} due to conflicting requirements",
20402039
var_description
20412040
)
20422041
}

src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs

+157-33
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
//! Error Reporting for static impl Traits.
22
3-
use crate::infer::error_reporting::msg_span_from_free_region;
43
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
54
use crate::infer::lexical_region_resolve::RegionResolutionError;
6-
use rustc_errors::{Applicability, ErrorReported};
5+
use rustc_errors::{struct_span_err, Applicability, ErrorReported};
6+
use rustc_hir::{GenericBound, ItemKind, Lifetime, LifetimeName, TyKind};
77
use rustc_middle::ty::RegionKind;
88

99
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
1010
/// Print the error message for lifetime errors when the return type is a static impl Trait.
1111
pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
12+
debug!("try_report_static_impl_trait(error={:?})", self.error);
1213
if let Some(ref error) = self.error {
1314
if let RegionResolutionError::SubSupConflict(
1415
_,
@@ -17,18 +18,36 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
1718
sub_r,
1819
sup_origin,
1920
sup_r,
20-
) = error.clone()
21+
) = error
2122
{
23+
debug!(
24+
"try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
25+
var_origin, sub_origin, sub_r, sup_origin, sup_r
26+
);
2227
let anon_reg_sup = self.tcx().is_suitable_region(sup_r)?;
23-
let (fn_return_span, is_dyn) =
24-
self.tcx().return_type_impl_or_dyn_trait(anon_reg_sup.def_id)?;
25-
if sub_r == &RegionKind::ReStatic {
28+
debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
29+
let fn_return = self.tcx().return_type_impl_or_dyn_trait(anon_reg_sup.def_id)?;
30+
debug!("try_report_static_impl_trait: fn_return={:?}", fn_return);
31+
if **sub_r == RegionKind::ReStatic {
2632
let sp = var_origin.span();
2733
let return_sp = sub_origin.span();
28-
let mut err =
29-
self.tcx().sess.struct_span_err(sp, "cannot infer an appropriate lifetime");
3034
let param_info = self.find_param_with_region(sup_r, sub_r)?;
31-
err.span_label(param_info.param_ty_span, "data with this lifetime...");
35+
let (lifetime_name, lifetime) = if sup_r.has_name() {
36+
(sup_r.to_string(), format!("lifetime `{}`", sup_r))
37+
} else {
38+
("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
39+
};
40+
let mut err = struct_span_err!(
41+
self.tcx().sess,
42+
sp,
43+
E0759,
44+
"cannot infer an appropriate lifetime"
45+
);
46+
err.span_label(
47+
param_info.param_ty_span,
48+
&format!("this data with {}...", lifetime),
49+
);
50+
debug!("try_report_static_impl_trait: param_info={:?}", param_info);
3251

3352
// We try to make the output have fewer overlapping spans if possible.
3453
if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span()))
@@ -38,41 +57,146 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
3857

3958
// Customize the spans and labels depending on their relative order so
4059
// that split sentences flow correctly.
41-
if sup_origin.span().shrink_to_hi() <= return_sp.shrink_to_lo() {
42-
err.span_label(sup_origin.span(), "...is captured here...");
43-
err.span_label(return_sp, "...and required to be `'static` by this");
60+
if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() {
61+
// Avoid the following:
62+
//
63+
// error: cannot infer an appropriate lifetime
64+
// --> $DIR/must_outlive_least_region_or_bound.rs:18:50
65+
// |
66+
// LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
67+
// | ---- ---------^-
68+
//
69+
// and instead show:
70+
//
71+
// error: cannot infer an appropriate lifetime
72+
// --> $DIR/must_outlive_least_region_or_bound.rs:18:50
73+
// |
74+
// LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
75+
// | ---- ^
76+
err.span_label(
77+
sup_origin.span(),
78+
"...is captured here, requiring it to live as long as `'static`",
79+
);
4480
} else {
45-
err.span_label(return_sp, "...is required to be `'static` by this...");
46-
err.span_label(sup_origin.span(), "...and is captured here");
81+
err.span_label(sup_origin.span(), "...is captured here...");
82+
if return_sp < sup_origin.span() {
83+
err.span_note(
84+
return_sp,
85+
"...and is required to live as long as `'static` here",
86+
);
87+
} else {
88+
err.span_label(
89+
return_sp,
90+
"...and is required to live as long as `'static` here",
91+
);
92+
}
4793
}
4894
} else {
4995
err.span_label(
5096
return_sp,
51-
"...is captured and required to be `'static` here",
97+
"...is captured and required to live as long as `'static` here",
5298
);
5399
}
54100

55-
let (lifetime, _) = msg_span_from_free_region(self.tcx(), sup_r);
56-
57-
let lifetime_name =
58-
if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
59101
// only apply this suggestion onto functions with
60102
// explicit non-desugar'able return.
61-
if fn_return_span.desugaring_kind().is_none() {
62-
let msg = format!(
63-
"to permit non-static references in {} `{} Trait` value, you can add \
64-
an explicit bound for {}",
65-
if is_dyn { "a" } else { "an" },
66-
if is_dyn { "dyn" } else { "impl" },
67-
lifetime,
68-
);
103+
if fn_return.span.desugaring_kind().is_none() {
69104
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
70-
err.span_suggestion_verbose(
71-
fn_return_span.shrink_to_hi(),
72-
&msg,
73-
format!(" + {}", lifetime_name),
74-
Applicability::MaybeIncorrect,
75-
);
105+
106+
let consider = "consider changing the";
107+
let declare = "to declare that the";
108+
let arg = match param_info.param.pat.simple_ident() {
109+
Some(simple_ident) => format!("argument `{}`", simple_ident),
110+
None => "the argument".to_string(),
111+
};
112+
let explicit =
113+
format!("you can add an explicit `{}` lifetime bound", lifetime_name);
114+
let explicit_static =
115+
format!("explicit `'static` bound to the lifetime of {}", arg);
116+
let captures = format!("captures data from {}", arg);
117+
let add_static_bound =
118+
"alternatively, add an explicit `'static` bound to this reference";
119+
let plus_lt = format!(" + {}", lifetime_name);
120+
match fn_return.kind {
121+
TyKind::OpaqueDef(item_id, _) => {
122+
let item = self.tcx().hir().item(item_id.id);
123+
let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind {
124+
opaque
125+
} else {
126+
err.emit();
127+
return Some(ErrorReported);
128+
};
129+
130+
if let Some(span) = opaque
131+
.bounds
132+
.iter()
133+
.filter_map(|arg| match arg {
134+
GenericBound::Outlives(Lifetime {
135+
name: LifetimeName::Static,
136+
span,
137+
..
138+
}) => Some(*span),
139+
_ => None,
140+
})
141+
.next()
142+
{
143+
err.span_suggestion_verbose(
144+
span,
145+
&format!("{} `impl Trait`'s {}", consider, explicit_static),
146+
lifetime_name,
147+
Applicability::MaybeIncorrect,
148+
);
149+
err.span_suggestion_verbose(
150+
param_info.param_ty_span,
151+
add_static_bound,
152+
param_info.param_ty.to_string(),
153+
Applicability::MaybeIncorrect,
154+
);
155+
} else {
156+
err.span_suggestion_verbose(
157+
fn_return.span.shrink_to_hi(),
158+
&format!(
159+
"{declare} `impl Trait` {captures}, {explicit}",
160+
declare = declare,
161+
captures = captures,
162+
explicit = explicit,
163+
),
164+
plus_lt,
165+
Applicability::MaybeIncorrect,
166+
);
167+
};
168+
}
169+
TyKind::TraitObject(_, lt) => match lt.name {
170+
LifetimeName::ImplicitObjectLifetimeDefault => {
171+
err.span_suggestion_verbose(
172+
fn_return.span.shrink_to_hi(),
173+
&format!(
174+
"{declare} trait object {captures}, {explicit}",
175+
declare = declare,
176+
captures = captures,
177+
explicit = explicit,
178+
),
179+
plus_lt,
180+
Applicability::MaybeIncorrect,
181+
);
182+
}
183+
_ => {
184+
err.span_suggestion_verbose(
185+
lt.span,
186+
&format!("{} trait object's {}", consider, explicit_static),
187+
lifetime_name,
188+
Applicability::MaybeIncorrect,
189+
);
190+
err.span_suggestion_verbose(
191+
param_info.param_ty_span,
192+
add_static_bound,
193+
param_info.param_ty.to_string(),
194+
Applicability::MaybeIncorrect,
195+
);
196+
}
197+
},
198+
_ => {}
199+
}
76200
}
77201
err.emit();
78202
return Some(ErrorReported);

src/librustc_middle/ty/context.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,10 @@ impl<'tcx> TyCtxt<'tcx> {
13831383
})
13841384
}
13851385

1386-
pub fn return_type_impl_or_dyn_trait(&self, scope_def_id: DefId) -> Option<(Span, bool)> {
1386+
pub fn return_type_impl_or_dyn_trait(
1387+
&self,
1388+
scope_def_id: DefId,
1389+
) -> Option<&'tcx hir::Ty<'tcx>> {
13871390
let hir_id = self.hir().as_local_hir_id(scope_def_id.expect_local());
13881391
let hir_output = match self.hir().get(hir_id) {
13891392
Node::Item(hir::Item {
@@ -1429,15 +1432,17 @@ impl<'tcx> TyCtxt<'tcx> {
14291432
let output = self.erase_late_bound_regions(&sig.output());
14301433
if output.is_impl_trait() {
14311434
let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap();
1432-
Some((fn_decl.output.span(), false))
1435+
if let hir::FnRetTy::Return(ty) = fn_decl.output {
1436+
return Some(ty);
1437+
}
14331438
} else {
14341439
let mut v = TraitObjectVisitor(vec![]);
14351440
rustc_hir::intravisit::walk_ty(&mut v, hir_output);
14361441
if v.0.len() == 1 {
1437-
return Some((v.0[0], true));
1442+
return Some(v.0[0]);
14381443
}
1439-
None
14401444
}
1445+
None
14411446
}
14421447
_ => None,
14431448
}

src/librustc_middle/ty/diagnostics.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -236,21 +236,24 @@ pub fn suggest_constraining_type_param(
236236
}
237237
}
238238

239-
pub struct TraitObjectVisitor(pub Vec<rustc_span::Span>);
240-
impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor {
239+
pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>);
240+
impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
241241
type Map = rustc_hir::intravisit::ErasedMap<'v>;
242242

243243
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
244244
hir::intravisit::NestedVisitorMap::None
245245
}
246246

247-
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
247+
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
248248
if let hir::TyKind::TraitObject(
249249
_,
250-
hir::Lifetime { name: hir::LifetimeName::ImplicitObjectLifetimeDefault, .. },
250+
hir::Lifetime {
251+
name: hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
252+
..
253+
},
251254
) = ty.kind
252255
{
253-
self.0.push(ty.span);
256+
self.0.push(ty);
254257
}
255258
}
256259
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
error: cannot infer an appropriate lifetime
1+
error[E0759]: cannot infer an appropriate lifetime
22
--> $DIR/issue-62097.rs:12:31
33
|
44
LL | pub async fn run_dummy_fn(&self) {
55
| ^^^^^
66
| |
7-
| data with this lifetime...
7+
| this data with an anonymous lifetime `'_`...
88
| ...is captured here...
99
LL | foo(|| self.bar()).await;
10-
| --- ...and required to be `'static` by this
10+
| --- ...and is required to live as long as `'static` here
1111

1212
error: aborting due to previous error
1313

14+
For more information about this error, try `rustc --explain E0759`.

0 commit comments

Comments
 (0)