about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/utils.rs2
-rw-r--r--compiler/rustc_infer/src/infer/outlives/components.rs266
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs1
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs4
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs6
-rw-r--r--compiler/rustc_infer/src/traits/util.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs7
-rw-r--r--compiler/rustc_middle/src/ty/walk.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs4
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs8
-rw-r--r--compiler/rustc_type_ir/src/interner.rs2
-rw-r--r--compiler/rustc_type_ir/src/lib.rs1
-rw-r--r--compiler/rustc_type_ir/src/outlives.rs335
-rw-r--r--tests/ui/async-await/in-trait/async-generics-and-bounds.stderr12
-rw-r--r--tests/ui/async-await/in-trait/async-generics.stderr12
15 files changed, 373 insertions, 306 deletions
diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs
index 5086c2af3f6..08015c28a26 100644
--- a/compiler/rustc_hir_analysis/src/outlives/utils.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs
@@ -1,9 +1,9 @@
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
 use rustc_middle::ty::{self, Region, Ty, TyCtxt};
 use rustc_middle::ty::{GenericArg, GenericArgKind};
 use rustc_middle::{bug, span_bug};
 use rustc_span::Span;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::smallvec;
 
 /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs
deleted file mode 100644
index 6bab3ad6ba3..00000000000
--- a/compiler/rustc_infer/src/infer/outlives/components.rs
+++ /dev/null
@@ -1,266 +0,0 @@
-// The outlines relation `T: 'a` or `'a: 'b`. This code frequently
-// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
-// RFC for reference.
-
-use rustc_data_structures::sso::SsoHashSet;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::ty::{GenericArg, GenericArgKind};
-use smallvec::{smallvec, SmallVec};
-
-#[derive(Debug)]
-pub enum Component<'tcx> {
-    Region(ty::Region<'tcx>),
-    Param(ty::ParamTy),
-    Placeholder(ty::PlaceholderType),
-    UnresolvedInferenceVariable(ty::InferTy),
-
-    // Projections like `T::Foo` are tricky because a constraint like
-    // `T::Foo: 'a` can be satisfied in so many ways. There may be a
-    // where-clause that says `T::Foo: 'a`, or the defining trait may
-    // include a bound like `type Foo: 'static`, or -- in the most
-    // conservative way -- we can prove that `T: 'a` (more generally,
-    // that all components in the projection outlive `'a`). This code
-    // is not in a position to judge which is the best technique, so
-    // we just product the projection as a component and leave it to
-    // the consumer to decide (but see `EscapingProjection` below).
-    Alias(ty::AliasTy<'tcx>),
-
-    // In the case where a projection has escaping regions -- meaning
-    // regions bound within the type itself -- we always use
-    // the most conservative rule, which requires that all components
-    // outlive the bound. So for example if we had a type like this:
-    //
-    //     for<'a> Trait1<  <T as Trait2<'a,'b>>::Foo  >
-    //                      ~~~~~~~~~~~~~~~~~~~~~~~~~
-    //
-    // then the inner projection (underlined) has an escaping region
-    // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
-    // outlives `'b: 'c`, and we don't consider whether the trait
-    // declares that `Foo: 'static` etc. Therefore, we just return the
-    // free components of such a projection (in this case, `'b`).
-    //
-    // However, in the future, we may want to get smarter, and
-    // actually return a "higher-ranked projection" here. Therefore,
-    // we mark that these components are part of an escaping
-    // projection, so that implied bounds code can avoid relying on
-    // them. This gives us room to improve the regionck reasoning in
-    // the future without breaking backwards compat.
-    EscapingAlias(Vec<Component<'tcx>>),
-}
-
-/// Push onto `out` all the things that must outlive `'a` for the condition
-/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
-pub fn push_outlives_components<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty0: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-) {
-    let mut visited = SsoHashSet::new();
-    compute_components(tcx, ty0, out, &mut visited);
-    debug!("components({:?}) = {:?}", ty0, out);
-}
-
-fn compute_components<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    // Descend through the types, looking for the various "base"
-    // components and collecting them into `out`. This is not written
-    // with `collect()` because of the need to sometimes skip subtrees
-    // in the `subtys` iterator (e.g., when encountering a
-    // projection).
-    match *ty.kind() {
-            ty::FnDef(_, args) => {
-                // HACK(eddyb) ignore lifetimes found shallowly in `args`.
-                // This is inconsistent with `ty::Adt` (including all args)
-                // and with `ty::Closure` (ignoring all args other than
-                // upvars, of which a `ty::FnDef` doesn't have any), but
-                // consistent with previous (accidental) behavior.
-                // See https://github.com/rust-lang/rust/issues/70917
-                // for further background and discussion.
-                for child in args {
-                    match child.unpack() {
-                        GenericArgKind::Type(ty) => {
-                            compute_components(tcx, ty, out, visited);
-                        }
-                        GenericArgKind::Lifetime(_) => {}
-                        GenericArgKind::Const(_) => {
-                            compute_components_recursive(tcx, child, out, visited);
-                        }
-                    }
-                }
-            }
-
-            ty::Pat(element, _) |
-            ty::Array(element, _) => {
-                // Don't look into the len const as it doesn't affect regions
-                compute_components(tcx, element, out, visited);
-            }
-
-            ty::Closure(_, args) => {
-                let tupled_ty = args.as_closure().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-            }
-
-            ty::CoroutineClosure(_, args) => {
-                let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-            }
-
-            ty::Coroutine(_, args) => {
-                // Same as the closure case
-                let tupled_ty = args.as_coroutine().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-
-                // We ignore regions in the coroutine interior as we don't
-                // want these to affect region inference
-            }
-
-            // All regions are bound inside a witness
-            ty::CoroutineWitness(..) => (),
-
-            // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
-            // is implied by the environment is done in regionck.
-            ty::Param(p) => {
-                out.push(Component::Param(p));
-            }
-
-            ty::Placeholder(p) => {
-                out.push(Component::Placeholder(p));
-            }
-
-            // For projections, we prefer to generate an obligation like
-            // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
-            // regionck more ways to prove that it holds. However,
-            // regionck is not (at least currently) prepared to deal with
-            // higher-ranked regions that may appear in the
-            // trait-ref. Therefore, if we see any higher-ranked regions,
-            // we simply fallback to the most restrictive rule, which
-            // requires that `Pi: 'a` for all `i`.
-            ty::Alias(_, alias_ty) => {
-                if !alias_ty.has_escaping_bound_vars() {
-                    // best case: no escaping regions, so push the
-                    // projection and skip the subtree (thus generating no
-                    // constraints for Pi). This defers the choice between
-                    // the rules OutlivesProjectionEnv,
-                    // OutlivesProjectionTraitDef, and
-                    // OutlivesProjectionComponents to regionck.
-                    out.push(Component::Alias(alias_ty));
-                } else {
-                    // fallback case: hard code
-                    // OutlivesProjectionComponents. Continue walking
-                    // through and constrain Pi.
-                    let mut subcomponents = smallvec![];
-                    let mut subvisited = SsoHashSet::new();
-                    compute_alias_components_recursive(tcx, ty, &mut subcomponents, &mut subvisited);
-                    out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
-                }
-            }
-
-            // We assume that inference variables are fully resolved.
-            // So, if we encounter an inference variable, just record
-            // the unresolved variable as a component.
-            ty::Infer(infer_ty) => {
-                out.push(Component::UnresolvedInferenceVariable(infer_ty));
-            }
-
-            // Most types do not introduce any region binders, nor
-            // involve any other subtle cases, and so the WF relation
-            // simply constraints any regions referenced directly by
-            // the type and then visits the types that are lexically
-            // contained within. (The comments refer to relevant rules
-            // from RFC1214.)
-            ty::Bool |            // OutlivesScalar
-            ty::Char |            // OutlivesScalar
-            ty::Int(..) |         // OutlivesScalar
-            ty::Uint(..) |        // OutlivesScalar
-            ty::Float(..) |       // OutlivesScalar
-            ty::Never |           // ...
-            ty::Adt(..) |         // OutlivesNominalType
-            ty::Foreign(..) |     // OutlivesNominalType
-            ty::Str |             // OutlivesScalar (ish)
-            ty::Slice(..) |       // ...
-            ty::RawPtr(..) |      // ...
-            ty::Ref(..) |         // OutlivesReference
-            ty::Tuple(..) |       // ...
-            ty::FnPtr(_) |        // OutlivesFunction (*)
-            ty::Dynamic(..) |     // OutlivesObject, OutlivesFragment (*)
-            ty::Bound(..) |
-            ty::Error(_) => {
-                // (*) Function pointers and trait objects are both binders.
-                // In the RFC, this means we would add the bound regions to
-                // the "bound regions list". In our representation, no such
-                // list is maintained explicitly, because bound regions
-                // themselves can be readily identified.
-                compute_components_recursive(tcx, ty.into(), out, visited);
-            }
-        }
-}
-
-/// Collect [Component]s for *all* the args of `parent`.
-///
-/// This should not be used to get the components of `parent` itself.
-/// Use [push_outlives_components] instead.
-pub(super) fn compute_alias_components_recursive<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    alias_ty: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
-        unreachable!("can only call `compute_alias_components_recursive` on an alias type")
-    };
-    let opt_variances = if *kind == ty::Opaque { tcx.variances_of(alias_ty.def_id) } else { &[] };
-    for (index, child) in alias_ty.args.iter().enumerate() {
-        if opt_variances.get(index) == Some(&ty::Bivariant) {
-            continue;
-        }
-        if !visited.insert(child) {
-            continue;
-        }
-        match child.unpack() {
-            GenericArgKind::Type(ty) => {
-                compute_components(tcx, ty, out, visited);
-            }
-            GenericArgKind::Lifetime(lt) => {
-                // Ignore higher ranked regions.
-                if !lt.is_bound() {
-                    out.push(Component::Region(lt));
-                }
-            }
-            GenericArgKind::Const(_) => {
-                compute_components_recursive(tcx, child, out, visited);
-            }
-        }
-    }
-}
-
-/// Collect [Component]s for *all* the args of `parent`.
-///
-/// This should not be used to get the components of `parent` itself.
-/// Use [push_outlives_components] instead.
-fn compute_components_recursive<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    parent: GenericArg<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    for child in parent.walk_shallow(visited) {
-        match child.unpack() {
-            GenericArgKind::Type(ty) => {
-                compute_components(tcx, ty, out, visited);
-            }
-            GenericArgKind::Lifetime(lt) => {
-                // Ignore higher ranked regions.
-                if !lt.is_bound() {
-                    out.push(Component::Region(lt));
-                }
-            }
-            GenericArgKind::Const(_) => {
-                compute_components_recursive(tcx, child, out, visited);
-            }
-        }
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index 48d006e7fbc..89ff4604560 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -8,7 +8,6 @@ use crate::infer::lexical_region_resolve;
 use rustc_middle::traits::query::{NoSolution, OutlivesBound};
 use rustc_middle::ty;
 
-pub mod components;
 pub mod env;
 pub mod for_liveness;
 pub mod obligations;
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 32c790523b6..9bb5f775e4a 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -59,7 +59,6 @@
 //! might later infer `?U` to something like `&'b u32`, which would
 //! imply that `'b: 'a`.
 
-use crate::infer::outlives::components::{push_outlives_components, Component};
 use crate::infer::outlives::env::RegionBoundPairs;
 use crate::infer::outlives::verify::VerifyBoundCx;
 use crate::infer::resolve::OpportunisticRegionResolver;
@@ -75,6 +74,7 @@ use rustc_middle::ty::{
 };
 use rustc_middle::ty::{GenericArgKind, PolyTypeOutlivesPredicate};
 use rustc_span::DUMMY_SP;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::smallvec;
 
 use super::env::OutlivesEnvironment;
@@ -291,7 +291,7 @@ where
     fn components_must_outlive(
         &mut self,
         origin: infer::SubregionOrigin<'tcx>,
-        components: &[Component<'tcx>],
+        components: &[Component<TyCtxt<'tcx>>],
         region: ty::Region<'tcx>,
         category: ConstraintCategory<'tcx>,
     ) {
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index 7e977b9b954..ad102dfcc1f 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -1,10 +1,10 @@
-use crate::infer::outlives::components::{compute_alias_components_recursive, Component};
 use crate::infer::outlives::env::RegionBoundPairs;
 use crate::infer::region_constraints::VerifyIfEq;
 use crate::infer::{GenericKind, VerifyBound};
 use rustc_data_structures::sso::SsoHashSet;
 use rustc_middle::ty::GenericArg;
 use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt};
+use rustc_type_ir::outlives::{compute_alias_components_recursive, Component};
 
 use smallvec::smallvec;
 
@@ -139,7 +139,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
 
     fn bound_from_components(
         &self,
-        components: &[Component<'tcx>],
+        components: &[Component<TyCtxt<'tcx>>],
         visited: &mut SsoHashSet<GenericArg<'tcx>>,
     ) -> VerifyBound<'tcx> {
         let mut bounds = components
@@ -158,7 +158,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
 
     fn bound_from_single_component(
         &self,
-        component: &Component<'tcx>,
+        component: &Component<TyCtxt<'tcx>>,
         visited: &mut SsoHashSet<GenericArg<'tcx>>,
     ) -> VerifyBound<'tcx> {
         match *component {
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index 18d36c3a5df..b269bfcbfeb 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -1,12 +1,12 @@
 use smallvec::smallvec;
 
-use crate::infer::outlives::components::{push_outlives_components, Component};
 use crate::traits::{self, Obligation, ObligationCauseCode, PredicateObligation};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::ty::ToPolyTraitRef;
 use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 
 pub fn anonymize_predicate<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index bf834ef7607..98f35b6b8ab 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -54,6 +54,13 @@ pub struct Expr<'tcx> {
     pub kind: ExprKind,
     args: ty::GenericArgsRef<'tcx>,
 }
+
+impl<'tcx> rustc_type_ir::inherent::ExprConst<TyCtxt<'tcx>> for Expr<'tcx> {
+    fn args(self) -> ty::GenericArgsRef<'tcx> {
+        self.args
+    }
+}
+
 impl<'tcx> Expr<'tcx> {
     pub fn new_binop(
         tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index e0f204a687f..efcaf89081f 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -78,23 +78,6 @@ impl<'tcx> GenericArg<'tcx> {
     pub fn walk(self) -> TypeWalker<'tcx> {
         TypeWalker::new(self)
     }
-
-    /// Iterator that walks the immediate children of `self`. Hence
-    /// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]`
-    /// (but not `i32`, like `walk`).
-    ///
-    /// Iterator only walks items once.
-    /// It accepts visited set, updates it with all visited types
-    /// and skips any types that are already there.
-    pub fn walk_shallow(
-        self,
-        visited: &mut SsoHashSet<GenericArg<'tcx>>,
-    ) -> impl Iterator<Item = GenericArg<'tcx>> {
-        let mut stack = SmallVec::new();
-        push_inner(&mut stack, self);
-        stack.retain(|a| visited.insert(*a));
-        stack.into_iter()
-    }
 }
 
 impl<'tcx> Ty<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index b38841db923..8525215a3bc 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -3,7 +3,6 @@ use crate::traits::wf;
 use crate::traits::ObligationCtxt;
 
 use rustc_infer::infer::canonical::Canonical;
-use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::traits::query::OutlivesBound;
 use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
@@ -12,6 +11,7 @@ use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::DUMMY_SP;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::{smallvec, SmallVec};
 
 #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
@@ -284,7 +284,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
 /// those relationships.
 fn implied_bounds_from_components<'tcx>(
     sub_region: ty::Region<'tcx>,
-    sup_components: SmallVec<[Component<'tcx>; 4]>,
+    sup_components: SmallVec<[Component<TyCtxt<'tcx>>; 4]>,
 ) -> Vec<OutlivesBound<'tcx>> {
     sup_components
         .into_iter()
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index ffe16964ae5..68c2575258d 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -232,6 +232,10 @@ pub trait Region<I: Interner<Region = Self>>:
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
     fn new_static(interner: I) -> Self;
+
+    fn is_bound(self) -> bool {
+        matches!(self.kind(), ty::ReBound(..))
+    }
 }
 
 pub trait Const<I: Interner<Const = Self>>:
@@ -272,6 +276,10 @@ pub trait Const<I: Interner<Const = Self>>:
     }
 }
 
+pub trait ExprConst<I: Interner<ExprConst = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
+    fn args(self) -> I::GenericArgs;
+}
+
 pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
     fn count(&self) -> usize;
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index db97bdca382..61a42fb7262 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -109,7 +109,7 @@ pub trait Interner:
     type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
     type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
     type ValueConst: Copy + Debug + Hash + Eq;
-    type ExprConst: Copy + Debug + Hash + Eq + Relate<Self>;
+    type ExprConst: ExprConst<Self>;
 
     // Kinds of regions
     type Region: Region<Self>;
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index c57ac74cdfd..2a909b06baf 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -27,6 +27,7 @@ pub mod inherent;
 pub mod ir_print;
 pub mod lang_items;
 pub mod lift;
+pub mod outlives;
 pub mod relate;
 pub mod solve;
 
diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs
new file mode 100644
index 00000000000..61dfa2643d8
--- /dev/null
+++ b/compiler/rustc_type_ir/src/outlives.rs
@@ -0,0 +1,335 @@
+//! The outlives relation `T: 'a` or `'a: 'b`. This code frequently
+//! refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
+//! RFC for reference.
+
+use smallvec::{smallvec, SmallVec};
+use tracing::debug;
+
+use crate::data_structures::SsoHashSet;
+use crate::inherent::*;
+use crate::visit::TypeVisitableExt as _;
+use crate::{self as ty, Interner};
+
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = ""))]
+pub enum Component<I: Interner> {
+    Region(I::Region),
+    Param(I::ParamTy),
+    Placeholder(I::PlaceholderTy),
+    UnresolvedInferenceVariable(ty::InferTy),
+
+    // Projections like `T::Foo` are tricky because a constraint like
+    // `T::Foo: 'a` can be satisfied in so many ways. There may be a
+    // where-clause that says `T::Foo: 'a`, or the defining trait may
+    // include a bound like `type Foo: 'static`, or -- in the most
+    // conservative way -- we can prove that `T: 'a` (more generally,
+    // that all components in the projection outlive `'a`). This code
+    // is not in a position to judge which is the best technique, so
+    // we just product the projection as a component and leave it to
+    // the consumer to decide (but see `EscapingProjection` below).
+    Alias(ty::AliasTy<I>),
+
+    // In the case where a projection has escaping regions -- meaning
+    // regions bound within the type itself -- we always use
+    // the most conservative rule, which requires that all components
+    // outlive the bound. So for example if we had a type like this:
+    //
+    //     for<'a> Trait1<  <T as Trait2<'a,'b>>::Foo  >
+    //                      ~~~~~~~~~~~~~~~~~~~~~~~~~
+    //
+    // then the inner projection (underlined) has an escaping region
+    // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
+    // outlives `'b: 'c`, and we don't consider whether the trait
+    // declares that `Foo: 'static` etc. Therefore, we just return the
+    // free components of such a projection (in this case, `'b`).
+    //
+    // However, in the future, we may want to get smarter, and
+    // actually return a "higher-ranked projection" here. Therefore,
+    // we mark that these components are part of an escaping
+    // projection, so that implied bounds code can avoid relying on
+    // them. This gives us room to improve the regionck reasoning in
+    // the future without breaking backwards compat.
+    EscapingAlias(Vec<Component<I>>),
+}
+
+/// Push onto `out` all the things that must outlive `'a` for the condition
+/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
+pub fn push_outlives_components<I: Interner>(
+    tcx: I,
+    ty0: I::Ty,
+    out: &mut SmallVec<[Component<I>; 4]>,
+) {
+    let mut visited = SsoHashSet::new();
+    compute_components_for_ty(tcx, ty0, out, &mut visited);
+    debug!("components({:?}) = {:?}", ty0, out);
+}
+
+fn compute_components_for_arg<I: Interner>(
+    tcx: I,
+    arg: I::GenericArg,
+    out: &mut SmallVec<[Component<I>; 4]>,
+    visited: &mut SsoHashSet<I::GenericArg>,
+) {
+    match arg.kind() {
+        ty::GenericArgKind::Type(ty) => {
+            compute_components_for_ty(tcx, ty, out, visited);
+        }
+        ty::GenericArgKind::Lifetime(lt) => {
+            compute_components_for_lt(lt, out);
+        }
+        ty::GenericArgKind::Const(ct) => {
+            compute_components_for_const(tcx, ct, out, visited);
+        }
+    }
+}
+
+fn compute_components_for_ty<I: Interner>(
+    tcx: I,
+    ty: I::Ty,
+    out: &mut SmallVec<[Component<I>; 4]>,
+    visited: &mut SsoHashSet<I::GenericArg>,
+) {
+    if !visited.insert(ty.into()) {
+        return;
+    }
+    // Descend through the types, looking for the various "base"
+    // components and collecting them into `out`. This is not written
+    // with `collect()` because of the need to sometimes skip subtrees
+    // in the `subtys` iterator (e.g., when encountering a
+    // projection).
+    match ty.kind() {
+        ty::FnDef(_, args) => {
+            // HACK(eddyb) ignore lifetimes found shallowly in `args`.
+            // This is inconsistent with `ty::Adt` (including all args)
+            // and with `ty::Closure` (ignoring all args other than
+            // upvars, of which a `ty::FnDef` doesn't have any), but
+            // consistent with previous (accidental) behavior.
+            // See https://github.com/rust-lang/rust/issues/70917
+            // for further background and discussion.
+            for child in args.iter() {
+                match child.kind() {
+                    ty::GenericArgKind::Type(ty) => {
+                        compute_components_for_ty(tcx, ty, out, visited);
+                    }
+                    ty::GenericArgKind::Lifetime(_) => {}
+                    ty::GenericArgKind::Const(ct) => {
+                        compute_components_for_const(tcx, ct, out, visited);
+                    }
+                }
+            }
+        }
+
+        ty::Pat(element, _) | ty::Array(element, _) => {
+            compute_components_for_ty(tcx, element, out, visited);
+        }
+
+        ty::Closure(_, args) => {
+            let tupled_ty = args.as_closure().tupled_upvars_ty();
+            compute_components_for_ty(tcx, tupled_ty, out, visited);
+        }
+
+        ty::CoroutineClosure(_, args) => {
+            let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
+            compute_components_for_ty(tcx, tupled_ty, out, visited);
+        }
+
+        ty::Coroutine(_, args) => {
+            // Same as the closure case
+            let tupled_ty = args.as_coroutine().tupled_upvars_ty();
+            compute_components_for_ty(tcx, tupled_ty, out, visited);
+
+            // We ignore regions in the coroutine interior as we don't
+            // want these to affect region inference
+        }
+
+        // All regions are bound inside a witness, and we don't emit
+        // higher-ranked outlives components currently.
+        ty::CoroutineWitness(..) => {},
+
+        // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
+        // is implied by the environment is done in regionck.
+        ty::Param(p) => {
+            out.push(Component::Param(p));
+        }
+
+        ty::Placeholder(p) => {
+            out.push(Component::Placeholder(p));
+        }
+
+        // For projections, we prefer to generate an obligation like
+        // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
+        // regionck more ways to prove that it holds. However,
+        // regionck is not (at least currently) prepared to deal with
+        // higher-ranked regions that may appear in the
+        // trait-ref. Therefore, if we see any higher-ranked regions,
+        // we simply fallback to the most restrictive rule, which
+        // requires that `Pi: 'a` for all `i`.
+        ty::Alias(_, alias_ty) => {
+            if !alias_ty.has_escaping_bound_vars() {
+                // best case: no escaping regions, so push the
+                // projection and skip the subtree (thus generating no
+                // constraints for Pi). This defers the choice between
+                // the rules OutlivesProjectionEnv,
+                // OutlivesProjectionTraitDef, and
+                // OutlivesProjectionComponents to regionck.
+                out.push(Component::Alias(alias_ty));
+            } else {
+                // fallback case: hard code
+                // OutlivesProjectionComponents. Continue walking
+                // through and constrain Pi.
+                let mut subcomponents = smallvec![];
+                let mut subvisited = SsoHashSet::new();
+                compute_alias_components_recursive(tcx, ty, &mut subcomponents, &mut subvisited);
+                out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
+            }
+        }
+
+        // We assume that inference variables are fully resolved.
+        // So, if we encounter an inference variable, just record
+        // the unresolved variable as a component.
+        ty::Infer(infer_ty) => {
+            out.push(Component::UnresolvedInferenceVariable(infer_ty));
+        }
+
+        // Most types do not introduce any region binders, nor
+        // involve any other subtle cases, and so the WF relation
+        // simply constraints any regions referenced directly by
+        // the type and then visits the types that are lexically
+        // contained within. (The comments refer to relevant rules
+        // from RFC1214.)
+
+        ty::Bool |            // OutlivesScalar
+        ty::Char |            // OutlivesScalar
+        ty::Int(..) |         // OutlivesScalar
+        ty::Uint(..) |        // OutlivesScalar
+        ty::Float(..) |       // OutlivesScalar
+        ty::Never |           // OutlivesScalar
+        ty::Foreign(..) |     // OutlivesNominalType
+        ty::Str |             // OutlivesScalar (ish)
+        ty::Bound(..) |
+        ty::Error(_) => {
+            // Trivial.
+        }
+
+        // OutlivesNominalType
+        ty::Adt(_, args) => {
+            for arg in args.iter() {
+                compute_components_for_arg(tcx, arg, out, visited);
+            }
+        }
+
+        // OutlivesNominalType
+        ty::Slice(ty) |
+        ty::RawPtr(ty, _) => {
+            compute_components_for_ty(tcx, ty, out, visited);
+        }
+        ty::Tuple(tys) => {
+            for ty in tys.iter() {
+                compute_components_for_ty(tcx, ty, out, visited);
+            }
+        }
+
+        // OutlivesReference
+        ty::Ref(lt, ty, _) => {
+            compute_components_for_lt(lt, out);
+            compute_components_for_ty(tcx, ty, out, visited);
+        }
+
+        ty::Dynamic(preds, lt, _) => {
+            compute_components_for_lt(lt, out);
+            for pred in preds.iter() {
+                match pred.skip_binder() {
+                    ty::ExistentialPredicate::Trait(tr) => {
+                        for arg in tr.args.iter() {
+                            compute_components_for_arg(tcx, arg, out, visited);
+                        }
+                    }
+                    ty::ExistentialPredicate::Projection(proj) => {
+                        for arg in proj.args.iter() {
+                            compute_components_for_arg(tcx, arg, out, visited);
+                        }
+                        match proj.term.kind() {
+                            ty::TermKind::Ty(ty) => {
+                                compute_components_for_ty(tcx, ty, out, visited)
+                            }
+                            ty::TermKind::Const(ct) => {
+                                compute_components_for_const(tcx, ct, out, visited)
+                            }
+                        }
+                    }
+                    ty::ExistentialPredicate::AutoTrait(..) => {}
+                }
+            }
+        }
+
+        ty::FnPtr(sig) => {
+            for ty in sig.skip_binder().inputs_and_output.iter() {
+                compute_components_for_ty(tcx, ty, out, visited);
+            }
+        }
+    }
+}
+
+/// Collect [Component]s for *all* the args of `parent`.
+///
+/// This should not be used to get the components of `parent` itself.
+/// Use [push_outlives_components] instead.
+pub fn compute_alias_components_recursive<I: Interner>(
+    tcx: I,
+    alias_ty: I::Ty,
+    out: &mut SmallVec<[Component<I>; 4]>,
+    visited: &mut SsoHashSet<I::GenericArg>,
+) {
+    let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
+        unreachable!("can only call `compute_alias_components_recursive` on an alias type")
+    };
+
+    let opt_variances =
+        if kind == ty::Opaque { Some(tcx.variances_of(alias_ty.def_id)) } else { None };
+
+    for (index, child) in alias_ty.args.iter().enumerate() {
+        if opt_variances.and_then(|variances| variances.get(index)) == Some(ty::Bivariant) {
+            continue;
+        }
+        compute_components_for_arg(tcx, child, out, visited);
+    }
+}
+
+fn compute_components_for_lt<I: Interner>(lt: I::Region, out: &mut SmallVec<[Component<I>; 4]>) {
+    if !lt.is_bound() {
+        out.push(Component::Region(lt));
+    }
+}
+
+fn compute_components_for_const<I: Interner>(
+    tcx: I,
+    ct: I::Const,
+    out: &mut SmallVec<[Component<I>; 4]>,
+    visited: &mut SsoHashSet<I::GenericArg>,
+) {
+    if !visited.insert(ct.into()) {
+        return;
+    }
+    match ct.kind() {
+        ty::ConstKind::Param(_)
+        | ty::ConstKind::Bound(_, _)
+        | ty::ConstKind::Infer(_)
+        | ty::ConstKind::Placeholder(_)
+        | ty::ConstKind::Error(_) => {
+            // Trivial
+        }
+        ty::ConstKind::Expr(e) => {
+            for arg in e.args().iter() {
+                compute_components_for_arg(tcx, arg, out, visited);
+            }
+        }
+        ty::ConstKind::Value(ty, _) => {
+            compute_components_for_ty(tcx, ty, out, visited);
+        }
+        ty::ConstKind::Unevaluated(uv) => {
+            for arg in uv.args.iter() {
+                compute_components_for_arg(tcx, arg, out, visited);
+            }
+        }
+    }
+}
diff --git a/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr b/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
index 3cc35b21409..b547da7126a 100644
--- a/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
+++ b/tests/ui/async-await/in-trait/async-generics-and-bounds.stderr
@@ -1,29 +1,29 @@
-error[E0311]: the parameter type `U` may not live long enough
+error[E0311]: the parameter type `T` may not live long enough
   --> $DIR/async-generics-and-bounds.rs:9:5
    |
 LL |     async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
    |     ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     |            |
-   |     |            the parameter type `U` must be valid for the anonymous lifetime as defined here...
+   |     |            the parameter type `T` must be valid for the anonymous lifetime as defined here...
    |     ...so that the reference type `&(T, U)` does not outlive the data it points at
    |
 help: consider adding an explicit lifetime bound
    |
-LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, U: 'a;
+LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, T: 'a;
    |                 ++++  ++           ++                                       +++++++
 
-error[E0311]: the parameter type `T` may not live long enough
+error[E0311]: the parameter type `U` may not live long enough
   --> $DIR/async-generics-and-bounds.rs:9:5
    |
 LL |     async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
    |     ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     |            |
-   |     |            the parameter type `T` must be valid for the anonymous lifetime as defined here...
+   |     |            the parameter type `U` must be valid for the anonymous lifetime as defined here...
    |     ...so that the reference type `&(T, U)` does not outlive the data it points at
    |
 help: consider adding an explicit lifetime bound
    |
-LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, T: 'a;
+LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, U: 'a;
    |                 ++++  ++           ++                                       +++++++
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/async-await/in-trait/async-generics.stderr b/tests/ui/async-await/in-trait/async-generics.stderr
index 3b27f8fe2f0..2e29a9bcc77 100644
--- a/tests/ui/async-await/in-trait/async-generics.stderr
+++ b/tests/ui/async-await/in-trait/async-generics.stderr
@@ -1,29 +1,29 @@
-error[E0311]: the parameter type `U` may not live long enough
+error[E0311]: the parameter type `T` may not live long enough
   --> $DIR/async-generics.rs:6:5
    |
 LL |     async fn foo(&self) -> &(T, U);
    |     ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^
    |     |            |
-   |     |            the parameter type `U` must be valid for the anonymous lifetime as defined here...
+   |     |            the parameter type `T` must be valid for the anonymous lifetime as defined here...
    |     ...so that the reference type `&(T, U)` does not outlive the data it points at
    |
 help: consider adding an explicit lifetime bound
    |
-LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where U: 'a;
+LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: 'a;
    |                 ++++  ++           ++        +++++++++++
 
-error[E0311]: the parameter type `T` may not live long enough
+error[E0311]: the parameter type `U` may not live long enough
   --> $DIR/async-generics.rs:6:5
    |
 LL |     async fn foo(&self) -> &(T, U);
    |     ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^
    |     |            |
-   |     |            the parameter type `T` must be valid for the anonymous lifetime as defined here...
+   |     |            the parameter type `U` must be valid for the anonymous lifetime as defined here...
    |     ...so that the reference type `&(T, U)` does not outlive the data it points at
    |
 help: consider adding an explicit lifetime bound
    |
-LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where T: 'a;
+LL |     async fn foo<'a>(&'a self) -> &'a (T, U) where U: 'a;
    |                 ++++  ++           ++        +++++++++++
 
 error: aborting due to 2 previous errors