Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicit error message for async recursion. #62871

Merged
merged 1 commit into from
Jul 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = ()> {
gilescope marked this conversation as resolved.
Show resolved Hide resolved
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`.