rustc_type_ir/outlives.rs
1//! The outlives relation `T: 'a` or `'a: 'b`. This code frequently
2//! refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
3//! RFC for reference.
4
5use derive_where::derive_where;
6use smallvec::{SmallVec, smallvec};
7
8use crate::data_structures::SsoHashSet;
9use crate::inherent::*;
10use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, TypeVisitor};
11use crate::{self as ty, AliasTy, Interner, OutlivesPredicate, Region, Unnormalized};
12
13#[derive_where(Debug; I: Interner)]
14pub enum Component<I: Interner> {
15 Region(Region<I>),
16 Param(I::ParamTy),
17 Placeholder(ty::PlaceholderType<I>),
18 UnresolvedInferenceVariable(ty::InferTy),
19
20 // Projections like `T::Foo` are tricky because a constraint like
21 // `T::Foo: 'a` can be satisfied in so many ways. There may be a
22 // where-clause that says `T::Foo: 'a`, or the defining trait may
23 // include a bound like `type Foo: 'static`, or -- in the most
24 // conservative way -- we can prove that `T: 'a` (more generally,
25 // that all components in the projection outlive `'a`). This code
26 // is not in a position to judge which is the best technique, so
27 // we just product the projection as a component and leave it to
28 // the consumer to decide (but see `EscapingProjection` below).
29 Alias(ty::AliasTy<I>),
30
31 // In the case where a projection has escaping regions -- meaning
32 // regions bound within the type itself -- we always use
33 // the most conservative rule, which requires that all components
34 // outlive the bound. So for example if we had a type like this:
35 //
36 // for<'a> Trait1< <T as Trait2<'a,'b>>::Foo >
37 // ~~~~~~~~~~~~~~~~~~~~~~~~~
38 //
39 // then the inner projection (underlined) has an escaping region
40 // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
41 // outlives `'b: 'c`, and we don't consider whether the trait
42 // declares that `Foo: 'static` etc. Therefore, we just return the
43 // free components of such a projection (in this case, `'b`).
44 //
45 // However, in the future, we may want to get smarter, and
46 // actually return a "higher-ranked projection" here. Therefore,
47 // we mark that these components are part of an escaping
48 // projection, so that implied bounds code can avoid relying on
49 // them. This gives us room to improve the regionck reasoning in
50 // the future without breaking backwards compat.
51 EscapingAlias(Vec<Component<I>>),
52}
53
54/// Push onto `out` all the things that must outlive `'a` for the condition
55/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
56pub fn push_outlives_components<I: Interner>(
57 cx: I,
58 ty: I::Ty,
59 out: &mut SmallVec<[Component<I>; 4]>,
60) {
61 ty.visit_with(&mut OutlivesCollector { cx, out, visited: Default::default() });
62}
63
64struct OutlivesCollector<'a, I: Interner> {
65 cx: I,
66 out: &'a mut SmallVec<[Component<I>; 4]>,
67 visited: SsoHashSet<I::Ty>,
68}
69
70impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
71 #[cfg(not(feature = "nightly"))]
72 type Result = ();
73
74 fn visit_ty(&mut self, ty: I::Ty) -> Self::Result {
75 if !self.visited.insert(ty) {
76 return;
77 }
78 // Descend through the types, looking for the various "base"
79 // components and collecting them into `out`. This is not written
80 // with `collect()` because of the need to sometimes skip subtrees
81 // in the `subtys` iterator (e.g., when encountering a
82 // projection).
83 match ty.kind() {
84 ty::FnDef(_, args) => {
85 let args = args.no_bound_vars().unwrap();
86 // HACK(eddyb) ignore lifetimes found shallowly in `args`.
87 // This is inconsistent with `ty::Adt` (including all args)
88 // and with `ty::Closure` (ignoring all args other than
89 // upvars, of which a `ty::FnDef` doesn't have any), but
90 // consistent with previous (accidental) behavior.
91 // See https://github.com/rust-lang/rust/issues/70917
92 // for further background and discussion.
93 for child in args.iter() {
94 match child.kind() {
95 ty::GenericArgKind::Lifetime(_) => {}
96 ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_) => {
97 child.visit_with(self);
98 }
99 }
100 }
101 }
102
103 ty::Closure(_, args) => {
104 args.as_closure().tupled_upvars_ty().visit_with(self);
105 }
106
107 ty::CoroutineClosure(_, args) => {
108 args.as_coroutine_closure().tupled_upvars_ty().visit_with(self);
109 }
110
111 ty::Coroutine(_, args) => {
112 args.as_coroutine().tupled_upvars_ty().visit_with(self);
113
114 // Coroutines may not outlive a region unless the resume
115 // ty outlives a region. This is because the resume ty may
116 // store data that lives shorter than this outlives region
117 // across yield points, which may subsequently be accessed
118 // after the coroutine is resumed again.
119 //
120 // Conceptually, you may think of the resume arg as an upvar
121 // of `&mut Option<ResumeArgTy>`, since it is kinda like
122 // storage shared between the callee of the coroutine and the
123 // coroutine body.
124 args.as_coroutine().resume_ty().visit_with(self);
125
126 // We ignore regions in the coroutine interior as we don't
127 // want these to affect region inference
128 }
129
130 // All regions are bound inside a witness, and we don't emit
131 // higher-ranked outlives components currently.
132 ty::CoroutineWitness(..) => {}
133
134 // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
135 // is implied by the environment is done in regionck.
136 ty::Param(p) => {
137 self.out.push(Component::Param(p));
138 }
139
140 ty::Placeholder(p) => {
141 self.out.push(Component::Placeholder(p));
142 }
143
144 // For projections, we prefer to generate an obligation like
145 // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
146 // regionck more ways to prove that it holds. However,
147 // regionck is not (at least currently) prepared to deal with
148 // higher-ranked regions that may appear in the
149 // trait-ref. Therefore, if we see any higher-ranked regions,
150 // we simply fallback to the most restrictive rule, which
151 // requires that `Pi: 'a` for all `i`.
152 ty::Alias(_, alias_ty) => {
153 if !alias_ty.has_escaping_bound_vars() {
154 // best case: no escaping regions, so push the
155 // projection and skip the subtree (thus generating no
156 // constraints for Pi). This defers the choice between
157 // the rules OutlivesProjectionEnv,
158 // OutlivesProjectionTraitDef, and
159 // OutlivesProjectionComponents to regionck.
160 self.out.push(Component::Alias(alias_ty));
161 } else {
162 // fallback case: hard code
163 // OutlivesProjectionComponents. Continue walking
164 // through and constrain Pi.
165 let mut subcomponents = smallvec![];
166 compute_alias_components_recursive(self.cx, alias_ty, &mut subcomponents);
167 self.out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
168 }
169 }
170
171 // We assume that inference variables are fully resolved.
172 // So, if we encounter an inference variable, just record
173 // the unresolved variable as a component.
174 ty::Infer(infer_ty) => {
175 self.out.push(Component::UnresolvedInferenceVariable(infer_ty));
176 }
177
178 // Most types do not introduce any region binders, nor
179 // involve any other subtle cases, and so the WF relation
180 // simply constraints any regions referenced directly by
181 // the type and then visits the types that are lexically
182 // contained within.
183 ty::Bool
184 | ty::Char
185 | ty::Int(_)
186 | ty::Uint(_)
187 | ty::Float(_)
188 | ty::Str
189 | ty::Never
190 | ty::Error(_) => {
191 // Trivial
192 }
193
194 ty::Bound(_, _) => {
195 // FIXME: Bound vars matter here!
196 }
197
198 ty::Adt(_, _)
199 | ty::Foreign(_)
200 | ty::Array(_, _)
201 | ty::Pat(_, _)
202 | ty::Slice(_)
203 | ty::RawPtr(_, _)
204 | ty::Ref(_, _, _)
205 | ty::FnPtr(..)
206 | ty::UnsafeBinder(_)
207 | ty::Dynamic(_, _)
208 | ty::Tuple(_) => {
209 ty.super_visit_with(self);
210 }
211 }
212 }
213
214 fn visit_region(&mut self, lt: Region<I>) -> Self::Result {
215 if !lt.is_bound() {
216 self.out.push(Component::Region(lt));
217 }
218 }
219}
220
221/// Collect [Component]s for *all* the args of `alias_ty`.
222///
223/// This should not be used to get the components of `alias_ty` itself.
224/// Use [push_outlives_components] instead.
225pub fn compute_alias_components_recursive<I: Interner>(
226 cx: I,
227 alias_ty: ty::AliasTy<I>,
228 out: &mut SmallVec<[Component<I>; 4]>,
229) {
230 let opt_variances = cx.opt_alias_variances(alias_ty.kind);
231
232 let mut visitor = OutlivesCollector { cx, out, visited: Default::default() };
233
234 for (index, child) in alias_ty.args.iter().enumerate() {
235 if opt_variances.and_then(|variances| variances.get(index)) == Some(ty::Bivariant) {
236 continue;
237 }
238 child.visit_with(&mut visitor);
239 }
240}
241
242/// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
243/// declared in the trait definition. For example, if the trait were
244///
245/// ```rust
246/// trait Foo<'a> {
247/// type Bar: 'a;
248/// }
249/// ```
250///
251/// If we were given `<T as Foo<'b>>::Bar`, we would return
252/// `'b`. This doesn't work for higher-ranked bounds such as:
253///
254/// ```ignore (this does compile today, previously was marked as compile_fail,E0311)
255/// trait Foo<'a, 'b>
256/// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
257/// {
258/// type Bar;
259/// }
260/// ```
261///
262/// This is for simplicity, and because we are not really smart
263/// enough to cope with such bounds anywhere.
264pub fn declared_bounds_from_definition<I: Interner>(
265 cx: I,
266 alias_ty: AliasTy<I>,
267) -> impl Iterator<Item = Region<I>> {
268 let def_id = match alias_ty.kind {
269 ty::AliasTyKind::Projection { def_id } => def_id.into(),
270 ty::AliasTyKind::Inherent { def_id } => def_id.into(),
271 ty::AliasTyKind::Opaque { def_id } => def_id.into(),
272 ty::AliasTyKind::Free { def_id } => def_id.into(),
273 };
274
275 let bounds = cx.item_self_bounds(def_id);
276 bounds
277 .iter_instantiated(cx, alias_ty.args)
278 .map(Unnormalized::skip_norm_wip)
279 .filter_map(|p| p.as_type_outlives_clause())
280 .filter_map(|p| p.no_bound_vars())
281 .map(|OutlivesPredicate(_, r)| r)
282}