rustc_infer/infer/outlives/verify.rs
1use std::assert_matches::assert_matches;
2
3use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt};
4use rustc_type_ir::outlives::{Component, compute_alias_components_recursive};
5use smallvec::smallvec;
6use tracing::{debug, instrument, trace};
7
8use crate::infer::outlives::env::RegionBoundPairs;
9use crate::infer::region_constraints::VerifyIfEq;
10use crate::infer::{GenericKind, VerifyBound};
11
12/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
13/// obligation into a series of `'a: 'b` constraints and "verifys", as
14/// described on the module comment. The final constraints are emitted
15/// via a "delegate" of type `D` -- this is usually the `infcx`, which
16/// accrues them into the `region_obligations` code, but for NLL we
17/// use something else.
18pub(crate) struct VerifyBoundCx<'cx, 'tcx> {
19 tcx: TyCtxt<'tcx>,
20 region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
21 /// During borrowck, if there are no outlives bounds on a generic
22 /// parameter `T`, we assume that `T: 'in_fn_body` holds.
23 ///
24 /// Outside of borrowck the only way to prove `T: '?0` is by
25 /// setting `'?0` to `'empty`.
26 implicit_region_bound: Option<ty::Region<'tcx>>,
27 caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
28}
29
30impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
31 pub(crate) fn new(
32 tcx: TyCtxt<'tcx>,
33 region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
34 implicit_region_bound: Option<ty::Region<'tcx>>,
35 caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
36 ) -> Self {
37 Self { tcx, region_bound_pairs, implicit_region_bound, caller_bounds }
38 }
39
40 #[instrument(level = "debug", skip(self))]
41 pub(crate) fn param_or_placeholder_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
42 // Start with anything like `T: 'a` we can scrape from the
43 // environment. If the environment contains something like
44 // `for<'a> T: 'a`, then we know that `T` outlives everything.
45 let declared_bounds_from_env = self.declared_generic_bounds_from_env(ty);
46 debug!(?declared_bounds_from_env);
47 let mut param_bounds = vec![];
48 for declared_bound in declared_bounds_from_env {
49 let bound_region = declared_bound.map_bound(|outlives| outlives.1);
50 if let Some(region) = bound_region.no_bound_vars() {
51 // This is `T: 'a` for some free region `'a`.
52 param_bounds.push(VerifyBound::OutlivedBy(region));
53 } else {
54 // This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here.
55 debug!("found that {ty:?} outlives any lifetime, returning empty vector");
56 return VerifyBound::AllBounds(vec![]);
57 }
58 }
59
60 // Add in the default bound of fn body that applies to all in
61 // scope type parameters:
62 if let Some(r) = self.implicit_region_bound {
63 debug!("adding implicit region bound of {r:?}");
64 param_bounds.push(VerifyBound::OutlivedBy(r));
65 }
66
67 if param_bounds.is_empty() {
68 // We know that all types `T` outlive `'empty`, so if we
69 // can find no other bound, then check that the region
70 // being tested is `'empty`.
71 VerifyBound::IsEmpty
72 } else if param_bounds.len() == 1 {
73 // Micro-opt: no need to store the vector if it's just len 1
74 param_bounds.pop().unwrap()
75 } else {
76 // If we can find any other bound `R` such that `T: R`, then
77 // we don't need to check for `'empty`, because `R: 'empty`.
78 VerifyBound::AnyBound(param_bounds)
79 }
80 }
81
82 /// Given a projection like `T::Item`, searches the environment
83 /// for where-clauses like `T::Item: 'a`. Returns the set of
84 /// regions `'a` that it finds.
85 ///
86 /// This is an "approximate" check -- it may not find all
87 /// applicable bounds, and not all the bounds it returns can be
88 /// relied upon. In particular, this check ignores region
89 /// identity. So, for example, if we have `<T as
90 /// Trait<'0>>::Item` where `'0` is a region variable, and the
91 /// user has `<T as Trait<'a>>::Item: 'b` in the environment, then
92 /// the clause from the environment only applies if `'0 = 'a`,
93 /// which we don't know yet. But we would still include `'b` in
94 /// this list.
95 pub(crate) fn approx_declared_bounds_from_env(
96 &self,
97 alias_ty: ty::AliasTy<'tcx>,
98 ) -> Vec<ty::PolyTypeOutlivesPredicate<'tcx>> {
99 let erased_alias_ty = self.tcx.erase_regions(alias_ty.to_ty(self.tcx));
100 self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty)
101 }
102
103 #[instrument(level = "debug", skip(self))]
104 pub(crate) fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> {
105 // Search the env for where clauses like `P: 'a`.
106 let env_bounds = self.approx_declared_bounds_from_env(alias_ty).into_iter().map(|binder| {
107 if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars()
108 && let ty::Alias(_, alias_ty_from_bound) = *ty.kind()
109 && alias_ty_from_bound == alias_ty
110 {
111 // Micro-optimize if this is an exact match (this
112 // occurs often when there are no region variables
113 // involved).
114 VerifyBound::OutlivedBy(r)
115 } else {
116 let verify_if_eq_b =
117 binder.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound });
118 VerifyBound::IfEq(verify_if_eq_b)
119 }
120 });
121
122 // Extend with bounds that we can find from the definition.
123 let definition_bounds =
124 self.declared_bounds_from_definition(alias_ty).map(|r| VerifyBound::OutlivedBy(r));
125
126 // see the extensive comment in projection_must_outlive
127 let recursive_bound = {
128 let mut components = smallvec![];
129 let kind = alias_ty.kind(self.tcx);
130 compute_alias_components_recursive(self.tcx, kind, alias_ty, &mut components);
131 self.bound_from_components(&components)
132 };
133
134 VerifyBound::AnyBound(env_bounds.chain(definition_bounds).collect()).or(recursive_bound)
135 }
136
137 fn bound_from_components(&self, components: &[Component<TyCtxt<'tcx>>]) -> VerifyBound<'tcx> {
138 let mut bounds = components
139 .iter()
140 .map(|component| self.bound_from_single_component(component))
141 // Remove bounds that must hold, since they are not interesting.
142 .filter(|bound| !bound.must_hold());
143
144 match (bounds.next(), bounds.next()) {
145 (Some(first), None) => first,
146 (first, second) => {
147 VerifyBound::AllBounds(first.into_iter().chain(second).chain(bounds).collect())
148 }
149 }
150 }
151
152 fn bound_from_single_component(
153 &self,
154 component: &Component<TyCtxt<'tcx>>,
155 ) -> VerifyBound<'tcx> {
156 match *component {
157 Component::Region(lt) => VerifyBound::OutlivedBy(lt),
158 Component::Param(param_ty) => self.param_or_placeholder_bound(param_ty.to_ty(self.tcx)),
159 Component::Placeholder(placeholder_ty) => {
160 self.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty))
161 }
162 Component::Alias(alias_ty) => self.alias_bound(alias_ty),
163 Component::EscapingAlias(ref components) => self.bound_from_components(components),
164 Component::UnresolvedInferenceVariable(v) => {
165 // Ignore this, we presume it will yield an error later, since
166 // if a type variable is not resolved by this point it never
167 // will be.
168 self.tcx
169 .dcx()
170 .delayed_bug(format!("unresolved inference variable in outlives: {v:?}"));
171 // Add a bound that never holds.
172 VerifyBound::AnyBound(vec![])
173 }
174 }
175 }
176
177 /// Searches the environment for where-clauses like `G: 'a` where
178 /// `G` is either some type parameter `T` or a projection like
179 /// `T::Item`. Returns a vector of the `'a` bounds it can find.
180 ///
181 /// This is a conservative check -- it may not find all applicable
182 /// bounds, but all the bounds it returns can be relied upon.
183 fn declared_generic_bounds_from_env(
184 &self,
185 generic_ty: Ty<'tcx>,
186 ) -> Vec<ty::PolyTypeOutlivesPredicate<'tcx>> {
187 assert_matches!(generic_ty.kind(), ty::Param(_) | ty::Placeholder(_));
188 self.declared_generic_bounds_from_env_for_erased_ty(generic_ty)
189 }
190
191 /// Searches the environment to find all bounds that apply to `erased_ty`.
192 /// Obviously these must be approximate -- they are in fact both *over* and
193 /// and *under* approximated:
194 ///
195 /// * Over-approximated because we don't consider equality of regions.
196 /// * Under-approximated because we look for syntactic equality and so for complex types
197 /// like `<T as Foo<fn(&u32, &u32)>>::Item` or whatever we may fail to figure out
198 /// all the subtleties.
199 ///
200 /// In some cases, such as when `erased_ty` represents a `ty::Param`, however,
201 /// the result is precise.
202 #[instrument(level = "debug", skip(self))]
203 fn declared_generic_bounds_from_env_for_erased_ty(
204 &self,
205 erased_ty: Ty<'tcx>,
206 ) -> Vec<ty::PolyTypeOutlivesPredicate<'tcx>> {
207 let tcx = self.tcx;
208 let mut bounds = vec![];
209
210 // To start, collect bounds from user environment. Note that
211 // parameter environments are already elaborated, so we don't
212 // have to worry about that.
213 bounds.extend(self.caller_bounds.iter().copied().filter(move |outlives_predicate| {
214 super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty)
215 }));
216
217 // Next, collect regions we scraped from the well-formedness
218 // constraints in the fn signature. To do that, we walk the list
219 // of known relations from the fn ctxt.
220 //
221 // This is crucial because otherwise code like this fails:
222 //
223 // fn foo<'a, A>(x: &'a A) { x.bar() }
224 //
225 // The problem is that the type of `x` is `&'a A`. To be
226 // well-formed, then, A must outlive `'a`, but we don't know that
227 // this holds from first principles.
228 bounds.extend(self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| {
229 debug!(
230 "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}",
231 (r, p)
232 );
233 // Fast path for the common case.
234 match (&p, erased_ty.kind()) {
235 // In outlive routines, all types are expected to be fully normalized.
236 // And therefore we can safely use structural equality for alias types.
237 (GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {}
238 (GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {}
239 (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.def_id == a2.def_id => {}
240 _ => return None,
241 }
242
243 let p_ty = p.to_ty(tcx);
244 let erased_p_ty = self.tcx.erase_regions(p_ty);
245 (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r)))
246 }));
247
248 bounds
249 }
250
251 /// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
252 /// declared in the trait definition. For example, if the trait were
253 ///
254 /// ```rust
255 /// trait Foo<'a> {
256 /// type Bar: 'a;
257 /// }
258 /// ```
259 ///
260 /// If we were given the `DefId` of `Foo::Bar`, we would return
261 /// `'a`. You could then apply the instantiations from the
262 /// projection to convert this into your namespace. This also
263 /// works if the user writes `where <Self as Foo<'a>>::Bar: 'a` on
264 /// the trait. In fact, it works by searching for just such a
265 /// where-clause.
266 ///
267 /// It will not, however, work for higher-ranked bounds like:
268 ///
269 /// ```ignore(this does compile today, previously was marked as `compile_fail,E0311`)
270 /// trait Foo<'a, 'b>
271 /// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
272 /// {
273 /// type Bar;
274 /// }
275 /// ```
276 ///
277 /// This is for simplicity, and because we are not really smart
278 /// enough to cope with such bounds anywhere.
279 pub(crate) fn declared_bounds_from_definition(
280 &self,
281 alias_ty: ty::AliasTy<'tcx>,
282 ) -> impl Iterator<Item = ty::Region<'tcx>> {
283 let tcx = self.tcx;
284 let bounds = tcx.item_self_bounds(alias_ty.def_id);
285 trace!("{:#?}", bounds.skip_binder());
286 bounds
287 .iter_instantiated(tcx, alias_ty.args)
288 .filter_map(|p| p.as_type_outlives_clause())
289 .filter_map(|p| p.no_bound_vars())
290 .map(|OutlivesPredicate(_, r)| r)
291 }
292}