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;
1011use crate::infer::InferCtxt;
12use crate::traits::ObligationCause;
1314/// 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.
33x;#[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 disable_implied_bounds_hack: bool,
40) -> Vec<OutlivesBound<'tcx>> {
41let ty = infcx.resolve_vars_if_possible(ty);
42let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty);
4344// 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.
50assert!(!ty.has_non_region_infer());
5152let mut canonical_var_values = OriginalQueryValues::default();
53let input = ImpliedOutlivesBounds { ty };
54let canonical = infcx.canonicalize_query(param_env.and(input), &mut canonical_var_values);
55let implied_bounds_result =
56 infcx.tcx.implied_outlives_bounds((canonical, disable_implied_bounds_hack));
57let Ok(canonical_result) = implied_bounds_result else {
58return vec![];
59 };
6061let mut constraints = QueryRegionConstraints::default();
62let span = infcx.tcx.def_span(body_id);
63let Ok(InferOk { value: mut bounds, obligations }) = infcx
64 .instantiate_nll_query_response_and_region_obligations(
65&ObligationCause::dummy_with_span(span),
66 param_env,
67&canonical_var_values,
68 canonical_result,
69&mut constraints,
70 )
71else {
72return vec![];
73 };
74assert_eq!(obligations.len(), 0);
7576// Because of #109628, we may have unexpected placeholders. Ignore them!
77 // FIXME(#109628): panic in this case once the issue is fixed.
78bounds.retain(|bound| !bound.has_placeholders());
7980if !constraints.is_empty() {
81// FIXME(higher_ranked_auto): Should we register assumptions here?
82 // We otherwise would get spurious errors if normalizing an implied
83 // outlives bound required proving some higher-ranked coroutine obl.
84let QueryRegionConstraints { outlives, assumptions: _ } = constraints;
85let cause = ObligationCause::misc(span, body_id);
86for &(predicate, _) in &outlives {
87 infcx.register_outlives_constraint(predicate, &cause);
88 }
89 };
9091 bounds
92}
9394impl<'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>)]95impl<'tcx> InferCtxt<'tcx> {
96/// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment`
97 /// instead if you're interested in the implied bounds for a given signature.
98fn implied_bounds_tys<Tys: IntoIterator<Item = Ty<'tcx>>>(
99&self,
100 body_id: LocalDefId,
101 param_env: ParamEnv<'tcx>,
102 tys: Tys,
103 disable_implied_bounds_hack: bool,
104 ) -> impl Iterator<Item = OutlivesBound<'tcx>> {
105tys.into_iter().flat_map(move |ty| {
106implied_outlives_bounds(self, param_env, body_id, ty, disable_implied_bounds_hack)
107 })
108 }
109}