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;
1112use crate::infer::InferCtxt;
13use crate::traits::ObligationCause;
1415/// 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>> {
42let ty = infcx.resolve_vars_if_possible(ty);
43let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty);
4445// 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.
51assert!(!ty.has_non_region_infer());
5253let mut canonical_var_values = OriginalQueryValues::default();
54let input = ImpliedOutlivesBounds { ty };
55let canonical = infcx.canonicalize_query(param_env.and(input), &mut canonical_var_values);
56let implied_bounds_result =
57 infcx.tcx.implied_outlives_bounds((canonical, disable_implied_bounds_hack));
58let Ok(canonical_result) = implied_bounds_result else {
59return vec![];
60 };
6162let mut constraints = QueryRegionConstraints::default();
63let span = infcx.tcx.def_span(body_id);
64let 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 )
72else {
73return vec![];
74 };
75assert_eq!(obligations.len(), 0);
7677// Because of #109628, we may have unexpected placeholders. Ignore them!
78 // FIXME(#109628): panic in this case once the issue is fixed.
79bounds.retain(|bound| !bound.has_placeholders());
8081if !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.
85let QueryRegionConstraints { constraints, assumptions: _ } = constraints;
86let cause = ObligationCause::misc(span, body_id);
87for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints {
88match 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 };
9899 bounds
100}
101102impl<'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.
106fn 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>> {
113tys.into_iter().flat_map(move |ty| {
114implied_outlives_bounds(self, param_env, body_id, ty, disable_implied_bounds_hack)
115 })
116 }
117}