]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/outlives.rs
Rollup merge of #80133 - Aaron1011:fix/const-mut-deref, r=estebank
[rust.git] / compiler / rustc_middle / src / ty / outlives.rs
1 // The outlines 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
5 use crate::ty::subst::{GenericArg, GenericArgKind};
6 use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
7 use rustc_data_structures::sso::SsoHashSet;
8 use smallvec::SmallVec;
9
10 #[derive(Debug)]
11 pub enum Component<'tcx> {
12     Region(ty::Region<'tcx>),
13     Param(ty::ParamTy),
14     UnresolvedInferenceVariable(ty::InferTy),
15
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>),
26
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:
31     //
32     //     for<'a> Trait1<  <T as Trait2<'a,'b>>::Foo  >
33     //                      ~~~~~~~~~~~~~~~~~~~~~~~~~
34     //
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`).
40     //
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>>),
48 }
49
50 impl<'tcx> TyCtxt<'tcx> {
51     /// Push onto `out` all the things that must outlive `'a` for the condition
52     /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
53     pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) {
54         let mut visited = SsoHashSet::new();
55         compute_components(self, ty0, out, &mut visited);
56         debug!("components({:?}) = {:?}", ty0, out);
57     }
58 }
59
60 fn compute_components(
61     tcx: TyCtxt<'tcx>,
62     ty: Ty<'tcx>,
63     out: &mut SmallVec<[Component<'tcx>; 4]>,
64     visited: &mut SsoHashSet<GenericArg<'tcx>>,
65 ) {
66     // Descend through the types, looking for the various "base"
67     // components and collecting them into `out`. This is not written
68     // with `collect()` because of the need to sometimes skip subtrees
69     // in the `subtys` iterator (e.g., when encountering a
70     // projection).
71     match *ty.kind() {
72             ty::FnDef(_, substs) => {
73                 // HACK(eddyb) ignore lifetimes found shallowly in `substs`.
74                 // This is inconsistent with `ty::Adt` (including all substs)
75                 // and with `ty::Closure` (ignoring all substs other than
76                 // upvars, of which a `ty::FnDef` doesn't have any), but
77                 // consistent with previous (accidental) behavior.
78                 // See https://github.com/rust-lang/rust/issues/70917
79                 // for further background and discussion.
80                 for child in substs {
81                     match child.unpack() {
82                         GenericArgKind::Type(ty) => {
83                             compute_components(tcx, ty, out, visited);
84                         }
85                         GenericArgKind::Lifetime(_) => {}
86                         GenericArgKind::Const(_) => {
87                             compute_components_recursive(tcx, child, out, visited);
88                         }
89                     }
90                 }
91             }
92
93             ty::Array(element, _) => {
94                 // Don't look into the len const as it doesn't affect regions
95                 compute_components(tcx, element, out, visited);
96             }
97
98             ty::Closure(_, ref substs) => {
99                 let tupled_ty = substs.as_closure().tupled_upvars_ty();
100                 compute_components(tcx, tupled_ty, out, visited);
101             }
102
103             ty::Generator(_, ref substs, _) => {
104                 // Same as the closure case
105                 let tupled_ty = substs.as_generator().tupled_upvars_ty();
106                 compute_components(tcx, tupled_ty, out, visited);
107
108                 // We ignore regions in the generator interior as we don't
109                 // want these to affect region inference
110             }
111
112             // All regions are bound inside a witness
113             ty::GeneratorWitness(..) => (),
114
115             // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
116             // is implied by the environment is done in regionck.
117             ty::Param(p) => {
118                 out.push(Component::Param(p));
119             }
120
121             // For projections, we prefer to generate an obligation like
122             // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
123             // regionck more ways to prove that it holds. However,
124             // regionck is not (at least currently) prepared to deal with
125             // higher-ranked regions that may appear in the
126             // trait-ref. Therefore, if we see any higher-ranke regions,
127             // we simply fallback to the most restrictive rule, which
128             // requires that `Pi: 'a` for all `i`.
129             ty::Projection(ref data) => {
130                 if !data.has_escaping_bound_vars() {
131                     // best case: no escaping regions, so push the
132                     // projection and skip the subtree (thus generating no
133                     // constraints for Pi). This defers the choice between
134                     // the rules OutlivesProjectionEnv,
135                     // OutlivesProjectionTraitDef, and
136                     // OutlivesProjectionComponents to regionck.
137                     out.push(Component::Projection(*data));
138                 } else {
139                     // fallback case: hard code
140                     // OutlivesProjectionComponents.  Continue walking
141                     // through and constrain Pi.
142                     let mut subcomponents = smallvec![];
143                     let mut subvisited = SsoHashSet::new();
144                     compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited);
145                     out.push(Component::EscapingProjection(subcomponents.into_iter().collect()));
146                 }
147             }
148
149             // We assume that inference variables are fully resolved.
150             // So, if we encounter an inference variable, just record
151             // the unresolved variable as a component.
152             ty::Infer(infer_ty) => {
153                 out.push(Component::UnresolvedInferenceVariable(infer_ty));
154             }
155
156             // Most types do not introduce any region binders, nor
157             // involve any other subtle cases, and so the WF relation
158             // simply constraints any regions referenced directly by
159             // the type and then visits the types that are lexically
160             // contained within. (The comments refer to relevant rules
161             // from RFC1214.)
162             ty::Bool |            // OutlivesScalar
163             ty::Char |            // OutlivesScalar
164             ty::Int(..) |         // OutlivesScalar
165             ty::Uint(..) |        // OutlivesScalar
166             ty::Float(..) |       // OutlivesScalar
167             ty::Never |           // ...
168             ty::Adt(..) |         // OutlivesNominalType
169             ty::Opaque(..) |      // OutlivesNominalType (ish)
170             ty::Foreign(..) |     // OutlivesNominalType
171             ty::Str |             // OutlivesScalar (ish)
172             ty::Slice(..) |       // ...
173             ty::RawPtr(..) |      // ...
174             ty::Ref(..) |         // OutlivesReference
175             ty::Tuple(..) |       // ...
176             ty::FnPtr(_) |        // OutlivesFunction (*)
177             ty::Dynamic(..) |     // OutlivesObject, OutlivesFragment (*)
178             ty::Placeholder(..) |
179             ty::Bound(..) |
180             ty::Error(_) => {
181                 // (*) Function pointers and trait objects are both binders.
182                 // In the RFC, this means we would add the bound regions to
183                 // the "bound regions list".  In our representation, no such
184                 // list is maintained explicitly, because bound regions
185                 // themselves can be readily identified.
186                 compute_components_recursive(tcx, ty.into(), out, visited);
187             }
188         }
189 }
190
191 fn compute_components_recursive(
192     tcx: TyCtxt<'tcx>,
193     parent: GenericArg<'tcx>,
194     out: &mut SmallVec<[Component<'tcx>; 4]>,
195     visited: &mut SsoHashSet<GenericArg<'tcx>>,
196 ) {
197     for child in parent.walk_shallow(visited) {
198         match child.unpack() {
199             GenericArgKind::Type(ty) => {
200                 compute_components(tcx, ty, out, visited);
201             }
202             GenericArgKind::Lifetime(lt) => {
203                 // Ignore late-bound regions.
204                 if !lt.is_late_bound() {
205                     out.push(Component::Region(lt));
206                 }
207             }
208             GenericArgKind::Const(_) => {
209                 compute_components_recursive(tcx, child, out, visited);
210             }
211         }
212     }
213 }