Skip to main content

rustc_trait_selection/traits/
outlives_bounds.rs

1use rustc_infer::infer::InferOk;
2use rustc_infer::infer::canonical::QueryRegionConstraint;
3use rustc_infer::infer::resolve::OpportunisticRegionResolver;
4use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
5use rustc_macros::extension;
6use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints};
7pub use rustc_middle::traits::query::OutlivesBound;
8use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt};
9use rustc_span::def_id::LocalDefId;
10use tracing::instrument;
11
12use crate::infer::InferCtxt;
13use crate::traits::ObligationCause;
14
15/// Implied bounds are region relationships that we deduce
16/// automatically. The idea is that (e.g.) a caller must check that a
17/// function's argument types are well-formed immediately before
18/// calling that fn, and hence the *callee* can assume that its
19/// argument types are well-formed. This may imply certain relationships
20/// between generic parameters. For example:
21/// ```
22/// fn foo<T>(x: &T) {}
23/// ```
24/// can only be called with a `'a` and `T` such that `&'a T` is WF.
25/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
26///
27/// # Parameters
28///
29/// - `param_env`, the where-clauses in scope
30/// - `body_id`, the body-id to use when normalizing assoc types.
31///   Note that this may cause outlives obligations to be injected
32///   into the inference context with this body-id.
33/// - `ty`, the type that we are supposed to assume is WF.
34x;#[instrument(level = "debug", skip(infcx, param_env, body_id), ret)]
35fn implied_outlives_bounds<'a, 'tcx>(
36    infcx: &'a InferCtxt<'tcx>,
37    param_env: ty::ParamEnv<'tcx>,
38    body_id: LocalDefId,
39    ty: Ty<'tcx>,
40    disable_implied_bounds_hack: bool,
41) -> Vec<OutlivesBound<'tcx>> {
42    let ty = infcx.resolve_vars_if_possible(ty);
43    let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty);
44
45    // We do not expect existential variables in implied bounds.
46    // We may however encounter unconstrained lifetime variables
47    // in very rare cases.
48    //
49    // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
50    // an example.
51    assert!(!ty.has_non_region_infer());
52
53    let mut canonical_var_values = OriginalQueryValues::default();
54    let input = ImpliedOutlivesBounds { ty };
55    let canonical = infcx.canonicalize_query(param_env.and(input), &mut canonical_var_values);
56    let implied_bounds_result =
57        infcx.tcx.implied_outlives_bounds((canonical, disable_implied_bounds_hack));
58    let Ok(canonical_result) = implied_bounds_result else {
59        return vec![];
60    };
61
62    let mut constraints = QueryRegionConstraints::default();
63    let span = infcx.tcx.def_span(body_id);
64    let Ok(InferOk { value: mut bounds, obligations }) = infcx
65        .instantiate_nll_query_response_and_region_obligations(
66            &ObligationCause::dummy_with_span(span),
67            param_env,
68            &canonical_var_values,
69            canonical_result,
70            &mut constraints,
71        )
72    else {
73        return vec![];
74    };
75    assert_eq!(obligations.len(), 0);
76
77    // Because of #109628, we may have unexpected placeholders. Ignore them!
78    // FIXME(#109628): panic in this case once the issue is fixed.
79    bounds.retain(|bound| !bound.has_placeholders());
80
81    if !constraints.is_empty() {
82        // FIXME(higher_ranked_auto): Should we register assumptions here?
83        // We otherwise would get spurious errors if normalizing an implied
84        // outlives bound required proving some higher-ranked coroutine obl.
85        let QueryRegionConstraints { constraints, assumptions: _ } = constraints;
86        let cause = ObligationCause::misc(span, body_id);
87        for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints {
88            match constraint {
89                ty::RegionConstraint::Outlives(predicate) => {
90                    infcx.register_outlives_constraint(predicate, vis, &cause)
91                }
92                ty::RegionConstraint::Eq(predicate) => {
93                    infcx.register_region_eq_constraint(predicate, vis, &cause)
94                }
95            }
96        }
97    };
98
99    bounds
100}
101
102impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
    #[doc =
    " Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment`"]
    #[doc =
    " instead if you\'re interested in the implied bounds for a given signature."]
    fn implied_bounds_tys<Tys: IntoIterator<Item =
        Ty<'tcx>>>(&self, body_id: LocalDefId, param_env: ParamEnv<'tcx>,
        tys: Tys, disable_implied_bounds_hack: bool)
        -> impl Iterator<Item = OutlivesBound<'tcx>> {
        tys.into_iter().flat_map(move |ty|
                {
                    implied_outlives_bounds(self, param_env, body_id, ty,
                        disable_implied_bounds_hack)
                })
    }
}#[extension(pub trait InferCtxtExt<'tcx>)]
103impl<'tcx> InferCtxt<'tcx> {
104    /// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment`
105    /// instead if you're interested in the implied bounds for a given signature.
106    fn implied_bounds_tys<Tys: IntoIterator<Item = Ty<'tcx>>>(
107        &self,
108        body_id: LocalDefId,
109        param_env: ParamEnv<'tcx>,
110        tys: Tys,
111        disable_implied_bounds_hack: bool,
112    ) -> impl Iterator<Item = OutlivesBound<'tcx>> {
113        tys.into_iter().flat_map(move |ty| {
114            implied_outlives_bounds(self, param_env, body_id, ty, disable_implied_bounds_hack)
115        })
116    }
117}