1use std::ops::ControlFlow;
23use rustc_infer::infer::TypeOutlivesConstraint;
4use rustc_infer::infer::canonical::CanonicalQueryInput;
5use rustc_infer::traits::query::OutlivesBound;
6use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
7use rustc_middle::infer::canonical::CanonicalQueryResponse;
8use rustc_middle::traits::ObligationCause;
9use rustc_middle::ty::outlives::{Component, push_outlives_components};
10use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitable, TypeVisitor};
11use rustc_span::def_id::CRATE_DEF_ID;
12use rustc_span::{DUMMY_SP, Span, sym};
13use smallvec::{SmallVec, smallvec};
1415use crate::traits::query::NoSolution;
16use crate::traits::{ObligationCtxt, wf};
1718impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
19type QueryResponse = Vec<OutlivesBound<'tcx>>;
2021fn try_fast_path(
22 _tcx: TyCtxt<'tcx>,
23 key: &ParamEnvAnd<'tcx, Self>,
24 ) -> Option<Self::QueryResponse> {
25// Don't go into the query for things that can't possibly have lifetimes.
26match key.value.ty.kind() {
27 ty::Tuple(elems) if elems.is_empty() => Some(::alloc::vec::Vec::new()vec![]),
28 ty::Never | ty::Str | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
29Some(::alloc::vec::Vec::new()vec![])
30 }
31_ => None,
32 }
33 }
3435fn perform_query(
36 tcx: TyCtxt<'tcx>,
37 canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>,
38 ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
39tcx.implied_outlives_bounds((canonicalized, false))
40 }
4142fn perform_locally_with_next_solver(
43 ocx: &ObligationCtxt<'_, 'tcx>,
44 key: ParamEnvAnd<'tcx, Self>,
45 span: Span,
46 ) -> Result<Self::QueryResponse, NoSolution> {
47compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span, false)
48 }
49}
5051pub fn compute_implied_outlives_bounds_inner<'tcx>(
52 ocx: &ObligationCtxt<'_, 'tcx>,
53 param_env: ty::ParamEnv<'tcx>,
54 ty: Ty<'tcx>,
55 span: Span,
56 disable_implied_bounds_hack: bool,
57) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
58// Inside mir borrowck, each computation starts with an empty list.
59if !ocx.infcx.inner.borrow().region_obligations().is_empty() {
{
::core::panicking::panic_fmt(format_args!("compute_implied_outlives_bounds assumes region obligations are empty before starting"));
}
};assert!(
60 ocx.infcx.inner.borrow().region_obligations().is_empty(),
61"compute_implied_outlives_bounds assumes region obligations are empty before starting"
62);
6364// FIXME: This doesn't seem right. All call sites already normalize `ty`:
65 // - `Ty`s from the `DefiningTy` in Borrowck: we have to normalize in the caller
66 // in order to get implied bounds involving any unconstrained region vars
67 // created as part of normalizing the sig. See #136547
68 // - `Ty`s from impl headers in Borrowck and in Non-Borrowck contexts: we have
69 // to normalize in the caller as computing implied bounds from unnormalized
70 // types would be unsound. See #100989
71 //
72 // We must normalize the type so we can compute the right outlives components.
73 // for example, if we have some constrained param type like `T: Trait<Out = U>`,
74 // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`.
75let normalized_ty = ocx76 .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty)
77 .map_err(|_| NoSolution)?;
7879// Sometimes when we ask what it takes for T: WF, we get back that
80 // U: WF is required; in that case, we push U onto this stack and
81 // process it next. Because the resulting predicates aren't always
82 // guaranteed to be a subset of the original type, so we need to store the
83 // WF args we've computed in a set.
84let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
85let mut wf_args = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[ty.into(), normalized_ty.into()]))vec![ty.into(), normalized_ty.into()];
8687let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = ::alloc::vec::Vec::new()vec![];
8889while let Some(arg) = wf_args.pop() {
90if !checked_wf_args.insert(arg) {
91continue;
92 }
9394// From the full set of obligations, just filter down to the region relationships.
95for obligation in
96wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
97 .into_iter()
98 .flatten()
99 {
100let pred = ocx
101 .deeply_normalize(
102&ObligationCause::dummy_with_span(span),
103 param_env,
104 obligation.predicate,
105 )
106 .map_err(|_| NoSolution)?;
107let Some(pred) = pred.kind().no_bound_vars() else {
108continue;
109 };
110match pred {
111// FIXME(generic_const_parameter_types): Make sure that `<'a, 'b, const N: &'a &'b u32>`
112 // is sound if we ever support that
113ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
114 | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
115 | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
116 | ty::PredicateKind::Subtype(..)
117 | ty::PredicateKind::Coerce(..)
118 | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
119 | ty::PredicateKind::DynCompatible(..)
120 | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
121 | ty::PredicateKind::ConstEquate(..)
122 | ty::PredicateKind::Ambiguous
123 | ty::PredicateKind::NormalizesTo(..)
124 | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_))
125 | ty::PredicateKind::AliasRelate(..) => {}
126127// We need to search through *all* WellFormed predicates
128ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => {
129 wf_args.push(term);
130 }
131132// We need to register region relationships
133ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(
134 ty::OutlivesPredicate(r_a, r_b),
135 )) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)),
136137 ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
138 ty_a,
139 r_b,
140 ))) => {
141let mut components = ::smallvec::SmallVec::new()smallvec![];
142 push_outlives_components(ocx.infcx.tcx, ty_a, &mut components);
143 outlives_bounds.extend(implied_bounds_from_components(r_b, components))
144 }
145 }
146 }
147 }
148149// If we detect `bevy_ecs::*::ParamSet` in the WF args list (and `disable_implied_bounds_hack`
150 // or `-Zno-implied-bounds-compat` are not set), then use the registered outlives obligations
151 // as implied bounds.
152if !disable_implied_bounds_hack153 && !ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat
154 && ty.visit_with(&mut ContainsBevyParamSet { tcx: ocx.infcx.tcx }).is_break()
155 {
156for TypeOutlivesConstraint { sup_type, sub_region, .. } in
157ocx.infcx.clone_registered_region_obligations()
158 {
159let mut components = ::smallvec::SmallVec::new()smallvec![];
160 push_outlives_components(ocx.infcx.tcx, sup_type, &mut components);
161 outlives_bounds.extend(implied_bounds_from_components(sub_region, components));
162 }
163 }
164165Ok(outlives_bounds)
166}
167168struct ContainsBevyParamSet<'tcx> {
169 tcx: TyCtxt<'tcx>,
170}
171172impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsBevyParamSet<'tcx> {
173type Result = ControlFlow<()>;
174175fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
176// We only care to match `ParamSet<T>` or `&ParamSet<T>`.
177match t.kind() {
178 ty::Adt(def, _) => {
179if self.tcx.item_name(def.did()) == sym::ParamSet180 && self.tcx.crate_name(def.did().krate) == sym::bevy_ecs181 {
182return ControlFlow::Break(());
183 }
184 }
185 ty::Ref(_, ty, _) => ty.visit_with(self)?,
186_ => {}
187 }
188189 ControlFlow::Continue(())
190 }
191}
192193/// When we have an implied bound that `T: 'a`, we can further break
194/// this down to determine what relationships would have to hold for
195/// `T: 'a` to hold. We get to assume that the caller has validated
196/// those relationships.
197fn implied_bounds_from_components<'tcx>(
198 sub_region: ty::Region<'tcx>,
199 sup_components: SmallVec<[Component<TyCtxt<'tcx>>; 4]>,
200) -> Vec<OutlivesBound<'tcx>> {
201sup_components202 .into_iter()
203 .filter_map(|component| {
204match component {
205 Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
206 Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
207 Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
208 Component::Placeholder(_p) => {
209// FIXME(non_lifetime_binders): Placeholders don't currently
210 // imply anything for outlives, though they could easily.
211None212 }
213 Component::EscapingAlias(_) =>
214// If the projection has escaping regions, don't
215 // try to infer any implied bounds even for its
216 // free components. This is conservative, because
217 // the caller will still have to prove that those
218 // free components outlive `sub_region`. But the
219 // idea is that the WAY that the caller proves
220 // that may change in the future and we want to
221 // give ourselves room to get smarter here.
222{
223None224 }
225 Component::UnresolvedInferenceVariable(..) => None,
226 }
227 })
228 .collect()
229}