]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/outlives/env.rs
Stabilize File::options()
[rust.git] / compiler / rustc_infer / src / infer / outlives / env.rs
1 use crate::infer::free_regions::FreeRegionMap;
2 use crate::infer::{GenericKind, InferCtxt};
3 use crate::traits::query::OutlivesBound;
4 use rustc_data_structures::fx::FxHashMap;
5 use rustc_hir as hir;
6 use rustc_middle::ty;
7
8 use super::explicit_outlives_bounds;
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> {
31     pub param_env: ty::ParamEnv<'tcx>,
32     free_region_map: FreeRegionMap<'tcx>,
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`).
59     region_bound_pairs_map: FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>,
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>,
64 }
65
66 /// "Region-bound pairs" tracks outlives relations that are known to
67 /// be true, either because of explicit where-clauses like `T: 'a` or
68 /// because of implied bounds.
69 pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>;
70
71 impl<'a, 'tcx> OutlivesEnvironment<'tcx> {
72     pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
73         let mut env = OutlivesEnvironment {
74             param_env,
75             free_region_map: Default::default(),
76             region_bound_pairs_map: Default::default(),
77             region_bound_pairs_accum: vec![],
78         };
79
80         env.add_outlives_bounds(None, explicit_outlives_bounds(param_env));
81
82         env
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`.
91     pub fn region_bound_pairs_map(&self) -> &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>> {
92         &self.region_bound_pairs_map
93     }
94
95     /// This is a hack to support the old-skool regionck, which
96     /// processes region constraints from the main function and the
97     /// closure together. In that context, when we enter a closure, we
98     /// want to be able to "save" the state of the surrounding a
99     /// function. We can then add implied bounds and the like from the
100     /// closure arguments into the environment -- these should only
101     /// apply in the closure body, so once we exit, we invoke
102     /// `pop_snapshot_post_closure` to remove them.
103     ///
104     /// Example:
105     ///
106     /// ```
107     /// fn foo<T>() {
108     ///    callback(for<'a> |x: &'a T| {
109     ///         // ^^^^^^^ not legal syntax, but probably should be
110     ///         // within this closure body, `T: 'a` holds
111     ///    })
112     /// }
113     /// ```
114     ///
115     /// This "containment" of closure's effects only works so well. In
116     /// particular, we (intentionally) leak relationships between free
117     /// regions that are created by the closure's bounds. The case
118     /// where this is useful is when you have (e.g.) a closure with a
119     /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this
120     /// case, we want to keep the relationship `'b: 'a` in the
121     /// free-region-map, so that later if we have to take `LUB('b,
122     /// 'a)` we can get the result `'b`.
123     ///
124     /// I have opted to keep **all modifications** to the
125     /// free-region-map, however, and not just those that concern free
126     /// variables bound in the closure. The latter seems more correct,
127     /// but it is not the existing behavior, and I could not find a
128     /// case where the existing behavior went wrong. In any case, it
129     /// seems like it'd be readily fixed if we wanted. There are
130     /// similar leaks around givens that seem equally suspicious, to
131     /// be honest. --nmatsakis
132     pub fn push_snapshot_pre_closure(&self) -> usize {
133         self.region_bound_pairs_accum.len()
134     }
135
136     /// See `push_snapshot_pre_closure`.
137     pub fn pop_snapshot_post_closure(&mut self, len: usize) {
138         self.region_bound_pairs_accum.truncate(len);
139     }
140
141     /// Save the current set of region-bound pairs under the given `body_id`.
142     pub fn save_implied_bounds(&mut self, body_id: hir::HirId) {
143         let old =
144             self.region_bound_pairs_map.insert(body_id, self.region_bound_pairs_accum.clone());
145         assert!(old.is_none());
146     }
147
148     /// Processes outlives bounds that are known to hold, whether from implied or other sources.
149     ///
150     /// The `infcx` parameter is optional; if the implied bounds may
151     /// contain inference variables, it must be supplied, in which
152     /// case we will register "givens" on the inference context. (See
153     /// `RegionConstraintData`.)
154     pub fn add_outlives_bounds<I>(
155         &mut self,
156         infcx: Option<&InferCtxt<'a, 'tcx>>,
157         outlives_bounds: I,
158     ) where
159         I: IntoIterator<Item = OutlivesBound<'tcx>>,
160     {
161         // Record relationships such as `T:'x` that don't go into the
162         // free-region-map but which we use here.
163         for outlives_bound in outlives_bounds {
164             debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
165             match outlives_bound {
166                 OutlivesBound::RegionSubRegion(
167                     r_a @ (&ty::ReEarlyBound(_) | &ty::ReFree(_)),
168                     &ty::ReVar(vid_b),
169                 ) => {
170                     infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b);
171                 }
172                 OutlivesBound::RegionSubParam(r_a, param_b) => {
173                     self.region_bound_pairs_accum.push((r_a, GenericKind::Param(param_b)));
174                 }
175                 OutlivesBound::RegionSubProjection(r_a, projection_b) => {
176                     self.region_bound_pairs_accum
177                         .push((r_a, GenericKind::Projection(projection_b)));
178                 }
179                 OutlivesBound::RegionSubRegion(r_a, r_b) => {
180                     // In principle, we could record (and take
181                     // advantage of) every relationship here, but
182                     // we are also free not to -- it simply means
183                     // strictly less that we can successfully type
184                     // check. Right now we only look for things
185                     // relationships between free regions. (It may
186                     // also be that we should revise our inference
187                     // system to be more general and to make use
188                     // of *every* relationship that arises here,
189                     // but presently we do not.)
190                     self.free_region_map.relate_regions(r_a, r_b);
191                 }
192             }
193         }
194     }
195 }