about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-12-26 04:25:53 +0000
committerbors <bors@rust-lang.org>2023-12-26 04:25:53 +0000
commit1ab783112ab4e4807304dbd249b39771246013ef (patch)
tree827d6051e0e3ffcaadd0d6c7369b71547319a525
parent2271c26e4a8e062bb00d709d0ccb5846e0c341b9 (diff)
parentba912855cc1aff5f2b403411c8d890d3978abf9a (diff)
downloadrust-1ab783112ab4e4807304dbd249b39771246013ef.tar.gz
rust-1ab783112ab4e4807304dbd249b39771246013ef.zip
Auto merge of #119258 - compiler-errors:closure-kind, r=eholk
Make closures carry their own ClosureKind

Right now, we use the "`movability`" field of `hir::Closure` to distinguish a closure and a coroutine. This is paired together with the `CoroutineKind`, which is located not in the `hir::Closure`, but the `hir::Body`. This is strange and redundant.

This PR introduces `ClosureKind` with two variants -- `Closure` and `Coroutine`, which is put into `hir::Closure`. The `CoroutineKind` is thus removed from `hir::Body`, and `Option<Movability>` no longer needs to be a stand-in for "is this a closure or a coroutine".

r? eholk
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs65
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs50
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs60
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs3
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs10
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs128
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs13
-rw-r--r--compiler/rustc_hir/src/hir.rs35
-rw-r--r--compiler/rustc_hir/src/intravisit.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs12
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs112
-rw-r--r--compiler/rustc_metadata/src/rmeta/table.rs3
-rw-r--r--compiler/rustc_middle/src/middle/region.rs14
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs8
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs4
-rw-r--r--compiler/rustc_middle/src/ty/util.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs9
-rw-r--r--compiler/rustc_passes/src/loops.rs19
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs55
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs99
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs85
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs4
-rw-r--r--compiler/stable_mir/src/mir/body.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/async_yields_async.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/await_holding_invalid.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_question_mark.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs21
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.stdout4
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_closure.stdout2
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.fixed4
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.rs4
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.stderr8
-rw-r--r--tests/ui/stats/hir-stats.stderr8
47 files changed, 570 insertions, 488 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 7e638266478..1f195916c77 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -668,11 +668,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         };
         let params = arena_vec![self; param];
 
+        let coroutine_kind =
+            hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, async_coroutine_source);
         let body = self.lower_body(move |this| {
-            this.coroutine_kind = Some(hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::Async,
-                async_coroutine_source,
-            ));
+            this.coroutine_kind = Some(coroutine_kind);
 
             let old_ctx = this.task_context;
             this.task_context = Some(task_context_hid);
@@ -691,7 +690,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             body,
             fn_decl_span: self.lower_span(span),
             fn_arg_span: None,
-            movability: Some(hir::Movability::Static),
+            kind: hir::ClosureKind::Coroutine(coroutine_kind),
             constness: hir::Constness::NotConst,
         }))
     }
@@ -725,11 +724,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
             lifetime_elision_allowed: false,
         });
 
+        let coroutine_kind =
+            hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, coroutine_source);
         let body = self.lower_body(move |this| {
-            this.coroutine_kind = Some(hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::Gen,
-                coroutine_source,
-            ));
+            this.coroutine_kind = Some(coroutine_kind);
 
             let res = body(this);
             (&[], res)
@@ -745,7 +743,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             body,
             fn_decl_span: self.lower_span(span),
             fn_arg_span: None,
-            movability: Some(Movability::Movable),
+            kind: hir::ClosureKind::Coroutine(coroutine_kind),
             constness: hir::Constness::NotConst,
         }))
     }
@@ -806,11 +804,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
         };
         let params = arena_vec![self; param];
 
+        let coroutine_kind = hir::CoroutineKind::Desugared(
+            hir::CoroutineDesugaring::AsyncGen,
+            async_coroutine_source,
+        );
         let body = self.lower_body(move |this| {
-            this.coroutine_kind = Some(hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::AsyncGen,
-                async_coroutine_source,
-            ));
+            this.coroutine_kind = Some(coroutine_kind);
 
             let old_ctx = this.task_context;
             this.task_context = Some(task_context_hid);
@@ -829,7 +828,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             body,
             fn_decl_span: self.lower_span(span),
             fn_arg_span: None,
-            movability: Some(hir::Movability::Static),
+            kind: hir::ClosureKind::Coroutine(coroutine_kind),
             constness: hir::Constness::NotConst,
         }))
     }
@@ -898,7 +897,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let is_async_gen = match self.coroutine_kind {
             Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => false,
             Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => true,
-            Some(hir::CoroutineKind::Coroutine)
+            Some(hir::CoroutineKind::Coroutine(_))
             | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _))
             | None => {
                 return hir::ExprKind::Err(self.dcx().emit_err(AwaitOnlyInAsyncFnAndBlocks {
@@ -1086,7 +1085,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) -> hir::ExprKind<'hir> {
         let (binder_clause, generic_params) = self.lower_closure_binder(binder);
 
-        let (body_id, coroutine_option) = self.with_new_scopes(fn_decl_span, move |this| {
+        let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
             let mut coroutine_kind = None;
             let body_id = this.lower_fn_body(decl, |this| {
                 let e = this.lower_expr_mut(body);
@@ -1094,7 +1093,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 e
             });
             let coroutine_option =
-                this.coroutine_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability);
+                this.closure_movability_for_fn(decl, fn_decl_span, coroutine_kind, movability);
             (body_id, coroutine_option)
         });
 
@@ -1111,26 +1110,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
             body: body_id,
             fn_decl_span: self.lower_span(fn_decl_span),
             fn_arg_span: Some(self.lower_span(fn_arg_span)),
-            movability: coroutine_option,
+            kind: closure_kind,
             constness: self.lower_constness(constness),
         });
 
         hir::ExprKind::Closure(c)
     }
 
-    fn coroutine_movability_for_fn(
+    fn closure_movability_for_fn(
         &mut self,
         decl: &FnDecl,
         fn_decl_span: Span,
         coroutine_kind: Option<hir::CoroutineKind>,
         movability: Movability,
-    ) -> Option<hir::Movability> {
+    ) -> hir::ClosureKind {
         match coroutine_kind {
-            Some(hir::CoroutineKind::Coroutine) => {
+            Some(hir::CoroutineKind::Coroutine(_)) => {
                 if decl.inputs.len() > 1 {
                     self.dcx().emit_err(CoroutineTooManyParameters { fn_decl_span });
                 }
-                Some(movability)
+                hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(movability))
             }
             Some(
                 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
@@ -1143,7 +1142,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 if movability == Movability::Static {
                     self.dcx().emit_err(ClosureCannotBeStatic { fn_decl_span });
                 }
-                None
+                hir::ClosureKind::Closure
             }
         }
     }
@@ -1235,7 +1234,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             body,
             fn_decl_span: self.lower_span(fn_decl_span),
             fn_arg_span: Some(self.lower_span(fn_arg_span)),
-            movability: None,
+            kind: hir::ClosureKind::Closure,
             constness: hir::Constness::NotConst,
         });
         hir::ExprKind::Closure(c)
@@ -1655,7 +1654,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     self.dcx().emit_err(AsyncCoroutinesNotSupported { span }),
                 );
             }
-            Some(hir::CoroutineKind::Coroutine) | None => {
+            Some(hir::CoroutineKind::Coroutine(_)) => {
+                if !self.tcx.features().coroutines {
+                    rustc_session::parse::feature_err(
+                        &self.tcx.sess.parse_sess,
+                        sym::coroutines,
+                        span,
+                        "yield syntax is experimental",
+                    )
+                    .emit();
+                }
+                false
+            }
+            None => {
                 if !self.tcx.features().coroutines {
                     rustc_session::parse::feature_err(
                         &self.tcx.sess.parse_sess,
@@ -1665,7 +1676,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     )
                     .emit();
                 }
-                self.coroutine_kind = Some(hir::CoroutineKind::Coroutine);
+                self.coroutine_kind = Some(hir::CoroutineKind::Coroutine(Movability::Movable));
                 false
             }
         };
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 81457018b37..b70b9356226 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -952,11 +952,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         params: &'hir [hir::Param<'hir>],
         value: hir::Expr<'hir>,
     ) -> hir::BodyId {
-        let body = hir::Body {
-            coroutine_kind: self.coroutine_kind,
-            params,
-            value: self.arena.alloc(value),
-        };
+        let body = hir::Body { params, value: self.arena.alloc(value) };
         let id = body.id();
         debug_assert_eq!(id.hir_id.owner, self.current_hir_id_owner);
         self.bodies.push((id.hir_id.local_id, self.arena.alloc(body)));
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index da6fffc167c..ca65488375c 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -848,8 +848,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         move_spans.var_subdiag(None, &mut err, None, |kind, var_span| {
             use crate::session_diagnostics::CaptureVarCause::*;
             match kind {
-                Some(_) => MoveUseInCoroutine { var_span },
-                None => MoveUseInClosure { var_span },
+                hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span },
+                hir::ClosureKind::Closure => MoveUseInClosure { var_span },
             }
         });
 
@@ -893,10 +893,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let place = &borrow.borrowed_place;
             let desc_place = self.describe_any_place(place.as_ref());
             match kind {
-                Some(_) => {
+                hir::ClosureKind::Coroutine(_) => {
                     BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true }
                 }
-                None => BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true },
+                hir::ClosureKind::Closure => {
+                    BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true }
+                }
             }
         });
 
@@ -1040,12 +1042,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         |kind, var_span| {
                             use crate::session_diagnostics::CaptureVarCause::*;
                             match kind {
-                                Some(_) => BorrowUsePlaceCoroutine {
+                                hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {
                                     place: desc_place,
                                     var_span,
                                     is_single_var: true,
                                 },
-                                None => BorrowUsePlaceClosure {
+                                hir::ClosureKind::Closure => BorrowUsePlaceClosure {
                                     place: desc_place,
                                     var_span,
                                     is_single_var: true,
@@ -1124,12 +1126,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             borrow_spans.var_subdiag(None, &mut err, Some(gen_borrow_kind), |kind, var_span| {
                 use crate::session_diagnostics::CaptureVarCause::*;
                 match kind {
-                    Some(_) => BorrowUsePlaceCoroutine {
+                    hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {
                         place: desc_place,
                         var_span,
                         is_single_var: false,
                     },
-                    None => {
+                    hir::ClosureKind::Closure => {
                         BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
                     }
                 }
@@ -1144,10 +1146,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     let borrow_place = &issued_borrow.borrowed_place;
                     let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
                     match kind {
-                        Some(_) => {
+                        hir::ClosureKind::Coroutine(_) => {
                             FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }
                         }
-                        None => FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span },
+                        hir::ClosureKind::Closure => {
+                            FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }
+                        }
                     }
                 },
             );
@@ -1159,8 +1163,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 |kind, var_span| {
                     use crate::session_diagnostics::CaptureVarCause::*;
                     match kind {
-                        Some(_) => SecondBorrowUsePlaceCoroutine { place: desc_place, var_span },
-                        None => SecondBorrowUsePlaceClosure { place: desc_place, var_span },
+                        hir::ClosureKind::Coroutine(_) => {
+                            SecondBorrowUsePlaceCoroutine { place: desc_place, var_span }
+                        }
+                        hir::ClosureKind::Closure => {
+                            SecondBorrowUsePlaceClosure { place: desc_place, var_span }
+                        }
                     }
                 },
             );
@@ -1651,7 +1659,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
                 if e.span.contains(self.capture_span) {
                     if let hir::ExprKind::Closure(&hir::Closure {
-                        movability: None,
+                        kind: hir::ClosureKind::Closure,
                         body,
                         fn_arg_span,
                         fn_decl: hir::FnDecl { inputs, .. },
@@ -1686,7 +1694,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     && let Some(init) = local.init
                 {
                     if let hir::Expr {
-                        kind: hir::ExprKind::Closure(&hir::Closure { movability: None, .. }),
+                        kind:
+                            hir::ExprKind::Closure(&hir::Closure {
+                                kind: hir::ClosureKind::Closure,
+                                ..
+                            }),
                         ..
                     } = init
                         && init.span.contains(self.capture_span)
@@ -2537,7 +2549,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         }
                     }
                 }
-                CoroutineKind::Coroutine => "coroutine",
+                CoroutineKind::Coroutine(_) => "coroutine",
             },
             None => "closure",
         };
@@ -2838,8 +2850,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
                     use crate::session_diagnostics::CaptureVarCause::*;
                     match kind {
-                        Some(_) => BorrowUseInCoroutine { var_span },
-                        None => BorrowUseInClosure { var_span },
+                        hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
+                        hir::ClosureKind::Closure => BorrowUseInClosure { var_span },
                     }
                 });
 
@@ -2854,8 +2866,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         loan_spans.var_subdiag(None, &mut err, Some(loan.kind), |kind, var_span| {
             use crate::session_diagnostics::CaptureVarCause::*;
             match kind {
-                Some(_) => BorrowUseInCoroutine { var_span },
-                None => BorrowUseInClosure { var_span },
+                hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
+                hir::ClosureKind::Closure => BorrowUseInClosure { var_span },
             }
         });
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 65dee9d0e00..c14a05bd145 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -505,7 +505,7 @@ pub(super) enum UseSpans<'tcx> {
     /// The access is caused by capturing a variable for a closure.
     ClosureUse {
         /// This is true if the captured variable was from a coroutine.
-        coroutine_kind: Option<CoroutineKind>,
+        closure_kind: hir::ClosureKind,
         /// The span of the args of the closure, including the `move` keyword if
         /// it's present.
         args_span: Span,
@@ -572,9 +572,13 @@ impl UseSpans<'_> {
         }
     }
 
+    // FIXME(coroutines): Make this just return the `ClosureKind` directly?
     pub(super) fn coroutine_kind(self) -> Option<CoroutineKind> {
         match self {
-            UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind,
+            UseSpans::ClosureUse {
+                closure_kind: hir::ClosureKind::Coroutine(coroutine_kind),
+                ..
+            } => Some(coroutine_kind),
             _ => None,
         }
     }
@@ -599,9 +603,9 @@ impl UseSpans<'_> {
     ) {
         use crate::InitializationRequiringAction::*;
         use CaptureVarPathUseCause::*;
-        if let UseSpans::ClosureUse { coroutine_kind, path_span, .. } = self {
-            match coroutine_kind {
-                Some(_) => {
+        if let UseSpans::ClosureUse { closure_kind, path_span, .. } = self {
+            match closure_kind {
+                hir::ClosureKind::Coroutine(_) => {
                     err.subdiagnostic(match action {
                         Borrow => BorrowInCoroutine { path_span },
                         MatchOn | Use => UseInCoroutine { path_span },
@@ -609,7 +613,7 @@ impl UseSpans<'_> {
                         PartialAssignment => AssignPartInCoroutine { path_span },
                     });
                 }
-                None => {
+                hir::ClosureKind::Closure => {
                     err.subdiagnostic(match action {
                         Borrow => BorrowInClosure { path_span },
                         MatchOn | Use => UseInClosure { path_span },
@@ -627,9 +631,9 @@ impl UseSpans<'_> {
         dcx: Option<&rustc_errors::DiagCtxt>,
         err: &mut Diagnostic,
         kind: Option<rustc_middle::mir::BorrowKind>,
-        f: impl FnOnce(Option<CoroutineKind>, Span) -> CaptureVarCause,
+        f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
     ) {
-        if let UseSpans::ClosureUse { coroutine_kind, capture_kind_span, path_span, .. } = self {
+        if let UseSpans::ClosureUse { closure_kind, capture_kind_span, path_span, .. } = self {
             if capture_kind_span != path_span {
                 err.subdiagnostic(match kind {
                     Some(kd) => match kd {
@@ -645,7 +649,7 @@ impl UseSpans<'_> {
                     None => CaptureVarKind::Move { kind_span: capture_kind_span },
                 });
             };
-            let diag = f(coroutine_kind, path_span);
+            let diag = f(closure_kind, path_span);
             match dcx {
                 Some(hd) => err.eager_subdiagnostic(hd, diag),
                 None => err.subdiagnostic(diag),
@@ -656,7 +660,9 @@ impl UseSpans<'_> {
     /// Returns `false` if this place is not used in a closure.
     pub(super) fn for_closure(&self) -> bool {
         match *self {
-            UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind.is_none(),
+            UseSpans::ClosureUse { closure_kind, .. } => {
+                matches!(closure_kind, hir::ClosureKind::Closure)
+            }
             _ => false,
         }
     }
@@ -664,7 +670,10 @@ impl UseSpans<'_> {
     /// Returns `false` if this place is not used in a coroutine.
     pub(super) fn for_coroutine(&self) -> bool {
         match *self {
-            UseSpans::ClosureUse { coroutine_kind, .. } => coroutine_kind.is_some(),
+            // FIXME(coroutines): Do we want this to apply to synthetic coroutines?
+            UseSpans::ClosureUse { closure_kind, .. } => {
+                matches!(closure_kind, hir::ClosureKind::Coroutine(..))
+            }
             _ => false,
         }
     }
@@ -788,10 +797,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         {
             debug!("move_spans: def_id={:?} places={:?}", def_id, places);
             let def_id = def_id.expect_local();
-            if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) =
+            if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
                 self.closure_span(def_id, moved_place, places)
             {
-                return ClosureUse { coroutine_kind, args_span, capture_kind_span, path_span };
+                return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
             }
         }
 
@@ -803,11 +812,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 | FakeReadCause::ForLet(Some(closure_def_id)) => {
                     debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
                     let places = &[Operand::Move(place)];
-                    if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) =
+                    if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
                         self.closure_span(closure_def_id, moved_place, IndexSlice::from_raw(places))
                     {
                         return ClosureUse {
-                            coroutine_kind,
+                            closure_kind,
                             args_span,
                             capture_kind_span,
                             path_span,
@@ -928,10 +937,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     "borrow_spans: def_id={:?} is_coroutine={:?} places={:?}",
                     def_id, is_coroutine, places
                 );
-                if let Some((args_span, coroutine_kind, capture_kind_span, path_span)) =
+                if let Some((args_span, closure_kind, capture_kind_span, path_span)) =
                     self.closure_span(def_id, Place::from(target).as_ref(), places)
                 {
-                    return ClosureUse { coroutine_kind, args_span, capture_kind_span, path_span };
+                    return ClosureUse { closure_kind, args_span, capture_kind_span, path_span };
                 } else {
                     return OtherUse(use_span);
                 }
@@ -953,7 +962,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         def_id: LocalDefId,
         target_place: PlaceRef<'tcx>,
         places: &IndexSlice<FieldIdx, Operand<'tcx>>,
-    ) -> Option<(Span, Option<CoroutineKind>, Span, Span)> {
+    ) -> Option<(Span, hir::ClosureKind, Span, Span)> {
         debug!(
             "closure_span: def_id={:?} target_place={:?} places={:?}",
             def_id, target_place, places
@@ -961,7 +970,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
         let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
-        if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr {
+        if let hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
             for (captured_place, place) in
                 self.infcx.tcx.closure_captures(def_id).iter().zip(places)
             {
@@ -970,12 +979,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         if target_place == place.as_ref() =>
                     {
                         debug!("closure_span: found captured local {:?}", place);
-                        let body = self.infcx.tcx.hir().body(body);
-                        let coroutine_kind = body.coroutine_kind();
-
                         return Some((
                             fn_decl_span,
-                            coroutine_kind,
+                            kind,
                             captured_place.get_capture_kind_span(self.infcx.tcx),
                             captured_place.get_path_span(self.infcx.tcx),
                         ));
@@ -1242,8 +1248,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             // another message for the same span
             if !is_loop_message {
                 move_spans.var_subdiag(None, err, None, |kind, var_span| match kind {
-                    Some(_) => CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial },
-                    None => CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial },
+                    hir::ClosureKind::Coroutine(_) => {
+                        CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
+                    }
+                    hir::ClosureKind::Closure => {
+                        CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
+                    }
                 })
             }
         }
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 506933c470e..3b3d440df97 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -1030,8 +1030,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         let hir = self.infcx.tcx.hir();
         if let InstanceDef::Item(def_id) = source.instance
             && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) = hir.get_if_local(def_id)
-            && let ExprKind::Closure(closure) = kind
-            && closure.movability == None
+            && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind
             && let Some(Node::Expr(expr)) = hir.find_parent(*hir_id)
         {
             let mut cur_expr = expr;
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 8387eaed61c..b3450b09cdf 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -1041,13 +1041,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
             hir::ExprKind::Closure(hir::Closure {
                 capture_clause: hir::CaptureBy::Ref,
-                body,
+                kind,
                 ..
             }) => {
-                let body = map.body(*body);
                 if !matches!(
-                    body.coroutine_kind,
-                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _))
+                    kind,
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        _
+                    ),)
                 ) {
                     closure_span = Some(expr.span.shrink_to_lo());
                 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 73dc7a9600f..4cb49362863 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -674,7 +674,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
 
         let (return_span, mir_description, hir_ty) = match tcx.hir_node(mir_hir_id) {
             hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, body, fn_decl_span, .. }),
+                kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, kind, fn_decl_span, .. }),
                 ..
             }) => {
                 let (mut span, mut hir_ty) = match fn_decl.output {
@@ -683,62 +683,86 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                     }
                     hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)),
                 };
-                let mir_description = match hir.body(body).coroutine_kind {
-                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, src)) => {
-                        match src {
-                            hir::CoroutineSource::Block => " of async block",
-                            hir::CoroutineSource::Closure => " of async closure",
-                            hir::CoroutineSource::Fn => {
-                                let parent_item =
-                                    tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
-                                let output = &parent_item
-                                    .fn_decl()
-                                    .expect("coroutine lowered from async fn should be in fn")
-                                    .output;
-                                span = output.span();
-                                if let hir::FnRetTy::Return(ret) = output {
-                                    hir_ty = Some(self.get_future_inner_return_ty(*ret));
-                                }
-                                " of async function"
-                            }
+                let mir_description = match kind {
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Block,
+                    )) => " of async block",
+
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Closure,
+                    )) => " of async closure",
+
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Fn,
+                    )) => {
+                        let parent_item =
+                            tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+                        let output = &parent_item
+                            .fn_decl()
+                            .expect("coroutine lowered from async fn should be in fn")
+                            .output;
+                        span = output.span();
+                        if let hir::FnRetTy::Return(ret) = output {
+                            hir_ty = Some(self.get_future_inner_return_ty(*ret));
                         }
+                        " of async function"
                     }
-                    Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, src)) => {
-                        match src {
-                            hir::CoroutineSource::Block => " of gen block",
-                            hir::CoroutineSource::Closure => " of gen closure",
-                            hir::CoroutineSource::Fn => {
-                                let parent_item =
-                                    tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
-                                let output = &parent_item
-                                    .fn_decl()
-                                    .expect("coroutine lowered from gen fn should be in fn")
-                                    .output;
-                                span = output.span();
-                                " of gen function"
-                            }
-                        }
+
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Block,
+                    )) => " of gen block",
+
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Closure,
+                    )) => " of gen closure",
+
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Fn,
+                    )) => {
+                        let parent_item =
+                            tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+                        let output = &parent_item
+                            .fn_decl()
+                            .expect("coroutine lowered from gen fn should be in fn")
+                            .output;
+                        span = output.span();
+                        " of gen function"
                     }
 
-                    Some(hir::CoroutineKind::Desugared(
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
                         hir::CoroutineDesugaring::AsyncGen,
-                        src,
-                    )) => match src {
-                        hir::CoroutineSource::Block => " of async gen block",
-                        hir::CoroutineSource::Closure => " of async gen closure",
-                        hir::CoroutineSource::Fn => {
-                            let parent_item =
-                                tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
-                            let output = &parent_item
-                                .fn_decl()
-                                .expect("coroutine lowered from async gen fn should be in fn")
-                                .output;
-                            span = output.span();
-                            " of async gen function"
-                        }
-                    },
-                    Some(hir::CoroutineKind::Coroutine) => " of coroutine",
-                    None => " of closure",
+                        hir::CoroutineSource::Block,
+                    )) => " of async gen block",
+
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        hir::CoroutineSource::Closure,
+                    )) => " of async gen closure",
+
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        hir::CoroutineSource::Fn,
+                    )) => {
+                        let parent_item =
+                            tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+                        let output = &parent_item
+                            .fn_decl()
+                            .expect("coroutine lowered from async gen fn should be in fn")
+                            .output;
+                        span = output.span();
+                        " of async gen function"
+                    }
+
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) => {
+                        " of coroutine"
+                    }
+                    hir::ClosureKind::Closure => " of closure",
                 };
                 (span, mir_description, hir_ty)
             }
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 1b01fe03654..4f9f70648bd 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -585,7 +585,7 @@ fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
         Some(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, CoroutineSource::Fn)) => {
             "async_gen_fn"
         }
-        Some(CoroutineKind::Coroutine) => "coroutine",
+        Some(CoroutineKind::Coroutine(_)) => "coroutine",
         None => "closure",
     }
 }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index c1ab62ac0b8..92955c4ed14 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -938,8 +938,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
             TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm),
 
-            TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } => {
-                self.check_op(ops::Coroutine(hir::CoroutineKind::Coroutine))
+            TerminatorKind::Yield { .. } => self.check_op(ops::Coroutine(
+                self.tcx
+                    .coroutine_kind(self.body.source.def_id())
+                    .expect("Only expected to have a yield in a coroutine"),
+            )),
+
+            TerminatorKind::CoroutineDrop => {
+                span_bug!(
+                    self.body.source_info(location).span,
+                    "We should not encounter TerminatorKind::CoroutineDrop after coroutine transform"
+                );
             }
 
             TerminatorKind::UnwindTerminate(_) => {
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 452f5d0b7ac..2b840860166 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -945,7 +945,18 @@ pub struct Closure<'hir> {
     pub fn_decl_span: Span,
     /// The span of the argument block `|...|`
     pub fn_arg_span: Option<Span>,
-    pub movability: Option<Movability>,
+    pub kind: ClosureKind,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash, HashStable_Generic, Encodable, Decodable)]
+pub enum ClosureKind {
+    /// This is a plain closure expression.
+    Closure,
+    /// This is a coroutine expression -- i.e. a closure expression in which
+    /// we've found a `yield`. These can arise either from "plain" coroutine
+    ///  usage (e.g. `let x = || { yield (); }`) or from a desugared expression
+    /// (e.g. `async` and `gen` blocks).
+    Coroutine(CoroutineKind),
 }
 
 /// A block of statements `{ .. }`, which may have a label (in this case the
@@ -1335,17 +1346,12 @@ pub struct BodyId {
 pub struct Body<'hir> {
     pub params: &'hir [Param<'hir>],
     pub value: &'hir Expr<'hir>,
-    pub coroutine_kind: Option<CoroutineKind>,
 }
 
 impl<'hir> Body<'hir> {
     pub fn id(&self) -> BodyId {
         BodyId { hir_id: self.value.hir_id }
     }
-
-    pub fn coroutine_kind(&self) -> Option<CoroutineKind> {
-        self.coroutine_kind
-    }
 }
 
 /// The type of source expression that caused this coroutine to be created.
@@ -1355,7 +1361,18 @@ pub enum CoroutineKind {
     Desugared(CoroutineDesugaring, CoroutineSource),
 
     /// A coroutine literal created via a `yield` inside a closure.
-    Coroutine,
+    Coroutine(Movability),
+}
+
+impl CoroutineKind {
+    pub fn movability(self) -> Movability {
+        match self {
+            CoroutineKind::Desugared(CoroutineDesugaring::Async, _)
+            | CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _) => Movability::Static,
+            CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => Movability::Movable,
+            CoroutineKind::Coroutine(mov) => mov,
+        }
+    }
 }
 
 impl fmt::Display for CoroutineKind {
@@ -1365,7 +1382,7 @@ impl fmt::Display for CoroutineKind {
                 d.fmt(f)?;
                 k.fmt(f)
             }
-            CoroutineKind::Coroutine => f.write_str("coroutine"),
+            CoroutineKind::Coroutine(_) => f.write_str("coroutine"),
         }
     }
 }
@@ -3661,7 +3678,7 @@ mod size_asserts {
     use super::*;
     // tidy-alphabetical-start
     static_assert_size!(Block<'_>, 48);
-    static_assert_size!(Body<'_>, 32);
+    static_assert_size!(Body<'_>, 24);
     static_assert_size!(Expr<'_>, 64);
     static_assert_size!(ExprKind<'_>, 48);
     static_assert_size!(FnDecl<'_>, 40);
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 67e058a3219..e58e4c8fe0e 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -757,7 +757,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
             capture_clause: _,
             fn_decl_span: _,
             fn_arg_span: _,
-            movability: _,
+            kind: _,
             constness: _,
         }) => {
             walk_list!(visitor, visit_generic_param, bound_generic_params);
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 34d3f20d0cf..eab83c7a254 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -824,10 +824,6 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
             resolve_local(self, None, Some(body.value));
         }
 
-        if body.coroutine_kind.is_some() {
-            self.scope_tree.body_expr_count.insert(body_id, self.expr_and_pat_count);
-        }
-
         // Restore context we had at the start.
         self.expr_and_pat_count = outer_ec;
         self.cx = outer_cx;
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 4513653b644..8aeab2ca67e 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1551,10 +1551,14 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
 
 fn coroutine_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<hir::CoroutineKind> {
     match tcx.hir_node_by_def_id(def_id) {
-        Node::Expr(&rustc_hir::Expr {
-            kind: rustc_hir::ExprKind::Closure(&rustc_hir::Closure { body, .. }),
+        Node::Expr(&hir::Expr {
+            kind:
+                hir::ExprKind::Closure(&rustc_hir::Closure {
+                    kind: hir::ClosureKind::Coroutine(kind),
+                    ..
+                }),
             ..
-        }) => tcx.hir().body(body).coroutine_kind(),
+        }) => Some(kind),
         _ => None,
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 4abebb45966..5abc752309a 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -338,14 +338,14 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
     // cares about anything but the length is instantiation,
     // and we don't do that for closures.
     if let Node::Expr(&hir::Expr {
-        kind: hir::ExprKind::Closure(hir::Closure { movability: gen, .. }),
-        ..
+        kind: hir::ExprKind::Closure(hir::Closure { kind, .. }), ..
     }) = node
     {
-        let dummy_args = if gen.is_some() {
-            &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..]
-        } else {
-            &["<closure_kind>", "<closure_signature>", "<upvars>"][..]
+        let dummy_args = match kind {
+            ClosureKind::Closure => &["<closure_kind>", "<closure_signature>", "<upvars>"][..],
+            ClosureKind::Coroutine(_) => {
+                &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..]
+            }
         };
 
         params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef {
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index feaec5ac620..d6eea07cfbc 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1407,7 +1407,7 @@ impl<'a> State<'a> {
                 body,
                 fn_decl_span: _,
                 fn_arg_span: _,
-                movability: _,
+                kind: _,
                 def_id: _,
             }) => {
                 self.print_closure_binder(binder, bound_generic_params);
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 2f8ad96deb4..de2cb5a6d5c 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -298,17 +298,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let parent_node = self.tcx.hir_node(parent_hir_id);
         if let (
             hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, body, .. }),
+                kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, kind, .. }),
                 ..
             }),
             hir::ExprKind::Block(..),
         ) = (parent_node, callee_node)
         {
-            let fn_decl_span = if hir.body(body).coroutine_kind
-                == Some(hir::CoroutineKind::Desugared(
+            let fn_decl_span = if matches!(
+                kind,
+                hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
                     hir::CoroutineDesugaring::Async,
-                    hir::CoroutineSource::Closure,
-                )) {
+                    hir::CoroutineSource::Closure
+                ),)
+            ) {
                 // Actually need to unwrap one more layer of HIR to get to
                 // the _real_ closure...
                 let async_closure = hir.parent_id(parent_hir_id);
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index f4bcee384a7..984c2829c81 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -31,7 +31,7 @@ pub(super) fn check_fn<'a, 'tcx>(
     decl: &'tcx hir::FnDecl<'tcx>,
     fn_def_id: LocalDefId,
     body: &'tcx hir::Body<'tcx>,
-    can_be_coroutine: Option<hir::Movability>,
+    closure_kind: Option<hir::ClosureKind>,
     params_can_be_unsized: bool,
 ) -> Option<CoroutineTypes<'tcx>> {
     let fn_id = fcx.tcx.local_def_id_to_hir_id(fn_def_id);
@@ -55,12 +55,10 @@ pub(super) fn check_fn<'a, 'tcx>(
 
     forbid_intrinsic_abi(tcx, span, fn_sig.abi);
 
-    if let Some(kind) = body.coroutine_kind
-        && can_be_coroutine.is_some()
-    {
+    if let Some(hir::ClosureKind::Coroutine(kind)) = closure_kind {
         let yield_ty = match kind {
             hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
-            | hir::CoroutineKind::Coroutine => {
+            | hir::CoroutineKind::Coroutine(_) => {
                 let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
                     kind: TypeVariableOriginKind::TypeInference,
                     span,
@@ -151,9 +149,7 @@ pub(super) fn check_fn<'a, 'tcx>(
     // We insert the deferred_coroutine_interiors entry after visiting the body.
     // This ensures that all nested coroutines appear before the entry of this coroutine.
     // resolve_coroutine_interiors relies on this property.
-    let coroutine_ty = if let (Some(_), Some(coroutine_kind)) =
-        (can_be_coroutine, body.coroutine_kind)
-    {
+    let coroutine_ty = if let Some(hir::ClosureKind::Coroutine(coroutine_kind)) = closure_kind {
         let interior = fcx
             .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
         fcx.deferred_coroutine_interiors.borrow_mut().push((
@@ -168,7 +164,7 @@ pub(super) fn check_fn<'a, 'tcx>(
             resume_ty,
             yield_ty,
             interior,
-            movability: can_be_coroutine.unwrap(),
+            movability: coroutine_kind.movability(),
         })
     } else {
         None
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index cd42be28e6f..c29ef375ce4 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -60,25 +60,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             None => (None, None),
         };
-        let body = self.tcx.hir().body(closure.body);
-        self.check_closure(closure, expr_span, expected_kind, body, expected_sig)
+
+        self.check_closure(closure, expr_span, expected_kind, expected_sig)
     }
 
-    #[instrument(skip(self, closure, body), level = "debug", ret)]
+    #[instrument(skip(self, closure), level = "debug", ret)]
     fn check_closure(
         &self,
         closure: &hir::Closure<'tcx>,
         expr_span: Span,
         opt_kind: Option<ty::ClosureKind>,
-        body: &'tcx hir::Body<'tcx>,
         expected_sig: Option<ExpectedSig<'tcx>>,
     ) -> Ty<'tcx> {
+        let body = self.tcx.hir().body(closure.body);
+
         trace!("decl = {:#?}", closure.fn_decl);
         let expr_def_id = closure.def_id;
         debug!(?expr_def_id);
 
         let ClosureSignatures { bound_sig, liberated_sig } =
-            self.sig_of_closure(expr_def_id, closure.fn_decl, body, expected_sig);
+            self.sig_of_closure(expr_def_id, closure.fn_decl, closure.kind, expected_sig);
 
         debug!(?bound_sig, ?liberated_sig);
 
@@ -89,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             closure.fn_decl,
             expr_def_id,
             body,
-            closure.movability,
+            Some(closure.kind),
             // Closure "rust-call" ABI doesn't support unsized params
             false,
         );
@@ -351,28 +352,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
-        body: &hir::Body<'_>,
+        closure_kind: hir::ClosureKind,
         expected_sig: Option<ExpectedSig<'tcx>>,
     ) -> ClosureSignatures<'tcx> {
         if let Some(e) = expected_sig {
-            self.sig_of_closure_with_expectation(expr_def_id, decl, body, e)
+            self.sig_of_closure_with_expectation(expr_def_id, decl, closure_kind, e)
         } else {
-            self.sig_of_closure_no_expectation(expr_def_id, decl, body)
+            self.sig_of_closure_no_expectation(expr_def_id, decl, closure_kind)
         }
     }
 
     /// If there is no expected signature, then we will convert the
     /// types that the user gave into a signature.
-    #[instrument(skip(self, expr_def_id, decl, body), level = "debug")]
+    #[instrument(skip(self, expr_def_id, decl), level = "debug")]
     fn sig_of_closure_no_expectation(
         &self,
         expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
-        body: &hir::Body<'_>,
+        closure_kind: hir::ClosureKind,
     ) -> ClosureSignatures<'tcx> {
-        let bound_sig = self.supplied_sig_of_closure(expr_def_id, decl, body);
+        let bound_sig = self.supplied_sig_of_closure(expr_def_id, decl, closure_kind);
 
-        self.closure_sigs(expr_def_id, body, bound_sig)
+        self.closure_sigs(expr_def_id, bound_sig)
     }
 
     /// Invoked to compute the signature of a closure expression. This
@@ -422,24 +423,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// - `expected_sig`: the expected signature (if any). Note that
     ///   this is missing a binder: that is, there may be late-bound
     ///   regions with depth 1, which are bound then by the closure.
-    #[instrument(skip(self, expr_def_id, decl, body), level = "debug")]
+    #[instrument(skip(self, expr_def_id, decl), level = "debug")]
     fn sig_of_closure_with_expectation(
         &self,
         expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
-        body: &hir::Body<'_>,
+        closure_kind: hir::ClosureKind,
         expected_sig: ExpectedSig<'tcx>,
     ) -> ClosureSignatures<'tcx> {
         // Watch out for some surprises and just ignore the
         // expectation if things don't see to match up with what we
         // expect.
         if expected_sig.sig.c_variadic() != decl.c_variadic {
-            return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
+            return self.sig_of_closure_no_expectation(expr_def_id, decl, closure_kind);
         } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 {
             return self.sig_of_closure_with_mismatched_number_of_arguments(
                 expr_def_id,
                 decl,
-                body,
                 expected_sig,
             );
         }
@@ -463,16 +463,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // anonymize away, so as not to confuse the user.
         let bound_sig = self.tcx.anonymize_bound_vars(bound_sig);
 
-        let closure_sigs = self.closure_sigs(expr_def_id, body, bound_sig);
+        let closure_sigs = self.closure_sigs(expr_def_id, bound_sig);
 
         // Up till this point, we have ignored the annotations that the user
         // gave. This function will check that they unify successfully.
         // Along the way, it also writes out entries for types that the user
         // wrote into our typeck results, which are then later used by the privacy
         // check.
-        match self.merge_supplied_sig_with_expectation(expr_def_id, decl, body, closure_sigs) {
+        match self.merge_supplied_sig_with_expectation(
+            expr_def_id,
+            decl,
+            closure_kind,
+            closure_sigs,
+        ) {
             Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok),
-            Err(_) => self.sig_of_closure_no_expectation(expr_def_id, decl, body),
+            Err(_) => self.sig_of_closure_no_expectation(expr_def_id, decl, closure_kind),
         }
     }
 
@@ -480,7 +485,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
-        body: &hir::Body<'_>,
         expected_sig: ExpectedSig<'tcx>,
     ) -> ClosureSignatures<'tcx> {
         let expr_map_node = self.tcx.hir_node_by_def_id(expr_def_id);
@@ -511,25 +515,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let error_sig = self.error_sig_of_closure(decl, guar);
 
-        self.closure_sigs(expr_def_id, body, error_sig)
+        self.closure_sigs(expr_def_id, error_sig)
     }
 
     /// Enforce the user's types against the expectation. See
     /// `sig_of_closure_with_expectation` for details on the overall
     /// strategy.
-    #[instrument(level = "debug", skip(self, expr_def_id, decl, body, expected_sigs))]
+    #[instrument(level = "debug", skip(self, expr_def_id, decl, expected_sigs))]
     fn merge_supplied_sig_with_expectation(
         &self,
         expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
-        body: &hir::Body<'_>,
+        closure_kind: hir::ClosureKind,
         mut expected_sigs: ClosureSignatures<'tcx>,
     ) -> InferResult<'tcx, ClosureSignatures<'tcx>> {
         // Get the signature S that the user gave.
         //
         // (See comment on `sig_of_closure_with_expectation` for the
         // meaning of these letters.)
-        let supplied_sig = self.supplied_sig_of_closure(expr_def_id, decl, body);
+        let supplied_sig = self.supplied_sig_of_closure(expr_def_id, decl, closure_kind);
 
         debug!(?supplied_sig);
 
@@ -611,17 +615,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// types that the user gave into a signature.
     ///
     /// Also, record this closure signature for later.
-    #[instrument(skip(self, decl, body), level = "debug", ret)]
+    #[instrument(skip(self, decl), level = "debug", ret)]
     fn supplied_sig_of_closure(
         &self,
         expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
-        body: &hir::Body<'_>,
+        closure_kind: hir::ClosureKind,
     ) -> ty::PolyFnSig<'tcx> {
         let astconv: &dyn AstConv<'_> = self;
 
         trace!("decl = {:#?}", decl);
-        debug!(?body.coroutine_kind);
+        debug!(?closure_kind);
 
         let hir_id = self.tcx.local_def_id_to_hir_id(expr_def_id);
         let bound_vars = self.tcx.late_bound_vars(hir_id);
@@ -630,36 +634,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a));
         let supplied_return = match decl.output {
             hir::FnRetTy::Return(ref output) => astconv.ast_ty_to_ty(output),
-            hir::FnRetTy::DefaultReturn(_) => match body.coroutine_kind {
+            hir::FnRetTy::DefaultReturn(_) => match closure_kind {
                 // In the case of the async block that we create for a function body,
                 // we expect the return type of the block to match that of the enclosing
                 // function.
-                Some(hir::CoroutineKind::Desugared(
+                hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
                     hir::CoroutineDesugaring::Async,
                     hir::CoroutineSource::Fn,
                 )) => {
                     debug!("closure is async fn body");
-                    let def_id = self.tcx.hir().body_owner_def_id(body.id());
-                    self.deduce_future_output_from_obligations(expr_def_id, def_id).unwrap_or_else(
-                        || {
-                            // AFAIK, deducing the future output
-                            // always succeeds *except* in error cases
-                            // like #65159. I'd like to return Error
-                            // here, but I can't because I can't
-                            // easily (and locally) prove that we
-                            // *have* reported an
-                            // error. --nikomatsakis
-                            astconv.ty_infer(None, decl.output.span())
-                        },
-                    )
+                    self.deduce_future_output_from_obligations(expr_def_id).unwrap_or_else(|| {
+                        // AFAIK, deducing the future output
+                        // always succeeds *except* in error cases
+                        // like #65159. I'd like to return Error
+                        // here, but I can't because I can't
+                        // easily (and locally) prove that we
+                        // *have* reported an
+                        // error. --nikomatsakis
+                        astconv.ty_infer(None, decl.output.span())
+                    })
                 }
                 // All `gen {}` and `async gen {}` must return unit.
-                Some(
+                hir::ClosureKind::Coroutine(
                     hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
                     | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _),
                 ) => self.tcx.types.unit,
 
-                _ => astconv.ty_infer(None, decl.output.span()),
+                // For async blocks, we just fall back to `_` here.
+                // For closures/coroutines, we know nothing about the return
+                // type unless it was supplied.
+                hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    _,
+                ))
+                | hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_))
+                | hir::ClosureKind::Closure => astconv.ty_infer(None, decl.output.span()),
             },
         };
 
@@ -688,16 +697,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Future<Output = T>`, so we do this by searching through the
     /// obligations to extract the `T`.
     #[instrument(skip(self), level = "debug", ret)]
-    fn deduce_future_output_from_obligations(
-        &self,
-        expr_def_id: LocalDefId,
-        body_def_id: LocalDefId,
-    ) -> Option<Ty<'tcx>> {
+    fn deduce_future_output_from_obligations(&self, body_def_id: LocalDefId) -> Option<Ty<'tcx>> {
         let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| {
-            span_bug!(self.tcx.def_span(expr_def_id), "async fn coroutine outside of a fn")
+            span_bug!(self.tcx.def_span(body_def_id), "async fn coroutine outside of a fn")
         });
 
-        let closure_span = self.tcx.def_span(expr_def_id);
+        let closure_span = self.tcx.def_span(body_def_id);
         let ret_ty = ret_coercion.borrow().expected_ty();
         let ret_ty = self.try_structurally_resolve_type(closure_span, ret_ty);
 
@@ -842,12 +847,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn closure_sigs(
         &self,
         expr_def_id: LocalDefId,
-        body: &hir::Body<'_>,
         bound_sig: ty::PolyFnSig<'tcx>,
     ) -> ClosureSignatures<'tcx> {
         let liberated_sig =
             self.tcx().liberate_late_bound_regions(expr_def_id.to_def_id(), bound_sig);
-        let liberated_sig = self.normalize(body.value.span, liberated_sig);
+        let liberated_sig = self.normalize(self.tcx.def_span(expr_def_id), liberated_sig);
         ClosureSignatures { bound_sig, liberated_sig }
     }
 }
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index 916ff469e09..306bf07a976 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -199,7 +199,8 @@ fixed_size_enum! {
 
 fixed_size_enum! {
     hir::CoroutineKind {
-        ( Coroutine                                                                    )
+        ( Coroutine(hir::Movability::Movable)                                          )
+        ( Coroutine(hir::Movability::Static)                                           )
         ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Block)        )
         ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Fn)           )
         ( Desugared(hir::CoroutineDesugaring::Gen, hir::CoroutineSource::Closure)      )
diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs
index 3f6dc2b9f12..b4dd8f6f4a7 100644
--- a/compiler/rustc_middle/src/middle/region.rs
+++ b/compiler/rustc_middle/src/middle/region.rs
@@ -307,11 +307,6 @@ pub struct ScopeTree {
     /// the values are still owned by their containing expressions. So
     /// we'll see that `&x`.
     pub yield_in_scope: FxHashMap<Scope, Vec<YieldData>>,
-
-    /// The number of visit_expr and visit_pat calls done in the body.
-    /// Used to sanity check visit_expr/visit_pat call count when
-    /// calculating coroutine interiors.
-    pub body_expr_count: FxHashMap<hir::BodyId, usize>,
 }
 
 /// Identifies the reason that a given expression is an rvalue candidate
@@ -408,20 +403,12 @@ impl ScopeTree {
     pub fn yield_in_scope(&self, scope: Scope) -> Option<&[YieldData]> {
         self.yield_in_scope.get(&scope).map(Deref::deref)
     }
-
-    /// Gives the number of expressions visited in a body.
-    /// Used to sanity check visit_expr call count when
-    /// calculating coroutine interiors.
-    pub fn body_expr_count(&self, body_id: hir::BodyId) -> Option<usize> {
-        self.body_expr_count.get(&body_id).copied()
-    }
 }
 
 impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         let ScopeTree {
             root_body,
-            ref body_expr_count,
             ref parent_map,
             ref var_map,
             ref destruction_scopes,
@@ -430,7 +417,6 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
         } = *self;
 
         root_body.hash_stable(hcx, hasher);
-        body_expr_count.hash_stable(hcx, hasher);
         parent_map.hash_stable(hcx, hasher);
         var_map.hash_stable(hcx, hasher);
         destruction_scopes.hash_stable(hcx, hasher);
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index e0c9def0379..7be6deb6141 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -147,7 +147,7 @@ impl<O> AssertKind<O> {
             Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
             DivisionByZero(_) => "attempt to divide by zero",
             RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
-            ResumedAfterReturn(CoroutineKind::Coroutine) => "coroutine resumed after completion",
+            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => "coroutine resumed after completion",
             ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
                 "`async fn` resumed after completion"
             }
@@ -157,7 +157,7 @@ impl<O> AssertKind<O> {
             ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
                 "`gen fn` should just keep returning `None` after completion"
             }
-            ResumedAfterPanic(CoroutineKind::Coroutine) => "coroutine resumed after panicking",
+            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => "coroutine resumed after panicking",
             ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
                 "`async fn` resumed after panicking"
             }
@@ -262,7 +262,7 @@ impl<O> AssertKind<O> {
             ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
                 bug!("gen blocks can be resumed after they return and will keep returning `None`")
             }
-            ResumedAfterReturn(CoroutineKind::Coroutine) => {
+            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
                 middle_assert_coroutine_resume_after_return
             }
             ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
@@ -274,7 +274,7 @@ impl<O> AssertKind<O> {
             ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
                 middle_assert_gen_resume_after_panic
             }
-            ResumedAfterPanic(CoroutineKind::Coroutine) => {
+            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
                 middle_assert_coroutine_resume_after_panic
             }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index ac675a70b54..17691de630f 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -858,7 +858,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Returns `true` if the node pointed to by `def_id` is a general coroutine that implements `Coroutine`.
     /// This means it is neither an `async` or `gen` construct.
     pub fn is_general_coroutine(self, def_id: DefId) -> bool {
-        matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Coroutine))
+        matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Coroutine(_)))
     }
 
     /// Returns `true` if the node pointed to by `def_id` is a coroutine for a `gen` construct.
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 8e045397b0f..13a5e881134 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -786,8 +786,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             ty::Coroutine(did, args, movability) => {
                 p!(write("{{"));
                 let coroutine_kind = self.tcx().coroutine_kind(did).unwrap();
-                let should_print_movability =
-                    self.should_print_verbose() || coroutine_kind == hir::CoroutineKind::Coroutine;
+                let should_print_movability = self.should_print_verbose()
+                    || matches!(coroutine_kind, hir::CoroutineKind::Coroutine(_));
 
                 if should_print_movability {
                     match movability {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 55dc72b19d3..5e24b47fbd2 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -735,7 +735,7 @@ impl<'tcx> TyCtxt<'tcx> {
                     hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
                         "async gen closure"
                     }
-                    hir::CoroutineKind::Coroutine => "coroutine",
+                    hir::CoroutineKind::Coroutine(_) => "coroutine",
                     hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => {
                         "gen closure"
                     }
@@ -759,7 +759,7 @@ impl<'tcx> TyCtxt<'tcx> {
                     hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, ..) => "an",
                     hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, ..) => "an",
                     hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, ..) => "a",
-                    hir::CoroutineKind::Coroutine => "a",
+                    hir::CoroutineKind::Coroutine(_) => "a",
                 }
             }
             _ => def_kind.article(),
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index d1d5b72af70..05d8d842b58 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -257,7 +257,7 @@ impl<'tcx> TransformVisitor<'tcx> {
             CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => {
                 span_bug!(body.span, "`Future`s are not fused inherently")
             }
-            CoroutineKind::Coroutine => span_bug!(body.span, "`Coroutine`s cannot be fused"),
+            CoroutineKind::Coroutine(_) => span_bug!(body.span, "`Coroutine`s cannot be fused"),
             // `gen` continues return `None`
             CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {
                 let option_def_id = self.tcx.require_lang_item(LangItem::Option, None);
@@ -396,7 +396,7 @@ impl<'tcx> TransformVisitor<'tcx> {
                     Rvalue::Use(val)
                 }
             }
-            CoroutineKind::Coroutine => {
+            CoroutineKind::Coroutine(_) => {
                 let coroutine_state_def_id =
                     self.tcx.require_lang_item(LangItem::CoroutineState, None);
                 let args = self.tcx.mk_args(&[self.old_yield_ty.into(), self.old_ret_ty.into()]);
@@ -1428,7 +1428,8 @@ fn create_coroutine_resume_function<'tcx>(
 
     if can_return {
         let block = match coroutine_kind {
-            CoroutineKind::Desugared(CoroutineDesugaring::Async, _) | CoroutineKind::Coroutine => {
+            CoroutineKind::Desugared(CoroutineDesugaring::Async, _)
+            | CoroutineKind::Coroutine(_) => {
                 insert_panic_block(tcx, body, ResumedAfterReturn(coroutine_kind))
             }
             CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)
@@ -1643,7 +1644,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
                 // The yield ty is already `Poll<Option<yield_ty>>`
                 old_yield_ty
             }
-            CoroutineKind::Coroutine => {
+            CoroutineKind::Coroutine(_) => {
                 // Compute CoroutineState<yield_ty, return_ty>
                 let state_did = tcx.require_lang_item(LangItem::CoroutineState, None);
                 let state_adt_ref = tcx.adt_def(state_did);
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 24db708196b..0f8cc583b03 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -3,7 +3,7 @@ use Context::*;
 use rustc_hir as hir;
 use rustc_hir::def_id::{LocalDefId, LocalModDefId};
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{Destination, Movability, Node};
+use rustc_hir::{Destination, Node};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
@@ -86,16 +86,15 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                 self.with_context(Loop(source), |v| v.visit_block(b));
             }
             hir::ExprKind::Closure(&hir::Closure {
-                ref fn_decl,
-                body,
-                fn_decl_span,
-                movability,
-                ..
+                ref fn_decl, body, fn_decl_span, kind, ..
             }) => {
-                let cx = if let Some(Movability::Static) = movability {
-                    AsyncClosure(fn_decl_span)
-                } else {
-                    Closure(fn_decl_span)
+                // FIXME(coroutines): This doesn't handle coroutines correctly
+                let cx = match kind {
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Block,
+                    )) => AsyncClosure(fn_decl_span),
+                    _ => Closure(fn_decl_span),
                 };
                 self.visit_fn_decl(fn_decl);
                 self.with_context(cx, |v| v.visit_nested_body(body));
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
index 5f505ac181c..2446671770e 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs
@@ -42,7 +42,7 @@ impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
     type T = stable_mir::mir::CoroutineKind;
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
         use rustc_hir::{CoroutineDesugaring, CoroutineKind};
-        match self {
+        match *self {
             CoroutineKind::Desugared(CoroutineDesugaring::Async, source) => {
                 stable_mir::mir::CoroutineKind::Desugared(
                     stable_mir::mir::CoroutineDesugaring::Async,
@@ -55,7 +55,9 @@ impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
                     source.stable(tables),
                 )
             }
-            CoroutineKind::Coroutine => stable_mir::mir::CoroutineKind::Coroutine,
+            CoroutineKind::Coroutine(movability) => {
+                stable_mir::mir::CoroutineKind::Coroutine(movability.stable(tables))
+            }
             CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, source) => {
                 stable_mir::mir::CoroutineKind::Desugared(
                     stable_mir::mir::CoroutineDesugaring::AsyncGen,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 13ac2e26e40..52f91d282f0 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -8,7 +8,7 @@ use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{struct_span_err, ErrorGuaranteed};
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
 use rustc_parse_format::{ParseMode, Parser, Piece, Position};
@@ -32,7 +32,7 @@ pub trait TypeErrCtxtExt<'tcx> {
     ) -> Option<(DefId, GenericArgsRef<'tcx>)>;
 
     /*private*/
-    fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>;
+    fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str>;
 
     fn on_unimplemented_note(
         &self,
@@ -101,43 +101,19 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
     /// Used to set on_unimplemented's `ItemContext`
     /// to be the enclosing (async) block/function/closure
-    fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
-        let hir = self.tcx.hir();
-        let node = self.tcx.opt_hir_node(hir_id)?;
-        match &node {
-            hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => {
-                self.describe_coroutine(*body_id).or_else(|| {
-                    Some(match sig.header {
-                        hir::FnHeader { asyncness: hir::IsAsync::Async(_), .. } => {
-                            "an async function"
-                        }
-                        _ => "a function",
-                    })
-                })
+    fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> {
+        match self.tcx.opt_hir_node_by_def_id(def_id)? {
+            hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) => Some("a function"),
+            hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
+                Some("a trait method")
+            }
+            hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
+                Some("a method")
             }
-            hir::Node::TraitItem(hir::TraitItem {
-                kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)),
-                ..
-            }) => self.describe_coroutine(*body_id).or_else(|| Some("a trait method")),
-            hir::Node::ImplItem(hir::ImplItem {
-                kind: hir::ImplItemKind::Fn(sig, body_id),
-                ..
-            }) => self.describe_coroutine(*body_id).or_else(|| {
-                Some(match sig.header {
-                    hir::FnHeader { asyncness: hir::IsAsync::Async(_), .. } => "an async method",
-                    _ => "a method",
-                })
-            }),
             hir::Node::Expr(hir::Expr {
-                kind: hir::ExprKind::Closure(hir::Closure { body, movability, .. }),
+                kind: hir::ExprKind::Closure(hir::Closure { kind, .. }),
                 ..
-            }) => self.describe_coroutine(*body).or_else(|| {
-                Some(if movability.is_some() { "an async closure" } else { "a closure" })
-            }),
-            hir::Node::Expr(hir::Expr { .. }) => {
-                let parent_hid = hir.parent_id(hir_id);
-                if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None }
-            }
+            }) => Some(self.describe_closure(*kind)),
             _ => None,
         }
     }
@@ -156,12 +132,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
         // but I guess we could synthesize one here. We don't see any errors that rely on
         // that yet, though.
-        let enclosure =
-            if let Some(body_hir) = self.tcx.opt_local_def_id_to_hir_id(obligation.cause.body_id) {
-                self.describe_enclosure(body_hir).map(|s| s.to_owned())
-            } else {
-                None
-            };
+        let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned());
         flags.push((sym::ItemContext, enclosure));
 
         match obligation.cause.code() {
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index e1f3c6d4f97..171b86d4f25 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -2577,7 +2577,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             let message = outer_coroutine
                 .and_then(|coroutine_did| {
                     Some(match self.tcx.coroutine_kind(coroutine_did).unwrap() {
-                        CoroutineKind::Coroutine => format!("coroutine is not {trait_name}"),
+                        CoroutineKind::Coroutine(_) => format!("coroutine is not {trait_name}"),
                         CoroutineKind::Desugared(
                             CoroutineDesugaring::Async,
                             CoroutineSource::Fn,
@@ -3169,7 +3169,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => {
                 let what = match self.tcx.coroutine_kind(coroutine_def_id) {
                     None
-                    | Some(hir::CoroutineKind::Coroutine)
+                    | Some(hir::CoroutineKind::Coroutine(_))
                     | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => {
                         "yield"
                     }
@@ -3564,55 +3564,52 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
     ) {
-        if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) {
-            let body = self.tcx.hir().body(body_id);
-            if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
-                body.coroutine_kind
-            {
-                let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+        if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
+            self.tcx.coroutine_kind(obligation.cause.body_id)
+        {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
 
-                let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
-                let impls_future = self.type_implements_trait(
-                    future_trait,
-                    [self.tcx.instantiate_bound_regions_with_erased(self_ty)],
-                    obligation.param_env,
-                );
-                if !impls_future.must_apply_modulo_regions() {
-                    return;
-                }
+            let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
+            let impls_future = self.type_implements_trait(
+                future_trait,
+                [self.tcx.instantiate_bound_regions_with_erased(self_ty)],
+                obligation.param_env,
+            );
+            if !impls_future.must_apply_modulo_regions() {
+                return;
+            }
 
-                let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
-                // `<T as Future>::Output`
-                let projection_ty = trait_pred.map_bound(|trait_pred| {
-                    Ty::new_projection(
-                        self.tcx,
-                        item_def_id,
-                        // Future::Output has no args
-                        [trait_pred.self_ty()],
-                    )
-                });
-                let InferOk { value: projection_ty, .. } =
-                    self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
+            let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
+            // `<T as Future>::Output`
+            let projection_ty = trait_pred.map_bound(|trait_pred| {
+                Ty::new_projection(
+                    self.tcx,
+                    item_def_id,
+                    // Future::Output has no args
+                    [trait_pred.self_ty()],
+                )
+            });
+            let InferOk { value: projection_ty, .. } =
+                self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
 
-                debug!(
-                    normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
-                );
-                let try_obligation = self.mk_trait_obligation_with_new_self_ty(
-                    obligation.param_env,
-                    trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
+            debug!(
+                normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
+            );
+            let try_obligation = self.mk_trait_obligation_with_new_self_ty(
+                obligation.param_env,
+                trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
+            );
+            debug!(try_trait_obligation = ?try_obligation);
+            if self.predicate_may_hold(&try_obligation)
+                && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+                && snippet.ends_with('?')
+            {
+                err.span_suggestion_verbose(
+                    span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
+                    "consider `await`ing on the `Future`",
+                    ".await",
+                    Applicability::MaybeIncorrect,
                 );
-                debug!(try_trait_obligation = ?try_obligation);
-                if self.predicate_may_hold(&try_obligation)
-                    && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
-                    && snippet.ends_with('?')
-                {
-                    err.span_suggestion_verbose(
-                        span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
-                        "consider `await`ing on the `Future`",
-                        ".await",
-                        Applicability::MaybeIncorrect,
-                    );
-                }
             }
         }
     }
@@ -4665,13 +4662,7 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
 
     fn visit_body(&mut self, body: &'v hir::Body<'v>) {
         assert!(!self.in_block_tail);
-        if body.coroutine_kind().is_none() {
-            if let hir::ExprKind::Block(block, None) = body.value.kind {
-                if block.expr.is_some() {
-                    self.in_block_tail = true;
-                }
-            }
-        }
+        self.in_block_tail = true;
         hir::intravisit::walk_body(self, body);
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 4b84aae70fd..d2598b0defe 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -1348,7 +1348,7 @@ pub(super) trait InferCtxtPrivExt<'tcx> {
         ignoring_lifetimes: bool,
     ) -> Option<CandidateSimilarity>;
 
-    fn describe_coroutine(&self, body_id: hir::BodyId) -> Option<&'static str>;
+    fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str;
 
     fn find_similar_impl_candidates(
         &self,
@@ -1925,46 +1925,49 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn describe_coroutine(&self, body_id: hir::BodyId) -> Option<&'static str> {
-        self.tcx.hir().body(body_id).coroutine_kind.map(|coroutine_source| match coroutine_source {
-            hir::CoroutineKind::Coroutine => "a coroutine",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::Async,
-                hir::CoroutineSource::Block,
-            ) => "an async block",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::Async,
-                hir::CoroutineSource::Fn,
-            ) => "an async function",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::Async,
-                hir::CoroutineSource::Closure,
-            ) => "an async closure",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::AsyncGen,
-                hir::CoroutineSource::Block,
-            ) => "an async gen block",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::AsyncGen,
-                hir::CoroutineSource::Fn,
-            ) => "an async gen function",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::AsyncGen,
-                hir::CoroutineSource::Closure,
-            ) => "an async gen closure",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::Gen,
-                hir::CoroutineSource::Block,
-            ) => "a gen block",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::Gen,
-                hir::CoroutineSource::Fn,
-            ) => "a gen function",
-            hir::CoroutineKind::Desugared(
-                hir::CoroutineDesugaring::Gen,
-                hir::CoroutineSource::Closure,
-            ) => "a gen closure",
-        })
+    fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str {
+        match kind {
+            hir::ClosureKind::Closure => "a closure",
+            hir::ClosureKind::Coroutine(kind) => match kind {
+                hir::CoroutineKind::Coroutine(_) => "a coroutine",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    hir::CoroutineSource::Block,
+                ) => "an async block",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    hir::CoroutineSource::Fn,
+                ) => "an async function",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Async,
+                    hir::CoroutineSource::Closure,
+                ) => "an async closure",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::AsyncGen,
+                    hir::CoroutineSource::Block,
+                ) => "an async gen block",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::AsyncGen,
+                    hir::CoroutineSource::Fn,
+                ) => "an async gen function",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::AsyncGen,
+                    hir::CoroutineSource::Closure,
+                ) => "an async gen closure",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Gen,
+                    hir::CoroutineSource::Block,
+                ) => "a gen block",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Gen,
+                    hir::CoroutineSource::Fn,
+                ) => "a gen function",
+                hir::CoroutineKind::Desugared(
+                    hir::CoroutineDesugaring::Gen,
+                    hir::CoroutineSource::Closure,
+                ) => "a gen closure",
+            },
+        }
     }
 
     fn find_similar_impl_candidates(
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index 86501b5a72d..4756a45a447 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -121,7 +121,7 @@ fn fn_sig_for_fn_abi<'tcx>(
                 }
                 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
                 | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)
-                | hir::CoroutineKind::Coroutine => Ty::new_adt(tcx, pin_adt_ref, pin_args),
+                | hir::CoroutineKind::Coroutine(_) => Ty::new_adt(tcx, pin_adt_ref, pin_args),
             };
 
             // The `FnSig` and the `ret_ty` here is for a coroutines main
@@ -192,7 +192,7 @@ fn fn_sig_for_fn_abi<'tcx>(
 
                     (Some(context_mut_ref), ret_ty)
                 }
-                hir::CoroutineKind::Coroutine => {
+                hir::CoroutineKind::Coroutine(_) => {
                     // The signature should be `Coroutine::resume(_, Resume) -> CoroutineState<Yield, Return>`
                     let state_did = tcx.require_lang_item(LangItem::CoroutineState, None);
                     let state_adt_ref = tcx.adt_def(state_did);
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 89d75569ce3..5871600bece 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -285,7 +285,7 @@ impl AssertMessage {
             AssertMessage::RemainderByZero(_) => {
                 Ok("attempt to calculate the remainder with a divisor of zero")
             }
-            AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine) => {
+            AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
                 Ok("coroutine resumed after completion")
             }
             AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared(
@@ -300,7 +300,7 @@ impl AssertMessage {
                 CoroutineDesugaring::AsyncGen,
                 _,
             )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after completion"),
-            AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine) => {
+            AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
                 Ok("coroutine resumed after panicking")
             }
             AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared(
@@ -399,7 +399,7 @@ pub enum UnOp {
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum CoroutineKind {
     Desugared(CoroutineDesugaring, CoroutineSource),
-    Coroutine,
+    Coroutine(Movability),
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
index 28e6614f03f..c965341d3fd 100644
--- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs
+++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
@@ -2,7 +2,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::implements_trait;
 use rustc_errors::Applicability;
-use rustc_hir::{Body, BodyId, CoroutineKind, CoroutineSource, CoroutineDesugaring, ExprKind, QPath};
+use rustc_hir::{
+    Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath,
+};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -44,15 +46,22 @@ declare_clippy_lint! {
 declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);
 
 impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
-    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         // For functions, with explicitly defined types, don't warn.
         // XXXkhuey maybe we should?
-        if let Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Block | CoroutineSource::Closure)) = body.coroutine_kind {
+        if let ExprKind::Closure(Closure {
+            kind:
+                ClosureKind::Coroutine(CoroutineKind::Desugared(
+                    CoroutineDesugaring::Async,
+                    CoroutineSource::Block | CoroutineSource::Closure,
+                )),
+            body: body_id,
+            ..
+        }) = expr.kind
+        {
             if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() {
-                let body_id = BodyId {
-                    hir_id: body.value.hir_id,
-                };
-                let typeck_results = cx.tcx.typeck_body(body_id);
+                let typeck_results = cx.tcx.typeck_body(*body_id);
+                let body = cx.tcx.hir().body(*body_id);
                 let expr_ty = typeck_results.expr_ty(body.value);
 
                 if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
index dff6e884fa1..765cc7c0a54 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -2,8 +2,8 @@ use clippy_config::types::DisallowedPath;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{match_def_path, paths};
 use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{Body, CoroutineKind, CoroutineDesugaring};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir::CoroutineLayout;
 use rustc_session::impl_lint_pass;
@@ -183,8 +183,8 @@ impl AwaitHolding {
     }
 }
 
-impl LateLintPass<'_> for AwaitHolding {
-    fn check_crate(&mut self, cx: &LateContext<'_>) {
+impl<'tcx> LateLintPass<'tcx> for AwaitHolding {
+    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
         for conf in &self.conf_invalid_types {
             let segs: Vec<_> = conf.path().split("::").collect();
             for id in clippy_utils::def_path_def_ids(cx, &segs) {
@@ -193,10 +193,14 @@ impl LateLintPass<'_> for AwaitHolding {
         }
     }
 
-    fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
-        if let Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) = body.coroutine_kind {
-            let def_id = cx.tcx.hir().body_owner_def_id(body.id());
-            if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(def_id) {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
+        if let hir::ExprKind::Closure(hir::Closure {
+            kind: hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)),
+            def_id,
+            ..
+        }) = expr.kind
+        {
+            if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) {
                 self.check_interior_types(cx, coroutine_layout);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 8982ce5e196..9ba1d3afcbe 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -3,8 +3,9 @@ use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    Block, Body, Closure, CoroutineKind, CoroutineSource, CoroutineDesugaring, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, GenericBound,
-    ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, TypeBindingKind,
+    Block, Body, Closure, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl, FnRetTy,
+    GenericArg, GenericBound, ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind,
+    TypeBindingKind, ClosureKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -171,16 +172,25 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
             .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
 }
 
-fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
-    if let Some(block_expr) = block.expr
-        && let Expr {
-            kind: ExprKind::Closure(&Closure { body, .. }),
-            ..
-        } = block_expr
-        && let closure_body = cx.tcx.hir().body(body)
-        && closure_body.coroutine_kind == Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Block))
+fn desugared_async_block<'tcx>(
+    cx: &LateContext<'tcx>,
+    block: &'tcx Block<'tcx>,
+) -> Option<&'tcx Body<'tcx>> {
+    if let Some(Expr {
+        kind:
+            ExprKind::Closure(&Closure {
+                kind:
+                    ClosureKind::Coroutine(CoroutineKind::Desugared(
+                        CoroutineDesugaring::Async,
+                        CoroutineSource::Block,
+                    )),
+                body,
+                ..
+            }),
+        ..
+    }) = block.expr
     {
-        return Some(closure_body);
+        return Some(cx.tcx.hir().body(body));
     }
 
     None
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index e1b934d36ea..6394f35f860 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -32,7 +32,6 @@ pub(super) fn check<'tcx>(
         && let Body {
             params: [p],
             value: body_expr,
-            coroutine_kind: _,
         } = cx.tcx.hir().body(c.body)
         && let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind
         && let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) {
diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
index 350707d3a13..d7adf22ff32 100644
--- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
@@ -3,7 +3,7 @@ use clippy_utils::path_res;
 use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Block, Body, CoroutineKind, CoroutineSource, CoroutineDesugaring, Expr, ExprKind, LangItem, MatchSource, QPath};
+use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -86,22 +86,20 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
     }
 
     fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
-        if let Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Fn)) = body.coroutine_kind {
-            if let ExprKind::Block(
-                Block {
-                    expr:
-                        Some(Expr {
-                            kind: ExprKind::DropTemps(async_body),
-                            ..
-                        }),
-                    ..
-                },
-                _,
-            ) = body.value.kind
-            {
-                if let ExprKind::Block(Block { expr: Some(expr), .. }, ..) = async_body.kind {
-                    check(cx, expr);
-                }
+        if let ExprKind::Block(
+            Block {
+                expr:
+                    Some(Expr {
+                        kind: ExprKind::DropTemps(async_body),
+                        ..
+                    }),
+                ..
+            },
+            _,
+        ) = body.value.kind
+        {
+            if let ExprKind::Block(Block { expr: Some(expr), .. }, ..) = async_body.kind {
+                check(cx, expr.peel_blocks());
             }
         } else {
             check(cx, body.value.peel_blocks());
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
index 4b3fe9c0bb5..b50141f048c 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -5,7 +5,7 @@ use clippy_utils::peel_blocks;
 use clippy_utils::source::{snippet, walk_span_to_context};
 use clippy_utils::visitors::for_each_expr;
 use rustc_errors::Applicability;
-use rustc_hir::{Closure, CoroutineKind, CoroutineSource, CoroutineDesugaring, Expr, ExprKind, MatchSource};
+use rustc_hir::{Closure, ClosureKind, CoroutineKind, CoroutineSource, CoroutineDesugaring, Expr, ExprKind, MatchSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::UpvarCapture;
@@ -69,9 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
 /// If `expr` is a desugared `async` block, return the original expression if it does not capture
 /// any variable by ref.
 fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if let ExprKind::Closure(Closure { body, def_id, .. }) = expr.kind
+    if let ExprKind::Closure(Closure { body, def_id, kind, .. }) = expr.kind
         && let body = cx.tcx.hir().body(*body)
-        && matches!(body.coroutine_kind, Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Block)))
+        && matches!(kind, ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Block)))
     {
         cx.typeck_results()
             .closure_min_captures
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index 9312a9c89b7..16c929edb92 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -5,7 +5,7 @@ use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor};
-use rustc_hir::{intravisit as hir_visit, CoroutineKind, CoroutineSource, CoroutineDesugaring, Node};
+use rustc_hir::{intravisit as hir_visit, CoroutineKind, CoroutineSource, CoroutineDesugaring, Node, ClosureKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::lint::in_external_macro;
@@ -63,11 +63,10 @@ impl<'tcx> Visitor<'tcx> for ReturnVisitor {
 /// Checks if the body is owned by an async closure.
 /// Returns true for `async || whatever_expression`, but false for `|| async { whatever_expression
 /// }`.
-fn is_async_closure(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
+fn is_async_closure(body: &hir::Body<'_>) -> bool {
     if let hir::ExprKind::Closure(innermost_closure_generated_by_desugar) = body.value.kind
-        && let desugared_inner_closure_body = cx.tcx.hir().body(innermost_closure_generated_by_desugar.body)
         // checks whether it is `async || whatever_expression`
-        && let Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)) = desugared_inner_closure_body.coroutine_kind
+        && let ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)) = innermost_closure_generated_by_desugar.kind
     {
         true
     } else {
@@ -103,7 +102,7 @@ fn find_innermost_closure<'tcx>(
         data = Some((
             body.value,
             closure.fn_decl,
-            if is_async_closure(cx, body) {
+            if is_async_closure(body) {
                 ty::Asyncness::Yes
             } else {
                 ty::Asyncness::No
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index f71fe4e1e92..1d42375ba8e 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::is_def_id_trait_method;
 use rustc_hir::def::DefKind;
-use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor};
+use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
 use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
@@ -78,32 +78,32 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
                 self.await_in_async_block = Some(ex.span);
             }
         }
-        walk_expr(self, ex);
-    }
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
-    }
 
-    fn visit_body(&mut self, b: &'tcx Body<'tcx>) {
         let is_async_block = matches!(
-            b.coroutine_kind,
-            Some(rustc_hir::CoroutineKind::Desugared(
-                rustc_hir::CoroutineDesugaring::Async,
-                _
-            ))
+            ex.kind,
+            ExprKind::Closure(rustc_hir::Closure {
+                kind: rustc_hir::ClosureKind::Coroutine(rustc_hir::CoroutineKind::Desugared(
+                    rustc_hir::CoroutineDesugaring::Async,
+                    _
+                )),
+                ..
+            })
         );
 
         if is_async_block {
             self.async_depth += 1;
         }
 
-        walk_body(self, b);
+        walk_expr(self, ex);
 
         if is_async_block {
             self.async_depth -= 1;
         }
     }
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.cx.tcx.hir()
+    }
 }
 
 impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index e83c04eda20..df715b12dea 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -7,7 +7,8 @@ use rustc_ast::LitIntType;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::{
-    ArrayLen, BindingAnnotation, CaptureBy, Closure, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind,
+    ArrayLen, BindingAnnotation, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit,
+    PatKind, QPath, StmtKind, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
@@ -476,7 +477,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 capture_clause,
                 fn_decl,
                 body: body_id,
-                movability,
+                kind,
                 ..
             }) => {
                 let capture_clause = match capture_clause {
@@ -484,7 +485,17 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                     CaptureBy::Ref => "Ref",
                 };
 
-                let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}")));
+                let closure_kind = match kind {
+                    ClosureKind::Closure => "ClosureKind::Closure".to_string(),
+                    ClosureKind::Coroutine(coroutine_kind) => match coroutine_kind {
+                        CoroutineKind::Desugared(desugaring, source) => format!(
+                            "ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::{desugaring:?}, CoroutineSource::{source:?}))"
+                        ),
+                        CoroutineKind::Coroutine(movability) => {
+                            format!("ClosureKind::Coroutine(CoroutineKind::Coroutine(Movability::{movability:?})")
+                        },
+                    },
+                };
 
                 let ret_ty = match fn_decl.output {
                     FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)",
@@ -492,7 +503,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 };
 
                 bind!(self, fn_decl, body_id);
-                kind!("Closure(CaptureBy::{capture_clause}, {fn_decl}, {body_id}, _, {movability})");
+                kind!(
+                    "Closure {{ capture_clause: CaptureBy::{capture_clause}, fn_decl: {fn_decl}, body: {body_id}, closure_kind: {closure_kind}, .. }}"
+                );
                 chain!(self, "let {ret_ty} = {fn_decl}.output");
                 self.body(body_id);
             },
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
index 140300a1673..62de661f8ff 100644
--- a/src/tools/clippy/tests/ui/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
@@ -40,10 +40,10 @@ if let ExprKind::Block(block, None) = expr.kind
 {
     // report your lint here
 }
-if let ExprKind::Closure(CaptureBy::Value { .. }, fn_decl, body_id, _, None) = expr.kind
+if let ExprKind::Closure { capture_clause: CaptureBy::Value { .. }, fn_decl: fn_decl, body: body_id, closure_kind: ClosureKind::Closure, .. } = expr.kind
     && let FnRetTy::DefaultReturn(_) = fn_decl.output
     && expr1 = &cx.tcx.hir().body(body_id).value
-    && let ExprKind::Closure(CaptureBy::Value { .. }, fn_decl1, body_id1, _, Some(Movability::Static)) = expr1.kind
+    && let ExprKind::Closure { capture_clause: CaptureBy::Value { .. }, fn_decl: fn_decl1, body: body_id1, closure_kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)), .. } = expr1.kind
     && let FnRetTy::DefaultReturn(_) = fn_decl1.output
     && expr2 = &cx.tcx.hir().body(body_id1).value
     && let ExprKind::Block(block, None) = expr2.kind
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
index 9ab71986f40..06386d1d7ec 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
@@ -1,6 +1,6 @@
 if let StmtKind::Local(local) = stmt.kind
     && let Some(init) = local.init
-    && let ExprKind::Closure(CaptureBy::Ref, fn_decl, body_id, _, None) = init.kind
+    && let ExprKind::Closure { capture_clause: CaptureBy::Ref, fn_decl: fn_decl, body: body_id, closure_kind: ClosureKind::Closure, .. } = init.kind
     && let FnRetTy::DefaultReturn(_) = fn_decl.output
     && expr = &cx.tcx.hir().body(body_id).value
     && let ExprKind::Block(block, None) = expr.kind
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.fixed b/src/tools/clippy/tests/ui/needless_question_mark.fixed
index 07bd6b6f3c1..92f01c217c1 100644
--- a/src/tools/clippy/tests/ui/needless_question_mark.fixed
+++ b/src/tools/clippy/tests/ui/needless_question_mark.fixed
@@ -135,3 +135,7 @@ async fn async_deref_ref(s: Option<&String>) -> Option<&str> {
 async fn async_result_bad(s: TR) -> Result<usize, bool> {
     s.magic
 }
+
+async fn async_wrapped<T>(a: Option<T>) -> Option<T> {
+    { a }
+}
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.rs b/src/tools/clippy/tests/ui/needless_question_mark.rs
index fbf8a12fd50..21c858c291f 100644
--- a/src/tools/clippy/tests/ui/needless_question_mark.rs
+++ b/src/tools/clippy/tests/ui/needless_question_mark.rs
@@ -135,3 +135,7 @@ async fn async_deref_ref(s: Option<&String>) -> Option<&str> {
 async fn async_result_bad(s: TR) -> Result<usize, bool> {
     Ok(s.magic?)
 }
+
+async fn async_wrapped<T>(a: Option<T>) -> Option<T> {
+    { Some(a?) }
+}
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr
index cd961a49f42..bf090302ef7 100644
--- a/src/tools/clippy/tests/ui/needless_question_mark.stderr
+++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr
@@ -90,5 +90,11 @@ error: question mark operator is useless here
 LL |     Ok(s.magic?)
    |     ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic`
 
-error: aborting due to 14 previous errors
+error: question mark operator is useless here
+  --> $DIR/needless_question_mark.rs:140:7
+   |
+LL |     { Some(a?) }
+   |       ^^^^^^^^ help: try removing question mark and `Some()`: `a`
+
+error: aborting due to 15 previous errors
 
diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr
index 070dbbb10bb..5296475c94a 100644
--- a/tests/ui/stats/hir-stats.stderr
+++ b/tests/ui/stats/hir-stats.stderr
@@ -125,9 +125,9 @@ hir-stats ExprField                 40 ( 0.4%)             1            40
 hir-stats TraitItemRef              56 ( 0.6%)             2            28
 hir-stats Local                     64 ( 0.7%)             1            64
 hir-stats Param                     64 ( 0.7%)             2            32
+hir-stats Body                      72 ( 0.8%)             3            24
 hir-stats InlineAsm                 72 ( 0.8%)             1            72
 hir-stats ImplItemRef               72 ( 0.8%)             2            36
-hir-stats Body                      96 ( 1.1%)             3            32
 hir-stats FieldDef                  96 ( 1.1%)             2            48
 hir-stats Arm                       96 ( 1.1%)             2            48
 hir-stats Stmt                      96 ( 1.1%)             3            32
@@ -146,7 +146,7 @@ hir-stats - Trait                    192 ( 2.1%)             4
 hir-stats WherePredicate           192 ( 2.1%)             3            64
 hir-stats - BoundPredicate           192 ( 2.1%)             3
 hir-stats Block                    288 ( 3.2%)             6            48
-hir-stats Pat                      360 ( 3.9%)             5            72
+hir-stats Pat                      360 ( 4.0%)             5            72
 hir-stats - Wild                      72 ( 0.8%)             1
 hir-stats - Struct                    72 ( 0.8%)             1
 hir-stats - Binding                  216 ( 2.4%)             3
@@ -172,7 +172,7 @@ hir-stats - Impl                      88 ( 1.0%)             1
 hir-stats - Fn                       176 ( 1.9%)             2
 hir-stats - Use                      352 ( 3.9%)             4
 hir-stats Path                   1_240 (13.6%)            31            40
-hir-stats PathSegment            1_920 (21.0%)            40            48
+hir-stats PathSegment            1_920 (21.1%)            40            48
 hir-stats ----------------------------------------------------------------
-hir-stats Total                  9_136
+hir-stats Total                  9_112
 hir-stats