Skip to content

Commit 4b1d404

Browse files
gilescopeCentril
andcommitted
Better recursive async fn error message.
Co-Authored-By: Mazdak Farrokhzad <[email protected]>
1 parent a7f2867 commit 4b1d404

File tree

3 files changed

+78
-16
lines changed

3 files changed

+78
-16
lines changed

src/librustc_typeck/check/mod.rs

+28-12
Original file line numberDiff line numberDiff line change
@@ -1325,19 +1325,35 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
13251325
check_packed(tcx, span, def_id);
13261326
}
13271327

1328-
fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>, span: Span) {
1328+
fn check_opaque<'tcx>(
1329+
tcx: TyCtxt<'tcx>,
1330+
def_id: DefId,
1331+
substs: SubstsRef<'tcx>,
1332+
span: Span,
1333+
origin: &hir::ExistTyOrigin
1334+
) {
13291335
if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id, substs) {
1330-
let mut err = struct_span_err!(
1331-
tcx.sess, span, E0720,
1332-
"opaque type expands to a recursive type",
1333-
);
1334-
err.span_label(span, "expands to a recursive type");
1335-
if let ty::Opaque(..) = partially_expanded_type.sty {
1336-
err.note("type resolves to itself");
1336+
if let hir::ExistTyOrigin::AsyncFn = origin {
1337+
struct_span_err!(
1338+
tcx.sess, span, E0733,
1339+
"recursion in an `async fn` requires boxing",
1340+
)
1341+
.span_label(span, "an `async fn` cannot invoke itself directly")
1342+
.note("a recursive `async fn` must be rewritten to return a boxed future.")
1343+
.emit();
13371344
} else {
1338-
err.note(&format!("expanded type is `{}`", partially_expanded_type));
1345+
let mut err = struct_span_err!(
1346+
tcx.sess, span, E0720,
1347+
"opaque type expands to a recursive type",
1348+
);
1349+
err.span_label(span, "expands to a recursive type");
1350+
if let ty::Opaque(..) = partially_expanded_type.sty {
1351+
err.note("type resolves to itself");
1352+
} else {
1353+
err.note(&format!("expanded type is `{}`", partially_expanded_type));
1354+
}
1355+
err.emit();
13391356
}
1340-
err.emit();
13411357
}
13421358
}
13431359

@@ -1387,11 +1403,11 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item) {
13871403
hir::ItemKind::Union(..) => {
13881404
check_union(tcx, it.hir_id, it.span);
13891405
}
1390-
hir::ItemKind::Existential(..) => {
1406+
hir::ItemKind::Existential(hir::ExistTy{origin, ..}) => {
13911407
let def_id = tcx.hir().local_def_id(it.hir_id);
13921408

13931409
let substs = InternalSubsts::identity_for_item(tcx, def_id);
1394-
check_opaque(tcx, def_id, substs, it.span);
1410+
check_opaque(tcx, def_id, substs, it.span, &origin);
13951411
}
13961412
hir::ItemKind::Ty(..) => {
13971413
let def_id = tcx.hir().local_def_id(it.hir_id);

src/librustc_typeck/error_codes.rs

+46
Original file line numberDiff line numberDiff line change
@@ -4765,7 +4765,53 @@ assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11}));
47654765
```
47664766
"##,
47674767

4768+
E0733: r##"
4769+
Recursion in an `async fn` requires boxing. For example, this will not compile:
4770+
4771+
```edition2018,compile_fail,E0733
4772+
#![feature(async_await)]
4773+
async fn foo(n: usize) {
4774+
if n > 0 {
4775+
foo(n - 1).await;
4776+
}
4777+
}
4778+
```
4779+
4780+
To achieve async recursion, the `async fn` needs to be desugared
4781+
such that the `Future` is explicit in the return type:
4782+
4783+
```edition2018,compile_fail,E0720
4784+
# #![feature(async_await)]
4785+
use std::future::Future;
4786+
fn foo_desugered(n: usize) -> impl Future<Output = ()> {
4787+
async move {
4788+
if n > 0 {
4789+
foo_desugered(n - 1).await;
4790+
}
4791+
}
4792+
}
4793+
```
4794+
4795+
Finally, the future is wrapped in a pinned box:
4796+
4797+
```edition2018
4798+
# #![feature(async_await)]
4799+
use std::future::Future;
4800+
use std::pin::Pin;
4801+
fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
4802+
Box::pin(async move {
4803+
if n > 0 {
4804+
foo_recursive(n - 1).await;
4805+
}
4806+
})
47684807
}
4808+
```
4809+
4810+
The `Box<...>` ensures that the result is of known size,
4811+
and the pin is required to keep it in the same place in memory.
4812+
"##,
4813+
4814+
} // (end of detailed error messages)
47694815

47704816
register_diagnostics! {
47714817
// E0035, merged into E0087/E0089
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
error[E0720]: opaque type expands to a recursive type
1+
error[E0733]: recursion in an `async fn` requires boxing
22
--> $DIR/recursive-async-impl-trait-type.rs:7:40
33
|
44
LL | async fn recursive_async_function() -> () {
5-
| ^^ expands to a recursive type
5+
| ^^ an `async fn` cannot invoke itself directly
66
|
7-
= note: expanded type is `std::future::GenFuture<[static generator@$DIR/recursive-async-impl-trait-type.rs:7:43: 9:2 {impl std::future::Future, ()}]>`
7+
= note: a recursive `async fn` must be rewritten to return a boxed future.
88

99
error: aborting due to previous error
1010

11-
For more information about this error, try `rustc --explain E0720`.
11+
For more information about this error, try `rustc --explain E0733`.

0 commit comments

Comments
 (0)