]>
Commit | Line | Data |
---|---|---|
f9f354fc | 1 | use crate::infer::free_regions::FreeRegionMap; |
9fa01778 | 2 | use crate::infer::{GenericKind, InferCtxt}; |
ba9703b0 | 3 | use crate::traits::query::OutlivesBound; |
dfeec247 XL |
4 | use rustc_data_structures::fx::FxHashMap; |
5 | use rustc_hir as hir; | |
ba9703b0 | 6 | use rustc_middle::ty; |
ba9703b0 XL |
7 | |
8 | use super::explicit_outlives_bounds; | |
abe05a73 XL |
9 | |
10 | /// The `OutlivesEnvironment` collects information about what outlives | |
11 | /// what in a given type-checking setting. For example, if we have a | |
12 | /// where-clause like `where T: 'a` in scope, then the | |
13 | /// `OutlivesEnvironment` would record that (in its | |
14 | /// `region_bound_pairs` field). Similarly, it contains methods for | |
15 | /// processing and adding implied bounds into the outlives | |
16 | /// environment. | |
17 | /// | |
18 | /// Other code at present does not typically take a | |
19 | /// `&OutlivesEnvironment`, but rather takes some of its fields (e.g., | |
20 | /// `process_registered_region_obligations` wants the | |
21 | /// region-bound-pairs). There is no mistaking it: the current setup | |
22 | /// of tracking region information is quite scattered! The | |
23 | /// `OutlivesEnvironment`, for example, needs to sometimes be combined | |
24 | /// with the `middle::RegionRelations`, to yield a full picture of how | |
25 | /// (lexical) lifetimes interact. However, I'm reluctant to do more | |
26 | /// refactoring here, since the setup with NLL is quite different. | |
27 | /// For example, NLL has no need of `RegionRelations`, and is solely | |
28 | /// interested in the `OutlivesEnvironment`. -nmatsakis | |
29 | #[derive(Clone)] | |
30 | pub struct OutlivesEnvironment<'tcx> { | |
416331ca | 31 | pub param_env: ty::ParamEnv<'tcx>, |
abe05a73 | 32 | free_region_map: FreeRegionMap<'tcx>, |
0bf4aa26 XL |
33 | |
34 | // Contains, for each body B that we are checking (that is, the fn | |
35 | // item, but also any nested closures), the set of implied region | |
36 | // bounds that are in scope in that particular body. | |
37 | // | |
38 | // Example: | |
39 | // | |
40 | // ``` | |
41 | // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) { | |
42 | // bar(x, y, |y: &'b T| { .. } // body B1) | |
43 | // } // body B0 | |
44 | // ``` | |
45 | // | |
46 | // Here, for body B0, the list would be `[T: 'a]`, because we | |
47 | // infer that `T` must outlive `'a` from the implied bounds on the | |
48 | // fn declaration. | |
49 | // | |
50 | // For the body B1, the list would be `[T: 'a, T: 'b]`, because we | |
51 | // also can see that -- within the closure body! -- `T` must | |
52 | // outlive `'b`. This is not necessarily true outside the closure | |
53 | // body, since the closure may never be called. | |
54 | // | |
55 | // We collect this map as we descend the tree. We then use the | |
56 | // results when proving outlives obligations like `T: 'x` later | |
57 | // (e.g., if `T: 'x` must be proven within the body B1, then we | |
58 | // know it is true if either `'a: 'x` or `'b: 'x`). | |
9fa01778 | 59 | region_bound_pairs_map: FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>, |
0bf4aa26 XL |
60 | |
61 | // Used to compute `region_bound_pairs_map`: contains the set of | |
62 | // in-scope region-bound pairs thus far. | |
63 | region_bound_pairs_accum: RegionBoundPairs<'tcx>, | |
abe05a73 XL |
64 | } |
65 | ||
0bf4aa26 | 66 | /// "Region-bound pairs" tracks outlives relations that are known to |
9fa01778 | 67 | /// be true, either because of explicit where-clauses like `T: 'a` or |
0bf4aa26 XL |
68 | /// because of implied bounds. |
69 | pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>; | |
70 | ||
dc9dc135 | 71 | impl<'a, 'tcx> OutlivesEnvironment<'tcx> { |
abe05a73 | 72 | pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { |
ff7c6d11 | 73 | let mut env = OutlivesEnvironment { |
abe05a73 | 74 | param_env, |
0bf4aa26 XL |
75 | free_region_map: Default::default(), |
76 | region_bound_pairs_map: Default::default(), | |
77 | region_bound_pairs_accum: vec![], | |
ff7c6d11 XL |
78 | }; |
79 | ||
ba9703b0 | 80 | env.add_outlives_bounds(None, explicit_outlives_bounds(param_env)); |
ff7c6d11 XL |
81 | |
82 | env | |
abe05a73 XL |
83 | } |
84 | ||
85 | /// Borrows current value of the `free_region_map`. | |
86 | pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> { | |
87 | &self.free_region_map | |
88 | } | |
89 | ||
90 | /// Borrows current value of the `region_bound_pairs`. | |
9fa01778 | 91 | pub fn region_bound_pairs_map(&self) -> &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>> { |
0bf4aa26 | 92 | &self.region_bound_pairs_map |
abe05a73 XL |
93 | } |
94 | ||
95 | /// Returns ownership of the `free_region_map`. | |
96 | pub fn into_free_region_map(self) -> FreeRegionMap<'tcx> { | |
97 | self.free_region_map | |
98 | } | |
99 | ||
100 | /// This is a hack to support the old-skool regionck, which | |
101 | /// processes region constraints from the main function and the | |
102 | /// closure together. In that context, when we enter a closure, we | |
103 | /// want to be able to "save" the state of the surrounding a | |
104 | /// function. We can then add implied bounds and the like from the | |
105 | /// closure arguments into the environment -- these should only | |
106 | /// apply in the closure body, so once we exit, we invoke | |
107 | /// `pop_snapshot_post_closure` to remove them. | |
108 | /// | |
109 | /// Example: | |
110 | /// | |
111 | /// ``` | |
112 | /// fn foo<T>() { | |
113 | /// callback(for<'a> |x: &'a T| { | |
114 | /// // ^^^^^^^ not legal syntax, but probably should be | |
115 | /// // within this closure body, `T: 'a` holds | |
116 | /// }) | |
117 | /// } | |
118 | /// ``` | |
119 | /// | |
120 | /// This "containment" of closure's effects only works so well. In | |
121 | /// particular, we (intentionally) leak relationships between free | |
122 | /// regions that are created by the closure's bounds. The case | |
123 | /// where this is useful is when you have (e.g.) a closure with a | |
124 | /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this | |
125 | /// case, we want to keep the relationship `'b: 'a` in the | |
126 | /// free-region-map, so that later if we have to take `LUB('b, | |
127 | /// 'a)` we can get the result `'b`. | |
128 | /// | |
129 | /// I have opted to keep **all modifications** to the | |
130 | /// free-region-map, however, and not just those that concern free | |
131 | /// variables bound in the closure. The latter seems more correct, | |
132 | /// but it is not the existing behavior, and I could not find a | |
133 | /// case where the existing behavior went wrong. In any case, it | |
134 | /// seems like it'd be readily fixed if we wanted. There are | |
135 | /// similar leaks around givens that seem equally suspicious, to | |
136 | /// be honest. --nmatsakis | |
137 | pub fn push_snapshot_pre_closure(&self) -> usize { | |
0bf4aa26 | 138 | self.region_bound_pairs_accum.len() |
abe05a73 XL |
139 | } |
140 | ||
141 | /// See `push_snapshot_pre_closure`. | |
142 | pub fn pop_snapshot_post_closure(&mut self, len: usize) { | |
0bf4aa26 | 143 | self.region_bound_pairs_accum.truncate(len); |
abe05a73 XL |
144 | } |
145 | ||
0bf4aa26 | 146 | /// Save the current set of region-bound pairs under the given `body_id`. |
9fa01778 | 147 | pub fn save_implied_bounds(&mut self, body_id: hir::HirId) { |
dfeec247 XL |
148 | let old = |
149 | self.region_bound_pairs_map.insert(body_id, self.region_bound_pairs_accum.clone()); | |
0bf4aa26 XL |
150 | assert!(old.is_none()); |
151 | } | |
152 | ||
ff7c6d11 XL |
153 | /// Processes outlives bounds that are known to hold, whether from implied or other sources. |
154 | /// | |
155 | /// The `infcx` parameter is optional; if the implied bounds may | |
156 | /// contain inference variables, it must be supplied, in which | |
157 | /// case we will register "givens" on the inference context. (See | |
158 | /// `RegionConstraintData`.) | |
ba9703b0 XL |
159 | pub fn add_outlives_bounds<I>( |
160 | &mut self, | |
161 | infcx: Option<&InferCtxt<'a, 'tcx>>, | |
162 | outlives_bounds: I, | |
163 | ) where | |
ff7c6d11 XL |
164 | I: IntoIterator<Item = OutlivesBound<'tcx>>, |
165 | { | |
166 | // Record relationships such as `T:'x` that don't go into the | |
167 | // free-region-map but which we use here. | |
168 | for outlives_bound in outlives_bounds { | |
169 | debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); | |
170 | match outlives_bound { | |
ba9703b0 XL |
171 | OutlivesBound::RegionSubRegion( |
172 | r_a @ (&ty::ReEarlyBound(_) | &ty::ReFree(_)), | |
173 | &ty::ReVar(vid_b), | |
174 | ) => { | |
dfeec247 | 175 | infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b); |
abe05a73 | 176 | } |
ff7c6d11 | 177 | OutlivesBound::RegionSubParam(r_a, param_b) => { |
dfeec247 | 178 | self.region_bound_pairs_accum.push((r_a, GenericKind::Param(param_b))); |
abe05a73 | 179 | } |
ff7c6d11 | 180 | OutlivesBound::RegionSubProjection(r_a, projection_b) => { |
0bf4aa26 | 181 | self.region_bound_pairs_accum |
ff7c6d11 XL |
182 | .push((r_a, GenericKind::Projection(projection_b))); |
183 | } | |
184 | OutlivesBound::RegionSubRegion(r_a, r_b) => { | |
185 | // In principle, we could record (and take | |
186 | // advantage of) every relationship here, but | |
187 | // we are also free not to -- it simply means | |
188 | // strictly less that we can successfully type | |
189 | // check. Right now we only look for things | |
190 | // relationships between free regions. (It may | |
191 | // also be that we should revise our inference | |
192 | // system to be more general and to make use | |
193 | // of *every* relationship that arises here, | |
194 | // but presently we do not.) | |
195 | self.free_region_map.relate_regions(r_a, r_b); | |
196 | } | |
197 | } | |
198 | } | |
abe05a73 XL |
199 | } |
200 | } |