1 // The outlines relation `T: 'a` or `'a: 'b`. This code frequently
2 // refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
5 use rustc_data_structures
::sso
::SsoHashSet
;
6 use rustc_middle
::ty
::subst
::{GenericArg, GenericArgKind}
;
7 use rustc_middle
::ty
::{self, Ty, TyCtxt, TypeFoldable}
;
8 use smallvec
::{smallvec, SmallVec}
;
11 pub enum Component
<'tcx
> {
12 Region(ty
::Region
<'tcx
>),
14 UnresolvedInferenceVariable(ty
::InferTy
),
16 // Projections like `T::Foo` are tricky because a constraint like
17 // `T::Foo: 'a` can be satisfied in so many ways. There may be a
18 // where-clause that says `T::Foo: 'a`, or the defining trait may
19 // include a bound like `type Foo: 'static`, or -- in the most
20 // conservative way -- we can prove that `T: 'a` (more generally,
21 // that all components in the projection outlive `'a`). This code
22 // is not in a position to judge which is the best technique, so
23 // we just product the projection as a component and leave it to
24 // the consumer to decide (but see `EscapingProjection` below).
25 Projection(ty
::ProjectionTy
<'tcx
>),
27 // In the case where a projection has escaping regions -- meaning
28 // regions bound within the type itself -- we always use
29 // the most conservative rule, which requires that all components
30 // outlive the bound. So for example if we had a type like this:
32 // for<'a> Trait1< <T as Trait2<'a,'b>>::Foo >
33 // ~~~~~~~~~~~~~~~~~~~~~~~~~
35 // then the inner projection (underlined) has an escaping region
36 // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
37 // outlives `'b: 'c`, and we don't consider whether the trait
38 // declares that `Foo: 'static` etc. Therefore, we just return the
39 // free components of such a projection (in this case, `'b`).
41 // However, in the future, we may want to get smarter, and
42 // actually return a "higher-ranked projection" here. Therefore,
43 // we mark that these components are part of an escaping
44 // projection, so that implied bounds code can avoid relying on
45 // them. This gives us room to improve the regionck reasoning in
46 // the future without breaking backwards compat.
47 EscapingProjection(Vec
<Component
<'tcx
>>),
50 /// Push onto `out` all the things that must outlive `'a` for the condition
51 /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
52 pub fn push_outlives_components
<'tcx
>(
55 out
: &mut SmallVec
<[Component
<'tcx
>; 4]>,
57 let mut visited
= SsoHashSet
::new();
58 compute_components(tcx
, ty0
, out
, &mut visited
);
59 debug
!("components({:?}) = {:?}", ty0
, out
);
62 fn compute_components
<'tcx
>(
65 out
: &mut SmallVec
<[Component
<'tcx
>; 4]>,
66 visited
: &mut SsoHashSet
<GenericArg
<'tcx
>>,
68 // Descend through the types, looking for the various "base"
69 // components and collecting them into `out`. This is not written
70 // with `collect()` because of the need to sometimes skip subtrees
71 // in the `subtys` iterator (e.g., when encountering a
74 ty
::FnDef(_
, substs
) => {
75 // HACK(eddyb) ignore lifetimes found shallowly in `substs`.
76 // This is inconsistent with `ty::Adt` (including all substs)
77 // and with `ty::Closure` (ignoring all substs other than
78 // upvars, of which a `ty::FnDef` doesn't have any), but
79 // consistent with previous (accidental) behavior.
80 // See https://github.com/rust-lang/rust/issues/70917
81 // for further background and discussion.
83 match child
.unpack() {
84 GenericArgKind
::Type(ty
) => {
85 compute_components(tcx
, ty
, out
, visited
);
87 GenericArgKind
::Lifetime(_
) => {}
88 GenericArgKind
::Const(_
) => {
89 compute_components_recursive(tcx
, child
, out
, visited
);
95 ty
::Array(element
, _
) => {
96 // Don't look into the len const as it doesn't affect regions
97 compute_components(tcx
, element
, out
, visited
);
100 ty
::Closure(_
, ref substs
) => {
101 let tupled_ty
= substs
.as_closure().tupled_upvars_ty();
102 compute_components(tcx
, tupled_ty
, out
, visited
);
105 ty
::Generator(_
, ref substs
, _
) => {
106 // Same as the closure case
107 let tupled_ty
= substs
.as_generator().tupled_upvars_ty();
108 compute_components(tcx
, tupled_ty
, out
, visited
);
110 // We ignore regions in the generator interior as we don't
111 // want these to affect region inference
114 // All regions are bound inside a witness
115 ty
::GeneratorWitness(..) => (),
117 // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
118 // is implied by the environment is done in regionck.
120 out
.push(Component
::Param(p
));
123 // For projections, we prefer to generate an obligation like
124 // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
125 // regionck more ways to prove that it holds. However,
126 // regionck is not (at least currently) prepared to deal with
127 // higher-ranked regions that may appear in the
128 // trait-ref. Therefore, if we see any higher-ranke regions,
129 // we simply fallback to the most restrictive rule, which
130 // requires that `Pi: 'a` for all `i`.
131 ty
::Projection(ref data
) => {
132 if !data
.has_escaping_bound_vars() {
133 // best case: no escaping regions, so push the
134 // projection and skip the subtree (thus generating no
135 // constraints for Pi). This defers the choice between
136 // the rules OutlivesProjectionEnv,
137 // OutlivesProjectionTraitDef, and
138 // OutlivesProjectionComponents to regionck.
139 out
.push(Component
::Projection(*data
));
141 // fallback case: hard code
142 // OutlivesProjectionComponents. Continue walking
143 // through and constrain Pi.
144 let mut subcomponents
= smallvec
![];
145 let mut subvisited
= SsoHashSet
::new();
146 compute_components_recursive(tcx
, ty
.into(), &mut subcomponents
, &mut subvisited
);
147 out
.push(Component
::EscapingProjection(subcomponents
.into_iter().collect()));
151 // We assume that inference variables are fully resolved.
152 // So, if we encounter an inference variable, just record
153 // the unresolved variable as a component.
154 ty
::Infer(infer_ty
) => {
155 out
.push(Component
::UnresolvedInferenceVariable(infer_ty
));
158 // Most types do not introduce any region binders, nor
159 // involve any other subtle cases, and so the WF relation
160 // simply constraints any regions referenced directly by
161 // the type and then visits the types that are lexically
162 // contained within. (The comments refer to relevant rules
164 ty
::Bool
| // OutlivesScalar
165 ty
::Char
| // OutlivesScalar
166 ty
::Int(..) | // OutlivesScalar
167 ty
::Uint(..) | // OutlivesScalar
168 ty
::Float(..) | // OutlivesScalar
170 ty
::Adt(..) | // OutlivesNominalType
171 ty
::Opaque(..) | // OutlivesNominalType (ish)
172 ty
::Foreign(..) | // OutlivesNominalType
173 ty
::Str
| // OutlivesScalar (ish)
174 ty
::Slice(..) | // ...
175 ty
::RawPtr(..) | // ...
176 ty
::Ref(..) | // OutlivesReference
177 ty
::Tuple(..) | // ...
178 ty
::FnPtr(_
) | // OutlivesFunction (*)
179 ty
::Dynamic(..) | // OutlivesObject, OutlivesFragment (*)
180 ty
::Placeholder(..) |
183 // (*) Function pointers and trait objects are both binders.
184 // In the RFC, this means we would add the bound regions to
185 // the "bound regions list". In our representation, no such
186 // list is maintained explicitly, because bound regions
187 // themselves can be readily identified.
188 compute_components_recursive(tcx
, ty
.into(), out
, visited
);
193 fn compute_components_recursive
<'tcx
>(
195 parent
: GenericArg
<'tcx
>,
196 out
: &mut SmallVec
<[Component
<'tcx
>; 4]>,
197 visited
: &mut SsoHashSet
<GenericArg
<'tcx
>>,
199 for child
in parent
.walk_shallow(tcx
, visited
) {
200 match child
.unpack() {
201 GenericArgKind
::Type(ty
) => {
202 compute_components(tcx
, ty
, out
, visited
);
204 GenericArgKind
::Lifetime(lt
) => {
205 // Ignore late-bound regions.
206 if !lt
.is_late_bound() {
207 out
.push(Component
::Region(lt
));
210 GenericArgKind
::Const(_
) => {
211 compute_components_recursive(tcx
, child
, out
, visited
);