Skip to content

Commit 7bbbaab

Browse files
committed
Auto merge of rust-lang#105805 - yanchen4791:issue-105227-fix, r=estebank
Suggest adding named lifetime when the return contains value borrowed from more than one lifetimes of function inputs fix for rust-lang#105227. The problem: The suggestion of adding an explicit `'_` lifetime bound is **incorrect** when the function's return type contains a value which could be borrowed from more than one lifetimes of the function's inputs. Instead, a named lifetime parameter can be introduced in such a case. The solution: Checking the number of elided lifetimes in the function signature. If more than one lifetimes found in the function inputs when the suggestion of adding explicit `'_` lifetime, change it to using named lifetime parameter `'a` instead.
2 parents 1146560 + 523fe7a commit 7bbbaab

File tree

8 files changed

+184
-40
lines changed

8 files changed

+184
-40
lines changed

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
192192
// buffered in the `MirBorrowckCtxt`.
193193

194194
let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
195+
let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
196+
None;
195197

196198
for nll_error in nll_errors.into_iter() {
197199
match nll_error {
@@ -234,13 +236,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
234236
let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty);
235237
let named_key = self.regioncx.name_regions(self.infcx.tcx, key);
236238
let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region);
237-
self.buffer_error(unexpected_hidden_region_diagnostic(
239+
let mut diag = unexpected_hidden_region_diagnostic(
238240
self.infcx.tcx,
239241
span,
240242
named_ty,
241243
named_region,
242244
named_key,
243-
));
245+
);
246+
if last_unexpected_hidden_region != Some((span, named_ty, named_key)) {
247+
self.buffer_error(diag);
248+
last_unexpected_hidden_region = Some((span, named_ty, named_key));
249+
} else {
250+
diag.delay_as_bug();
251+
}
244252
}
245253

246254
RegionErrorKind::BoundUniversalRegionError {
@@ -730,6 +738,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
730738
Some(arg),
731739
captures,
732740
Some((param.param_ty_span, param.param_ty.to_string())),
741+
self.infcx.tcx.is_suitable_region(f).map(|r| r.def_id),
733742
);
734743
}
735744
}

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
303303
None,
304304
format!("captures `{}`", hidden_region),
305305
None,
306+
Some(reg_info.def_id),
306307
)
307308
}
308309
}

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs

+70-7
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ use rustc_data_structures::fx::FxIndexSet;
88
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
99
use rustc_hir::def_id::DefId;
1010
use rustc_hir::intravisit::{walk_ty, Visitor};
11-
use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
11+
use rustc_hir::{
12+
self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node,
13+
TyKind,
14+
};
1215
use rustc_middle::ty::{
1316
self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
1417
};
1518
use rustc_span::symbol::Ident;
1619
use rustc_span::Span;
1720

21+
use rustc_span::def_id::LocalDefId;
1822
use std::ops::ControlFlow;
1923

2024
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
@@ -268,6 +272,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
268272
Some(arg),
269273
captures,
270274
Some((param.param_ty_span, param.param_ty.to_string())),
275+
Some(anon_reg_sup.def_id),
271276
);
272277

273278
let reported = err.emit();
@@ -283,6 +288,7 @@ pub fn suggest_new_region_bound(
283288
arg: Option<String>,
284289
captures: String,
285290
param: Option<(Span, String)>,
291+
scope_def_id: Option<LocalDefId>,
286292
) {
287293
debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
288294
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
@@ -340,12 +346,69 @@ pub fn suggest_new_region_bound(
340346
_ => false,
341347
}) {
342348
} else {
343-
err.span_suggestion_verbose(
344-
fn_return.span.shrink_to_hi(),
345-
&format!("{declare} `{ty}` {captures}, {explicit}",),
346-
&plus_lt,
347-
Applicability::MaybeIncorrect,
348-
);
349+
// get a lifetime name of existing named lifetimes if any
350+
let existing_lt_name = if let Some(id) = scope_def_id
351+
&& let Some(generics) = tcx.hir().get_generics(id)
352+
&& let named_lifetimes = generics
353+
.params
354+
.iter()
355+
.filter(|p| matches!(p.kind, GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }))
356+
.map(|p| { if let hir::ParamName::Plain(name) = p.name {Some(name.to_string())} else {None}})
357+
.filter(|n| ! matches!(n, None))
358+
.collect::<Vec<_>>()
359+
&& named_lifetimes.len() > 0 {
360+
named_lifetimes[0].clone()
361+
} else {
362+
None
363+
};
364+
let name = if let Some(name) = &existing_lt_name {
365+
format!("{}", name)
366+
} else {
367+
format!("'a")
368+
};
369+
// if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
370+
// introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
371+
if let Some(id) = scope_def_id
372+
&& let Some(generics) = tcx.hir().get_generics(id)
373+
&& let mut spans_suggs = generics
374+
.params
375+
.iter()
376+
.filter(|p| p.is_elided_lifetime())
377+
.map(|p|
378+
if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_)
379+
(p.span.shrink_to_hi(),format!("{name} "))
380+
} else { // Underscore (elided with '_)
381+
(p.span, format!("{name}"))
382+
}
383+
)
384+
.collect::<Vec<_>>()
385+
&& spans_suggs.len() > 1
386+
{
387+
let use_lt =
388+
if existing_lt_name == None {
389+
spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
390+
format!("you can introduce a named lifetime parameter `{name}`")
391+
} else {
392+
// make use the existing named lifetime
393+
format!("you can use the named lifetime parameter `{name}`")
394+
};
395+
spans_suggs
396+
.push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
397+
err.multipart_suggestion_verbose(
398+
&format!(
399+
"{declare} `{ty}` {captures}, {use_lt}",
400+
),
401+
spans_suggs,
402+
Applicability::MaybeIncorrect,
403+
);
404+
} else {
405+
err.span_suggestion_verbose(
406+
fn_return.span.shrink_to_hi(),
407+
&format!("{declare} `{ty}` {captures}, {explicit}",),
408+
&plus_lt,
409+
Applicability::MaybeIncorrect,
410+
);
411+
}
349412
}
350413
}
351414
TyKind::TraitObject(_, lt, _) => {

src/test/ui/impl-trait/static-return-lifetime-infered.rs

-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ impl A {
66
fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
77
self.x.iter().map(|a| a.0)
88
//~^ ERROR: captures lifetime that does not appear in bounds
9-
//~| ERROR: captures lifetime that does not appear in bounds
109
}
1110
fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> {
1211
self.x.iter().map(|a| a.0)
1312
//~^ ERROR: captures lifetime that does not appear in bounds
14-
//~| ERROR: captures lifetime that does not appear in bounds
1513
}
1614
}
1715

src/test/ui/impl-trait/static-return-lifetime-infered.stderr

+3-29
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,10 @@ LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + '_ {
1212
| ++++
1313

1414
error[E0700]: hidden type for `impl Iterator<Item = u32>` captures lifetime that does not appear in bounds
15-
--> $DIR/static-return-lifetime-infered.rs:7:9
16-
|
17-
LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
18-
| ----- hidden type `Map<std::slice::Iter<'_, (u32, u32)>, [closure@$DIR/static-return-lifetime-infered.rs:7:27: 7:30]>` captures the anonymous lifetime defined here
19-
LL | self.x.iter().map(|a| a.0)
20-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
21-
|
22-
help: to declare that `impl Iterator<Item = u32>` captures `'_`, you can add an explicit `'_` lifetime bound
23-
|
24-
LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + '_ {
25-
| ++++
26-
27-
error[E0700]: hidden type for `impl Iterator<Item = u32>` captures lifetime that does not appear in bounds
28-
--> $DIR/static-return-lifetime-infered.rs:12:9
29-
|
30-
LL | fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> {
31-
| -- hidden type `Map<std::slice::Iter<'a, (u32, u32)>, [closure@$DIR/static-return-lifetime-infered.rs:12:27: 12:30]>` captures the lifetime `'a` as defined here
32-
LL | self.x.iter().map(|a| a.0)
33-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
34-
|
35-
help: to declare that `impl Iterator<Item = u32>` captures `'a`, you can add an explicit `'a` lifetime bound
36-
|
37-
LL | fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> + 'a {
38-
| ++++
39-
40-
error[E0700]: hidden type for `impl Iterator<Item = u32>` captures lifetime that does not appear in bounds
41-
--> $DIR/static-return-lifetime-infered.rs:12:9
15+
--> $DIR/static-return-lifetime-infered.rs:11:9
4216
|
4317
LL | fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> {
44-
| -- hidden type `Map<std::slice::Iter<'a, (u32, u32)>, [closure@$DIR/static-return-lifetime-infered.rs:12:27: 12:30]>` captures the lifetime `'a` as defined here
18+
| -- hidden type `Map<std::slice::Iter<'a, (u32, u32)>, [closure@$DIR/static-return-lifetime-infered.rs:11:27: 11:30]>` captures the lifetime `'a` as defined here
4519
LL | self.x.iter().map(|a| a.0)
4620
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
4721
|
@@ -50,6 +24,6 @@ help: to declare that `impl Iterator<Item = u32>` captures `'a`, you can add an
5024
LL | fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> + 'a {
5125
| ++++
5226

53-
error: aborting due to 4 previous errors
27+
error: aborting due to 2 previous errors
5428

5529
For more information about this error, try `rustc --explain E0700`.
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Regression test for issue #105227.
2+
3+
// run-rustfix
4+
#![allow(warnings)]
5+
fn chars0<'a>(v :(&'a str, &'a str)) -> impl Iterator<Item = char> + 'a {
6+
//~^ HELP to declare that `impl Iterator<Item = char>` captures `'_`, you can introduce a named lifetime parameter `'a`
7+
v.0.chars().chain(v.1.chars())
8+
//~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds
9+
}
10+
11+
fn chars1<'a>(v0 : &'a str, v1 : &'a str) -> impl Iterator<Item = char> + 'a {
12+
//~^ HELP to declare that `impl Iterator<Item = char>` captures `'_`, you can introduce a named lifetime parameter `'a`
13+
v0.chars().chain(v1.chars())
14+
//~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound
15+
}
16+
17+
fn chars2<'b>(v0 : &'b str, v1 : &'b str, v2 : &'b str) ->
18+
//~^ HELP to declare that `impl Iterator<Item = char>` captures `'_`, you can use the named lifetime parameter `'b`
19+
(impl Iterator<Item = char> + 'b , &'b str)
20+
{
21+
(v0.chars().chain(v1.chars()), v2)
22+
//~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound
23+
}
24+
25+
fn main() {
26+
}

src/test/ui/lifetimes/issue-105227.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Regression test for issue #105227.
2+
3+
// run-rustfix
4+
#![allow(warnings)]
5+
fn chars0(v :(& str, &str)) -> impl Iterator<Item = char> {
6+
//~^ HELP to declare that `impl Iterator<Item = char>` captures `'_`, you can introduce a named lifetime parameter `'a`
7+
v.0.chars().chain(v.1.chars())
8+
//~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds
9+
}
10+
11+
fn chars1(v0 : & str, v1 : &str) -> impl Iterator<Item = char> {
12+
//~^ HELP to declare that `impl Iterator<Item = char>` captures `'_`, you can introduce a named lifetime parameter `'a`
13+
v0.chars().chain(v1.chars())
14+
//~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound
15+
}
16+
17+
fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) ->
18+
//~^ HELP to declare that `impl Iterator<Item = char>` captures `'_`, you can use the named lifetime parameter `'b`
19+
(impl Iterator<Item = char>, &'b str)
20+
{
21+
(v0.chars().chain(v1.chars()), v2)
22+
//~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound
23+
}
24+
25+
fn main() {
26+
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds
2+
--> $DIR/issue-105227.rs:7:5
3+
|
4+
LL | fn chars0(v :(& str, &str)) -> impl Iterator<Item = char> {
5+
| ----- hidden type `std::iter::Chain<Chars<'_>, Chars<'_>>` captures the anonymous lifetime defined here
6+
LL |
7+
LL | v.0.chars().chain(v.1.chars())
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9+
|
10+
help: to declare that `impl Iterator<Item = char>` captures `'_`, you can introduce a named lifetime parameter `'a`
11+
|
12+
LL | fn chars0<'a>(v :(&'a str, &'a str)) -> impl Iterator<Item = char> + 'a {
13+
| ++++ ++ ++ ++++
14+
15+
error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds
16+
--> $DIR/issue-105227.rs:13:5
17+
|
18+
LL | fn chars1(v0 : & str, v1 : &str) -> impl Iterator<Item = char> {
19+
| ----- hidden type `std::iter::Chain<Chars<'_>, Chars<'_>>` captures the anonymous lifetime defined here
20+
LL |
21+
LL | v0.chars().chain(v1.chars())
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
help: to declare that `impl Iterator<Item = char>` captures `'_`, you can introduce a named lifetime parameter `'a`
25+
|
26+
LL | fn chars1<'a>(v0 : &'a str, v1 : &'a str) -> impl Iterator<Item = char> + 'a {
27+
| ++++ ++ ++ ++++
28+
29+
error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds
30+
--> $DIR/issue-105227.rs:21:5
31+
|
32+
LL | fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) ->
33+
| ---- hidden type `std::iter::Chain<Chars<'_>, Chars<'_>>` captures the anonymous lifetime defined here
34+
...
35+
LL | (v0.chars().chain(v1.chars()), v2)
36+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
37+
|
38+
help: to declare that `impl Iterator<Item = char>` captures `'_`, you can use the named lifetime parameter `'b`
39+
|
40+
LL ~ fn chars2<'b>(v0 : &'b str, v1 : &'b str, v2 : &'b str) ->
41+
LL |
42+
LL ~ (impl Iterator<Item = char> + 'b , &'b str)
43+
|
44+
45+
error: aborting due to 3 previous errors
46+
47+
For more information about this error, try `rustc --explain E0700`.

0 commit comments

Comments
 (0)