Skip to content

Commit da175c7

Browse files
committed
Make lifetime errors more precise in the presence of Fresh lifetimes.
1 parent 8da2707 commit da175c7

27 files changed

+434
-46
lines changed

compiler/rustc_hir/src/hir.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,17 @@ impl LifetimeName {
131131
}
132132
}
133133

134+
pub fn is_anonymous(&self) -> bool {
135+
match *self {
136+
LifetimeName::ImplicitObjectLifetimeDefault
137+
| LifetimeName::Implicit
138+
| LifetimeName::Underscore
139+
| LifetimeName::Param(ParamName::Fresh(_))
140+
| LifetimeName::Error => true,
141+
LifetimeName::Static | LifetimeName::Param(_) => false,
142+
}
143+
}
144+
134145
pub fn is_elided(&self) -> bool {
135146
match self {
136147
LifetimeName::ImplicitObjectLifetimeDefault

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ use rustc_middle::ty::{
7272
subst::{GenericArgKind, Subst, SubstsRef},
7373
Binder, EarlyBinder, List, Region, Ty, TyCtxt, TypeFoldable,
7474
};
75-
use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
75+
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
7676
use rustc_target::spec::abi;
7777
use std::ops::ControlFlow;
7878
use std::{cmp, fmt, iter};
@@ -161,7 +161,12 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>(
161161
{
162162
sp = param.span;
163163
}
164-
(format!("the lifetime `{}` as defined here", br.name), sp)
164+
let text = if br.name == kw::UnderscoreLifetime {
165+
format!("the anonymous lifetime as defined here")
166+
} else {
167+
format!("the lifetime `{}` as defined here", br.name)
168+
};
169+
(text, sp)
165170
}
166171
ty::ReFree(ty::FreeRegion {
167172
bound_region: ty::BoundRegionKind::BrNamed(_, name), ..
@@ -172,7 +177,12 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>(
172177
{
173178
sp = param.span;
174179
}
175-
(format!("the lifetime `{}` as defined here", name), sp)
180+
let text = if name == kw::UnderscoreLifetime {
181+
format!("the anonymous lifetime as defined here")
182+
} else {
183+
format!("the lifetime `{}` as defined here", name)
184+
};
185+
(text, sp)
176186
}
177187
ty::ReFree(ref fr) => match fr.bound_region {
178188
ty::BrAnon(idx) => {

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

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
1212
use rustc_hir as hir;
1313
use rustc_hir::{GenericParamKind, Ty};
1414
use rustc_middle::ty::Region;
15+
use rustc_span::symbol::kw;
1516

1617
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
1718
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
@@ -169,7 +170,7 @@ pub fn suggest_adding_lifetime_params<'tcx>(
169170
return false;
170171
};
171172

172-
if !lifetime_sub.name.is_elided() || !lifetime_sup.name.is_elided() {
173+
if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() {
173174
return false;
174175
};
175176

@@ -188,32 +189,37 @@ pub fn suggest_adding_lifetime_params<'tcx>(
188189
_ => return false,
189190
};
190191

191-
let (suggestion_param_name, introduce_new) = generics
192+
let suggestion_param_name = generics
192193
.params
193194
.iter()
194-
.find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
195-
.and_then(|p| tcx.sess.source_map().span_to_snippet(p.span).ok())
196-
.map(|name| (name, false))
197-
.unwrap_or_else(|| ("'a".to_string(), true));
198-
199-
let mut suggestions = vec![
200-
if let hir::LifetimeName::Underscore = lifetime_sub.name {
201-
(lifetime_sub.span, suggestion_param_name.clone())
195+
.filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
196+
.map(|p| p.name.ident().name)
197+
.find(|i| *i != kw::UnderscoreLifetime);
198+
let introduce_new = suggestion_param_name.is_none();
199+
let suggestion_param_name =
200+
suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned());
201+
202+
debug!(?lifetime_sup.span);
203+
debug!(?lifetime_sub.span);
204+
let make_suggestion = |span: rustc_span::Span| {
205+
if span.is_empty() {
206+
(span, format!("{}, ", suggestion_param_name))
207+
} else if let Ok("&") = tcx.sess.source_map().span_to_snippet(span).as_deref() {
208+
(span.shrink_to_hi(), format!("{} ", suggestion_param_name))
202209
} else {
203-
(lifetime_sub.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
204-
},
205-
if let hir::LifetimeName::Underscore = lifetime_sup.name {
206-
(lifetime_sup.span, suggestion_param_name.clone())
207-
} else {
208-
(lifetime_sup.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
209-
},
210-
];
210+
(span, suggestion_param_name.clone())
211+
}
212+
};
213+
let mut suggestions =
214+
vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)];
211215

212216
if introduce_new {
213-
let new_param_suggestion = match &generics.params {
214-
[] => (generics.span, format!("<{}>", suggestion_param_name)),
215-
[first, ..] => (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)),
216-
};
217+
let new_param_suggestion =
218+
if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) {
219+
(first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name))
220+
} else {
221+
(generics.span, format!("<{}>", suggestion_param_name))
222+
};
217223

218224
suggestions.push(new_param_suggestion);
219225
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_
44
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
55
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
66
use rustc_middle::ty;
7+
use rustc_span::symbol::kw;
78

89
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
910
/// When given a `ConcreteFailure` for a function with parameters containing a named region and
@@ -67,7 +68,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
6768
let is_impl_item = region_info.is_impl_item;
6869

6970
match br {
70-
ty::BrAnon(_) => {}
71+
ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) => {}
7172
_ => {
7273
/* not an anonymous region */
7374
debug!("try_report_named_anon_conflict: not an anonymous region");

src/test/ui/async-await/issue-76547.base.stderr

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | async fn fut(bufs: &mut [&mut [u8]]) {
55
| ---------------- these two types are declared with different lifetimes...
66
LL | ListFut(bufs).await
77
| ^^^^ ...but data from `bufs` flows into `bufs` here
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | async fn fut<'a>(bufs: &'a mut [&'a mut [u8]]) {
13+
| ++++ ++ ++
814

915
error[E0623]: lifetime mismatch
1016
--> $DIR/issue-76547.rs:39:14
@@ -13,6 +19,12 @@ LL | async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
1319
| ---------------- these two types are declared with different lifetimes...
1420
LL | ListFut2(bufs).await
1521
| ^^^^ ...but data from `bufs` flows into `bufs` here
22+
|
23+
= note: each elided lifetime in input position becomes a distinct lifetime
24+
help: consider introducing a named lifetime parameter
25+
|
26+
LL | async fn fut2<'a>(bufs: &'a mut [&'a mut [u8]]) -> i32 {
27+
| ++++ ++ ++
1628

1729
error: aborting due to 2 previous errors
1830

src/test/ui/async-await/issue-76547.nll.stderr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | async fn fut(bufs: &mut [&mut [u8]]) {
77
| let's call the lifetime of this reference `'1`
88
LL | ListFut(bufs).await
99
| ^^^^ this usage requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | async fn fut<'a>(bufs: &'a mut [&'a mut [u8]]) {
14+
| ++++ ++ ++
1015

1116
error: lifetime may not live long enough
1217
--> $DIR/issue-76547.rs:39:14
@@ -17,6 +22,11 @@ LL | async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
1722
| let's call the lifetime of this reference `'1`
1823
LL | ListFut2(bufs).await
1924
| ^^^^ this usage requires that `'1` must outlive `'2`
25+
|
26+
help: consider introducing a named lifetime parameter
27+
|
28+
LL | async fn fut2<'a>(bufs: &'a mut [&'a mut [u8]]) -> i32 {
29+
| ++++ ++ ++
2030

2131
error: aborting due to 2 previous errors
2232

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
error[E0623]: lifetime mismatch
1+
error[E0621]: explicit lifetime required in the type of `foo`
22
--> $DIR/issue-63388-1.rs:19:9
33
|
44
LL | &'a self, foo: &dyn Foo
5-
| -------- this parameter and the return type are declared with different lifetimes...
6-
LL | ) -> &dyn Foo
7-
| --------
5+
| -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)`
86
...
97
LL | foo
10-
| ^^^ ...but data from `foo` is returned here
8+
| ^^^ lifetime `'a` required
119

1210
error: aborting due to previous error
1311

14-
For more information about this error, try `rustc --explain E0623`.
12+
For more information about this error, try `rustc --explain E0621`.
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
error: lifetime may not live long enough
1+
error[E0621]: explicit lifetime required in the type of `foo`
22
--> $DIR/issue-63388-1.rs:17:5
33
|
4-
LL | async fn do_sth<'a>(
5-
| -- lifetime `'a` defined here
64
LL | &'a self, foo: &dyn Foo
7-
| - let's call the lifetime of this reference `'1`
5+
| -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)`
86
LL | ) -> &dyn Foo
97
LL | / {
108
LL | |
119
LL | | foo
1210
LL | |
1311
LL | | }
14-
| |_____^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
12+
| |_____^ lifetime `'a` required
1513

1614
error: aborting due to previous error
1715

16+
For more information about this error, try `rustc --explain E0621`.

src/test/ui/async-await/issues/issue-63388-1.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ impl Xyz {
1515
&'a self, foo: &dyn Foo
1616
) -> &dyn Foo
1717
{
18-
//[nll]~^ ERROR lifetime may not live long enough
18+
//[nll]~^ ERROR explicit lifetime required in the type of `foo` [E0621]
1919
foo
20-
//[base]~^ ERROR lifetime mismatch
20+
//[base]~^ ERROR explicit lifetime required in the type of `foo` [E0621]
2121
}
2222
}
2323

src/test/ui/error-codes/E0308-2.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | impl Eq for &dyn DynEq {}
66
|
77
= note: expected trait `<&dyn DynEq as PartialEq>`
88
found trait `<&(dyn DynEq + 'static) as PartialEq>`
9-
note: the lifetime `'_` as defined here...
9+
note: the anonymous lifetime as defined here...
1010
--> $DIR/E0308-2.rs:9:13
1111
|
1212
LL | impl Eq for &dyn DynEq {}

src/test/ui/issues/issue-17905-2.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ note: the anonymous lifetime defined here...
1111
|
1212
LL | fn say(self: &Pair<&str, isize>) {
1313
| ^^^^
14-
note: ...does not necessarily outlive the lifetime `'_` as defined here
14+
note: ...does not necessarily outlive the anonymous lifetime as defined here
1515
--> $DIR/issue-17905-2.rs:5:5
1616
|
1717
LL | &str,
@@ -25,7 +25,7 @@ LL | fn say(self: &Pair<&str, isize>) {
2525
|
2626
= note: expected struct `Pair<&str, _>`
2727
found struct `Pair<&str, _>`
28-
note: the lifetime `'_` as defined here...
28+
note: the anonymous lifetime as defined here...
2929
--> $DIR/issue-17905-2.rs:5:5
3030
|
3131
LL | &str,

src/test/ui/issues/issue-65230.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | impl T1 for &dyn T2 {}
66
|
77
= note: expected trait `<&dyn T2 as T0>`
88
found trait `<&(dyn T2 + 'static) as T0>`
9-
note: the lifetime `'_` as defined here...
9+
note: the anonymous lifetime as defined here...
1010
--> $DIR/issue-65230.rs:8:13
1111
|
1212
LL | impl T1 for &dyn T2 {}

src/test/ui/nll/issue-52742.base.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0312]: lifetime of reference outlives lifetime of borrowed content...
44
LL | self.y = b.z
55
| ^^^
66
|
7-
note: ...the reference is valid for the lifetime `'_` as defined here...
7+
note: ...the reference is valid for the anonymous lifetime as defined here...
88
--> $DIR/issue-52742.rs:15:10
99
|
1010
LL | impl Foo<'_, '_> {

src/test/ui/nll/issue-55394.base.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ note: ...so that reference does not outlive borrowed content
1414
|
1515
LL | Foo { bar }
1616
| ^^^
17-
note: but, the lifetime must be valid for the lifetime `'_` as defined here...
17+
note: but, the lifetime must be valid for the anonymous lifetime as defined here...
1818
--> $DIR/issue-55394.rs:11:10
1919
|
2020
LL | impl Foo<'_> {

src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea
44
LL | async fn f(self: Pin<&Self>) -> impl Clone { self }
55
| - ^^^^^^^^
66
| |
7-
| hidden type `Pin<&Foo>` captures the lifetime `'_` as defined here
7+
| hidden type `Pin<&Foo>` captures the anonymous lifetime as defined here
88
|
99
help: to declare that the `impl Trait` captures `'_`, you can add an explicit `'_` lifetime bound
1010
|

src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.base.stderr

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | async fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f }
55
| ---- ---- ^ ...but data from `f` is returned here
66
| |
77
| this parameter and the return type are declared with different lifetimes...
8+
|
9+
= note: each elided lifetime in input position becomes a distinct lifetime
10+
help: consider introducing a named lifetime parameter and update trait if needed
11+
|
12+
LL | async fn a<'a>(self: Pin<&'a Foo>, f: &'a Foo) -> &Foo { f }
13+
| ++++ ++ ++
814

915
error[E0623]: lifetime mismatch
1016
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:15:82
@@ -13,6 +19,12 @@ LL | async fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (
1319
| ---- ----------------- ^ ...but data from `f` is returned here
1420
| |
1521
| this parameter and the return type are declared with different lifetimes...
22+
|
23+
= note: each elided lifetime in input position becomes a distinct lifetime
24+
help: consider introducing a named lifetime parameter and update trait if needed
25+
|
26+
LL | async fn c<'a>(self: Pin<&'a Self>, f: &'a Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
27+
| ++++ ++ ++
1628

1729
error[E0623]: lifetime mismatch
1830
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:22:64

src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.nll.stderr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ LL | async fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f }
66
| | |
77
| | let's call the lifetime of this reference `'1`
88
| let's call the lifetime of this reference `'2`
9+
|
10+
help: consider introducing a named lifetime parameter and update trait if needed
11+
|
12+
LL | async fn a<'a>(self: Pin<&'a Foo>, f: &'a Foo) -> &Foo { f }
13+
| ++++ ++ ++
914

1015
error: lifetime may not live long enough
1116
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:15:75
@@ -15,6 +20,11 @@ LL | async fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (
1520
| | |
1621
| | let's call the lifetime of this reference `'1`
1722
| let's call the lifetime of this reference `'2`
23+
|
24+
help: consider introducing a named lifetime parameter and update trait if needed
25+
|
26+
LL | async fn c<'a>(self: Pin<&'a Self>, f: &'a Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
27+
| ++++ ++ ++
1828

1929
error: lifetime may not live long enough
2030
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:22:64

0 commit comments

Comments
 (0)