Skip to content

Commit

Permalink
Better recursive async fn error message.
Browse files Browse the repository at this point in the history
Co-Authored-By: Mazdak Farrokhzad <twingoow@gmail.com>
  • Loading branch information
gilescope and Centril committed Jul 28, 2019
1 parent a7f2867 commit 4b1d404
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 16 deletions.
40 changes: 28 additions & 12 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1325,19 +1325,35 @@ 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) {
let mut err = struct_span_err!(
tcx.sess, span, E0720,
"opaque type expands to a recursive type",
);
err.span_label(span, "expands to a recursive type");
if let ty::Opaque(..) = partially_expanded_type.sty {
err.note("type resolves to itself");
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 {
err.note(&format!("expanded type is `{}`", partially_expanded_type));
let mut err = struct_span_err!(
tcx.sess, span, E0720,
"opaque type expands to a recursive type",
);
err.span_label(span, "expands to a recursive type");
if let ty::Opaque(..) = partially_expanded_type.sty {
err.note("type resolves to itself");
} else {
err.note(&format!("expanded type is `{}`", partially_expanded_type));
}
err.emit();
}
err.emit();
}
}

Expand Down Expand Up @@ -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);
Expand Down
46 changes: 46 additions & 0 deletions src/librustc_typeck/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 4b1d404

Please sign in to comment.