diff options
| author | Giles Cope <gilescope@gmail.com> | 2019-07-23 19:50:53 +0100 |
|---|---|---|
| committer | Giles Cope <gilescope@gmail.com> | 2019-07-28 17:03:42 +0100 |
| commit | 4b1d404d833929dffcc1ea8d704aa3d2c432ba11 (patch) | |
| tree | 1367e2a0fd3770ae7039bc777e69969c0f53797d | |
| parent | a7f28678bbf4e16893bb6a718e427504167a9494 (diff) | |
| download | rust-4b1d404d833929dffcc1ea8d704aa3d2c432ba11.tar.gz rust-4b1d404d833929dffcc1ea8d704aa3d2c432ba11.zip | |
Better recursive async fn error message.
Co-Authored-By: Mazdak Farrokhzad <twingoow@gmail.com>
| -rw-r--r-- | src/librustc_typeck/check/mod.rs | 40 | ||||
| -rw-r--r-- | src/librustc_typeck/error_codes.rs | 46 | ||||
| -rw-r--r-- | src/test/ui/async-await/recursive-async-impl-trait-type.stderr | 8 |
3 files changed, 78 insertions, 16 deletions
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 21cd4b694ae..09fe92415b9 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 19d5e8b3e84..8a6cb90310a 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<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 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 69914b6a791..64f6eccd547 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`. |
