Skip to main content

rustc_trait_selection/traits/
implied_outlives_bounds.rs

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