Skip to content

Commit dea29bd

Browse files
authored
Rollup merge of rust-lang#76765 - guswynn:async_return, r=tmandry
Make it more clear what an about async fn's returns when referring to what it returns see rust-lang#76547 This is *likely* not the ONLY place that this happens to be unclear, but we can move this fn to rustc_middle or something like that and reuse it if need be, to apply it to more diagnostics One outstanding question I have is, if the fn returns (), should I make the message more clear (what about `fn f()` vs `fn f() -> ()`, can you tell those apart in the hir?) R? `@tmandry` `@rustbot` modify labels +A-diagnostics +T-compiler
2 parents 30bb1f9 + 20e032e commit dea29bd

12 files changed

+331
-133
lines changed

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

+79-33
Original file line numberDiff line numberDiff line change
@@ -102,43 +102,89 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
102102
None => String::new(),
103103
};
104104

105-
let (span_1, span_2, main_label, span_label) = match (sup_is_ret_type, sub_is_ret_type) {
106-
(None, None) => {
107-
let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id {
105+
let (span_1, span_2, main_label, span_label, future_return_type) =
106+
match (sup_is_ret_type, sub_is_ret_type) {
107+
(None, None) => {
108+
let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id {
109+
(
110+
"this type is declared with multiple lifetimes...".to_owned(),
111+
"...but data with one lifetime flows into the other here".to_owned(),
112+
)
113+
} else {
114+
(
115+
"these two types are declared with different lifetimes...".to_owned(),
116+
format!("...but data{} flows{} here", span_label_var1, span_label_var2),
117+
)
118+
};
119+
(ty_sup.span, ty_sub.span, main_label_1, span_label_1, None)
120+
}
121+
122+
(Some(ret_span), _) => {
123+
let sup_future = self.future_return_type(scope_def_id_sup);
124+
let (return_type, action) = if let Some(_) = sup_future {
125+
("returned future", "held across an await point")
126+
} else {
127+
("return type", "returned")
128+
};
129+
108130
(
109-
"this type is declared with multiple lifetimes...".to_owned(),
110-
"...but data with one lifetime flows into the other here".to_owned(),
131+
ty_sub.span,
132+
ret_span,
133+
format!(
134+
"this parameter and the {} are declared with different lifetimes...",
135+
return_type
136+
),
137+
format!("...but data{} is {} here", span_label_var1, action),
138+
sup_future,
111139
)
112-
} else {
140+
}
141+
(_, Some(ret_span)) => {
142+
let sub_future = self.future_return_type(scope_def_id_sub);
143+
let (return_type, action) = if let Some(_) = sub_future {
144+
("returned future", "held across an await point")
145+
} else {
146+
("return type", "returned")
147+
};
148+
113149
(
114-
"these two types are declared with different lifetimes...".to_owned(),
115-
format!("...but data{} flows{} here", span_label_var1, span_label_var2),
150+
ty_sup.span,
151+
ret_span,
152+
format!(
153+
"this parameter and the {} are declared with different lifetimes...",
154+
return_type
155+
),
156+
format!("...but data{} is {} here", span_label_var1, action),
157+
sub_future,
116158
)
117-
};
118-
(ty_sup.span, ty_sub.span, main_label_1, span_label_1)
119-
}
120-
121-
(Some(ret_span), _) => (
122-
ty_sub.span,
123-
ret_span,
124-
"this parameter and the return type are declared with different lifetimes..."
125-
.to_owned(),
126-
format!("...but data{} is returned here", span_label_var1),
127-
),
128-
(_, Some(ret_span)) => (
129-
ty_sup.span,
130-
ret_span,
131-
"this parameter and the return type are declared with different lifetimes..."
132-
.to_owned(),
133-
format!("...but data{} is returned here", span_label_var1),
134-
),
135-
};
136-
137-
struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch")
138-
.span_label(span_1, main_label)
139-
.span_label(span_2, String::new())
140-
.span_label(span, span_label)
141-
.emit();
159+
}
160+
};
161+
162+
let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
163+
164+
e.span_label(span_1, main_label);
165+
e.span_label(span_2, String::new());
166+
e.span_label(span, span_label);
167+
168+
if let Some(t) = future_return_type {
169+
let snip = self
170+
.tcx()
171+
.sess
172+
.source_map()
173+
.span_to_snippet(t.span)
174+
.ok()
175+
.and_then(|s| match (&t.kind, s.as_str()) {
176+
(rustc_hir::TyKind::Tup(&[]), "") => Some("()".to_string()),
177+
(_, "") => None,
178+
_ => Some(s),
179+
})
180+
.unwrap_or("{unnamed_type}".to_string());
181+
182+
e.span_label(
183+
t.span,
184+
&format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip),
185+
);
186+
}
187+
e.emit();
142188
Some(ErrorReported)
143189
}
144190
}

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

+54
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,60 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
8585
})
8686
}
8787

88+
pub(super) fn future_return_type(
89+
&self,
90+
local_def_id: LocalDefId,
91+
) -> Option<&rustc_hir::Ty<'_>> {
92+
if let Some(hir::IsAsync::Async) = self.asyncness(local_def_id) {
93+
if let rustc_middle::ty::Opaque(def_id, _) =
94+
self.tcx().type_of(local_def_id).fn_sig(self.tcx()).output().skip_binder().kind()
95+
{
96+
match self.tcx().hir().get_if_local(*def_id) {
97+
Some(hir::Node::Item(hir::Item {
98+
kind:
99+
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
100+
bounds,
101+
origin: hir::OpaqueTyOrigin::AsyncFn,
102+
..
103+
}),
104+
..
105+
})) => {
106+
for b in bounds.iter() {
107+
if let hir::GenericBound::LangItemTrait(
108+
hir::LangItem::Future,
109+
_span,
110+
_hir_id,
111+
generic_args,
112+
) = b
113+
{
114+
for type_binding in generic_args.bindings.iter() {
115+
if type_binding.ident.name == rustc_span::sym::Output {
116+
if let hir::TypeBindingKind::Equality { ty } =
117+
type_binding.kind
118+
{
119+
return Some(ty);
120+
}
121+
}
122+
}
123+
}
124+
}
125+
}
126+
_ => {}
127+
}
128+
}
129+
}
130+
None
131+
}
132+
133+
pub(super) fn asyncness(&self, local_def_id: LocalDefId) -> Option<hir::IsAsync> {
134+
// similar to the asyncness fn in rustc_ty::ty
135+
let hir_id = self.tcx().hir().local_def_id_to_hir_id(local_def_id);
136+
let node = self.tcx().hir().get(hir_id);
137+
let fn_like = rustc_middle::hir::map::blocks::FnLikeNode::from_node(node)?;
138+
139+
Some(fn_like.asyncness())
140+
}
141+
88142
// Here, we check for the case where the anonymous region
89143
// is in the return type.
90144
// FIXME(#42703) - Need to handle certain cases here.

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ error[E0623]: lifetime mismatch
22
--> $DIR/issue-63388-1.rs:14:9
33
|
44
LL | &'a self, foo: &dyn Foo
5-
| -------- this parameter and the return type are declared with different lifetimes...
5+
| -------- this parameter and the returned future are declared with different lifetimes...
66
LL | ) -> &dyn Foo
77
| --------
8+
| |
9+
| this `async fn` implicitly returns an `impl Future<Output = &dyn Foo>`
810
LL | {
911
LL | foo
10-
| ^^^ ...but data from `foo` is returned here
12+
| ^^^ ...but data from `foo` is held across an await point here
1113

1214
error: aborting due to previous error
1315

src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ error[E0623]: lifetime mismatch
44
LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> {
55
| ------ ^^^^^^^^^^^^^^
66
| | |
7-
| | ...but data from `b` is returned here
8-
| this parameter and the return type are declared with different lifetimes...
7+
| | ...but data from `b` is held across an await point here
8+
| | this `async fn` implicitly returns an `impl Future<Output = impl Trait<'a>>`
9+
| this parameter and the returned future are declared with different lifetimes...
910

1011
error: aborting due to previous error
1112

src/test/ui/issues/issue-76547.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Test for for diagnostic improvement issue #76547
2+
// edition:2018
3+
4+
use std::{
5+
future::Future,
6+
task::{Context, Poll}
7+
};
8+
use std::pin::Pin;
9+
10+
pub struct ListFut<'a>(&'a mut [&'a mut [u8]]);
11+
impl<'a> Future for ListFut<'a> {
12+
type Output = ();
13+
14+
fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
15+
unimplemented!()
16+
}
17+
}
18+
19+
async fn fut(bufs: &mut [&mut [u8]]) {
20+
ListFut(bufs).await
21+
//~^ ERROR lifetime mismatch
22+
}
23+
24+
pub struct ListFut2<'a>(&'a mut [&'a mut [u8]]);
25+
impl<'a> Future for ListFut2<'a> {
26+
type Output = i32;
27+
28+
fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
29+
unimplemented!()
30+
}
31+
}
32+
33+
async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
34+
ListFut2(bufs).await
35+
//~^ ERROR lifetime mismatch
36+
}
37+
38+
fn main() {}

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

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0623]: lifetime mismatch
2+
--> $DIR/issue-76547.rs:20:13
3+
|
4+
LL | async fn fut(bufs: &mut [&mut [u8]]) {
5+
| --------- -
6+
| | |
7+
| | this `async fn` implicitly returns an `impl Future<Output = ()>`
8+
| this parameter and the returned future are declared with different lifetimes...
9+
LL | ListFut(bufs).await
10+
| ^^^^ ...but data from `bufs` is held across an await point here
11+
12+
error[E0623]: lifetime mismatch
13+
--> $DIR/issue-76547.rs:34:14
14+
|
15+
LL | async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
16+
| --------- ---
17+
| | |
18+
| | this `async fn` implicitly returns an `impl Future<Output = i32>`
19+
| this parameter and the returned future are declared with different lifetimes...
20+
LL | ListFut2(bufs).await
21+
| ^^^^ ...but data from `bufs` is held across an await point here
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0623`.

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

+12-9
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,28 @@ error[E0623]: lifetime mismatch
22
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:8:52
33
|
44
LL | async fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f }
5-
| ---- ---- ^ ...but data from `f` is returned here
6-
| |
7-
| this parameter and the return type are declared with different lifetimes...
5+
| ---- ---- ^ ...but data from `f` is held across an await point here
6+
| | |
7+
| | this `async fn` implicitly returns an `impl Future<Output = &Foo>`
8+
| this parameter and the returned future are declared with different lifetimes...
89

910
error[E0623]: lifetime mismatch
1011
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:11:82
1112
|
1213
LL | async fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
13-
| ----- ----------------- ^ ...but data from `f` is returned here
14-
| |
15-
| this parameter and the return type are declared with different lifetimes...
14+
| ----- ----------------- ^ ...but data from `f` is held across an await point here
15+
| | |
16+
| | this `async fn` implicitly returns an `impl Future<Output = (Pin<&Foo>, &Foo)>`
17+
| this parameter and the returned future are declared with different lifetimes...
1618

1719
error[E0623]: lifetime mismatch
1820
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch-async.rs:17:64
1921
|
2022
LL | async fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
21-
| ----- --- ^^^ ...but data from `arg` is returned here
22-
| |
23-
| this parameter and the return type are declared with different lifetimes...
23+
| ----- --- ^^^ ...but data from `arg` is held across an await point here
24+
| | |
25+
| | this `async fn` implicitly returns an `impl Future<Output = &()>`
26+
| this parameter and the returned future are declared with different lifetimes...
2427

2528
error: aborting due to 3 previous errors
2629

0 commit comments

Comments
 (0)