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