about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-09 07:20:50 +0000
committerbors <bors@rust-lang.org>2024-01-09 07:20:50 +0000
commitdc641039d2b3f5c0894694e4b45f7c3951030685 (patch)
tree56fd0a5404813f8421dda564516f97b769ea5cc8
parent387e7a5e42ac074e79a14361e82702a229a6aac8 (diff)
parent9a756034a981b0aa15598f990381203dffe96a1c (diff)
downloadrust-dc641039d2b3f5c0894694e4b45f7c3951030685.tar.gz
rust-dc641039d2b3f5c0894694e4b45f7c3951030685.zip
Auto merge of #117703 - compiler-errors:recursive-async, r=lcnr
Support async recursive calls (as long as they have indirection)

Before #101692, we stored coroutine witness types directly inside of the coroutine. That means that a coroutine could not contain itself (as a witness field) without creating a cycle in the type representation of the coroutine, which we detected with the `OpaqueTypeExpander`, which is used to detect cycles when expanding opaque types after that are inferred to contain themselves.

After `-Zdrop-tracking-mir` was stabilized, we no longer store these generator witness fields directly, but instead behind a def-id based query. That means there is no technical obstacle in the compiler preventing coroutines from containing themselves per se, other than the fact that for a coroutine to have a non-infinite layout, it must contain itself wrapped in a layer of allocation indirection (like a `Box`).

This means that it should be valid for this code to work:

```
async fn async_fibonacci(i: u32) -> u32 {
    if i == 0 || i == 1 {
        i
    } else {
        Box::pin(async_fibonacci(i - 1)).await
          + Box::pin(async_fibonacci(i - 2)).await
    }
}
```

Whereas previously, you'd need to coerce the future to `Pin<Box<dyn Future<Output = ...>>` before `await`ing it, to prevent the async's desugared coroutine from containing itself across as await point.

This PR does two things:
1. Only report an error if an opaque expansion cycle is detected *not* through coroutine witness fields.
    * Instead, if we find an opaque cycle through coroutine witness fields, we compute the layout of the coroutine. If that results in a cycle error, we report it as a recursive async fn.
4. Reworks the way we report layout errors having to do with coroutines, to make up for the diagnostic regressions introduced by (1.). We actually do even better now, pointing out the call sites of the recursion!
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0733.md26
-rw-r--r--compiler/rustc_hir/src/hir.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs52
-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/query/plumbing.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs63
-rw-r--r--compiler/rustc_middle/src/values.rs149
-rw-r--r--compiler/rustc_query_impl/src/lib.rs6
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs4
-rw-r--r--compiler/rustc_query_system/src/query/config.rs4
-rw-r--r--compiler/rustc_query_system/src/query/job.rs2
-rw-r--r--compiler/rustc_query_system/src/query/mod.rs11
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs8
-rw-r--r--compiler/rustc_query_system/src/values.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs13
-rw-r--r--src/librustdoc/html/render/print_item.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/drain_collect.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_key.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs5
-rw-r--r--tests/ui/async-await/in-trait/async-recursive-generic.rs2
-rw-r--r--tests/ui/async-await/in-trait/async-recursive-generic.stderr10
-rw-r--r--tests/ui/async-await/in-trait/async-recursive.rs2
-rw-r--r--tests/ui/async-await/in-trait/async-recursive.stderr10
-rw-r--r--tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs4
-rw-r--r--tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr15
-rw-r--r--tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs4
-rw-r--r--tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr21
-rw-r--r--tests/ui/async-await/recursive-async-impl-trait-type.rs2
-rw-r--r--tests/ui/async-await/recursive-async-impl-trait-type.stderr10
-rw-r--r--tests/ui/impl-trait/recursive-coroutine-boxed.rs (renamed from tests/ui/impl-trait/recursive-coroutine.rs)5
-rw-r--r--tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr11
-rw-r--r--tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr11
-rw-r--r--tests/ui/impl-trait/recursive-coroutine-indirect.rs13
-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-impl-trait-type-indirect.rs10
-rw-r--r--tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr47
-rw-r--r--tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr31
43 files changed, 394 insertions, 243 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..42c01975dd8 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0733.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0733.md
@@ -10,35 +10,31 @@ 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:
+The recursive invocation can be boxed:
 
-```edition2018,compile_fail,E0720
-use std::future::Future;
-fn foo_desugared(n: usize) -> impl Future<Output = ()> {
-    async move {
-        if n > 0 {
-            foo_desugared(n - 1).await;
-        }
+```edition2018
+async fn foo(n: usize) {
+    if n > 0 {
+        Box::pin(foo(n - 1)).await;
     }
 }
 ```
 
-Finally, the future is wrapped in a pinned box:
+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 body can be boxed:
 
 ```edition2018
 use std::future::Future;
 use std::pin::Pin;
-fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
+fn foo(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
     Box::pin(async move {
         if n > 0 {
-            foo_recursive(n - 1).await;
+            foo(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.
-
 [`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 7585f7bc902..082658a3a3b 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..7f23c04ce2d 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -17,7 +17,7 @@ use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode};
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
-use rustc_middle::ty::util::{Discr, IntTypeExt};
+use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt};
 use rustc_middle::ty::GenericArgKind;
 use rustc_middle::ty::{
     AdtDef, ParamEnv, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
@@ -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,19 +229,36 @@ 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> {
-    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),
-        };
-        Err(reported)
-    } else {
-        Ok(())
+    let args = GenericArgs::identity_for_item(tcx, def_id);
+
+    // First, try to look at any opaque expansion cycles, considering coroutine fields
+    // (even though these aren't necessarily true errors).
+    if tcx
+        .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::Yes)
+        .is_err()
+    {
+        // Look for true opaque expansion cycles, but ignore coroutines.
+        // This will give us any true errors. Coroutines are only problematic
+        // if they cause layout computation errors.
+        if tcx
+            .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::No)
+            .is_err()
+        {
+            let reported = opaque_type_cycle_error(tcx, def_id, span);
+            return Err(reported);
+        }
+
+        // And also look for cycle errors in the layout of coroutines.
+        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)))
+        {
+            return Err(guar);
+        }
     }
+
+    Ok(())
 }
 
 /// Check that the concrete type behind `impl Trait` actually implements `Trait`.
@@ -1300,16 +1316,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/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index d2ff1e3c094..414d4c8d949 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -53,7 +53,7 @@ pub struct DynamicQuery<'tcx, C: QueryCache> {
         fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool,
     pub hash_result: HashResult<C::Value>,
     pub value_from_cycle_error:
-        fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> C::Value,
+        fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value,
     pub format_value: fn(&C::Value) -> String,
 }
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index f2a33429bd7..7285cdb830e 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -702,6 +702,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
+        inspect_coroutine_fields: InspectCoroutineFields,
     ) -> Result<Ty<'tcx>, Ty<'tcx>> {
         let mut visitor = OpaqueTypeExpander {
             seen_opaque_tys: FxHashSet::default(),
@@ -712,6 +713,7 @@ impl<'tcx> TyCtxt<'tcx> {
             check_recursion: true,
             expand_coroutines: true,
             tcx: self,
+            inspect_coroutine_fields,
         };
 
         let expanded_type = visitor.expand_opaque_ty(def_id, args).unwrap();
@@ -729,16 +731,43 @@ impl<'tcx> TyCtxt<'tcx> {
             DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "method",
             DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
                 match coroutine_kind {
-                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => {
-                        "async closure"
-                    }
-                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
-                        "async gen closure"
-                    }
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Fn,
+                    ) => "async fn",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Block,
+                    ) => "async block",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Closure,
+                    ) => "async closure",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        hir::CoroutineSource::Fn,
+                    ) => "async gen fn",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        hir::CoroutineSource::Block,
+                    ) => "async gen block",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        hir::CoroutineSource::Closure,
+                    ) => "async gen closure",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Fn,
+                    ) => "gen fn",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Block,
+                    ) => "gen block",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Closure,
+                    ) => "gen closure",
                     hir::CoroutineKind::Coroutine(_) => "coroutine",
-                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => {
-                        "gen closure"
-                    }
                 }
             }
             _ => def_kind.descr(def_id),
@@ -865,6 +894,13 @@ struct OpaqueTypeExpander<'tcx> {
     /// recursion, and 'false' otherwise to avoid unnecessary work.
     check_recursion: bool,
     tcx: TyCtxt<'tcx>,
+    inspect_coroutine_fields: InspectCoroutineFields,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum InspectCoroutineFields {
+    No,
+    Yes,
 }
 
 impl<'tcx> OpaqueTypeExpander<'tcx> {
@@ -906,9 +942,11 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
             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);
+                    if matches!(self.inspect_coroutine_fields, InspectCoroutineFields::Yes) {
+                        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);
@@ -1486,6 +1524,7 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
         check_recursion: false,
         expand_coroutines: false,
         tcx,
+        inspect_coroutine_fields: InspectCoroutineFields::No,
     };
     val.fold_with(&mut visitor)
 }
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index b3c05a36a13..27d04dbe331 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -6,15 +6,17 @@ 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::QueryInfo;
+use rustc_query_system::query::{report_cycle, CycleError};
 use rustc_query_system::Value;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{ErrorGuaranteed, Span};
 
+use std::collections::VecDeque;
 use std::fmt::Write;
+use std::ops::ControlFlow;
 
 impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
         // SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
         // FIXME: Represent the above fact in the trait system somehow.
         unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_error(tcx, guar)) }
@@ -22,13 +24,13 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for Result<ty::EarlyBinder<Ty<'_>>, CyclePlaceholder> {
-    fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
         Err(CyclePlaceholder(guar))
     }
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo], _guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, _guar: ErrorGuaranteed) -> Self {
         // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
         // FIXME: Represent the above fact in the trait system somehow.
         unsafe {
@@ -40,10 +42,14 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        guar: ErrorGuaranteed,
+    ) -> Self {
         let err = Ty::new_error(tcx, guar);
 
-        let arity = if let Some(frame) = stack.get(0)
+        let arity = if let Some(frame) = cycle_error.cycle.get(0)
             && frame.query.dep_kind == dep_kinds::fn_sig
             && let Some(def_id) = frame.query.def_id
             && let Some(node) = tcx.hir().get_if_local(def_id)
@@ -70,10 +76,14 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], _guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        _guar: ErrorGuaranteed,
+    ) -> Self {
         let mut item_and_field_ids = Vec::new();
         let mut representable_ids = FxHashSet::default();
-        for info in cycle {
+        for info in &cycle_error.cycle {
             if info.query.dep_kind == dep_kinds::representability
                 && let Some(field_id) = info.query.def_id
                 && let Some(field_id) = field_id.as_local()
@@ -87,9 +97,9 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
                 item_and_field_ids.push((item_id.expect_local(), field_id));
             }
         }
-        for info in cycle {
+        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)
             {
@@ -102,19 +112,128 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<Ty<'_>> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
-        ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle, guar))
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        guar: ErrorGuaranteed,
+    ) -> Self {
+        ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar))
     }
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
-        ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle, guar))
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        guar: ErrorGuaranteed,
+    ) -> Self {
+        ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar))
     }
 }
 
+// Take a cycle of `Q` and try `try_cycle` on every permutation, falling back to `otherwise`.
+fn search_for_cycle_permutation<Q, T>(
+    cycle: &[Q],
+    try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow<T, ()>,
+    otherwise: impl FnOnce() -> T,
+) -> T {
+    let mut cycle: VecDeque<_> = cycle.iter().collect();
+    for _ in 0..cycle.len() {
+        match try_cycle(&mut cycle) {
+            ControlFlow::Continue(_) => {
+                cycle.rotate_left(1);
+            }
+            ControlFlow::Break(t) => return t,
+        }
+    }
+
+    otherwise()
+}
+
 impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> {
-    fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        _guar: ErrorGuaranteed,
+    ) -> Self {
+        let diag = search_for_cycle_permutation(
+            &cycle_error.cycle,
+            |cycle| {
+                if cycle[0].query.dep_kind == dep_kinds::layout_of
+                    && let Some(def_id) = cycle[0].query.ty_def_id
+                    && let Some(def_id) = def_id.as_local()
+                    && let def_kind = tcx.def_kind(def_id)
+                    && matches!(def_kind, 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)
+                    };
+                    let mut diag = struct_span_err!(
+                        tcx.sess.dcx(),
+                        span,
+                        E0733,
+                        "recursion in {} {} requires boxing",
+                        tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
+                        tcx.def_kind_descr(def_kind, def_id.to_def_id()),
+                    );
+                    for (i, frame) in cycle.iter().enumerate() {
+                        if frame.query.dep_kind != dep_kinds::layout_of {
+                            continue;
+                        }
+                        let Some(frame_def_id) = frame.query.ty_def_id else {
+                            continue;
+                        };
+                        let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
+                            continue;
+                        };
+                        let frame_span =
+                            frame.query.default_span(cycle[(i + 1) % cycle.len()].span);
+                        if frame_span.is_dummy() {
+                            continue;
+                        }
+                        if i == 0 {
+                            diag.span_label(frame_span, "recursive call here");
+                        } else {
+                            let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
+                                tcx.def_span(tcx.parent(frame_def_id))
+                            } else {
+                                tcx.def_span(frame_def_id)
+                            };
+                            let mut multispan = MultiSpan::from_span(coroutine_span);
+                            multispan
+                                .push_span_label(frame_span, "...leading to this recursive call");
+                            diag.span_note(
+                                multispan,
+                                format!("which leads to this {}", tcx.def_descr(frame_def_id)),
+                            );
+                        }
+                    }
+                    // FIXME: We could report a structured suggestion if we had
+                    // enough info here... Maybe we can use a hacky HIR walker.
+                    if matches!(
+                        coroutine_kind,
+                        hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
+                    ) {
+                        diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
+                    }
+
+                    ControlFlow::Break(diag)
+                } else {
+                    ControlFlow::Continue(())
+                }
+            },
+            || report_cycle(tcx.sess, cycle_error),
+        );
+
+        let guar = diag.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/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index d5883f52819..b5e8ac4018d 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -35,7 +35,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_query_system::dep_graph::SerializedDepNodeIndex;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_query_system::query::{
-    get_query_incr, get_query_non_incr, HashResult, QueryCache, QueryConfig, QueryInfo, QueryMap,
+    get_query_incr, get_query_non_incr, CycleError, HashResult, QueryCache, QueryConfig, QueryMap,
     QueryMode, QueryState,
 };
 use rustc_query_system::HandleCycleError;
@@ -144,10 +144,10 @@ where
     fn value_from_cycle_error(
         self,
         tcx: TyCtxt<'tcx>,
-        cycle: &[QueryInfo],
+        cycle_error: &CycleError,
         guar: ErrorGuaranteed,
     ) -> Self::Value {
-        (self.dynamic.value_from_cycle_error)(tcx, cycle, guar)
+        (self.dynamic.value_from_cycle_error)(tcx, cycle_error, guar)
     }
 
     #[inline(always)]
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/config.rs b/compiler/rustc_query_system/src/query/config.rs
index c025fac2631..958d9fdb52a 100644
--- a/compiler/rustc_query_system/src/query/config.rs
+++ b/compiler/rustc_query_system/src/query/config.rs
@@ -5,7 +5,7 @@ use crate::error::HandleCycleError;
 use crate::ich::StableHashingContext;
 use crate::query::caches::QueryCache;
 use crate::query::DepNodeIndex;
-use crate::query::{QueryContext, QueryInfo, QueryState};
+use crate::query::{CycleError, QueryContext, QueryState};
 
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_span::ErrorGuaranteed;
@@ -57,7 +57,7 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
     fn value_from_cycle_error(
         self,
         tcx: Qcx::DepContext,
-        cycle: &[QueryInfo],
+        cycle_error: &CycleError,
         guar: ErrorGuaranteed,
     ) -> Self::Value;
 
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 982b9ee94da..3ef9de7da74 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -556,7 +556,7 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
 
 #[inline(never)]
 #[cold]
-pub(crate) fn report_cycle<'a>(
+pub fn report_cycle<'a>(
     sess: &'a Session,
     CycleError { usage, cycle: stack }: &CycleError,
 ) -> DiagnosticBuilder<'a> {
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs
index 96a0c7a033a..9ff04c4e910 100644
--- a/compiler/rustc_query_system/src/query/mod.rs
+++ b/compiler/rustc_query_system/src/query/mod.rs
@@ -4,7 +4,9 @@ pub use self::plumbing::*;
 mod job;
 #[cfg(parallel_compiler)]
 pub use self::job::deadlock;
-pub use self::job::{print_query_stack, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap};
+pub use self::job::{
+    print_query_stack, report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap,
+};
 
 mod caches;
 pub use self::caches::{
@@ -33,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.
@@ -49,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 {
@@ -57,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_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index c5715d93859..c5a0cc753a8 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -134,7 +134,7 @@ where
     match query.handle_cycle_error() {
         Error => {
             let guar = error.emit();
-            query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
+            query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
         }
         Fatal => {
             error.emit();
@@ -143,7 +143,7 @@ where
         }
         DelayBug => {
             let guar = error.delay_as_bug();
-            query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
+            query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
         }
         Stash => {
             let guar = if let Some(root) = cycle_error.cycle.first()
@@ -154,7 +154,7 @@ where
             } else {
                 error.emit()
             };
-            query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
+            query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
         }
     }
 }
@@ -211,7 +211,7 @@ where
 }
 
 #[derive(Clone, Debug)]
-pub(crate) struct CycleError {
+pub struct CycleError {
     /// The query and related span that uses the cycle.
     pub usage: Option<(Span, QueryStackFrame)>,
     pub cycle: Vec<QueryInfo>,
diff --git a/compiler/rustc_query_system/src/values.rs b/compiler/rustc_query_system/src/values.rs
index 4f1c182cdb8..133904f59af 100644
--- a/compiler/rustc_query_system/src/values.rs
+++ b/compiler/rustc_query_system/src/values.rs
@@ -1,20 +1,21 @@
 use rustc_span::ErrorGuaranteed;
 
 use crate::dep_graph::DepContext;
-use crate::query::QueryInfo;
+use crate::query::CycleError;
 
 pub trait Value<Tcx: DepContext>: Sized {
-    fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self;
+    fn from_cycle_error(tcx: Tcx, cycle_error: &CycleError, guar: ErrorGuaranteed) -> Self;
 }
 
 impl<Tcx: DepContext, T> Value<Tcx> for T {
-    default fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo], _guar: ErrorGuaranteed) -> T {
+    default fn from_cycle_error(tcx: Tcx, cycle_error: &CycleError, _guar: ErrorGuaranteed) -> T {
         tcx.sess().dcx().abort_if_errors();
         // Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's
         // non-trivial to define it earlier.
         panic!(
-            "<{} as Value>::from_cycle_error called without errors: {cycle:#?}",
-            std::any::type_name::<T>()
+            "<{} as Value>::from_cycle_error called without errors: {:#?}",
+            std::any::type_name::<T>(),
+            cycle_error.cycle,
         );
     }
 }
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/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 5ca623f01f1..3b91fbdcb29 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -4,7 +4,6 @@ use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::DefId;
 use rustc_index::IndexVec;
-use rustc_middle::query::Key;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -1259,7 +1258,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
             clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
                 let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
                 let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
-                let enum_def_id = ty.ty_adt_id().unwrap();
+                let enum_def_id = ty.ty_adt_def().unwrap().did();
 
                 wrap_item(w, |w| {
                     let variants_len = variants.len();
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index d91af76f5e0..bd07c19a2d8 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -12,7 +12,6 @@ use rustc_errors::Applicability;
 use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::query::Key;
 use rustc_session::impl_lint_pass;
 use rustc_span::hygiene::walk_chain;
 use rustc_span::source_map::SourceMap;
@@ -574,7 +573,7 @@ fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignore
     let caller_ty = cx.typeck_results().expr_ty(caller_expr);
     // Check if given type has inner mutability and was not set to ignored by the configuration
     let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty)
-        && !matches!(caller_ty.ty_adt_id(), Some(adt_id) if ignored_ty_ids.contains(&adt_id));
+        && !matches!(caller_ty.ty_adt_def(), Some(adt) if ignored_ty_ids.contains(&adt.did()));
 
     is_inner_mut_ty
         || caller_ty.is_mutable_ptr()
diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
index 6a82d8f756a..3a8ca37610a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
@@ -6,7 +6,6 @@ use clippy_utils::ty::is_type_lang_item;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
 use rustc_lint::LateContext;
-use rustc_middle::query::Key;
 use rustc_middle::ty;
 use rustc_middle::ty::Ty;
 use rustc_span::{sym, Symbol};
@@ -18,10 +17,10 @@ use rustc_span::{sym, Symbol};
 /// `vec![1,2].drain(..).collect::<HashSet<_>>()`
 ///  ^^^^^^^^^                     ^^^^^^^^^^  false
 fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool {
-    if let Some(expr_adt_did) = expr.ty_adt_id()
-        && let Some(recv_adt_did) = recv.ty_adt_id()
+    if let Some(expr_adt) = expr.ty_adt_def()
+        && let Some(recv_adt) = recv.ty_adt_def()
     {
-        cx.tcx.is_diagnostic_item(sym, expr_adt_did) && cx.tcx.is_diagnostic_item(sym, recv_adt_did)
+        cx.tcx.is_diagnostic_item(sym, expr_adt.did()) && cx.tcx.is_diagnostic_item(sym, recv_adt.did())
     } else {
         false
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs b/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs
index 98cd6afc2b7..2a2feedd2b4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs
@@ -4,7 +4,6 @@ use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
-use rustc_middle::query::Key;
 use rustc_span::Span;
 
 pub(super) fn check(
@@ -14,11 +13,7 @@ pub(super) fn check(
     as_str_span: Span,
     other_method_span: Span,
 ) {
-    if cx
-        .tcx
-        .lang_items()
-        .string()
-        .is_some_and(|id| Some(id) == cx.typeck_results().expr_ty(recv).ty_adt_id())
+    if cx.typeck_results().expr_ty(recv).ty_adt_def().is_some_and(|adt| Some(adt.did()) == cx.tcx.lang_items().string())
     {
         let mut applicability = Applicability::MachineApplicable;
         span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs
index 04d2ced6abf..c32025fcbb6 100644
--- a/src/tools/clippy/clippy_lints/src/mut_key.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_key.rs
@@ -4,7 +4,6 @@ use clippy_utils::{def_path_def_ids, trait_ref_of_method};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::query::Key;
 use rustc_middle::ty::{Adt, Ty};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::LocalDefId;
@@ -166,7 +165,7 @@ impl MutableKeyType {
             // Determines if a type contains interior mutability which would affect its implementation of
             // [`Hash`] or [`Ord`].
             if is_interior_mut_ty(cx, subst_ty)
-                && !matches!(subst_ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id))
+                && !matches!(subst_ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did()))
             {
                 span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
             }
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 4013cb34561..f8365deebd4 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -15,7 +15,6 @@ use rustc_hir::{
 };
 use rustc_lint::{LateContext, LateLintPass, Lint};
 use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
-use rustc_middle::query::Key;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
@@ -188,7 +187,7 @@ impl NonCopyConst {
     }
 
     fn is_ty_ignored(&self, ty: Ty<'_>) -> bool {
-        matches!(ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id))
+        matches!(ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did()))
     }
 
     fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
index c0d0d2b93dc..5df645491ff 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
@@ -4,7 +4,6 @@ use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
-use rustc_middle::query::Key;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::sym;
 
@@ -17,10 +16,10 @@ pub(super) fn check<'tcx>(
     to_ty: Ty<'tcx>,
     arg: &'tcx Expr<'_>,
 ) -> bool {
-    let (ty::Int(_) | ty::Uint(_), Some(to_ty_id)) = (&from_ty.kind(), to_ty.ty_adt_id()) else {
+    let (ty::Int(_) | ty::Uint(_), Some(to_ty_adt)) = (&from_ty.kind(), to_ty.ty_adt_def()) else {
         return false;
     };
-    let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_id) else {
+    let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_adt.did()) else {
         return false;
     };
 
diff --git a/tests/ui/async-await/in-trait/async-recursive-generic.rs b/tests/ui/async-await/in-trait/async-recursive-generic.rs
index c6031ce28d1..33eb2b2de13 100644
--- a/tests/ui/async-await/in-trait/async-recursive-generic.rs
+++ b/tests/ui/async-await/in-trait/async-recursive-generic.rs
@@ -6,7 +6,7 @@ trait MyTrait<T> {
 
 impl<T> MyTrait<T> for T where T: Copy {
     async fn foo_recursive(&self, n: usize) -> T {
-        //~^ ERROR recursion in an `async fn` requires boxing
+        //~^ ERROR recursion in an async fn requires boxing
         if n > 0 {
             self.foo_recursive(n - 1).await
         } else {
diff --git a/tests/ui/async-await/in-trait/async-recursive-generic.stderr b/tests/ui/async-await/in-trait/async-recursive-generic.stderr
index 11489c18ad4..d085747bc4b 100644
--- a/tests/ui/async-await/in-trait/async-recursive-generic.stderr
+++ b/tests/ui/async-await/in-trait/async-recursive-generic.stderr
@@ -1,11 +1,13 @@
-error[E0733]: recursion in an `async fn` requires boxing
+error[E0733]: recursion in an async fn requires boxing
   --> $DIR/async-recursive-generic.rs:8:5
    |
 LL |     async fn foo_recursive(&self, n: usize) -> T {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |             self.foo_recursive(n - 1).await
+   |             ------------------------------- recursive call here
    |
-   = 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
+   = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/async-await/in-trait/async-recursive.rs b/tests/ui/async-await/in-trait/async-recursive.rs
index 09f1ffe499e..2534c43413e 100644
--- a/tests/ui/async-await/in-trait/async-recursive.rs
+++ b/tests/ui/async-await/in-trait/async-recursive.rs
@@ -6,7 +6,7 @@ trait MyTrait {
 
 impl MyTrait for i32 {
     async fn foo_recursive(&self, n: usize) -> i32 {
-        //~^ ERROR recursion in an `async fn` requires boxing
+        //~^ ERROR recursion in an async fn requires boxing
         if n > 0 {
             self.foo_recursive(n - 1).await
         } else {
diff --git a/tests/ui/async-await/in-trait/async-recursive.stderr b/tests/ui/async-await/in-trait/async-recursive.stderr
index 58796285726..25ebc6e77c4 100644
--- a/tests/ui/async-await/in-trait/async-recursive.stderr
+++ b/tests/ui/async-await/in-trait/async-recursive.stderr
@@ -1,11 +1,13 @@
-error[E0733]: recursion in an `async fn` requires boxing
+error[E0733]: recursion in an async fn requires boxing
   --> $DIR/async-recursive.rs:8:5
    |
 LL |     async fn foo_recursive(&self, n: usize) -> i32 {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |             self.foo_recursive(n - 1).await
+   |             ------------------------------- recursive call here
    |
-   = 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
+   = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
index 8443cbcf4ac..4b615343a05 100644
--- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
@@ -1,6 +1,7 @@
 // edition: 2021
+
+// Test doesn't fail until monomorphization time, unfortunately.
 // build-fail
-//~^^ ERROR cycle detected when computing layout of
 
 fn main() {
     let _ = async {
@@ -31,6 +32,7 @@ where
     C: First,
 {
     async fn second(self) {
+        //~^ ERROR recursion in an async fn requires boxing
         self.first().await.second().await;
     }
 }
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
index 8e573b512ad..8126c6e1394 100644
--- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
@@ -1,12 +1,11 @@
-error[E0391]: cycle detected when computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`
+error[E0733]: recursion in an async fn requires boxing
+  --> $DIR/indirect-recursion-issue-112047.rs:34:5
    |
-   = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`...
-   = note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`...
-   = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<<A as First>::Second as Second>::{opaque#0}>`...
-   = note: ...which again requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`, completing the cycle
-   = note: cycle used when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:6:13: 8:6}`
-   = 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
+LL |     async fn second(self) {
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
 
 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`.
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..fedc814b041 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
@@ -2,11 +2,11 @@
 // Test that impl trait does not allow creating recursive types that are
 // otherwise forbidden when using `async` and `await`.
 
-async fn rec_1() { //~ ERROR recursion in an `async fn`
+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..52fb41be1fb 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
@@ -1,21 +1,20 @@
-error[E0733]: recursion in an `async fn` requires boxing
+error[E0733]: recursion in an async fn requires boxing
   --> $DIR/mutually-recursive-async-impl-trait-type.rs:5:1
    |
 LL | async fn rec_1() {
-   | ^^^^^^^^^^^^^^^^ recursive `async fn`
+   | ^^^^^^^^^^^^^^^^
+LL |     rec_2().await;
+   |     ------------- recursive call here
    |
-   = 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
+note: which leads to this async fn
   --> $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
+   | ^^^^^^^^^^^^^^^^
+LL |     rec_1().await;
+   |     ------------- ...leading to this recursive call
+   = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
 
-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/async-await/recursive-async-impl-trait-type.rs b/tests/ui/async-await/recursive-async-impl-trait-type.rs
index edc4cb8ac5d..9351ee53f07 100644
--- a/tests/ui/async-await/recursive-async-impl-trait-type.rs
+++ b/tests/ui/async-await/recursive-async-impl-trait-type.rs
@@ -3,7 +3,7 @@
 // otherwise forbidden when using `async` and `await`.
 
 async fn recursive_async_function() -> () {
-    //~^ ERROR recursion in an `async fn` requires boxing
+    //~^ ERROR recursion in an async fn requires boxing
     recursive_async_function().await;
 }
 
diff --git a/tests/ui/async-await/recursive-async-impl-trait-type.stderr b/tests/ui/async-await/recursive-async-impl-trait-type.stderr
index 969258f84ed..5475469335f 100644
--- a/tests/ui/async-await/recursive-async-impl-trait-type.stderr
+++ b/tests/ui/async-await/recursive-async-impl-trait-type.stderr
@@ -1,11 +1,13 @@
-error[E0733]: recursion in an `async fn` requires boxing
+error[E0733]: recursion in an async fn requires boxing
   --> $DIR/recursive-async-impl-trait-type.rs:5:1
    |
 LL | async fn recursive_async_function() -> () {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     recursive_async_function().await;
+   |     -------------------------------- recursive call here
    |
-   = 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
+   = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/impl-trait/recursive-coroutine.rs b/tests/ui/impl-trait/recursive-coroutine-boxed.rs
index b82fe134a40..b9291f07e21 100644
--- a/tests/ui/impl-trait/recursive-coroutine.rs
+++ b/tests/ui/impl-trait/recursive-coroutine-boxed.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-coroutine-indirect.current.stderr b/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr
new file mode 100644
index 00000000000..11b3c4ef007
--- /dev/null
+++ b/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr
@@ -0,0 +1,11 @@
+error[E0733]: recursion in a coroutine requires boxing
+  --> $DIR/recursive-coroutine-indirect.rs:6:5
+   |
+LL |     move || {
+   |     ^^^^^^^
+LL |         let x = coroutine_hold();
+   |             - recursive call here
+
+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-indirect.next.stderr b/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr
new file mode 100644
index 00000000000..11b3c4ef007
--- /dev/null
+++ b/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr
@@ -0,0 +1,11 @@
+error[E0733]: recursion in a coroutine requires boxing
+  --> $DIR/recursive-coroutine-indirect.rs:6:5
+   |
+LL |     move || {
+   |     ^^^^^^^
+LL |         let x = coroutine_hold();
+   |             - recursive call here
+
+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-indirect.rs b/tests/ui/impl-trait/recursive-coroutine-indirect.rs
new file mode 100644
index 00000000000..4f8d4d33050
--- /dev/null
+++ b/tests/ui/impl-trait/recursive-coroutine-indirect.rs
@@ -0,0 +1,13 @@
+// revisions: current next
+//[next] compile-flags: -Znext-solver
+#![feature(coroutines)]
+#![allow(unconditional_recursion)]
+fn coroutine_hold() -> impl Sized {
+    move || { //~ ERROR recursion in a coroutine requires boxing
+        let x = coroutine_hold();
+        yield;
+        x;
+    }
+}
+
+fn main() {}
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-impl-trait-type-indirect.rs b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs
index 8331eec906e..432f80a1763 100644
--- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs
+++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs
@@ -1,6 +1,5 @@
 // Test that impl trait does not allow creating recursive types that are
 // otherwise forbidden.
-
 #![feature(coroutines)]
 #![allow(unconditional_recursion)]
 
@@ -69,15 +68,6 @@ fn substs_change<T: 'static>() -> impl Sized {
     (substs_change::<&T>(),)
 }
 
-fn coroutine_hold() -> impl Sized {
-    //~^ ERROR
-    move || {
-        let x = coroutine_hold();
-        yield;
-        x;
-    }
-}
-
 fn use_fn_ptr() -> impl Sized {
     // OK, error already reported
     fn_ptr()
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..d5b8c531fd6 100644
--- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr
+++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr
@@ -1,5 +1,5 @@
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:7:22
+  --> $DIR/recursive-impl-trait-type-indirect.rs:6:22
    |
 LL | fn option(i: i32) -> impl Sized {
    |                      ^^^^^^^^^^ recursive opaque type
@@ -10,7 +10,7 @@ LL |     if i < 0 { None } else { Some((option(i - 1), i)) }
    |                returning here with type `Option<(impl Sized, i32)>`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:12:15
+  --> $DIR/recursive-impl-trait-type-indirect.rs:11:15
    |
 LL | fn tuple() -> impl Sized {
    |               ^^^^^^^^^^ recursive opaque type
@@ -19,7 +19,7 @@ LL |     (tuple(),)
    |     ---------- returning here with type `(impl Sized,)`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:17:15
+  --> $DIR/recursive-impl-trait-type-indirect.rs:16:15
    |
 LL | fn array() -> impl Sized {
    |               ^^^^^^^^^^ recursive opaque type
@@ -28,7 +28,7 @@ LL |     [array()]
    |     --------- returning here with type `[impl Sized; 1]`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:22:13
+  --> $DIR/recursive-impl-trait-type-indirect.rs:21:13
    |
 LL | fn ptr() -> impl Sized {
    |             ^^^^^^^^^^ recursive opaque type
@@ -37,7 +37,7 @@ LL |     &ptr() as *const _
    |     ------------------ returning here with type `*const impl Sized`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:27:16
+  --> $DIR/recursive-impl-trait-type-indirect.rs:26:16
    |
 LL | fn fn_ptr() -> impl Sized {
    |                ^^^^^^^^^^ recursive opaque type
@@ -46,7 +46,7 @@ LL |     fn_ptr as fn() -> _
    |     ------------------- returning here with type `fn() -> impl Sized`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:32:25
+  --> $DIR/recursive-impl-trait-type-indirect.rs:31:25
    |
 LL |   fn closure_capture() -> impl Sized {
    |                           ^^^^^^^^^^ recursive opaque type
@@ -55,10 +55,10 @@ LL | /     move || {
 LL | |         x;
    | |         - closure captures itself here
 LL | |     }
-   | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 35:12}`
+   | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:34:5: 34:12}`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:40:29
+  --> $DIR/recursive-impl-trait-type-indirect.rs:39:29
    |
 LL |   fn closure_ref_capture() -> impl Sized {
    |                               ^^^^^^^^^^ recursive opaque type
@@ -67,28 +67,28 @@ LL | /     move || {
 LL | |         &x;
    | |          - closure captures itself here
 LL | |     }
-   | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 43:12}`
+   | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:42:5: 42:12}`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:48:21
+  --> $DIR/recursive-impl-trait-type-indirect.rs:47:21
    |
 LL | fn closure_sig() -> impl Sized {
    |                     ^^^^^^^^^^ recursive opaque type
 LL |
 LL |     || closure_sig()
-   |     ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:7}`
+   |     ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:49:5: 49:7}`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:53:23
+  --> $DIR/recursive-impl-trait-type-indirect.rs:52:23
    |
 LL | fn coroutine_sig() -> impl Sized {
    |                       ^^^^^^^^^^ recursive opaque type
 LL |
 LL |     || coroutine_sig()
-   |     ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:7}`
+   |     ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7}`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:58:27
+  --> $DIR/recursive-impl-trait-type-indirect.rs:57:27
    |
 LL |   fn coroutine_capture() -> impl Sized {
    |                             ^^^^^^^^^^ recursive opaque type
@@ -98,10 +98,10 @@ LL | |         yield;
 LL | |         x;
    | |         - coroutine captures itself here
 LL | |     }
-   | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 61:12}`
+   | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:60:5: 60:12}`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:67:35
+  --> $DIR/recursive-impl-trait-type-indirect.rs:66:35
    |
 LL | fn substs_change<T: 'static>() -> impl Sized {
    |                                   ^^^^^^^^^^ recursive opaque type
@@ -110,16 +110,7 @@ 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
-   |
-LL | fn coroutine_hold() -> impl Sized {
-   |                        ^^^^^^^^^^ recursive opaque type
-...
-LL |         let x = coroutine_hold();
-   |             - coroutine captures itself here
-
-error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:86:26
+  --> $DIR/recursive-impl-trait-type-indirect.rs:76:26
    |
 LL | fn mutual_recursion() -> impl Sync {
    |                          ^^^^^^^^^ recursive opaque type
@@ -131,7 +122,7 @@ LL | fn mutual_recursion_b() -> impl Sized {
    |                            ---------- returning this opaque type `impl Sized`
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-impl-trait-type-indirect.rs:91:28
+  --> $DIR/recursive-impl-trait-type-indirect.rs:81:28
    |
 LL | fn mutual_recursion() -> impl Sync {
    |                          --------- returning this opaque type `impl Sync`
@@ -142,6 +133,6 @@ LL |
 LL |     mutual_recursion()
    |     ------------------ returning here with type `impl Sync`
 
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
 
 For more information about this 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..e7b23d5f8a1 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
@@ -20,7 +20,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 block 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..b62186103c7 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,20 @@
-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 block requires boxing
+  --> $DIR/indirect-recursion-issue-112047.rs:22:9
    |
 LL |         async move { recur(self).await; }
-   |                      ^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^-----------------^^^
+   |                      |
+   |                      recursive call here
    |
-   = 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
+note: which leads to this async fn
+  --> $DIR/indirect-recursion-issue-112047.rs:14:1
    |
+LL | async fn recur(t: impl Recur) {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 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
+   |     --------------- ...leading to this recursive call
+   = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
 
 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`.