rustc_trait_selection/traits/
outlives_bounds.rs

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