about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0733.md16
-rw-r--r--compiler/rustc_hir/src/hir.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs25
-rw-r--r--compiler/rustc_middle/src/query/keys.rs13
-rw-r--r--compiler/rustc_middle/src/query/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs13
-rw-r--r--compiler/rustc_middle/src/values.rs36
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs4
-rw-r--r--compiler/rustc_query_system/src/query/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs13
-rw-r--r--tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs2
-rw-r--r--tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr11
-rw-r--r--tests/ui/impl-trait/recursive-coroutine.current.stderr12
-rw-r--r--tests/ui/impl-trait/recursive-coroutine.next.stderr12
-rw-r--r--tests/ui/impl-trait/recursive-coroutine.rs5
-rw-r--r--tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs2
-rw-r--r--tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr17
-rw-r--r--tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs3
-rw-r--r--tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr27
19 files changed, 102 insertions, 124 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0733.md b/compiler/rustc_error_codes/src/error_codes/E0733.md
index 051b75148e5..cceb0880350 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0733.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0733.md
@@ -13,7 +13,7 @@ async fn foo(n: usize) {
 To perform async recursion, the `async fn` needs to be desugared such that the
 `Future` is explicit in the return type:
 
-```edition2018,compile_fail,E0720
+```edition2018,compile_fail,E0733
 use std::future::Future;
 fn foo_desugared(n: usize) -> impl Future<Output = ()> {
     async move {
@@ -41,4 +41,18 @@ fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
 The `Box<...>` ensures that the result is of known size, and the pin is
 required to keep it in the same place in memory.
 
+Alternatively, the recursive call-site can be boxed:
+
+```edition2018
+use std::future::Future;
+use std::pin::Pin;
+fn foo_recursive(n: usize) -> impl Future<Output = ()> {
+    async move {
+        if n > 0 {
+            Box::pin(foo_recursive(n - 1)).await;
+        }
+    }
+}
+```
+
 [`async`]: https://doc.rust-lang.org/std/keyword.async.html
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index e88b876534e..cadf54c76a3 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1361,6 +1361,12 @@ impl CoroutineKind {
     }
 }
 
+impl CoroutineKind {
+    pub fn is_fn_like(self) -> bool {
+        matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn))
+    }
+}
+
 impl fmt::Display for CoroutineKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 4b26a469eb5..12430b1465c 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -213,13 +213,12 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
         return;
     }
 
-    let args = GenericArgs::identity_for_item(tcx, item.owner_id);
     let span = tcx.def_span(item.owner_id.def_id);
 
     if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() {
         return;
     }
-    if check_opaque_for_cycles(tcx, item.owner_id.def_id, args, span, origin).is_err() {
+    if check_opaque_for_cycles(tcx, item.owner_id.def_id, span).is_err() {
         return;
     }
 
@@ -230,16 +229,16 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 pub(super) fn check_opaque_for_cycles<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: LocalDefId,
-    args: GenericArgsRef<'tcx>,
     span: Span,
-    origin: &hir::OpaqueTyOrigin,
 ) -> Result<(), ErrorGuaranteed> {
+    let args = GenericArgs::identity_for_item(tcx, def_id);
     if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() {
-        let reported = match origin {
-            hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span),
-            _ => opaque_type_cycle_error(tcx, def_id, span),
-        };
+        let reported = opaque_type_cycle_error(tcx, def_id, span);
         Err(reported)
+    } else if let Err(&LayoutError::Cycle(guar)) =
+        tcx.layout_of(tcx.param_env(def_id).and(Ty::new_opaque(tcx, def_id.to_def_id(), args)))
+    {
+        Err(guar)
     } else {
         Ok(())
     }
@@ -1300,16 +1299,6 @@ pub(super) fn check_type_params_are_used<'tcx>(
     }
 }
 
-fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed {
-    struct_span_err!(tcx.dcx(), span, E0733, "recursion in an `async fn` requires boxing")
-        .span_label_mv(span, "recursive `async fn`")
-        .note_mv("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
-        .note_mv(
-            "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
-        )
-        .emit()
-}
-
 /// Emit an error for recursive opaque types.
 ///
 /// If this is a return `impl Trait`, find the item's return expressions and point at them. For
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index 11376345052..945f17d5df2 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -40,7 +40,7 @@ pub trait Key: Sized {
         None
     }
 
-    fn ty_adt_id(&self) -> Option<DefId> {
+    fn ty_def_id(&self) -> Option<DefId> {
         None
     }
 }
@@ -406,9 +406,10 @@ impl<'tcx> Key for Ty<'tcx> {
         DUMMY_SP
     }
 
-    fn ty_adt_id(&self) -> Option<DefId> {
-        match self.kind() {
+    fn ty_def_id(&self) -> Option<DefId> {
+        match *self.kind() {
             ty::Adt(adt, _) => Some(adt.did()),
+            ty::Coroutine(def_id, ..) => Some(def_id),
             _ => None,
         }
     }
@@ -452,6 +453,10 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.value.default_span(tcx)
     }
+
+    fn ty_def_id(&self) -> Option<DefId> {
+        self.value.ty_def_id()
+    }
 }
 
 impl Key for Symbol {
@@ -550,7 +555,7 @@ impl<'tcx> Key for (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>) {
         DUMMY_SP
     }
 
-    fn ty_adt_id(&self) -> Option<DefId> {
+    fn ty_def_id(&self) -> Option<DefId> {
         match self.1.value.kind() {
             ty::Adt(adt, _) => Some(adt.did()),
             _ => None,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 7d5abaceb20..0e3b9984423 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1387,6 +1387,8 @@ rustc_queries! {
     ) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
         depth_limit
         desc { "computing layout of `{}`", key.value }
+        // we emit our own error during query cycle handling
+        cycle_delay_bug
     }
 
     /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index b9c75bd205b..74dba41647b 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -896,18 +896,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
         }
         let args = args.fold_with(self);
         if !self.check_recursion || self.seen_opaque_tys.insert(def_id) {
-            let expanded_ty = match self.expanded_cache.get(&(def_id, args)) {
-                Some(expanded_ty) => *expanded_ty,
-                None => {
-                    for bty in self.tcx.coroutine_hidden_types(def_id) {
-                        let hidden_ty = bty.instantiate(self.tcx, args);
-                        self.fold_ty(hidden_ty);
-                    }
-                    let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args);
-                    self.expanded_cache.insert((def_id, args), expanded_ty);
-                    expanded_ty
-                }
-            };
+            let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args);
             if self.check_recursion {
                 self.seen_opaque_tys.remove(&def_id);
             }
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index 4ee97dac444..0179829cc46 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -6,7 +6,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_middle::ty::Representability;
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_query_system::query::CycleError;
+use rustc_query_system::query::{report_cycle, CycleError};
 use rustc_query_system::Value;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{ErrorGuaranteed, Span};
@@ -97,7 +97,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
         }
         for info in &cycle_error.cycle {
             if info.query.dep_kind == dep_kinds::representability_adt_ty
-                && let Some(def_id) = info.query.ty_adt_id
+                && let Some(def_id) = info.query.ty_def_id
                 && let Some(def_id) = def_id.as_local()
                 && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
             {
@@ -131,10 +131,36 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>
 
 impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> {
     fn from_cycle_error(
-        _tcx: TyCtxt<'tcx>,
-        _cycle_error: &CycleError,
-        guar: ErrorGuaranteed,
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        _guar: ErrorGuaranteed,
     ) -> Self {
+        let guar = if cycle_error.cycle[0].query.dep_kind == dep_kinds::layout_of
+            && let Some(def_id) = cycle_error.cycle[0].query.ty_def_id
+            && let Some(def_id) = def_id.as_local()
+            && matches!(tcx.def_kind(def_id), DefKind::Closure)
+            && let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
+        {
+            // FIXME: `def_span` for an fn-like coroutine will point to the fn's body
+            // due to interactions between the desugaring into a closure expr and the
+            // def_span code. I'm not motivated to fix it, because I tried and it was
+            // not working, so just hack around it by grabbing the parent fn's span.
+            let span = if coroutine_kind.is_fn_like() {
+                tcx.def_span(tcx.local_parent(def_id))
+            } else {
+                tcx.def_span(def_id)
+            };
+            struct_span_err!(tcx.sess.dcx(), span, E0733, "recursion in an `async fn` requires boxing")
+                .span_label(span, "recursive `async fn`")
+                .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
+                .note(
+                    "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
+                )
+                .emit()
+        } else {
+            report_cycle(tcx.sess, cycle_error).emit()
+        };
+
         // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
         // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
         // tcx.arena.alloc is pretty much equal to leaking).
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index f131a0f7593..7e0fbf3d76c 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -342,9 +342,9 @@ pub(crate) fn create_query_frame<
             hasher.finish::<Hash64>()
         })
     };
-    let ty_adt_id = key.ty_adt_id();
+    let ty_def_id = key.ty_def_id();
 
-    QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_adt_id, hash)
+    QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_def_id, hash)
 }
 
 pub(crate) fn encode_query_results<'a, 'tcx, Q>(
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs
index ce6a6d6cb1b..9ff04c4e910 100644
--- a/compiler/rustc_query_system/src/query/mod.rs
+++ b/compiler/rustc_query_system/src/query/mod.rs
@@ -35,7 +35,8 @@ pub struct QueryStackFrame {
     span: Option<Span>,
     pub def_id: Option<DefId>,
     pub def_kind: Option<DefKind>,
-    pub ty_adt_id: Option<DefId>,
+    /// A def-id that is extracted from a `Ty` in a query key
+    pub ty_def_id: Option<DefId>,
     pub dep_kind: DepKind,
     /// This hash is used to deterministically pick
     /// a query to remove cycles in the parallel compiler.
@@ -51,7 +52,7 @@ impl QueryStackFrame {
         def_id: Option<DefId>,
         def_kind: Option<DefKind>,
         dep_kind: DepKind,
-        ty_adt_id: Option<DefId>,
+        ty_def_id: Option<DefId>,
         _hash: impl FnOnce() -> Hash64,
     ) -> Self {
         Self {
@@ -59,7 +60,7 @@ impl QueryStackFrame {
             span,
             def_id,
             def_kind,
-            ty_adt_id,
+            ty_def_id,
             dep_kind,
             #[cfg(parallel_compiler)]
             hash: _hash(),
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index e8867187a40..425c4fbe9c5 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -239,16 +239,13 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                         }
 
                         let generic_ty = self.interner().type_of(data.def_id);
-                        let concrete_ty = generic_ty.instantiate(self.interner(), args);
+                        let mut concrete_ty = generic_ty.instantiate(self.interner(), args);
                         self.anon_depth += 1;
                         if concrete_ty == ty {
-                            bug!(
-                                "infinite recursion generic_ty: {:#?}, args: {:#?}, \
-                                 concrete_ty: {:#?}, ty: {:#?}",
-                                generic_ty,
-                                args,
-                                concrete_ty,
-                                ty
+                            concrete_ty = Ty::new_error_with_message(
+                                self.interner(),
+                                DUMMY_SP,
+                                "recursive opaque type",
                             );
                         }
                         let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty));
diff --git a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs
index bb2a61f03ce..d38ba1a569b 100644
--- a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs
+++ b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs
@@ -6,7 +6,7 @@ async fn rec_1() { //~ ERROR recursion in an `async fn`
     rec_2().await;
 }
 
-async fn rec_2() { //~ ERROR recursion in an `async fn`
+async fn rec_2() {
     rec_1().await;
 }
 
diff --git a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr
index 9442609e805..dd53075be60 100644
--- a/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr
+++ b/tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr
@@ -7,15 +7,6 @@ LL | async fn rec_1() {
    = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
    = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
 
-error[E0733]: recursion in an `async fn` requires boxing
-  --> $DIR/mutually-recursive-async-impl-trait-type.rs:9:1
-   |
-LL | async fn rec_2() {
-   | ^^^^^^^^^^^^^^^^ recursive `async fn`
-   |
-   = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
-   = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0733`.
diff --git a/tests/ui/impl-trait/recursive-coroutine.current.stderr b/tests/ui/impl-trait/recursive-coroutine.current.stderr
deleted file mode 100644
index e838634ed08..00000000000
--- a/tests/ui/impl-trait/recursive-coroutine.current.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-coroutine.rs:7:13
-   |
-LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
-...
-LL |         let mut gen = Box::pin(foo());
-   |             ------- coroutine captures itself here
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0720`.
diff --git a/tests/ui/impl-trait/recursive-coroutine.next.stderr b/tests/ui/impl-trait/recursive-coroutine.next.stderr
deleted file mode 100644
index e838634ed08..00000000000
--- a/tests/ui/impl-trait/recursive-coroutine.next.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-coroutine.rs:7:13
-   |
-LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
-...
-LL |         let mut gen = Box::pin(foo());
-   |             ------- coroutine captures itself here
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0720`.
diff --git a/tests/ui/impl-trait/recursive-coroutine.rs b/tests/ui/impl-trait/recursive-coroutine.rs
index b82fe134a40..b9291f07e21 100644
--- a/tests/ui/impl-trait/recursive-coroutine.rs
+++ b/tests/ui/impl-trait/recursive-coroutine.rs
@@ -1,3 +1,4 @@
+// check-pass
 // revisions: current next
 //[next] compile-flags: -Znext-solver
 #![feature(coroutines, coroutine_trait)]
@@ -5,12 +6,8 @@
 use std::ops::{Coroutine, CoroutineState};
 
 fn foo() -> impl Coroutine<Yield = (), Return = ()> {
-    //~^ ERROR cannot resolve opaque type
-    //~| NOTE recursive opaque type
-    //~| NOTE in this expansion of desugaring of
     || {
         let mut gen = Box::pin(foo());
-        //~^ NOTE coroutine captures itself here
         let mut r = gen.as_mut().resume(());
         while let CoroutineState::Yielded(v) = r {
             yield v;
diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs
index 8331eec906e..a6bca107b1e 100644
--- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs
+++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs
@@ -70,8 +70,8 @@ fn substs_change<T: 'static>() -> impl Sized {
 }
 
 fn coroutine_hold() -> impl Sized {
-    //~^ ERROR
     move || {
+    //~^ ERROR
         let x = coroutine_hold();
         yield;
         x;
diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr
index 8e9aa8ad0a6..0dabba26468 100644
--- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr
+++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr
@@ -109,14 +109,14 @@ LL |
 LL |     (substs_change::<&T>(),)
    |     ------------------------ returning here with type `(impl Sized,)`
 
-error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:72:24
+error[E0733]: recursion in an `async fn` requires boxing
+  --> $DIR/recursive-impl-trait-type-indirect.rs:73:5
    |
-LL | fn coroutine_hold() -> impl Sized {
-   |                        ^^^^^^^^^^ recursive opaque type
-...
-LL |         let x = coroutine_hold();
-   |             - coroutine captures itself here
+LL |     move || {
+   |     ^^^^^^^ recursive `async fn`
+   |
+   = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+   = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
 
 error[E0720]: cannot resolve opaque type
   --> $DIR/recursive-impl-trait-type-indirect.rs:86:26
@@ -144,4 +144,5 @@ LL |     mutual_recursion()
 
 error: aborting due to 14 previous errors
 
-For more information about this error, try `rustc --explain E0720`.
+Some errors have detailed explanations: E0720, E0733.
+For more information about an error, try `rustc --explain E0720`.
diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
index 6a2ee761e19..3637f416c7b 100644
--- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
+++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
@@ -1,5 +1,4 @@
 // edition: 2021
-// build-fail
 
 #![feature(impl_trait_in_assoc_type)]
 
@@ -20,7 +19,7 @@ impl Recur for () {
 
     fn recur(self) -> Self::Recur {
         async move { recur(self).await; }
-        //~^ ERROR cycle detected when computing layout of
+        //~^ ERROR recursion in an `async fn` requires boxing
     }
 }
 
diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
index 11d9cd0af08..aa352b326c6 100644
--- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
+++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
@@ -1,27 +1,12 @@
-error[E0391]: cycle detected when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}`
-  --> $DIR/indirect-recursion-issue-112047.rs:22:22
+error[E0733]: recursion in an `async fn` requires boxing
+  --> $DIR/indirect-recursion-issue-112047.rs:21:9
    |
 LL |         async move { recur(self).await; }
-   |                      ^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
    |
-   = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}>`...
-   = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}>`...
-note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}`...
-  --> $DIR/indirect-recursion-issue-112047.rs:15:5
-   |
-LL |     t.recur().await;
-   |     ^^^^^^^^^^^^^^^
-   = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<() as Recur>::Recur>`...
-   = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}>`...
-   = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}>`...
-   = note: ...which again requires computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}`, completing the cycle
-note: cycle used when elaborating drops for `<impl at $DIR/indirect-recursion-issue-112047.rs:18:1: 18:18>::recur`
-  --> $DIR/indirect-recursion-issue-112047.rs:21:5
-   |
-LL |     fn recur(self) -> Self::Recur {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
+   = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+   = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0391`.
+For more information about this error, try `rustc --explain E0733`.