Better recursive async fn error message.

Co-Authored-By: Mazdak Farrokhzad <twingoow@gmail.com>
This commit is contained in:
Giles Cope 2019-07-23 19:50:53 +01:00
parent a7f28678bb
commit 4b1d404d83
3 changed files with 78 additions and 16 deletions

View file

@ -1325,8 +1325,23 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
check_packed(tcx, span, def_id);
}
fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>, span: Span) {
fn check_opaque<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
span: Span,
origin: &hir::ExistTyOrigin
) {
if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id, substs) {
if let hir::ExistTyOrigin::AsyncFn = origin {
struct_span_err!(
tcx.sess, span, E0733,
"recursion in an `async fn` requires boxing",
)
.span_label(span, "an `async fn` cannot invoke itself directly")
.note("a recursive `async fn` must be rewritten to return a boxed future.")
.emit();
} else {
let mut err = struct_span_err!(
tcx.sess, span, E0720,
"opaque type expands to a recursive type",
@ -1339,6 +1354,7 @@ fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>,
}
err.emit();
}
}
}
pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item) {
@ -1387,11 +1403,11 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item) {
hir::ItemKind::Union(..) => {
check_union(tcx, it.hir_id, it.span);
}
hir::ItemKind::Existential(..) => {
hir::ItemKind::Existential(hir::ExistTy{origin, ..}) => {
let def_id = tcx.hir().local_def_id(it.hir_id);
let substs = InternalSubsts::identity_for_item(tcx, def_id);
check_opaque(tcx, def_id, substs, it.span);
check_opaque(tcx, def_id, substs, it.span, &origin);
}
hir::ItemKind::Ty(..) => {
let def_id = tcx.hir().local_def_id(it.hir_id);

View file

@ -4765,7 +4765,53 @@ assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11}));
```
"##,
E0733: r##"
Recursion in an `async fn` requires boxing. For example, this will not compile:
```edition2018,compile_fail,E0733
#![feature(async_await)]
async fn foo(n: usize) {
if n > 0 {
foo(n - 1).await;
}
}
```
To achieve async recursion, the `async fn` needs to be desugared
such that the `Future` is explicit in the return type:
```edition2018,compile_fail,E0720
# #![feature(async_await)]
use std::future::Future;
fn foo_desugered(n: usize) -> impl Future<Output = ()> {
async move {
if n > 0 {
foo_desugered(n - 1).await;
}
}
}
```
Finally, the future is wrapped in a pinned box:
```edition2018
# #![feature(async_await)]
use std::future::Future;
use std::pin::Pin;
fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
Box::pin(async move {
if n > 0 {
foo_recursive(n - 1).await;
}
})
}
```
The `Box<...>` ensures that the result is of known size,
and the pin is required to keep it in the same place in memory.
"##,
} // (end of detailed error messages)
register_diagnostics! {
// E0035, merged into E0087/E0089

View file

@ -1,11 +1,11 @@
error[E0720]: opaque type expands to a recursive type
error[E0733]: recursion in an `async fn` requires boxing
--> $DIR/recursive-async-impl-trait-type.rs:7:40
|
LL | async fn recursive_async_function() -> () {
| ^^ expands to a recursive type
| ^^ an `async fn` cannot invoke itself directly
|
= note: expanded type is `std::future::GenFuture<[static generator@$DIR/recursive-async-impl-trait-type.rs:7:43: 9:2 {impl std::future::Future, ()}]>`
= note: a recursive `async fn` must be rewritten to return a boxed future.
error: aborting due to previous error
For more information about this error, try `rustc --explain E0720`.
For more information about this error, try `rustc --explain E0733`.