1use std::ops::ControlFlow;
23use rustc_infer::infer::TypeOutlivesConstraint;
4use rustc_infer::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse};
5use rustc_infer::traits::query::OutlivesBound;
6use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
7use rustc_middle::traits::ObligationCause;
8use rustc_middle::ty::outlives::{Component, push_outlives_components};
9use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitable, TypeVisitor, Unnormalized};
10use rustc_span::def_id::CRATE_DEF_ID;
11use rustc_span::{DUMMY_SP, Span, sym};
12use smallvec::{SmallVec, smallvec};
1314use crate::traits::query::NoSolution;
15use crate::traits::query::type_op::QueryTypeOp;
16use crate::traits::{ObligationCtxt, wf};
1718// FIXME(#160491): Remove this once the new implied bounds impl is through FCP.
19impl<'tcx> QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
20type QueryResponse = Vec<OutlivesBound<'tcx>>;
2122fn try_fast_path(
23 _tcx: TyCtxt<'tcx>,
24 key: &ParamEnvAnd<'tcx, Self>,
25 ) -> Option<Self::QueryResponse> {
26// Don't go into the query for things that can't possibly have lifetimes.
27match key.value.ty.kind() {
28 ty::Tuple(elems) if elems.is_empty() => Some(::alloc::vec::Vec::new()vec![]),
29 ty::Never | ty::Str | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
30Some(::alloc::vec::Vec::new()vec![])
31 }
32_ => None,
33 }
34 }
3536fn perform_query(
37 tcx: TyCtxt<'tcx>,
38 canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>,
39 ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
40tcx.implied_outlives_bounds((canonicalized, false))
41 }
4243fn perform_locally_with_next_solver(
44 ocx: &ObligationCtxt<'_, 'tcx>,
45 key: ParamEnvAnd<'tcx, Self>,
46 _span: Span,
47 ) -> Result<Self::QueryResponse, NoSolution> {
48query_compute_implied_outlives_bounds(ocx, key.param_env, key.value.ty, false)
49 }
50}
5152pub fn compute_implied_outlives_bounds_inner<'tcx>(
53 ocx: &ObligationCtxt<'_, 'tcx>,
54 param_env: ty::ParamEnv<'tcx>,
55 ty: Ty<'tcx>,
56 normalized_ty: Ty<'tcx>,
57 span: Span,
58) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
59let tcx = ocx.infcx.tcx;
60// Sometimes when we ask what it takes for T: WF, we get back that
61 // U: WF is required; in that case, we push U onto this stack and
62 // process it next. Because the resulting predicates aren't always
63 // guaranteed to be a subset of the original type, so we need to store the
64 // WF args we've computed in a set.
65let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
66let 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()];
6768let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = ::alloc::vec::Vec::new()vec![];
6970while let Some(arg) = wf_args.pop() {
71if !checked_wf_args.insert(arg) {
72continue;
73 }
7475let arg = ocx.infcx.resolve_vars_if_possible(arg);
76// From the full set of obligations, just filter down to the region relationships.
77for obligation in
78wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
79 .into_flat_iter()
80 {
81let pred = ocx
82 .deeply_normalize(
83&ObligationCause::dummy_with_span(span),
84 param_env,
85 Unnormalized::new_wip(obligation.predicate),
86 )
87 .map_err(|_| NoSolution)?;
88let Some(pred) = pred.kind().no_bound_vars() else {
89continue;
90 };
91match pred {
92// FIXME(generic_const_parameter_types): Make sure that `<'a, 'b, const N: &'a &'b u32>`
93 // is sound if we ever support that
94ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
95 | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
96 | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
97 | ty::PredicateKind::Subtype(..)
98 | ty::PredicateKind::Coerce(..)
99 | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
100 | ty::PredicateKind::DynCompatible(..)
101 | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
102 | ty::PredicateKind::ConstEquate(..)
103 | ty::PredicateKind::Ambiguous
104 | ty::PredicateKind::NormalizesTo(..)
105 | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => {}
106107// We need to search through *all* WellFormed predicates
108ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => {
109 wf_args.push(term);
110 }
111112// We need to register region relationships
113ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesClause(
114 r_a,
115 r_b,
116 ))) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)),
117118 ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesClause(
119 ty_a,
120 r_b,
121 ))) => {
122let mut components = ::smallvec::SmallVec::new()smallvec![];
123 push_outlives_components(tcx, ty_a, &mut components);
124 outlives_bounds.extend(implied_bounds_from_components(tcx, r_b, components))
125 }
126 }
127 }
128 }
129130Ok(outlives_bounds)
131}
132133/// If we're at a callsite which should apply the bevy implied bounds hack and
134/// `-Zno-implied-bounds-compat` has not been set, then use the registered outlives
135/// obligations as implied bounds if we detect `bevy_ecs::*::ParamSet` in the arg.
136///
137/// cc #119956
138pub fn consider_implied_bounds_hack_for_ty<'tcx>(
139 ocx: &ObligationCtxt<'_, 'tcx>,
140 ty: Ty<'tcx>,
141 region_constraints: impl FnOnce() -> Vec<TypeOutlivesConstraint<'tcx>>,
142) -> Vec<OutlivesBound<'tcx>> {
143let tcx = ocx.infcx.tcx;
144if !ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat
145 && ty.visit_with(&mut ContainsBevyParamSet { tcx }).is_break()
146 {
147let mut outlives_bounds = ::alloc::vec::Vec::new()vec![];
148for TypeOutlivesConstraint { sup_type, sub_region, .. } in region_constraints() {
149let mut components = ::smallvec::SmallVec::new()smallvec![];
150 push_outlives_components(tcx, sup_type, &mut components);
151 outlives_bounds.extend(implied_bounds_from_components(tcx, sub_region, components));
152 }
153outlives_bounds154 } else {
155::alloc::vec::Vec::new()vec![]156 }
157}
158159pub fn query_compute_implied_outlives_bounds<'tcx>(
160 ocx: &ObligationCtxt<'_, 'tcx>,
161 param_env: ty::ParamEnv<'tcx>,
162 ty: Ty<'tcx>,
163 disable_implied_bounds_hack: bool,
164) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
165// When computing implied bounds by looking at types in the signature,
166 // we must be careful to never reveal the hidden types of opaques which
167 // the caller can not. That would be unsound as it may give us implied
168 // bounds which the caller never actually proves.
169 //
170 // FIXME(impl_trait_in_assoc_type): We currently do this incorrectly in
171 // `fn check_opaque_meets_bounds`, see trait-system-refactor-initiative#159.
172 /* if cfg!(debug_assertions) {
173 match ocx.infcx.typing_mode_raw() {
174 TypingMode::Typeck { defining_opaque_types_and_generators: opaque_types }
175 | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: opaque_types }
176 | TypingMode::PostBorrowck { defined_opaque_types: opaque_types } => {
177 assert!(opaque_types.is_empty())
178 }
179180 TypingMode::Coherence
181 | TypingMode::Reflection
182 | TypingMode::PostAnalysis
183 | TypingMode::Codegen
184 | TypingMode::ErasedNotCoherence(_) => unreachable!(),
185 }
186 } */
187188 // FIXME: This doesn't seem right. All call sites already normalize `ty`.
189 // We have to normalize in the caller as computing implied bounds from unnormalized
190 // types would be unsound. See #100989
191 //
192 // We must normalize the type so we can compute the right outlives components.
193 // for example, if we have some constrained param type like `T: Trait<Out = U>`,
194 // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`.
195let normalized_ty = ocx
196 .deeply_normalize(&ObligationCause::dummy(), param_env, Unnormalized::new_wip(ty))
197 .map_err(|_| NoSolution)?;
198199let mut outlives_bounds =
200 compute_implied_outlives_bounds_inner(ocx, param_env, ty, normalized_ty, DUMMY_SP)?;
201202if !disable_implied_bounds_hack {
203outlives_bounds.extend(consider_implied_bounds_hack_for_ty(ocx, ty, || {
204ocx.infcx.clone_registered_region_obligations()
205 }));
206 }
207208Ok(outlives_bounds)
209}
210211struct ContainsBevyParamSet<'tcx> {
212 tcx: TyCtxt<'tcx>,
213}
214215impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsBevyParamSet<'tcx> {
216type Result = ControlFlow<()>;
217218fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
219// We only care to match `ParamSet<T>` or `&ParamSet<T>`.
220match t.kind() {
221 ty::Adt(def, _) => {
222if self.tcx.item_name(def.did()) == sym::ParamSet223 && self.tcx.crate_name(def.did().krate) == sym::bevy_ecs224 {
225return ControlFlow::Break(());
226 }
227 }
228 ty::Ref(_, ty, _) => ty.visit_with(self)?,
229_ => {}
230 }
231232 ControlFlow::Continue(())
233 }
234}
235236/// When we have an implied bound that `T: 'a`, we can further break
237/// this down to determine what relationships would have to hold for
238/// `T: 'a` to hold. We get to assume that the caller has validated
239/// those relationships.
240fn implied_bounds_from_components<'tcx>(
241 tcx: TyCtxt<'tcx>,
242 sub_region: ty::Region<'tcx>,
243 sup_components: SmallVec<[Component<TyCtxt<'tcx>>; 4]>,
244) -> Vec<OutlivesBound<'tcx>> {
245sup_components246 .into_iter()
247 .filter_map(|component| {
248match component {
249 Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
250 Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
251 Component::Alias(is_rigid, p) => {
252// We expect them to be already deeply normalized.
253if true {
{
match (&is_rigid, &ty::IsRigid::yes_if_next_solver(tcx)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(is_rigid, ty::IsRigid::yes_if_next_solver(tcx));
254Some(OutlivesBound::RegionSubAlias(sub_region, p))
255 }
256 Component::Placeholder(_p) => {
257// FIXME(non_lifetime_binders): Placeholders don't currently
258 // imply anything for outlives, though they could easily.
259None260 }
261 Component::EscapingAlias(_) =>
262// If the projection has escaping regions, don't
263 // try to infer any implied bounds even for its
264 // free components. This is conservative, because
265 // the caller will still have to prove that those
266 // free components outlive `sub_region`. But the
267 // idea is that the WAY that the caller proves
268 // that may change in the future and we want to
269 // give ourselves room to get smarter here.
270{
271None272 }
273 Component::UnresolvedInferenceVariable(..) => None,
274 }
275 })
276 .collect()
277}