From ee47480f4cae83279d884d6bcc9e5e6ddf3334b8 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 27 May 2024 18:32:17 -0400
Subject: [PATCH 1/2] Yeet PolyFnSig from Interner

---
 compiler/rustc_middle/src/ty/context.rs          |  1 -
 compiler/rustc_middle/src/ty/structural_impls.rs | 12 ------------
 compiler/rustc_type_ir/src/binder.rs             | 15 ++++++++++++++-
 compiler/rustc_type_ir/src/interner.rs           |  1 -
 compiler/rustc_type_ir/src/ty_kind.rs            |  2 +-
 5 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index c2219fba023bc..30dd887c88a2a 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -113,7 +113,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
 
     type ErrorGuaranteed = ErrorGuaranteed;
     type BoundExistentialPredicates = &'tcx List<PolyExistentialPredicate<'tcx>>;
-    type PolyFnSig = PolyFnSig<'tcx>;
     type AllocId = crate::mir::interpret::AllocId;
 
     type Pat = Pattern<'tcx>;
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index af3aa3b56f7bb..90791bdd20dcd 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -259,18 +259,6 @@ impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for Region<'tcx> {
     }
 }
 
-impl<'tcx, T: DebugWithInfcx<TyCtxt<'tcx>>> DebugWithInfcx<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
-    fn fmt<Infcx: InferCtxtLike<Interner = TyCtxt<'tcx>>>(
-        this: WithInfcx<'_, Infcx, &Self>,
-        f: &mut core::fmt::Formatter<'_>,
-    ) -> core::fmt::Result {
-        f.debug_tuple("Binder")
-            .field(&this.map(|data| data.as_ref().skip_binder()))
-            .field(&this.data.bound_vars())
-            .finish()
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // Atomic structs
 //
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index e50d59ba5f0e6..9a2c9059967b1 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -8,11 +8,12 @@ use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
 use rustc_serialize::Decodable;
 use tracing::debug;
 
+use crate::debug::{DebugWithInfcx, WithInfcx};
 use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable};
 use crate::inherent::*;
 use crate::lift::Lift;
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
-use crate::{self as ty, Interner, SsoHashSet};
+use crate::{self as ty, InferCtxtLike, Interner, SsoHashSet};
 
 /// Binder is a binder for higher-ranked lifetimes or types. It is part of the
 /// compiler's representation for things like `for<'a> Fn(&'a isize)`
@@ -55,6 +56,18 @@ where
     }
 }
 
+impl<I: Interner, T: DebugWithInfcx<I>> DebugWithInfcx<I> for ty::Binder<I, T> {
+    fn fmt<Infcx: InferCtxtLike<Interner = I>>(
+        this: WithInfcx<'_, Infcx, &Self>,
+        f: &mut core::fmt::Formatter<'_>,
+    ) -> core::fmt::Result {
+        f.debug_tuple("Binder")
+            .field(&this.map(|data| data.as_ref().skip_binder()))
+            .field(&this.data.bound_vars())
+            .finish()
+    }
+}
+
 macro_rules! impl_binder_encode_decode {
     ($($t:ty),+ $(,)?) => {
         $(
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 2a228c973d34a..ca39318cc0c8b 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -69,7 +69,6 @@ pub trait Interner:
     // Things stored inside of tys
     type ErrorGuaranteed: Copy + Debug + Hash + Eq;
     type BoundExistentialPredicates: Copy + DebugWithInfcx<Self> + Hash + Eq;
-    type PolyFnSig: Copy + DebugWithInfcx<Self> + Hash + Eq;
     type AllocId: Copy + Debug + Hash + Eq;
     type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx<Self>;
     type Safety: Safety<Self>;
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 38082bf3c16fb..aa285169f276d 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -141,7 +141,7 @@ pub enum TyKind<I: Interner> {
     /// fn foo() -> i32 { 1 }
     /// let bar: fn() -> i32 = foo;
     /// ```
-    FnPtr(I::PolyFnSig),
+    FnPtr(ty::Binder<I, FnSig<I>>),
 
     /// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`.
     Dynamic(I::BoundExistentialPredicates, I::Region, DynKind),

From 333458c2cbafbea22480075decc4a827f913a020 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 31 May 2024 14:13:46 -0400
Subject: [PATCH 2/2] Uplift TypeRelation and Relate

---
 compiler/rustc_borrowck/src/type_check/mod.rs |   5 +-
 compiler/rustc_codegen_gcc/src/type_of.rs     |   2 +-
 .../src/debuginfo/metadata.rs                 |   3 +-
 .../src/debuginfo/metadata/enums/cpp_like.rs  |   2 +-
 .../src/debuginfo/metadata/enums/mod.rs       |   2 +-
 compiler/rustc_codegen_llvm/src/type_of.rs    |   2 +-
 .../src/interpret/discriminant.rs             |   2 +-
 compiler/rustc_middle/src/mir/query.rs        |   2 +-
 compiler/rustc_middle/src/ty/context.rs       |   4 +-
 compiler/rustc_middle/src/ty/generic_args.rs  |  69 +-
 compiler/rustc_middle/src/ty/layout.rs        |   2 +-
 compiler/rustc_middle/src/ty/mod.rs           |   6 +-
 compiler/rustc_middle/src/ty/print/pretty.rs  |   7 +-
 compiler/rustc_middle/src/ty/relate.rs        |  22 -
 compiler/rustc_middle/src/ty/sty.rs           | 731 ++----------------
 compiler/rustc_mir_transform/src/coroutine.rs |   2 +-
 compiler/rustc_mir_transform/src/shim.rs      |   4 +-
 compiler/rustc_mir_transform/src/validate.rs  |   4 +-
 .../src/solve/assembly/structural_traits.rs   |   8 +-
 .../rustc_trait_selection/src/traits/util.rs  |   8 +-
 compiler/rustc_ty_utils/src/layout.rs         |   3 +-
 compiler/rustc_type_ir/src/inherent.rs        |  28 +-
 compiler/rustc_type_ir/src/interner.rs        |   6 +-
 compiler/rustc_type_ir/src/predicate.rs       |   2 +-
 compiler/rustc_type_ir/src/ty_kind.rs         |   8 +-
 compiler/rustc_type_ir/src/ty_kind/closure.rs | 696 +++++++++++++++++
 .../clippy/clippy_lints/src/eta_reduction.rs  |   4 +-
 27 files changed, 882 insertions(+), 752 deletions(-)
 create mode 100644 compiler/rustc_type_ir/src/ty_kind/closure.rs

diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index a05fa967af03e..291d2782c32cc 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -27,8 +27,9 @@ use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::cast::CastTy;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{
-    self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, Dynamic,
-    OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserType, UserTypeAnnotationIndex,
+    self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt,
+    Dynamic, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserType,
+    UserTypeAnnotationIndex,
 };
 use rustc_middle::ty::{GenericArgsRef, UserArgs};
 use rustc_middle::{bug, span_bug};
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index 2155cabe171c2..a88d50cb43403 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -5,7 +5,7 @@ use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeM
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, Ty, TypeVisitableExt};
+use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt};
 use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
 use rustc_target::abi::{
     self, Abi, Align, FieldsShape, Float, Int, Integer, PointeeInfo, Pointer, Size, TyAbiInterface,
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 10d3c0d0e7443..a543ccbde0edf 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -31,7 +31,8 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::{
-    self, AdtKind, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt, Visibility,
+    self, AdtKind, CoroutineArgsExt, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt,
+    Visibility,
 };
 use rustc_session::config::{self, DebugInfo, Lto};
 use rustc_span::symbol::Symbol;
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
index 4edef14422e5f..12f98eef97d43 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
@@ -12,7 +12,7 @@ use rustc_middle::{
     ty::{
         self,
         layout::{LayoutOf, TyAndLayout},
-        AdtDef, CoroutineArgs, Ty,
+        AdtDef, CoroutineArgs, CoroutineArgsExt, Ty,
     },
 };
 use rustc_target::abi::{Align, Endian, Size, TagEncoding, VariantIdx, Variants};
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
index bacd74f430f73..2b00bb14593e4 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
@@ -10,7 +10,7 @@ use rustc_middle::{
     ty::{
         self,
         layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout},
-        AdtDef, CoroutineArgs, Ty, VariantDef,
+        AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef,
     },
 };
 use rustc_span::Symbol;
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index 011d8ab57c75c..7be941ed74980 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -4,7 +4,7 @@ use rustc_codegen_ssa::traits::*;
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
-use rustc_middle::ty::{self, Ty, TypeVisitableExt};
+use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt};
 use rustc_target::abi::{Abi, Align, FieldsShape};
 use rustc_target::abi::{Float, Int, Pointer};
 use rustc_target::abi::{Scalar, Size, Variants};
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index 224d17dbf52b3..67fbf9642bf0a 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -3,7 +3,7 @@
 use rustc_middle::mir;
 use rustc_middle::span_bug;
 use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
-use rustc_middle::ty::{self, ScalarInt, Ty};
+use rustc_middle::ty::{self, CoroutineArgsExt, ScalarInt, Ty};
 use rustc_target::abi::{self, TagEncoding};
 use rustc_target::abi::{VariantIdx, Variants};
 use tracing::{instrument, trace};
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 9d70231be3b0c..46b38e4a6a60c 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -1,7 +1,7 @@
 //! Values computed by queries that use MIR.
 
 use crate::mir;
-use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
+use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::LocalDefId;
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 30dd887c88a2a..47f66c6440627 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -90,7 +90,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type AdtDef = ty::AdtDef<'tcx>;
 
     type GenericArgs = ty::GenericArgsRef<'tcx>;
-    type OwnItemArgs = &'tcx [ty::GenericArg<'tcx>];
+    type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>];
     type GenericArg = ty::GenericArg<'tcx>;
     type Term = ty::Term<'tcx>;
 
@@ -190,7 +190,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self,
         def_id: Self::DefId,
         args: Self::GenericArgs,
-    ) -> (rustc_type_ir::TraitRef<Self>, Self::OwnItemArgs) {
+    ) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) {
         assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst);
         let trait_def_id = self.parent(def_id);
         assert_matches!(self.def_kind(trait_def_id), DefKind::Trait);
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index 7a516b9f2c809..c3ab755175d80 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -2,9 +2,10 @@
 
 use crate::ty::codec::{TyDecoder, TyEncoder};
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
-use crate::ty::sty::{ClosureArgs, CoroutineArgs, CoroutineClosureArgs, InlineConstArgs};
 use crate::ty::visit::{TypeVisitable, TypeVisitor};
-use crate::ty::{self, Lift, List, Ty, TyCtxt};
+use crate::ty::{
+    self, ClosureArgs, CoroutineArgs, CoroutineClosureArgs, InlineConstArgs, Lift, List, Ty, TyCtxt,
+};
 
 use rustc_ast_ir::visit::VisitorResult;
 use rustc_ast_ir::walk_visitable_list;
@@ -56,6 +57,64 @@ impl<'tcx> rustc_type_ir::inherent::GenericArgs<TyCtxt<'tcx>> for ty::GenericArg
     ) -> ty::GenericArgsRef<'tcx> {
         ty::GenericArgs::extend_with_error(tcx, def_id, original_args)
     }
+
+    fn split_closure_args(self) -> ty::ClosureArgsParts<TyCtxt<'tcx>> {
+        match self[..] {
+            [ref parent_args @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => {
+                ty::ClosureArgsParts {
+                    parent_args,
+                    closure_kind_ty: closure_kind_ty.expect_ty(),
+                    closure_sig_as_fn_ptr_ty: closure_sig_as_fn_ptr_ty.expect_ty(),
+                    tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
+                }
+            }
+            _ => bug!("closure args missing synthetics"),
+        }
+    }
+
+    fn split_coroutine_closure_args(self) -> ty::CoroutineClosureArgsParts<TyCtxt<'tcx>> {
+        match self[..] {
+            [
+                ref parent_args @ ..,
+                closure_kind_ty,
+                signature_parts_ty,
+                tupled_upvars_ty,
+                coroutine_captures_by_ref_ty,
+                coroutine_witness_ty,
+            ] => ty::CoroutineClosureArgsParts {
+                parent_args,
+                closure_kind_ty: closure_kind_ty.expect_ty(),
+                signature_parts_ty: signature_parts_ty.expect_ty(),
+                tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
+                coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(),
+                coroutine_witness_ty: coroutine_witness_ty.expect_ty(),
+            },
+            _ => bug!("closure args missing synthetics"),
+        }
+    }
+
+    fn split_coroutine_args(self) -> ty::CoroutineArgsParts<TyCtxt<'tcx>> {
+        match self[..] {
+            [
+                ref parent_args @ ..,
+                kind_ty,
+                resume_ty,
+                yield_ty,
+                return_ty,
+                witness,
+                tupled_upvars_ty,
+            ] => ty::CoroutineArgsParts {
+                parent_args,
+                kind_ty: kind_ty.expect_ty(),
+                resume_ty: resume_ty.expect_ty(),
+                yield_ty: yield_ty.expect_ty(),
+                return_ty: return_ty.expect_ty(),
+                witness: witness.expect_ty(),
+                tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
+            },
+            _ => bug!("coroutine args missing synthetics"),
+        }
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::IntoKind for GenericArg<'tcx> {
@@ -295,7 +354,7 @@ impl<'tcx> GenericArgs<'tcx> {
     /// Closure args have a particular structure controlled by the
     /// compiler that encodes information like the signature and closure kind;
     /// see `ty::ClosureArgs` struct for more comments.
-    pub fn as_closure(&'tcx self) -> ClosureArgs<'tcx> {
+    pub fn as_closure(&'tcx self) -> ClosureArgs<TyCtxt<'tcx>> {
         ClosureArgs { args: self }
     }
 
@@ -303,7 +362,7 @@ impl<'tcx> GenericArgs<'tcx> {
     /// Coroutine-closure args have a particular structure controlled by the
     /// compiler that encodes information like the signature and closure kind;
     /// see `ty::CoroutineClosureArgs` struct for more comments.
-    pub fn as_coroutine_closure(&'tcx self) -> CoroutineClosureArgs<'tcx> {
+    pub fn as_coroutine_closure(&'tcx self) -> CoroutineClosureArgs<TyCtxt<'tcx>> {
         CoroutineClosureArgs { args: self }
     }
 
@@ -311,7 +370,7 @@ impl<'tcx> GenericArgs<'tcx> {
     /// Coroutine args have a particular structure controlled by the
     /// compiler that encodes information like the signature and coroutine kind;
     /// see `ty::CoroutineArgs` struct for more comments.
-    pub fn as_coroutine(&'tcx self) -> CoroutineArgs<'tcx> {
+    pub fn as_coroutine(&'tcx self) -> CoroutineArgs<TyCtxt<'tcx>> {
         CoroutineArgs { args: self }
     }
 
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 60ce87440328b..3c84ee5e7347f 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2,7 +2,7 @@ use crate::error::UnsupportedFnAbi;
 use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use crate::query::TyCtxtAt;
 use crate::ty::normalize_erasing_regions::NormalizationError;
-use crate::ty::{self, Ty, TyCtxt, TypeVisitableExt};
+use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt};
 use rustc_error_messages::DiagMessage;
 use rustc_errors::{
     Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 90c154233dabb..2643ce976ded1 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -113,10 +113,8 @@ pub use self::region::{
 pub use self::rvalue_scopes::RvalueScopes;
 pub use self::sty::{
     AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
-    ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs,
-    CoroutineClosureArgsParts, CoroutineClosureSignature, EarlyBinder, FnSig, GenSig,
-    InlineConstArgs, InlineConstArgsParts, ParamConst, ParamTy, PolyFnSig, TyKind, TypeAndMut,
-    UpvarArgs, VarianceDiagInfo,
+    CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst,
+    ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo,
 };
 pub use self::trait_def::TraitDef;
 pub use self::typeck_results::{
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 83790db992600..126da2f5a7c57 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1938,7 +1938,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         Ok(())
     }
 
-    fn pretty_closure_as_impl(&mut self, closure: ty::ClosureArgs<'tcx>) -> Result<(), PrintError> {
+    fn pretty_closure_as_impl(
+        &mut self,
+        closure: ty::ClosureArgs<TyCtxt<'tcx>>,
+    ) -> Result<(), PrintError> {
         let sig = closure.sig();
         let kind = closure.kind_ty().to_opt_closure_kind().unwrap_or(ty::ClosureKind::Fn);
 
@@ -2973,7 +2976,7 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> {
 
 #[derive(Debug, Copy, Clone, Lift)]
 pub struct PrintClosureAsImpl<'tcx> {
-    pub closure: ty::ClosureArgs<'tcx>,
+    pub closure: ty::ClosureArgs<TyCtxt<'tcx>>,
 }
 
 macro_rules! forward_display_to_print {
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index f02b4849f83dd..b417985889094 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -756,28 +756,6 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
     }
 }
 
-impl<'tcx> Relate<'tcx> for ty::ClosureArgs<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::ClosureArgs<'tcx>,
-        b: ty::ClosureArgs<'tcx>,
-    ) -> RelateResult<'tcx, ty::ClosureArgs<'tcx>> {
-        let args = relate_args_invariantly(relation, a.args, b.args)?;
-        Ok(ty::ClosureArgs { args })
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::CoroutineArgs<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::CoroutineArgs<'tcx>,
-        b: ty::CoroutineArgs<'tcx>,
-    ) -> RelateResult<'tcx, ty::CoroutineArgs<'tcx>> {
-        let args = relate_args_invariantly(relation, a.args, b.args)?;
-        Ok(ty::CoroutineArgs { args })
-    }
-}
-
 impl<'tcx> Relate<'tcx> for GenericArgsRef<'tcx> {
     fn relate<R: TypeRelation<'tcx>>(
         relation: &mut R,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 5f7385fccc989..879396b067810 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -16,7 +16,7 @@ use rustc_errors::{ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::LangItem;
-use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable};
+use rustc_macros::{extension, HashStable, TyDecodable, TyEncodable, TypeFoldable};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
@@ -30,7 +30,6 @@ use ty::util::{AsyncDropGlueMorphology, IntTypeExt};
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind};
 
-use super::fold::FnMutDelegate;
 use super::GenericParamDefKind;
 
 // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
@@ -60,670 +59,14 @@ impl<'tcx> Article for TyKind<'tcx> {
     }
 }
 
-/// A closure can be modeled as a struct that looks like:
-/// ```ignore (illustrative)
-/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U);
-/// ```
-/// where:
-///
-/// - 'l0...'li and T0...Tj are the generic parameters
-///   in scope on the function that defined the closure,
-/// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This
-///   is rather hackily encoded via a scalar type. See
-///   `Ty::to_opt_closure_kind` for details.
-/// - CS represents the *closure signature*, representing as a `fn()`
-///   type. For example, `fn(u32, u32) -> u32` would mean that the closure
-///   implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait
-///   specified above.
-/// - U is a type parameter representing the types of its upvars, tupled up
-///   (borrowed, if appropriate; that is, if a U field represents a by-ref upvar,
-///    and the up-var has the type `Foo`, then that field of U will be `&Foo`).
-///
-/// So, for example, given this function:
-/// ```ignore (illustrative)
-/// fn foo<'a, T>(data: &'a mut T) {
-///      do(|| data.count += 1)
-/// }
-/// ```
-/// the type of the closure would be something like:
-/// ```ignore (illustrative)
-/// struct Closure<'a, T, U>(...U);
-/// ```
-/// Note that the type of the upvar is not specified in the struct.
-/// You may wonder how the impl would then be able to use the upvar,
-/// if it doesn't know it's type? The answer is that the impl is
-/// (conceptually) not fully generic over Closure but rather tied to
-/// instances with the expected upvar types:
-/// ```ignore (illustrative)
-/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> {
-///     ...
-/// }
-/// ```
-/// You can see that the *impl* fully specified the type of the upvar
-/// and thus knows full well that `data` has type `&'b mut &'a mut T`.
-/// (Here, I am assuming that `data` is mut-borrowed.)
-///
-/// Now, the last question you may ask is: Why include the upvar types
-/// in an extra type parameter? The reason for this design is that the
-/// upvar types can reference lifetimes that are internal to the
-/// creating function. In my example above, for example, the lifetime
-/// `'b` represents the scope of the closure itself; this is some
-/// subset of `foo`, probably just the scope of the call to the to
-/// `do()`. If we just had the lifetime/type parameters from the
-/// enclosing function, we couldn't name this lifetime `'b`. Note that
-/// there can also be lifetimes in the types of the upvars themselves,
-/// if one of them happens to be a reference to something that the
-/// creating fn owns.
-///
-/// OK, you say, so why not create a more minimal set of parameters
-/// that just includes the extra lifetime parameters? The answer is
-/// primarily that it would be hard --- we don't know at the time when
-/// we create the closure type what the full types of the upvars are,
-/// nor do we know which are borrowed and which are not. In this
-/// design, we can just supply a fresh type parameter and figure that
-/// out later.
-///
-/// All right, you say, but why include the type parameters from the
-/// original function then? The answer is that codegen may need them
-/// when monomorphizing, and they may not appear in the upvars. A
-/// closure could capture no variables but still make use of some
-/// in-scope type parameter with a bound (e.g., if our example above
-/// had an extra `U: Default`, and the closure called `U::default()`).
-///
-/// There is another reason. This design (implicitly) prohibits
-/// closures from capturing themselves (except via a trait
-/// object). This simplifies closure inference considerably, since it
-/// means that when we infer the kind of a closure or its upvars, we
-/// don't have to handle cycles where the decisions we make for
-/// closure C wind up influencing the decisions we ought to make for
-/// closure C (which would then require fixed point iteration to
-/// handle). Plus it fixes an ICE. :P
-///
-/// ## Coroutines
-///
-/// Coroutines are handled similarly in `CoroutineArgs`. The set of
-/// type parameters is similar, but `CK` and `CS` are replaced by the
-/// following type parameters:
-///
-/// * `GS`: The coroutine's "resume type", which is the type of the
-///   argument passed to `resume`, and the type of `yield` expressions
-///   inside the coroutine.
-/// * `GY`: The "yield type", which is the type of values passed to
-///   `yield` inside the coroutine.
-/// * `GR`: The "return type", which is the type of value returned upon
-///   completion of the coroutine.
-/// * `GW`: The "coroutine witness".
-#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable, Lift)]
-pub struct ClosureArgs<'tcx> {
-    /// Lifetime and type parameters from the enclosing function,
-    /// concatenated with a tuple containing the types of the upvars.
-    ///
-    /// These are separated out because codegen wants to pass them around
-    /// when monomorphizing.
-    pub args: GenericArgsRef<'tcx>,
-}
-
-/// Struct returned by `split()`.
-pub struct ClosureArgsParts<'tcx> {
-    /// This is the args of the typeck root.
-    pub parent_args: &'tcx [GenericArg<'tcx>],
-    /// Represents the maximum calling capability of the closure.
-    pub closure_kind_ty: Ty<'tcx>,
-    /// Captures the closure's signature. This closure signature is "tupled", and
-    /// thus has a peculiar signature of `extern "rust-call" fn((Args, ...)) -> Ty`.
-    pub closure_sig_as_fn_ptr_ty: Ty<'tcx>,
-    /// The upvars captured by the closure. Remains an inference variable
-    /// until the upvar analysis, which happens late in HIR typeck.
-    pub tupled_upvars_ty: Ty<'tcx>,
-}
-
-impl<'tcx> ClosureArgs<'tcx> {
-    /// Construct `ClosureArgs` from `ClosureArgsParts`, containing `Args`
-    /// for the closure parent, alongside additional closure-specific components.
-    pub fn new(tcx: TyCtxt<'tcx>, parts: ClosureArgsParts<'tcx>) -> ClosureArgs<'tcx> {
-        ClosureArgs {
-            args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([
-                parts.closure_kind_ty.into(),
-                parts.closure_sig_as_fn_ptr_ty.into(),
-                parts.tupled_upvars_ty.into(),
-            ])),
-        }
-    }
-
-    /// Divides the closure args into their respective components.
-    /// The ordering assumed here must match that used by `ClosureArgs::new` above.
-    fn split(self) -> ClosureArgsParts<'tcx> {
-        match self.args[..] {
-            [ref parent_args @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => {
-                ClosureArgsParts {
-                    parent_args,
-                    closure_kind_ty: closure_kind_ty.expect_ty(),
-                    closure_sig_as_fn_ptr_ty: closure_sig_as_fn_ptr_ty.expect_ty(),
-                    tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
-                }
-            }
-            _ => bug!("closure args missing synthetics"),
-        }
-    }
-
-    /// Returns the generic parameters of the closure's parent.
-    pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] {
-        self.split().parent_args
-    }
-
-    /// Returns an iterator over the list of types of captured paths by the closure.
-    /// In case there was a type error in figuring out the types of the captured path, an
-    /// empty iterator is returned.
-    #[inline]
-    pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> {
-        match *self.tupled_upvars_ty().kind() {
-            TyKind::Error(_) => ty::List::empty(),
-            TyKind::Tuple(tys) => tys,
-            TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
-            ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
-        }
-    }
-
-    /// Returns the tuple type representing the upvars for this closure.
-    #[inline]
-    pub fn tupled_upvars_ty(self) -> Ty<'tcx> {
-        self.split().tupled_upvars_ty
-    }
-
-    /// Returns the closure kind for this closure; may return a type
-    /// variable during inference. To get the closure kind during
-    /// inference, use `infcx.closure_kind(args)`.
-    pub fn kind_ty(self) -> Ty<'tcx> {
-        self.split().closure_kind_ty
-    }
-
-    /// Returns the `fn` pointer type representing the closure signature for this
-    /// closure.
-    // FIXME(eddyb) this should be unnecessary, as the shallowly resolved
-    // type is known at the time of the creation of `ClosureArgs`,
-    // see `rustc_hir_analysis::check::closure`.
-    pub fn sig_as_fn_ptr_ty(self) -> Ty<'tcx> {
-        self.split().closure_sig_as_fn_ptr_ty
-    }
-
-    /// Returns the closure kind for this closure; only usable outside
-    /// of an inference context, because in that context we know that
-    /// there are no type variables.
-    ///
-    /// If you have an inference context, use `infcx.closure_kind()`.
-    pub fn kind(self) -> ty::ClosureKind {
-        self.kind_ty().to_opt_closure_kind().unwrap()
-    }
-
-    /// Extracts the signature from the closure.
-    pub fn sig(self) -> ty::PolyFnSig<'tcx> {
-        match *self.sig_as_fn_ptr_ty().kind() {
-            ty::FnPtr(sig) => sig,
-            ty => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {ty:?}"),
-        }
-    }
-
-    pub fn print_as_impl_trait(self) -> ty::print::PrintClosureAsImpl<'tcx> {
-        ty::print::PrintClosureAsImpl { closure: self }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable, Lift)]
-pub struct CoroutineClosureArgs<'tcx> {
-    pub args: GenericArgsRef<'tcx>,
-}
-
-/// See docs for explanation of how each argument is used.
-///
-/// See [`CoroutineClosureSignature`] for how these arguments are put together
-/// to make a callable [`FnSig`] suitable for typeck and borrowck.
-pub struct CoroutineClosureArgsParts<'tcx> {
-    /// This is the args of the typeck root.
-    pub parent_args: &'tcx [GenericArg<'tcx>],
-    /// Represents the maximum calling capability of the closure.
-    pub closure_kind_ty: Ty<'tcx>,
-    /// Represents all of the relevant parts of the coroutine returned by this
-    /// coroutine-closure. This signature parts type will have the general
-    /// shape of `fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty)`, where
-    /// `resume_ty`, `return_ty`, and `yield_ty` are the respective types for the
-    /// coroutine returned by the coroutine-closure.
-    ///
-    /// Use `coroutine_closure_sig` to break up this type rather than using it
-    /// yourself.
-    pub signature_parts_ty: Ty<'tcx>,
-    /// The upvars captured by the closure. Remains an inference variable
-    /// until the upvar analysis, which happens late in HIR typeck.
-    pub tupled_upvars_ty: Ty<'tcx>,
-    /// a function pointer that has the shape `for<'env> fn() -> (&'env T, ...)`.
-    /// This allows us to represent the binder of the self-captures of the closure.
-    ///
-    /// For example, if the coroutine returned by the closure borrows `String`
-    /// from the closure's upvars, this will be `for<'env> fn() -> (&'env String,)`,
-    /// while the `tupled_upvars_ty`, representing the by-move version of the same
-    /// captures, will be `(String,)`.
-    pub coroutine_captures_by_ref_ty: Ty<'tcx>,
-    /// Witness type returned by the generator produced by this coroutine-closure.
-    pub coroutine_witness_ty: Ty<'tcx>,
-}
-
-impl<'tcx> CoroutineClosureArgs<'tcx> {
-    pub fn new(
-        tcx: TyCtxt<'tcx>,
-        parts: CoroutineClosureArgsParts<'tcx>,
-    ) -> CoroutineClosureArgs<'tcx> {
-        CoroutineClosureArgs {
-            args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([
-                parts.closure_kind_ty.into(),
-                parts.signature_parts_ty.into(),
-                parts.tupled_upvars_ty.into(),
-                parts.coroutine_captures_by_ref_ty.into(),
-                parts.coroutine_witness_ty.into(),
-            ])),
-        }
-    }
-
-    fn split(self) -> CoroutineClosureArgsParts<'tcx> {
-        match self.args[..] {
-            [
-                ref parent_args @ ..,
-                closure_kind_ty,
-                signature_parts_ty,
-                tupled_upvars_ty,
-                coroutine_captures_by_ref_ty,
-                coroutine_witness_ty,
-            ] => CoroutineClosureArgsParts {
-                parent_args,
-                closure_kind_ty: closure_kind_ty.expect_ty(),
-                signature_parts_ty: signature_parts_ty.expect_ty(),
-                tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
-                coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(),
-                coroutine_witness_ty: coroutine_witness_ty.expect_ty(),
-            },
-            _ => bug!("closure args missing synthetics"),
-        }
-    }
-
-    pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] {
-        self.split().parent_args
-    }
-
-    #[inline]
-    pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> {
-        match self.tupled_upvars_ty().kind() {
-            TyKind::Error(_) => ty::List::empty(),
-            TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(),
-            TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
-            ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
-        }
-    }
-
-    #[inline]
-    pub fn tupled_upvars_ty(self) -> Ty<'tcx> {
-        self.split().tupled_upvars_ty
-    }
-
-    pub fn kind_ty(self) -> Ty<'tcx> {
-        self.split().closure_kind_ty
-    }
-
-    pub fn kind(self) -> ty::ClosureKind {
-        self.kind_ty().to_opt_closure_kind().unwrap()
-    }
-
-    pub fn signature_parts_ty(self) -> Ty<'tcx> {
-        self.split().signature_parts_ty
-    }
-
-    pub fn coroutine_closure_sig(self) -> Binder<'tcx, CoroutineClosureSignature<'tcx>> {
-        let interior = self.coroutine_witness_ty();
-        let ty::FnPtr(sig) = self.signature_parts_ty().kind() else { bug!() };
-        sig.map_bound(|sig| {
-            let [resume_ty, tupled_inputs_ty] = *sig.inputs() else {
-                bug!();
-            };
-            let [yield_ty, return_ty] = **sig.output().tuple_fields() else { bug!() };
-            CoroutineClosureSignature {
-                interior,
-                tupled_inputs_ty,
-                resume_ty,
-                yield_ty,
-                return_ty,
-                c_variadic: sig.c_variadic,
-                safety: sig.safety,
-                abi: sig.abi,
-            }
-        })
-    }
-
-    pub fn coroutine_captures_by_ref_ty(self) -> Ty<'tcx> {
-        self.split().coroutine_captures_by_ref_ty
-    }
-
-    pub fn coroutine_witness_ty(self) -> Ty<'tcx> {
-        self.split().coroutine_witness_ty
-    }
-
-    pub fn has_self_borrows(&self) -> bool {
-        match self.coroutine_captures_by_ref_ty().kind() {
-            ty::FnPtr(sig) => sig
-                .skip_binder()
-                .visit_with(&mut HasRegionsBoundAt { binder: ty::INNERMOST })
-                .is_break(),
-            ty::Error(_) => true,
-            _ => bug!(),
-        }
-    }
-}
-/// Unlike `has_escaping_bound_vars` or `outermost_exclusive_binder`, this will
-/// detect only regions bound *at* the debruijn index.
-struct HasRegionsBoundAt {
-    binder: ty::DebruijnIndex,
-}
-// FIXME: Could be optimized to not walk into components with no escaping bound vars.
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasRegionsBoundAt {
-    type Result = ControlFlow<()>;
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &ty::Binder<'tcx, T>,
-    ) -> Self::Result {
-        self.binder.shift_in(1);
-        t.super_visit_with(self)?;
-        self.binder.shift_out(1);
-        ControlFlow::Continue(())
-    }
-
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
-        if let ty::ReBound(binder, _) = *r
-            && self.binder == binder
-        {
-            ControlFlow::Break(())
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
-pub struct CoroutineClosureSignature<'tcx> {
-    pub interior: Ty<'tcx>,
-    pub tupled_inputs_ty: Ty<'tcx>,
-    pub resume_ty: Ty<'tcx>,
-    pub yield_ty: Ty<'tcx>,
-    pub return_ty: Ty<'tcx>,
-
-    // Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types
-    // never actually differ. But we save them rather than recreating them
-    // from scratch just for good measure.
-    /// Always false
-    pub c_variadic: bool,
-    /// Always [`hir::Safety::Safe`]
-    pub safety: hir::Safety,
-    /// Always [`abi::Abi::RustCall`]
-    pub abi: abi::Abi,
-}
-
-impl<'tcx> CoroutineClosureSignature<'tcx> {
-    /// Construct a coroutine from the closure signature. Since a coroutine signature
-    /// is agnostic to the type of generator that is returned (by-ref/by-move),
-    /// the caller must specify what "flavor" of generator that they'd like to
-    /// create. Additionally, they must manually compute the upvars of the closure.
-    ///
-    /// This helper is not really meant to be used directly except for early on
-    /// during typeck, when we want to put inference vars into the kind and upvars tys.
-    /// When the kind and upvars are known, use the other helper functions.
-    pub fn to_coroutine(
-        self,
-        tcx: TyCtxt<'tcx>,
-        parent_args: &'tcx [GenericArg<'tcx>],
-        coroutine_kind_ty: Ty<'tcx>,
-        coroutine_def_id: DefId,
-        tupled_upvars_ty: Ty<'tcx>,
-    ) -> Ty<'tcx> {
-        let coroutine_args = ty::CoroutineArgs::new(
-            tcx,
-            ty::CoroutineArgsParts {
-                parent_args,
-                kind_ty: coroutine_kind_ty,
-                resume_ty: self.resume_ty,
-                yield_ty: self.yield_ty,
-                return_ty: self.return_ty,
-                witness: self.interior,
-                tupled_upvars_ty,
-            },
-        );
-
-        Ty::new_coroutine(tcx, coroutine_def_id, coroutine_args.args)
-    }
-
-    /// Given known upvars and a [`ClosureKind`](ty::ClosureKind), compute the coroutine
-    /// returned by that corresponding async fn trait.
-    ///
-    /// This function expects the upvars to have been computed already, and doesn't check
-    /// that the `ClosureKind` is actually supported by the coroutine-closure.
-    pub fn to_coroutine_given_kind_and_upvars(
-        self,
-        tcx: TyCtxt<'tcx>,
-        parent_args: &'tcx [GenericArg<'tcx>],
-        coroutine_def_id: DefId,
-        goal_kind: ty::ClosureKind,
-        env_region: ty::Region<'tcx>,
-        closure_tupled_upvars_ty: Ty<'tcx>,
-        coroutine_captures_by_ref_ty: Ty<'tcx>,
-    ) -> Ty<'tcx> {
-        let tupled_upvars_ty = Self::tupled_upvars_by_closure_kind(
-            tcx,
-            goal_kind,
-            self.tupled_inputs_ty,
-            closure_tupled_upvars_ty,
-            coroutine_captures_by_ref_ty,
-            env_region,
-        );
-
-        self.to_coroutine(
-            tcx,
-            parent_args,
-            Ty::from_coroutine_closure_kind(tcx, goal_kind),
-            coroutine_def_id,
-            tupled_upvars_ty,
-        )
-    }
-
-    /// Compute the tupled upvars that a coroutine-closure's output coroutine
-    /// would return for the given `ClosureKind`.
-    ///
-    /// When `ClosureKind` is `FnMut`/`Fn`, then this will use the "captures by ref"
-    /// to return a set of upvars which are borrowed with the given `env_region`.
-    ///
-    /// This ensures that the `AsyncFn::call` will return a coroutine whose upvars'
-    /// lifetimes are related to the lifetime of the borrow on the closure made for
-    /// the call. This allows borrowck to enforce the self-borrows correctly.
-    pub fn tupled_upvars_by_closure_kind(
-        tcx: TyCtxt<'tcx>,
-        kind: ty::ClosureKind,
-        tupled_inputs_ty: Ty<'tcx>,
-        closure_tupled_upvars_ty: Ty<'tcx>,
-        coroutine_captures_by_ref_ty: Ty<'tcx>,
-        env_region: ty::Region<'tcx>,
-    ) -> Ty<'tcx> {
-        match kind {
-            ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
-                let ty::FnPtr(sig) = *coroutine_captures_by_ref_ty.kind() else {
-                    bug!();
-                };
-                let coroutine_captures_by_ref_ty = tcx.replace_escaping_bound_vars_uncached(
-                    sig.output().skip_binder(),
-                    FnMutDelegate {
-                        consts: &mut |c, t| ty::Const::new_bound(tcx, ty::INNERMOST, c, t),
-                        types: &mut |t| Ty::new_bound(tcx, ty::INNERMOST, t),
-                        regions: &mut |_| env_region,
-                    },
-                );
-                Ty::new_tup_from_iter(
-                    tcx,
-                    tupled_inputs_ty
-                        .tuple_fields()
-                        .iter()
-                        .chain(coroutine_captures_by_ref_ty.tuple_fields()),
-                )
-            }
-            ty::ClosureKind::FnOnce => Ty::new_tup_from_iter(
-                tcx,
-                tupled_inputs_ty
-                    .tuple_fields()
-                    .iter()
-                    .chain(closure_tupled_upvars_ty.tuple_fields()),
-            ),
-        }
-    }
-}
-/// Similar to `ClosureArgs`; see the above documentation for more.
-#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
-pub struct CoroutineArgs<'tcx> {
-    pub args: GenericArgsRef<'tcx>,
-}
-
-pub struct CoroutineArgsParts<'tcx> {
-    /// This is the args of the typeck root.
-    pub parent_args: &'tcx [GenericArg<'tcx>],
-
-    /// The coroutines returned by a coroutine-closure's `AsyncFnOnce`/`AsyncFnMut`
-    /// implementations must be distinguished since the former takes the closure's
-    /// upvars by move, and the latter takes the closure's upvars by ref.
-    ///
-    /// This field distinguishes these fields so that codegen can select the right
-    /// body for the coroutine. This has the same type representation as the closure
-    /// kind: `i8`/`i16`/`i32`.
-    ///
-    /// For regular coroutines, this field will always just be `()`.
-    pub kind_ty: Ty<'tcx>,
-
-    pub resume_ty: Ty<'tcx>,
-    pub yield_ty: Ty<'tcx>,
-    pub return_ty: Ty<'tcx>,
-
-    /// The interior type of the coroutine.
-    /// Represents all types that are stored in locals
-    /// in the coroutine's body.
-    pub witness: Ty<'tcx>,
-
-    /// The upvars captured by the closure. Remains an inference variable
-    /// until the upvar analysis, which happens late in HIR typeck.
-    pub tupled_upvars_ty: Ty<'tcx>,
-}
-
-impl<'tcx> CoroutineArgs<'tcx> {
-    /// Construct `CoroutineArgs` from `CoroutineArgsParts`, containing `Args`
-    /// for the coroutine parent, alongside additional coroutine-specific components.
-    pub fn new(tcx: TyCtxt<'tcx>, parts: CoroutineArgsParts<'tcx>) -> CoroutineArgs<'tcx> {
-        CoroutineArgs {
-            args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([
-                parts.kind_ty.into(),
-                parts.resume_ty.into(),
-                parts.yield_ty.into(),
-                parts.return_ty.into(),
-                parts.witness.into(),
-                parts.tupled_upvars_ty.into(),
-            ])),
-        }
-    }
-
-    /// Divides the coroutine args into their respective components.
-    /// The ordering assumed here must match that used by `CoroutineArgs::new` above.
-    fn split(self) -> CoroutineArgsParts<'tcx> {
-        match self.args[..] {
-            [
-                ref parent_args @ ..,
-                kind_ty,
-                resume_ty,
-                yield_ty,
-                return_ty,
-                witness,
-                tupled_upvars_ty,
-            ] => CoroutineArgsParts {
-                parent_args,
-                kind_ty: kind_ty.expect_ty(),
-                resume_ty: resume_ty.expect_ty(),
-                yield_ty: yield_ty.expect_ty(),
-                return_ty: return_ty.expect_ty(),
-                witness: witness.expect_ty(),
-                tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
-            },
-            _ => bug!("coroutine args missing synthetics"),
-        }
-    }
-
-    /// Returns the generic parameters of the coroutine's parent.
-    pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] {
-        self.split().parent_args
-    }
-
-    // Returns the kind of the coroutine. See docs on the `kind_ty` field.
-    pub fn kind_ty(self) -> Ty<'tcx> {
-        self.split().kind_ty
-    }
-
-    /// This describes the types that can be contained in a coroutine.
-    /// It will be a type variable initially and unified in the last stages of typeck of a body.
-    /// It contains a tuple of all the types that could end up on a coroutine frame.
-    /// The state transformation MIR pass may only produce layouts which mention types
-    /// in this tuple. Upvars are not counted here.
-    pub fn witness(self) -> Ty<'tcx> {
-        self.split().witness
-    }
-
-    /// Returns an iterator over the list of types of captured paths by the coroutine.
-    /// In case there was a type error in figuring out the types of the captured path, an
-    /// empty iterator is returned.
-    #[inline]
-    pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> {
-        match *self.tupled_upvars_ty().kind() {
-            TyKind::Error(_) => ty::List::empty(),
-            TyKind::Tuple(tys) => tys,
-            TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
-            ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
-        }
-    }
-
-    /// Returns the tuple type representing the upvars for this coroutine.
-    #[inline]
-    pub fn tupled_upvars_ty(self) -> Ty<'tcx> {
-        self.split().tupled_upvars_ty
-    }
-
-    /// Returns the type representing the resume type of the coroutine.
-    pub fn resume_ty(self) -> Ty<'tcx> {
-        self.split().resume_ty
-    }
-
-    /// Returns the type representing the yield type of the coroutine.
-    pub fn yield_ty(self) -> Ty<'tcx> {
-        self.split().yield_ty
-    }
-
-    /// Returns the type representing the return type of the coroutine.
-    pub fn return_ty(self) -> Ty<'tcx> {
-        self.split().return_ty
-    }
-
-    /// Returns the "coroutine signature", which consists of its resume, yield
-    /// and return types.
-    pub fn sig(self) -> GenSig<'tcx> {
-        let parts = self.split();
-        ty::GenSig {
-            resume_ty: parts.resume_ty,
-            yield_ty: parts.yield_ty,
-            return_ty: parts.return_ty,
-        }
-    }
-}
-
-impl<'tcx> CoroutineArgs<'tcx> {
+#[extension(pub trait CoroutineArgsExt<'tcx>)]
+impl<'tcx> ty::CoroutineArgs<TyCtxt<'tcx>> {
     /// Coroutine has not been resumed yet.
-    pub const UNRESUMED: usize = 0;
+    const UNRESUMED: usize = 0;
     /// Coroutine has returned or is completed.
-    pub const RETURNED: usize = 1;
+    const RETURNED: usize = 1;
     /// Coroutine has been poisoned.
-    pub const POISONED: usize = 2;
+    const POISONED: usize = 2;
 
     const UNRESUMED_NAME: &'static str = "Unresumed";
     const RETURNED_NAME: &'static str = "Returned";
@@ -731,7 +74,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
 
     /// The valid variant indices of this coroutine.
     #[inline]
-    pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> Range<VariantIdx> {
+    fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> Range<VariantIdx> {
         // FIXME requires optimized MIR
         FIRST_VARIANT
             ..tcx.coroutine_layout(def_id, tcx.types.unit).unwrap().variant_fields.next_index()
@@ -740,7 +83,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
     /// The discriminant for the given variant. Panics if the `variant_index` is
     /// out of range.
     #[inline]
-    pub fn discriminant_for_variant(
+    fn discriminant_for_variant(
         &self,
         def_id: DefId,
         tcx: TyCtxt<'tcx>,
@@ -755,7 +98,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
     /// The set of all discriminants for the coroutine, enumerated with their
     /// variant indices.
     #[inline]
-    pub fn discriminants(
+    fn discriminants(
         self,
         def_id: DefId,
         tcx: TyCtxt<'tcx>,
@@ -767,7 +110,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
 
     /// Calls `f` with a reference to the name of the enumerator for the given
     /// variant `v`.
-    pub fn variant_name(v: VariantIdx) -> Cow<'static, str> {
+    fn variant_name(v: VariantIdx) -> Cow<'static, str> {
         match v.as_usize() {
             Self::UNRESUMED => Cow::from(Self::UNRESUMED_NAME),
             Self::RETURNED => Cow::from(Self::RETURNED_NAME),
@@ -778,7 +121,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
 
     /// The type of the state discriminant used in the coroutine type.
     #[inline]
-    pub fn discr_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+    fn discr_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
         tcx.types.u32
     }
 
@@ -789,7 +132,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
     /// The locals are grouped by their variant number. Note that some locals may
     /// be repeated in multiple variants.
     #[inline]
-    pub fn state_tys(
+    fn state_tys(
         self,
         def_id: DefId,
         tcx: TyCtxt<'tcx>,
@@ -805,7 +148,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
     /// This is the types of the fields of a coroutine which are not stored in a
     /// variant.
     #[inline]
-    pub fn prefix_tys(self) -> &'tcx List<Ty<'tcx>> {
+    fn prefix_tys(self) -> &'tcx List<Ty<'tcx>> {
         self.upvar_tys()
     }
 }
@@ -859,7 +202,7 @@ impl<'tcx> UpvarArgs<'tcx> {
 ///
 /// When the inline const is instantiated, `R` is instantiated as the actual inferred
 /// type of the constant. The reason that `R` is represented as an extra type parameter
-/// is the same reason that [`ClosureArgs`] have `CS` and `U` as type parameters:
+/// is the same reason that [`ty::ClosureArgs`] have `CS` and `U` as type parameters:
 /// inline const can reference lifetimes that are internal to the creating function.
 #[derive(Copy, Clone, Debug)]
 pub struct InlineConstArgs<'tcx> {
@@ -938,13 +281,6 @@ impl BoundVariableKind {
     }
 }
 
-#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)]
-pub struct GenSig<'tcx> {
-    pub resume_ty: Ty<'tcx>,
-    pub yield_ty: Ty<'tcx>,
-    pub return_ty: Ty<'tcx>,
-}
-
 pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>;
 pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>;
 
@@ -1451,6 +787,41 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
     ) -> Self {
         Ty::new_alias(interner, kind, alias_ty)
     }
+
+    fn new_coroutine(
+        interner: TyCtxt<'tcx>,
+        def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> Self {
+        Ty::new_coroutine(interner, def_id, args)
+    }
+
+    fn new_tup_from_iter<It, T>(interner: TyCtxt<'tcx>, iter: It) -> T::Output
+    where
+        It: Iterator<Item = T>,
+        T: CollectAndApply<Self, Self>,
+    {
+        Ty::new_tup_from_iter(interner, iter)
+    }
+
+    fn tuple_fields(self) -> &'tcx ty::List<Ty<'tcx>> {
+        self.tuple_fields()
+    }
+
+    fn to_opt_closure_kind(self) -> Option<ty::ClosureKind> {
+        self.to_opt_closure_kind()
+    }
+
+    fn from_closure_kind(interner: TyCtxt<'tcx>, kind: ty::ClosureKind) -> Self {
+        Ty::from_closure_kind(interner, kind)
+    }
+
+    fn from_coroutine_closure_kind(
+        interner: TyCtxt<'tcx>,
+        kind: rustc_type_ir::ClosureKind,
+    ) -> Self {
+        Ty::from_coroutine_closure_kind(interner, kind)
+    }
 }
 
 /// Type utilities
@@ -2169,8 +1540,8 @@ impl<'tcx> Ty<'tcx> {
     /// }
     /// ```
     ///
-    /// After upvar analysis, you should instead use [`ClosureArgs::kind()`]
-    /// or [`CoroutineClosureArgs::kind()`] to assert that the `ClosureKind`
+    /// After upvar analysis, you should instead use [`ty::ClosureArgs::kind()`]
+    /// or [`ty::CoroutineClosureArgs::kind()`] to assert that the `ClosureKind`
     /// has been constrained instead of manually calling this method.
     ///
     /// ```rust,ignore (snippet of compiler code)
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index a3e6e5a5a915c..ade2ac0080e5d 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -69,7 +69,7 @@ use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::CoroutineArgs;
 use rustc_middle::ty::InstanceDef;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_mir_dataflow::impls::{
     MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index dcf54ad2cfc8f..d03c2d18c0c35 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem;
 use rustc_middle::mir::*;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::GenericArgs;
-use rustc_middle::ty::{self, CoroutineArgs, EarlyBinder, Ty, TyCtxt};
+use rustc_middle::ty::{self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
 
@@ -634,7 +634,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
         dest: Place<'tcx>,
         src: Place<'tcx>,
         coroutine_def_id: DefId,
-        args: CoroutineArgs<'tcx>,
+        args: CoroutineArgs<TyCtxt<'tcx>>,
     ) {
         self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
         let unwind = self.block(vec![], TerminatorKind::UnwindResume, true);
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index 851e16559580e..5e83a6f373aad 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -8,7 +8,9 @@ use rustc_middle::mir::coverage::CoverageKind;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
-use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt, Variance};
+use rustc_middle::ty::{
+    self, CoroutineArgsExt, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt, Variance,
+};
 use rustc_middle::{bug, span_bug};
 use rustc_target::abi::{Size, FIRST_VARIANT};
 use rustc_target::spec::abi::Abi;
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 08796ef3109e4..48acf2a4e996e 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -554,8 +554,8 @@ fn coroutine_closure_to_certain_coroutine<'tcx>(
     goal_kind: ty::ClosureKind,
     goal_region: ty::Region<'tcx>,
     def_id: DefId,
-    args: ty::CoroutineClosureArgs<'tcx>,
-    sig: ty::CoroutineClosureSignature<'tcx>,
+    args: ty::CoroutineClosureArgs<TyCtxt<'tcx>>,
+    sig: ty::CoroutineClosureSignature<TyCtxt<'tcx>>,
 ) -> Ty<'tcx> {
     sig.to_coroutine_given_kind_and_upvars(
         tcx,
@@ -578,8 +578,8 @@ fn coroutine_closure_to_ambiguous_coroutine<'tcx>(
     goal_kind: ty::ClosureKind,
     goal_region: ty::Region<'tcx>,
     def_id: DefId,
-    args: ty::CoroutineClosureArgs<'tcx>,
-    sig: ty::CoroutineClosureSignature<'tcx>,
+    args: ty::CoroutineClosureArgs<TyCtxt<'tcx>>,
+    sig: ty::CoroutineClosureSignature<TyCtxt<'tcx>>,
 ) -> Ty<'tcx> {
     let upvars_projection_def_id = tcx.require_lang_item(LangItem::AsyncFnKindUpvars, None);
     let tupled_upvars_ty = Ty::new_projection(
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 445fa1761b9fc..8619c5ef77f67 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -295,7 +295,7 @@ pub fn coroutine_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     fn_trait_def_id: DefId,
     self_ty: Ty<'tcx>,
-    sig: ty::GenSig<'tcx>,
+    sig: ty::GenSig<TyCtxt<'tcx>>,
 ) -> (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>) {
     assert!(!self_ty.has_escaping_bound_vars());
     let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, sig.resume_ty]);
@@ -306,7 +306,7 @@ pub fn future_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     fn_trait_def_id: DefId,
     self_ty: Ty<'tcx>,
-    sig: ty::GenSig<'tcx>,
+    sig: ty::GenSig<TyCtxt<'tcx>>,
 ) -> (ty::TraitRef<'tcx>, Ty<'tcx>) {
     assert!(!self_ty.has_escaping_bound_vars());
     let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty]);
@@ -317,7 +317,7 @@ pub fn iterator_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     iterator_def_id: DefId,
     self_ty: Ty<'tcx>,
-    sig: ty::GenSig<'tcx>,
+    sig: ty::GenSig<TyCtxt<'tcx>>,
 ) -> (ty::TraitRef<'tcx>, Ty<'tcx>) {
     assert!(!self_ty.has_escaping_bound_vars());
     let trait_ref = ty::TraitRef::new(tcx, iterator_def_id, [self_ty]);
@@ -328,7 +328,7 @@ pub fn async_iterator_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     async_iterator_def_id: DefId,
     self_ty: Ty<'tcx>,
-    sig: ty::GenSig<'tcx>,
+    sig: ty::GenSig<TyCtxt<'tcx>>,
 ) -> (ty::TraitRef<'tcx>, Ty<'tcx>) {
     assert!(!self_ty.has_escaping_bound_vars());
     let trait_ref = ty::TraitRef::new(tcx, async_iterator_def_id, [self_ty]);
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 1dee14fae57e3..6045abc50a9da 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -10,7 +10,8 @@ use rustc_middle::ty::layout::{
 };
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{
-    self, AdtDef, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt,
+    self, AdtDef, CoroutineArgsExt, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt,
+    TypeVisitableExt,
 };
 use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
 use rustc_span::sym;
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 7b1dfecfee2e0..f305ed9b5d77d 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -9,7 +9,7 @@ use std::ops::Deref;
 
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
-use crate::{self as ty, DebugWithInfcx, Interner, UpcastFrom};
+use crate::{self as ty, CollectAndApply, DebugWithInfcx, Interner, UpcastFrom};
 
 pub trait Ty<I: Interner<Ty = Self>>:
     Copy
@@ -34,6 +34,21 @@ pub trait Ty<I: Interner<Ty = Self>>:
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
     fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy<I>) -> Self;
+
+    fn new_coroutine(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_tup_from_iter<It, T>(interner: I, iter: It) -> T::Output
+    where
+        It: Iterator<Item = T>,
+        T: CollectAndApply<Self, Self>;
+
+    fn tuple_fields(self) -> I::Tys;
+
+    fn to_opt_closure_kind(self) -> Option<ty::ClosureKind>;
+
+    fn from_closure_kind(interner: I, kind: ty::ClosureKind) -> Self;
+
+    fn from_coroutine_closure_kind(interner: I, kind: ty::ClosureKind) -> Self;
 }
 
 pub trait Tys<I: Interner<Tys = Self>>:
@@ -43,17 +58,18 @@ pub trait Tys<I: Interner<Tys = Self>>:
     + Eq
     + IntoIterator<Item = I::Ty>
     + Deref<Target: Deref<Target = [I::Ty]>>
-    + TypeVisitable<I>
+    + TypeFoldable<I>
+    + Default
 {
     fn split_inputs_and_output(self) -> (I::FnInputTys, I::Ty);
 }
 
-pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq {
+pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq + TypeVisitable<I> {
     /// Whether this ABI is `extern "Rust"`.
     fn is_rust(self) -> bool;
 }
 
-pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq {
+pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq + TypeVisitable<I> {
     fn is_safe(self) -> bool;
 
     fn prefix_str(self) -> &'static str;
@@ -129,6 +145,10 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
         def_id: I::DefId,
         original_args: &[I::GenericArg],
     ) -> I::GenericArgs;
+
+    fn split_closure_args(self) -> ty::ClosureArgsParts<I>;
+    fn split_coroutine_closure_args(self) -> ty::CoroutineClosureArgsParts<I>;
+    fn split_coroutine_args(self) -> ty::CoroutineArgsParts<I>;
 }
 
 pub trait Predicate<I: Interner<Predicate = Self>>:
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index ca39318cc0c8b..6ebb434299b30 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -29,9 +29,7 @@ pub trait Interner:
     type AdtDef: Copy + Debug + Hash + Eq;
 
     type GenericArgs: GenericArgs<Self>;
-    /// The slice of args for a specific item. For a GAT like `type Foo<'a>`, it will be `['a]`,
-    /// not including the args from the parent item (trait or impl).
-    type OwnItemArgs: Copy + Debug + Hash + Eq;
+    type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref<Target = [Self::GenericArg]>;
     type GenericArg: Copy
         + DebugWithInfcx<Self>
         + Hash
@@ -111,7 +109,7 @@ pub trait Interner:
         self,
         def_id: Self::DefId,
         args: Self::GenericArgs,
-    ) -> (ty::TraitRef<Self>, Self::OwnItemArgs);
+    ) -> (ty::TraitRef<Self>, Self::GenericArgsSlice);
 
     fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs;
     fn mk_args_from_iter(self, args: impl Iterator<Item = Self::GenericArg>) -> Self::GenericArgs;
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index 48a6f79993cc6..e7039583c91c7 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -604,7 +604,7 @@ impl<I: Interner> AliasTerm<I> {
     /// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
     /// then this function would return a `T: StreamingIterator` trait reference and
     /// `['a]` as the own args.
-    pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef<I>, I::OwnItemArgs) {
+    pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef<I>, I::GenericArgsSlice) {
         interner.trait_ref_and_own_args_for_alias(self.def_id, self.args)
     }
 
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index aa285169f276d..6569f3123aa92 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -7,13 +7,15 @@ use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEn
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
 use std::fmt;
 
+pub use self::closure::*;
+use self::TyKind::*;
 use crate::inherent::*;
 use crate::{self as ty, DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx};
 
-use self::TyKind::*;
-
 use rustc_ast_ir::Mutability;
 
+mod closure;
+
 /// Specifies how a trait object is represented.
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
 #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
@@ -514,7 +516,7 @@ impl<I: Interner> AliasTy<I> {
     /// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
     /// then this function would return a `T: StreamingIterator` trait reference and
     /// `['a]` as the own args.
-    pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef<I>, I::OwnItemArgs) {
+    pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef<I>, I::GenericArgsSlice) {
         debug_assert_eq!(self.kind(interner), AliasTyKind::Projection);
         interner.trait_ref_and_own_args_for_alias(self.def_id, self.args)
     }
diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs
new file mode 100644
index 0000000000000..97752934632c4
--- /dev/null
+++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs
@@ -0,0 +1,696 @@
+use std::ops::ControlFlow;
+
+use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
+
+use crate::fold::{shift_region, TypeFoldable, TypeFolder, TypeSuperFoldable};
+use crate::inherent::*;
+use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
+use crate::{self as ty, Interner};
+
+/// A closure can be modeled as a struct that looks like:
+/// ```ignore (illustrative)
+/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U);
+/// ```
+/// where:
+///
+/// - 'l0...'li and T0...Tj are the generic parameters
+///   in scope on the function that defined the closure,
+/// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This
+///   is rather hackily encoded via a scalar type. See
+///   `Ty::to_opt_closure_kind` for details.
+/// - CS represents the *closure signature*, representing as a `fn()`
+///   type. For example, `fn(u32, u32) -> u32` would mean that the closure
+///   implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait
+///   specified above.
+/// - U is a type parameter representing the types of its upvars, tupled up
+///   (borrowed, if appropriate; that is, if a U field represents a by-ref upvar,
+///    and the up-var has the type `Foo`, then that field of U will be `&Foo`).
+///
+/// So, for example, given this function:
+/// ```ignore (illustrative)
+/// fn foo<'a, T>(data: &'a mut T) {
+///      do(|| data.count += 1)
+/// }
+/// ```
+/// the type of the closure would be something like:
+/// ```ignore (illustrative)
+/// struct Closure<'a, T, U>(...U);
+/// ```
+/// Note that the type of the upvar is not specified in the struct.
+/// You may wonder how the impl would then be able to use the upvar,
+/// if it doesn't know it's type? The answer is that the impl is
+/// (conceptually) not fully generic over Closure but rather tied to
+/// instances with the expected upvar types:
+/// ```ignore (illustrative)
+/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> {
+///     ...
+/// }
+/// ```
+/// You can see that the *impl* fully specified the type of the upvar
+/// and thus knows full well that `data` has type `&'b mut &'a mut T`.
+/// (Here, I am assuming that `data` is mut-borrowed.)
+///
+/// Now, the last question you may ask is: Why include the upvar types
+/// in an extra type parameter? The reason for this design is that the
+/// upvar types can reference lifetimes that are internal to the
+/// creating function. In my example above, for example, the lifetime
+/// `'b` represents the scope of the closure itself; this is some
+/// subset of `foo`, probably just the scope of the call to the to
+/// `do()`. If we just had the lifetime/type parameters from the
+/// enclosing function, we couldn't name this lifetime `'b`. Note that
+/// there can also be lifetimes in the types of the upvars themselves,
+/// if one of them happens to be a reference to something that the
+/// creating fn owns.
+///
+/// OK, you say, so why not create a more minimal set of parameters
+/// that just includes the extra lifetime parameters? The answer is
+/// primarily that it would be hard --- we don't know at the time when
+/// we create the closure type what the full types of the upvars are,
+/// nor do we know which are borrowed and which are not. In this
+/// design, we can just supply a fresh type parameter and figure that
+/// out later.
+///
+/// All right, you say, but why include the type parameters from the
+/// original function then? The answer is that codegen may need them
+/// when monomorphizing, and they may not appear in the upvars. A
+/// closure could capture no variables but still make use of some
+/// in-scope type parameter with a bound (e.g., if our example above
+/// had an extra `U: Default`, and the closure called `U::default()`).
+///
+/// There is another reason. This design (implicitly) prohibits
+/// closures from capturing themselves (except via a trait
+/// object). This simplifies closure inference considerably, since it
+/// means that when we infer the kind of a closure or its upvars, we
+/// don't have to handle cycles where the decisions we make for
+/// closure C wind up influencing the decisions we ought to make for
+/// closure C (which would then require fixed point iteration to
+/// handle). Plus it fixes an ICE. :P
+///
+/// ## Coroutines
+///
+/// Coroutines are handled similarly in `CoroutineArgs`. The set of
+/// type parameters is similar, but `CK` and `CS` are replaced by the
+/// following type parameters:
+///
+/// * `GS`: The coroutine's "resume type", which is the type of the
+///   argument passed to `resume`, and the type of `yield` expressions
+///   inside the coroutine.
+/// * `GY`: The "yield type", which is the type of values passed to
+///   `yield` inside the coroutine.
+/// * `GR`: The "return type", which is the type of value returned upon
+///   completion of the coroutine.
+/// * `GW`: The "coroutine witness".
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+pub struct ClosureArgs<I: Interner> {
+    /// Lifetime and type parameters from the enclosing function,
+    /// concatenated with a tuple containing the types of the upvars.
+    ///
+    /// These are separated out because codegen wants to pass them around
+    /// when monomorphizing.
+    pub args: I::GenericArgs,
+}
+
+/// Struct returned by `split()`.
+pub struct ClosureArgsParts<I: Interner> {
+    /// This is the args of the typeck root.
+    pub parent_args: I::GenericArgsSlice,
+    /// Represents the maximum calling capability of the closure.
+    pub closure_kind_ty: I::Ty,
+    /// Captures the closure's signature. This closure signature is "tupled", and
+    /// thus has a peculiar signature of `extern "rust-call" fn((Args, ...)) -> Ty`.
+    pub closure_sig_as_fn_ptr_ty: I::Ty,
+    /// The upvars captured by the closure. Remains an inference variable
+    /// until the upvar analysis, which happens late in HIR typeck.
+    pub tupled_upvars_ty: I::Ty,
+}
+
+impl<I: Interner> ClosureArgs<I> {
+    /// Construct `ClosureArgs` from `ClosureArgsParts`, containing `Args`
+    /// for the closure parent, alongside additional closure-specific components.
+    pub fn new(tcx: I, parts: ClosureArgsParts<I>) -> ClosureArgs<I> {
+        ClosureArgs {
+            args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([
+                parts.closure_kind_ty.into(),
+                parts.closure_sig_as_fn_ptr_ty.into(),
+                parts.tupled_upvars_ty.into(),
+            ])),
+        }
+    }
+
+    /// Divides the closure args into their respective components.
+    /// The ordering assumed here must match that used by `ClosureArgs::new` above.
+    fn split(self) -> ClosureArgsParts<I> {
+        self.args.split_closure_args()
+    }
+
+    /// Returns the generic parameters of the closure's parent.
+    pub fn parent_args(self) -> I::GenericArgsSlice {
+        self.split().parent_args
+    }
+
+    /// Returns an iterator over the list of types of captured paths by the closure.
+    /// In case there was a type error in figuring out the types of the captured path, an
+    /// empty iterator is returned.
+    #[inline]
+    pub fn upvar_tys(self) -> I::Tys {
+        match self.tupled_upvars_ty().kind() {
+            ty::Error(_) => Default::default(),
+            ty::Tuple(tys) => tys,
+            ty::Infer(_) => panic!("upvar_tys called before capture types are inferred"),
+            ty => panic!("Unexpected representation of upvar types tuple {:?}", ty),
+        }
+    }
+
+    /// Returns the tuple type representing the upvars for this closure.
+    #[inline]
+    pub fn tupled_upvars_ty(self) -> I::Ty {
+        self.split().tupled_upvars_ty
+    }
+
+    /// Returns the closure kind for this closure; may return a type
+    /// variable during inference. To get the closure kind during
+    /// inference, use `infcx.closure_kind(args)`.
+    pub fn kind_ty(self) -> I::Ty {
+        self.split().closure_kind_ty
+    }
+
+    /// Returns the `fn` pointer type representing the closure signature for this
+    /// closure.
+    // FIXME(eddyb) this should be unnecessary, as the shallowly resolved
+    // type is known at the time of the creation of `ClosureArgs`,
+    // see `rustc_hir_analysis::check::closure`.
+    pub fn sig_as_fn_ptr_ty(self) -> I::Ty {
+        self.split().closure_sig_as_fn_ptr_ty
+    }
+
+    /// Returns the closure kind for this closure; only usable outside
+    /// of an inference context, because in that context we know that
+    /// there are no type variables.
+    ///
+    /// If you have an inference context, use `infcx.closure_kind()`.
+    pub fn kind(self) -> ty::ClosureKind {
+        self.kind_ty().to_opt_closure_kind().unwrap()
+    }
+
+    /// Extracts the signature from the closure.
+    pub fn sig(self) -> ty::Binder<I, ty::FnSig<I>> {
+        match self.sig_as_fn_ptr_ty().kind() {
+            ty::FnPtr(sig) => sig,
+            ty => panic!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {ty:?}"),
+        }
+    }
+}
+
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+pub struct CoroutineClosureArgs<I: Interner> {
+    pub args: I::GenericArgs,
+}
+
+/// See docs for explanation of how each argument is used.
+///
+/// See [`CoroutineClosureSignature`] for how these arguments are put together
+/// to make a callable [`ty::FnSig`] suitable for typeck and borrowck.
+pub struct CoroutineClosureArgsParts<I: Interner> {
+    /// This is the args of the typeck root.
+    pub parent_args: I::GenericArgsSlice,
+    /// Represents the maximum calling capability of the closure.
+    pub closure_kind_ty: I::Ty,
+    /// Represents all of the relevant parts of the coroutine returned by this
+    /// coroutine-closure. This signature parts type will have the general
+    /// shape of `fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty)`, where
+    /// `resume_ty`, `return_ty`, and `yield_ty` are the respective types for the
+    /// coroutine returned by the coroutine-closure.
+    ///
+    /// Use `coroutine_closure_sig` to break up this type rather than using it
+    /// yourself.
+    pub signature_parts_ty: I::Ty,
+    /// The upvars captured by the closure. Remains an inference variable
+    /// until the upvar analysis, which happens late in HIR typeck.
+    pub tupled_upvars_ty: I::Ty,
+    /// a function pointer that has the shape `for<'env> fn() -> (&'env T, ...)`.
+    /// This allows us to represent the binder of the self-captures of the closure.
+    ///
+    /// For example, if the coroutine returned by the closure borrows `String`
+    /// from the closure's upvars, this will be `for<'env> fn() -> (&'env String,)`,
+    /// while the `tupled_upvars_ty`, representing the by-move version of the same
+    /// captures, will be `(String,)`.
+    pub coroutine_captures_by_ref_ty: I::Ty,
+    /// Witness type returned by the generator produced by this coroutine-closure.
+    pub coroutine_witness_ty: I::Ty,
+}
+
+impl<I: Interner> CoroutineClosureArgs<I> {
+    pub fn new(tcx: I, parts: CoroutineClosureArgsParts<I>) -> CoroutineClosureArgs<I> {
+        CoroutineClosureArgs {
+            args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([
+                parts.closure_kind_ty.into(),
+                parts.signature_parts_ty.into(),
+                parts.tupled_upvars_ty.into(),
+                parts.coroutine_captures_by_ref_ty.into(),
+                parts.coroutine_witness_ty.into(),
+            ])),
+        }
+    }
+
+    fn split(self) -> CoroutineClosureArgsParts<I> {
+        self.args.split_coroutine_closure_args()
+    }
+
+    pub fn parent_args(self) -> I::GenericArgsSlice {
+        self.split().parent_args
+    }
+
+    #[inline]
+    pub fn upvar_tys(self) -> I::Tys {
+        match self.tupled_upvars_ty().kind() {
+            ty::Error(_) => Default::default(),
+            ty::Tuple(..) => self.tupled_upvars_ty().tuple_fields(),
+            ty::Infer(_) => panic!("upvar_tys called before capture types are inferred"),
+            ty => panic!("Unexpected representation of upvar types tuple {:?}", ty),
+        }
+    }
+
+    #[inline]
+    pub fn tupled_upvars_ty(self) -> I::Ty {
+        self.split().tupled_upvars_ty
+    }
+
+    pub fn kind_ty(self) -> I::Ty {
+        self.split().closure_kind_ty
+    }
+
+    pub fn kind(self) -> ty::ClosureKind {
+        self.kind_ty().to_opt_closure_kind().unwrap()
+    }
+
+    pub fn signature_parts_ty(self) -> I::Ty {
+        self.split().signature_parts_ty
+    }
+
+    pub fn coroutine_closure_sig(self) -> ty::Binder<I, CoroutineClosureSignature<I>> {
+        let interior = self.coroutine_witness_ty();
+        let ty::FnPtr(sig) = self.signature_parts_ty().kind() else { panic!() };
+        sig.map_bound(|sig| {
+            let [resume_ty, tupled_inputs_ty] = *sig.inputs() else {
+                panic!();
+            };
+            let [yield_ty, return_ty] = **sig.output().tuple_fields() else { panic!() };
+            CoroutineClosureSignature {
+                interior,
+                tupled_inputs_ty,
+                resume_ty,
+                yield_ty,
+                return_ty,
+                c_variadic: sig.c_variadic,
+                safety: sig.safety,
+                abi: sig.abi,
+            }
+        })
+    }
+
+    pub fn coroutine_captures_by_ref_ty(self) -> I::Ty {
+        self.split().coroutine_captures_by_ref_ty
+    }
+
+    pub fn coroutine_witness_ty(self) -> I::Ty {
+        self.split().coroutine_witness_ty
+    }
+
+    pub fn has_self_borrows(&self) -> bool {
+        match self.coroutine_captures_by_ref_ty().kind() {
+            ty::FnPtr(sig) => sig
+                .skip_binder()
+                .visit_with(&mut HasRegionsBoundAt { binder: ty::INNERMOST })
+                .is_break(),
+            ty::Error(_) => true,
+            _ => panic!(),
+        }
+    }
+}
+
+/// Unlike `has_escaping_bound_vars` or `outermost_exclusive_binder`, this will
+/// detect only regions bound *at* the debruijn index.
+struct HasRegionsBoundAt {
+    binder: ty::DebruijnIndex,
+}
+// FIXME: Could be optimized to not walk into components with no escaping bound vars.
+impl<I: Interner> TypeVisitor<I> for HasRegionsBoundAt {
+    type Result = ControlFlow<()>;
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+        self.binder.shift_in(1);
+        t.super_visit_with(self)?;
+        self.binder.shift_out(1);
+        ControlFlow::Continue(())
+    }
+
+    fn visit_region(&mut self, r: I::Region) -> Self::Result {
+        if matches!(r.kind(), ty::ReBound(binder, _) if self.binder == binder) {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+}
+
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
+pub struct CoroutineClosureSignature<I: Interner> {
+    pub interior: I::Ty,
+    pub tupled_inputs_ty: I::Ty,
+    pub resume_ty: I::Ty,
+    pub yield_ty: I::Ty,
+    pub return_ty: I::Ty,
+
+    // Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types
+    // never actually differ. But we save them rather than recreating them
+    // from scratch just for good measure.
+    /// Always false
+    pub c_variadic: bool,
+    /// Always `Normal` (safe)
+    pub safety: I::Safety,
+    /// Always `RustCall`
+    pub abi: I::Abi,
+}
+
+impl<I: Interner> CoroutineClosureSignature<I> {
+    /// Construct a coroutine from the closure signature. Since a coroutine signature
+    /// is agnostic to the type of generator that is returned (by-ref/by-move),
+    /// the caller must specify what "flavor" of generator that they'd like to
+    /// create. Additionally, they must manually compute the upvars of the closure.
+    ///
+    /// This helper is not really meant to be used directly except for early on
+    /// during typeck, when we want to put inference vars into the kind and upvars tys.
+    /// When the kind and upvars are known, use the other helper functions.
+    pub fn to_coroutine(
+        self,
+        tcx: I,
+        parent_args: I::GenericArgsSlice,
+        coroutine_kind_ty: I::Ty,
+        coroutine_def_id: I::DefId,
+        tupled_upvars_ty: I::Ty,
+    ) -> I::Ty {
+        let coroutine_args = ty::CoroutineArgs::new(
+            tcx,
+            ty::CoroutineArgsParts {
+                parent_args,
+                kind_ty: coroutine_kind_ty,
+                resume_ty: self.resume_ty,
+                yield_ty: self.yield_ty,
+                return_ty: self.return_ty,
+                witness: self.interior,
+                tupled_upvars_ty,
+            },
+        );
+
+        Ty::new_coroutine(tcx, coroutine_def_id, coroutine_args.args)
+    }
+
+    /// Given known upvars and a [`ClosureKind`](ty::ClosureKind), compute the coroutine
+    /// returned by that corresponding async fn trait.
+    ///
+    /// This function expects the upvars to have been computed already, and doesn't check
+    /// that the `ClosureKind` is actually supported by the coroutine-closure.
+    pub fn to_coroutine_given_kind_and_upvars(
+        self,
+        tcx: I,
+        parent_args: I::GenericArgsSlice,
+        coroutine_def_id: I::DefId,
+        goal_kind: ty::ClosureKind,
+        env_region: I::Region,
+        closure_tupled_upvars_ty: I::Ty,
+        coroutine_captures_by_ref_ty: I::Ty,
+    ) -> I::Ty {
+        let tupled_upvars_ty = Self::tupled_upvars_by_closure_kind(
+            tcx,
+            goal_kind,
+            self.tupled_inputs_ty,
+            closure_tupled_upvars_ty,
+            coroutine_captures_by_ref_ty,
+            env_region,
+        );
+
+        self.to_coroutine(
+            tcx,
+            parent_args,
+            Ty::from_coroutine_closure_kind(tcx, goal_kind),
+            coroutine_def_id,
+            tupled_upvars_ty,
+        )
+    }
+
+    /// Compute the tupled upvars that a coroutine-closure's output coroutine
+    /// would return for the given `ClosureKind`.
+    ///
+    /// When `ClosureKind` is `FnMut`/`Fn`, then this will use the "captures by ref"
+    /// to return a set of upvars which are borrowed with the given `env_region`.
+    ///
+    /// This ensures that the `AsyncFn::call` will return a coroutine whose upvars'
+    /// lifetimes are related to the lifetime of the borrow on the closure made for
+    /// the call. This allows borrowck to enforce the self-borrows correctly.
+    pub fn tupled_upvars_by_closure_kind(
+        tcx: I,
+        kind: ty::ClosureKind,
+        tupled_inputs_ty: I::Ty,
+        closure_tupled_upvars_ty: I::Ty,
+        coroutine_captures_by_ref_ty: I::Ty,
+        env_region: I::Region,
+    ) -> I::Ty {
+        match kind {
+            ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
+                let ty::FnPtr(sig) = coroutine_captures_by_ref_ty.kind() else {
+                    panic!();
+                };
+                let coroutine_captures_by_ref_ty =
+                    sig.output().skip_binder().fold_with(&mut FoldEscapingRegions {
+                        interner: tcx,
+                        region: env_region,
+                        debruijn: ty::INNERMOST,
+                    });
+                Ty::new_tup_from_iter(
+                    tcx,
+                    tupled_inputs_ty
+                        .tuple_fields()
+                        .into_iter()
+                        .chain(coroutine_captures_by_ref_ty.tuple_fields()),
+                )
+            }
+            ty::ClosureKind::FnOnce => Ty::new_tup_from_iter(
+                tcx,
+                tupled_inputs_ty
+                    .tuple_fields()
+                    .into_iter()
+                    .chain(closure_tupled_upvars_ty.tuple_fields()),
+            ),
+        }
+    }
+}
+
+/// Instantiates a `for<'env> ...` binder with a specific region.
+// FIXME(async_closures): Get rid of this in favor of `BoundVarReplacerDelegate`
+// when that is uplifted.
+struct FoldEscapingRegions<I: Interner> {
+    interner: I,
+    debruijn: ty::DebruijnIndex,
+    region: I::Region,
+}
+
+impl<I: Interner> TypeFolder<I> for FoldEscapingRegions<I> {
+    fn interner(&self) -> I {
+        self.interner
+    }
+
+    fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
+    where
+        T: TypeFoldable<I>,
+    {
+        self.debruijn.shift_in(1);
+        let result = t.super_fold_with(self);
+        self.debruijn.shift_out(1);
+        result
+    }
+
+    fn fold_region(&mut self, r: <I as Interner>::Region) -> <I as Interner>::Region {
+        if let ty::ReBound(debruijn, _) = r.kind() {
+            assert!(
+                debruijn <= self.debruijn,
+                "cannot instantiate binder with escaping bound vars"
+            );
+            if self.debruijn == debruijn {
+                shift_region(self.interner, self.region, self.debruijn.as_u32())
+            } else {
+                r
+            }
+        } else {
+            r
+        }
+    }
+}
+
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
+pub struct GenSig<I: Interner> {
+    pub resume_ty: I::Ty,
+    pub yield_ty: I::Ty,
+    pub return_ty: I::Ty,
+}
+
+/// Similar to `ClosureArgs`; see the above documentation for more.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
+pub struct CoroutineArgs<I: Interner> {
+    pub args: I::GenericArgs,
+}
+
+pub struct CoroutineArgsParts<I: Interner> {
+    /// This is the args of the typeck root.
+    pub parent_args: I::GenericArgsSlice,
+
+    /// The coroutines returned by a coroutine-closure's `AsyncFnOnce`/`AsyncFnMut`
+    /// implementations must be distinguished since the former takes the closure's
+    /// upvars by move, and the latter takes the closure's upvars by ref.
+    ///
+    /// This field distinguishes these fields so that codegen can select the right
+    /// body for the coroutine. This has the same type representation as the closure
+    /// kind: `i8`/`i16`/`i32`.
+    ///
+    /// For regular coroutines, this field will always just be `()`.
+    pub kind_ty: I::Ty,
+
+    pub resume_ty: I::Ty,
+    pub yield_ty: I::Ty,
+    pub return_ty: I::Ty,
+
+    /// The interior type of the coroutine.
+    /// Represents all types that are stored in locals
+    /// in the coroutine's body.
+    pub witness: I::Ty,
+
+    /// The upvars captured by the closure. Remains an inference variable
+    /// until the upvar analysis, which happens late in HIR typeck.
+    pub tupled_upvars_ty: I::Ty,
+}
+
+impl<I: Interner> CoroutineArgs<I> {
+    /// Construct `CoroutineArgs` from `CoroutineArgsParts`, containing `Args`
+    /// for the coroutine parent, alongside additional coroutine-specific components.
+    pub fn new(tcx: I, parts: CoroutineArgsParts<I>) -> CoroutineArgs<I> {
+        CoroutineArgs {
+            args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([
+                parts.kind_ty.into(),
+                parts.resume_ty.into(),
+                parts.yield_ty.into(),
+                parts.return_ty.into(),
+                parts.witness.into(),
+                parts.tupled_upvars_ty.into(),
+            ])),
+        }
+    }
+
+    /// Divides the coroutine args into their respective components.
+    /// The ordering assumed here must match that used by `CoroutineArgs::new` above.
+    fn split(self) -> CoroutineArgsParts<I> {
+        self.args.split_coroutine_args()
+    }
+
+    /// Returns the generic parameters of the coroutine's parent.
+    pub fn parent_args(self) -> I::GenericArgsSlice {
+        self.split().parent_args
+    }
+
+    // Returns the kind of the coroutine. See docs on the `kind_ty` field.
+    pub fn kind_ty(self) -> I::Ty {
+        self.split().kind_ty
+    }
+
+    /// This describes the types that can be contained in a coroutine.
+    /// It will be a type variable initially and unified in the last stages of typeck of a body.
+    /// It contains a tuple of all the types that could end up on a coroutine frame.
+    /// The state transformation MIR pass may only produce layouts which mention types
+    /// in this tuple. Upvars are not counted here.
+    pub fn witness(self) -> I::Ty {
+        self.split().witness
+    }
+
+    /// Returns an iterator over the list of types of captured paths by the coroutine.
+    /// In case there was a type error in figuring out the types of the captured path, an
+    /// empty iterator is returned.
+    #[inline]
+    pub fn upvar_tys(self) -> I::Tys {
+        match self.tupled_upvars_ty().kind() {
+            ty::Error(_) => Default::default(),
+            ty::Tuple(tys) => tys,
+            ty::Infer(_) => panic!("upvar_tys called before capture types are inferred"),
+            ty => panic!("Unexpected representation of upvar types tuple {:?}", ty),
+        }
+    }
+
+    /// Returns the tuple type representing the upvars for this coroutine.
+    #[inline]
+    pub fn tupled_upvars_ty(self) -> I::Ty {
+        self.split().tupled_upvars_ty
+    }
+
+    /// Returns the type representing the resume type of the coroutine.
+    pub fn resume_ty(self) -> I::Ty {
+        self.split().resume_ty
+    }
+
+    /// Returns the type representing the yield type of the coroutine.
+    pub fn yield_ty(self) -> I::Ty {
+        self.split().yield_ty
+    }
+
+    /// Returns the type representing the return type of the coroutine.
+    pub fn return_ty(self) -> I::Ty {
+        self.split().return_ty
+    }
+
+    /// Returns the "coroutine signature", which consists of its resume, yield
+    /// and return types.
+    pub fn sig(self) -> GenSig<I> {
+        let parts = self.split();
+        GenSig { resume_ty: parts.resume_ty, yield_ty: parts.yield_ty, return_ty: parts.return_ty }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index b58018ca0353b..48c4c4206fe88 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -10,7 +10,7 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{
     self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty,
-    TypeVisitableExt, TypeckResults,
+    TypeVisitableExt, TypeckResults, TyCtxt,
 };
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
@@ -240,7 +240,7 @@ fn check_inputs(
         })
 }
 
-fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<'tcx>, call_sig: FnSig<'_>) -> bool {
+fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, call_sig: FnSig<'_>) -> bool {
     call_sig.safety == Safety::Safe
         && !has_late_bound_to_non_late_bound_regions(
             cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(),