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< >::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>), -} - -/// 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>, -) { - // 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 - // `>::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>, -) { - 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>, -) { - 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..6e1b044cbac 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -7,8 +7,9 @@ use crate::infer::lexical_region_resolve; use rustc_middle::traits::query::{NoSolution, OutlivesBound}; use rustc_middle::ty; +// TODO: Remove me +pub use rustc_type_ir::outlives as components; -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..c09b5de23b5 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -291,7 +291,7 @@ pub fn type_must_outlive( fn components_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, - components: &[Component<'tcx>], + components: &[Component>], 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..27dec66ca77 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -139,7 +139,7 @@ pub fn alias_bound( fn bound_from_components( &self, - components: &[Component<'tcx>], + components: &[Component>], visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { let mut bounds = components @@ -158,7 +158,7 @@ fn bound_from_components( fn bound_from_single_component( &self, - component: &Component<'tcx>, + component: &Component>, visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { match *component { 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> 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_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..40fdf549969 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 @@ -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>; 4]>, ) -> Vec> { 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>: 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>: @@ -272,6 +276,10 @@ fn is_ct_var(self) -> bool { } } +pub trait ExprConst>: Copy + Debug + Hash + Eq + Relate { + fn args(self) -> I::GenericArgs; +} + pub trait GenericsOf> { 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 @@ fn mk_external_constraints( type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike; type ValueConst: Copy + Debug + Hash + Eq; - type ExprConst: Copy + Debug + Hash + Eq + Relate; + type ExprConst: ExprConst; // Kinds of regions type Region: Region; 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 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 { + 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), + + // 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< >::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>), +} + +/// 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: I, + ty0: I::Ty, + out: &mut SmallVec<[Component; 4]>, +) { + let mut visited = SsoHashSet::new(); + compute_components_for_ty(tcx, ty0, out, &mut visited); + debug!("components({:?}) = {:?}", ty0, out); +} + +fn compute_components_for_arg( + tcx: I, + arg: I::GenericArg, + out: &mut SmallVec<[Component; 4]>, + visited: &mut SsoHashSet, +) { + 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( + tcx: I, + ty: I::Ty, + out: &mut SmallVec<[Component; 4]>, + visited: &mut SsoHashSet, +) { + 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 + // `>::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( + tcx: I, + alias_ty: I::Ty, + out: &mut SmallVec<[Component; 4]>, + visited: &mut SsoHashSet, +) { + 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(lt: I::Region, out: &mut SmallVec<[Component; 4]>) { + if !lt.is_bound() { + out.push(Component::Region(lt)); + } +} + +fn compute_components_for_const( + tcx: I, + ct: I::Const, + out: &mut SmallVec<[Component; 4]>, + visited: &mut SsoHashSet, +) { + 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,17 +1,3 @@ -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 `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, U: 'a; - | ++++ ++ ++ +++++++ - error[E0311]: the parameter type `T` may not live long enough --> $DIR/async-generics-and-bounds.rs:9:5 | @@ -26,6 +12,20 @@ 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; | ++++ ++ ++ +++++++ +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 `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, U: 'a; + | ++++ ++ ++ +++++++ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0311`. 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,17 +1,3 @@ -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 `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 U: 'a; - | ++++ ++ ++ +++++++++++ - error[E0311]: the parameter type `T` may not live long enough --> $DIR/async-generics.rs:6:5 | @@ -26,6 +12,20 @@ help: consider adding an explicit lifetime bound LL | async fn foo<'a>(&'a self) -> &'a (T, U) where T: 'a; | ++++ ++ ++ +++++++++++ +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 `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 U: 'a; + | ++++ ++ ++ +++++++++++ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0311`.