about summary refs log tree commit diff
path: root/compiler/rustc_infer/src/infer/outlives/env.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_infer/src/infer/outlives/env.rs')
-rw-r--r--compiler/rustc_infer/src/infer/outlives/env.rs200
1 files changed, 200 insertions, 0 deletions
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs
new file mode 100644
index 00000000000..1a9e20e79fe
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -0,0 +1,200 @@
+use crate::infer::free_regions::FreeRegionMap;
+use crate::infer::{GenericKind, InferCtxt};
+use crate::traits::query::OutlivesBound;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_middle::ty;
+
+use super::explicit_outlives_bounds;
+
+/// The `OutlivesEnvironment` collects information about what outlives
+/// what in a given type-checking setting. For example, if we have a
+/// where-clause like `where T: 'a` in scope, then the
+/// `OutlivesEnvironment` would record that (in its
+/// `region_bound_pairs` field). Similarly, it contains methods for
+/// processing and adding implied bounds into the outlives
+/// environment.
+///
+/// Other code at present does not typically take a
+/// `&OutlivesEnvironment`, but rather takes some of its fields (e.g.,
+/// `process_registered_region_obligations` wants the
+/// region-bound-pairs). There is no mistaking it: the current setup
+/// of tracking region information is quite scattered! The
+/// `OutlivesEnvironment`, for example, needs to sometimes be combined
+/// with the `middle::RegionRelations`, to yield a full picture of how
+/// (lexical) lifetimes interact. However, I'm reluctant to do more
+/// refactoring here, since the setup with NLL is quite different.
+/// For example, NLL has no need of `RegionRelations`, and is solely
+/// interested in the `OutlivesEnvironment`. -nmatsakis
+#[derive(Clone)]
+pub struct OutlivesEnvironment<'tcx> {
+    pub param_env: ty::ParamEnv<'tcx>,
+    free_region_map: FreeRegionMap<'tcx>,
+
+    // Contains, for each body B that we are checking (that is, the fn
+    // item, but also any nested closures), the set of implied region
+    // bounds that are in scope in that particular body.
+    //
+    // Example:
+    //
+    // ```
+    // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) {
+    //   bar(x, y, |y: &'b T| { .. } // body B1)
+    // } // body B0
+    // ```
+    //
+    // Here, for body B0, the list would be `[T: 'a]`, because we
+    // infer that `T` must outlive `'a` from the implied bounds on the
+    // fn declaration.
+    //
+    // For the body B1, the list would be `[T: 'a, T: 'b]`, because we
+    // also can see that -- within the closure body! -- `T` must
+    // outlive `'b`. This is not necessarily true outside the closure
+    // body, since the closure may never be called.
+    //
+    // We collect this map as we descend the tree. We then use the
+    // results when proving outlives obligations like `T: 'x` later
+    // (e.g., if `T: 'x` must be proven within the body B1, then we
+    // know it is true if either `'a: 'x` or `'b: 'x`).
+    region_bound_pairs_map: FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>,
+
+    // Used to compute `region_bound_pairs_map`: contains the set of
+    // in-scope region-bound pairs thus far.
+    region_bound_pairs_accum: RegionBoundPairs<'tcx>,
+}
+
+/// "Region-bound pairs" tracks outlives relations that are known to
+/// be true, either because of explicit where-clauses like `T: 'a` or
+/// because of implied bounds.
+pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>;
+
+impl<'a, 'tcx> OutlivesEnvironment<'tcx> {
+    pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
+        let mut env = OutlivesEnvironment {
+            param_env,
+            free_region_map: Default::default(),
+            region_bound_pairs_map: Default::default(),
+            region_bound_pairs_accum: vec![],
+        };
+
+        env.add_outlives_bounds(None, explicit_outlives_bounds(param_env));
+
+        env
+    }
+
+    /// Borrows current value of the `free_region_map`.
+    pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> {
+        &self.free_region_map
+    }
+
+    /// Borrows current value of the `region_bound_pairs`.
+    pub fn region_bound_pairs_map(&self) -> &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>> {
+        &self.region_bound_pairs_map
+    }
+
+    /// Returns ownership of the `free_region_map`.
+    pub fn into_free_region_map(self) -> FreeRegionMap<'tcx> {
+        self.free_region_map
+    }
+
+    /// This is a hack to support the old-skool regionck, which
+    /// processes region constraints from the main function and the
+    /// closure together. In that context, when we enter a closure, we
+    /// want to be able to "save" the state of the surrounding a
+    /// function. We can then add implied bounds and the like from the
+    /// closure arguments into the environment -- these should only
+    /// apply in the closure body, so once we exit, we invoke
+    /// `pop_snapshot_post_closure` to remove them.
+    ///
+    /// Example:
+    ///
+    /// ```
+    /// fn foo<T>() {
+    ///    callback(for<'a> |x: &'a T| {
+    ///         // ^^^^^^^ not legal syntax, but probably should be
+    ///         // within this closure body, `T: 'a` holds
+    ///    })
+    /// }
+    /// ```
+    ///
+    /// This "containment" of closure's effects only works so well. In
+    /// particular, we (intentionally) leak relationships between free
+    /// regions that are created by the closure's bounds. The case
+    /// where this is useful is when you have (e.g.) a closure with a
+    /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this
+    /// case, we want to keep the relationship `'b: 'a` in the
+    /// free-region-map, so that later if we have to take `LUB('b,
+    /// 'a)` we can get the result `'b`.
+    ///
+    /// I have opted to keep **all modifications** to the
+    /// free-region-map, however, and not just those that concern free
+    /// variables bound in the closure. The latter seems more correct,
+    /// but it is not the existing behavior, and I could not find a
+    /// case where the existing behavior went wrong. In any case, it
+    /// seems like it'd be readily fixed if we wanted. There are
+    /// similar leaks around givens that seem equally suspicious, to
+    /// be honest. --nmatsakis
+    pub fn push_snapshot_pre_closure(&self) -> usize {
+        self.region_bound_pairs_accum.len()
+    }
+
+    /// See `push_snapshot_pre_closure`.
+    pub fn pop_snapshot_post_closure(&mut self, len: usize) {
+        self.region_bound_pairs_accum.truncate(len);
+    }
+
+    /// Save the current set of region-bound pairs under the given `body_id`.
+    pub fn save_implied_bounds(&mut self, body_id: hir::HirId) {
+        let old =
+            self.region_bound_pairs_map.insert(body_id, self.region_bound_pairs_accum.clone());
+        assert!(old.is_none());
+    }
+
+    /// Processes outlives bounds that are known to hold, whether from implied or other sources.
+    ///
+    /// The `infcx` parameter is optional; if the implied bounds may
+    /// contain inference variables, it must be supplied, in which
+    /// case we will register "givens" on the inference context. (See
+    /// `RegionConstraintData`.)
+    pub fn add_outlives_bounds<I>(
+        &mut self,
+        infcx: Option<&InferCtxt<'a, 'tcx>>,
+        outlives_bounds: I,
+    ) where
+        I: IntoIterator<Item = OutlivesBound<'tcx>>,
+    {
+        // Record relationships such as `T:'x` that don't go into the
+        // free-region-map but which we use here.
+        for outlives_bound in outlives_bounds {
+            debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
+            match outlives_bound {
+                OutlivesBound::RegionSubRegion(
+                    r_a @ (&ty::ReEarlyBound(_) | &ty::ReFree(_)),
+                    &ty::ReVar(vid_b),
+                ) => {
+                    infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b);
+                }
+                OutlivesBound::RegionSubParam(r_a, param_b) => {
+                    self.region_bound_pairs_accum.push((r_a, GenericKind::Param(param_b)));
+                }
+                OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+                    self.region_bound_pairs_accum
+                        .push((r_a, GenericKind::Projection(projection_b)));
+                }
+                OutlivesBound::RegionSubRegion(r_a, r_b) => {
+                    // In principle, we could record (and take
+                    // advantage of) every relationship here, but
+                    // we are also free not to -- it simply means
+                    // strictly less that we can successfully type
+                    // check. Right now we only look for things
+                    // relationships between free regions. (It may
+                    // also be that we should revise our inference
+                    // system to be more general and to make use
+                    // of *every* relationship that arises here,
+                    // but presently we do not.)
+                    self.free_region_map.relate_regions(r_a, r_b);
+                }
+            }
+        }
+    }
+}