diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 21cd4b694ae4c..09fe92415b91a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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(); } } @@ -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); diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs index 19d5e8b3e8447..8a6cb90310ab0 100644 --- a/src/librustc_typeck/error_codes.rs +++ b/src/librustc_typeck/error_codes.rs @@ -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 { + 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::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 diff --git a/src/test/ui/async-await/recursive-async-impl-trait-type.stderr b/src/test/ui/async-await/recursive-async-impl-trait-type.stderr index 69914b6a7910f..64f6eccd5479a 100644 --- a/src/test/ui/async-await/recursive-async-impl-trait-type.stderr +++ b/src/test/ui/async-await/recursive-async-impl-trait-type.stderr @@ -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`.