]> git.lizzy.rs Git - rust.git/commitdiff
promote region_infer into its own module
authorNiko Matsakis <niko@alum.mit.edu>
Mon, 20 Nov 2017 21:37:33 +0000 (16:37 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Mon, 4 Dec 2017 13:51:13 +0000 (08:51 -0500)
src/librustc_mir/borrow_check/nll/region_infer.rs [deleted file]
src/librustc_mir/borrow_check/nll/region_infer/mod.rs [new file with mode: 0644]

diff --git a/src/librustc_mir/borrow_check/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer.rs
deleted file mode 100644 (file)
index b304ea4..0000000
+++ /dev/null
@@ -1,542 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use super::free_regions::FreeRegions;
-use rustc::infer::InferCtxt;
-use rustc::infer::RegionVariableOrigin;
-use rustc::infer::NLLRegionVariableOrigin;
-use rustc::infer::region_constraints::VarOrigins;
-use rustc::infer::outlives::free_region_map::FreeRegionMap;
-use rustc::mir::{Location, Mir};
-use rustc::ty::{self, RegionVid};
-use rustc_data_structures::indexed_vec::IndexVec;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::bitvec::BitMatrix;
-use rustc_data_structures::indexed_vec::Idx;
-use std::collections::BTreeMap;
-use std::fmt;
-use syntax_pos::Span;
-
-pub struct RegionInferenceContext<'tcx> {
-    /// Contains the definition for every region variable.  Region
-    /// variables are identified by their index (`RegionVid`). The
-    /// definition contains information about where the region came
-    /// from as well as its final inferred value.
-    definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
-
-    /// The liveness constraints added to each region. For most
-    /// regions, these start out empty and steadily grow, though for
-    /// each free region R they start out containing the entire CFG
-    /// and `end(R)`.
-    ///
-    /// In this `BitMatrix` representation, the rows are the region
-    /// variables and the columns are the free regions and MIR locations.
-    liveness_constraints: BitMatrix,
-
-    /// The final inferred values of the inference variables; `None`
-    /// until `solve` is invoked.
-    inferred_values: Option<BitMatrix>,
-
-    /// The constraints we have accumulated and used during solving.
-    constraints: Vec<Constraint>,
-
-    /// A map from each MIR Location to its column index in
-    /// `liveness_constraints`/`inferred_values`. (The first N columns are
-    /// the free regions.)
-    point_indices: BTreeMap<Location, usize>,
-
-    num_free_regions: usize,
-
-    free_region_map: &'tcx FreeRegionMap<'tcx>,
-}
-
-struct RegionDefinition<'tcx> {
-    /// Why we created this variable. Mostly these will be
-    /// `RegionVariableOrigin::NLL`, but some variables get created
-    /// elsewhere in the code with other causes (e.g., instantiation
-    /// late-bound-regions).
-    origin: RegionVariableOrigin,
-
-    /// If this is a free-region, then this is `Some(X)` where `X` is
-    /// the name of the region.
-    name: Option<ty::Region<'tcx>>,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct Constraint {
-    // NB. The ordering here is not significant for correctness, but
-    // it is for convenience. Before we dump the constraints in the
-    // debugging logs, we sort them, and we'd like the "super region"
-    // to be first, etc. (In particular, span should remain last.)
-    /// The region SUP must outlive SUB...
-    sup: RegionVid,
-
-    /// Region that must be outlived.
-    sub: RegionVid,
-
-    /// At this location.
-    point: Location,
-
-    /// Where did this constraint arise?
-    span: Span,
-}
-
-impl<'tcx> RegionInferenceContext<'tcx> {
-    /// Creates a new region inference context with a total of
-    /// `num_region_variables` valid inference variables; the first N
-    /// of those will be constant regions representing the free
-    /// regions defined in `free_regions`.
-    pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self {
-        let num_region_variables = var_origins.len();
-        let num_free_regions = free_regions.indices.len();
-
-        let mut num_points = 0;
-        let mut point_indices = BTreeMap::new();
-
-        for (block, block_data) in mir.basic_blocks().iter_enumerated() {
-            for statement_index in 0..block_data.statements.len() + 1 {
-                let location = Location {
-                    block,
-                    statement_index,
-                };
-                point_indices.insert(location, num_free_regions + num_points);
-                num_points += 1;
-            }
-        }
-
-        // Create a RegionDefinition for each inference variable.
-        let definitions = var_origins
-            .into_iter()
-            .map(|origin| RegionDefinition::new(origin))
-            .collect();
-
-        let mut result = Self {
-            definitions,
-            liveness_constraints: BitMatrix::new(
-                num_region_variables,
-                num_free_regions + num_points,
-            ),
-            inferred_values: None,
-            constraints: Vec::new(),
-            point_indices,
-            num_free_regions,
-            free_region_map: free_regions.free_region_map,
-        };
-
-        result.init_free_regions(free_regions);
-
-        result
-    }
-
-    /// Initializes the region variables for each free region
-    /// (lifetime parameter). The first N variables always correspond
-    /// to the free regions appearing in the function signature (both
-    /// named and anonymous) and where clauses. This function iterates
-    /// over those regions and initializes them with minimum values.
-    ///
-    /// For example:
-    ///
-    ///     fn foo<'a, 'b>(..) where 'a: 'b
-    ///
-    /// would initialize two variables like so:
-    ///
-    ///     R0 = { CFG, R0 } // 'a
-    ///     R1 = { CFG, R0, R1 } // 'b
-    ///
-    /// Here, R0 represents `'a`, and it contains (a) the entire CFG
-    /// and (b) any free regions that it outlives, which in this case
-    /// is just itself. R1 (`'b`) in contrast also outlives `'a` and
-    /// hence contains R0 and R1.
-    fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>) {
-        let FreeRegions {
-            indices,
-            free_region_map: _,
-        } = free_regions;
-
-        // For each free region X:
-        for (free_region, &variable) in indices {
-            // These should be free-region variables.
-            assert!(match self.definitions[variable].origin {
-                RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
-                _ => false,
-            });
-
-            // Initialize the name and a few other details.
-            self.definitions[variable].name = Some(free_region);
-
-            // Add all nodes in the CFG to liveness constraints
-            for (_location, point_index) in &self.point_indices {
-                self.liveness_constraints
-                    .add(variable.index(), *point_index);
-            }
-
-            // Add `end(X)` into the set for X.
-            self.liveness_constraints
-                .add(variable.index(), variable.index());
-        }
-    }
-
-    /// Returns an iterator over all the region indices.
-    pub fn regions(&self) -> impl Iterator<Item = RegionVid> {
-        self.definitions.indices()
-    }
-
-    /// Returns true if the region `r` contains the point `p`.
-    ///
-    /// Panics if called before `solve()` executes,
-    pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool {
-        let inferred_values = self.inferred_values
-            .as_ref()
-            .expect("region values not yet inferred");
-        self.region_contains_point_in_matrix(inferred_values, r, p)
-    }
-
-    /// True if given region `r` contains the point `p`, when
-    /// evaluated in the set of region values `matrix`.
-    fn region_contains_point_in_matrix(
-        &self,
-        matrix: &BitMatrix,
-        r: RegionVid,
-        p: Location,
-    ) -> bool {
-        let point_index = self.point_indices
-            .get(&p)
-            .expect("point index should be known");
-        matrix.contains(r.index(), *point_index)
-    }
-
-    /// True if given region `r` contains the `end(s)`, when
-    /// evaluated in the set of region values `matrix`.
-    fn region_contains_region_in_matrix(
-        &self,
-        matrix: &BitMatrix,
-        r: RegionVid,
-        s: RegionVid
-    ) -> bool {
-        matrix.contains(r.index(), s.index())
-    }
-
-    /// Returns access to the value of `r` for debugging purposes.
-    pub(super) fn region_value_str(&self, r: RegionVid) -> String {
-        let inferred_values = self.inferred_values
-            .as_ref()
-            .expect("region values not yet inferred");
-
-        let mut result = String::new();
-        result.push_str("{");
-        let mut sep = "";
-
-        for &point in self.point_indices.keys() {
-            if self.region_contains_point_in_matrix(inferred_values, r, point) {
-                result.push_str(&format!("{}{:?}", sep, point));
-                sep = ", ";
-            }
-        }
-
-        for fr in (0 .. self.num_free_regions).map(RegionVid::new) {
-            if self.region_contains_region_in_matrix(inferred_values, r, fr) {
-                result.push_str(&format!("{}{:?}", sep, fr));
-                sep = ", ";
-            }
-        }
-
-        result.push_str("}");
-
-        result
-    }
-
-    /// Indicates that the region variable `v` is live at the point `point`.
-    pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) -> bool {
-        debug!("add_live_point({:?}, {:?})", v, point);
-        assert!(self.inferred_values.is_none(), "values already inferred");
-        let point_index = self.point_indices
-            .get(&point)
-            .expect("point index should be known");
-        self.liveness_constraints.add(v.index(), *point_index)
-    }
-
-    /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
-    pub(super) fn add_outlives(
-        &mut self,
-        span: Span,
-        sup: RegionVid,
-        sub: RegionVid,
-        point: Location,
-    ) {
-        debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
-        assert!(self.inferred_values.is_none(), "values already inferred");
-        self.constraints.push(Constraint {
-            span,
-            sup,
-            sub,
-            point,
-        });
-    }
-
-    /// Perform region inference.
-    pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
-        assert!(self.inferred_values.is_none(), "values already inferred");
-
-        // Find the minimal regions that can solve the constraints. This is infallible.
-        self.propagate_constraints(mir);
-
-        // Now, see whether any of the constraints were too strong. In particular,
-        // we want to check for a case where a free region exceeded its bounds.
-        // Consider:
-        //
-        //     fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
-        //
-        // In this case, returning `x` requires `&'a u32 <: &'b u32`
-        // and hence we establish (transitively) a constraint that
-        // `'a: 'b`. The `propagate_constraints` code above will
-        // therefore add `end('a)` into the region for `'b` -- but we
-        // have no evidence that `'b` outlives `'a`, so we want to report
-        // an error.
-
-        // The free regions are always found in a prefix of the full list.
-        let free_region_definitions = self.definitions
-            .iter_enumerated()
-            .take_while(|(_, fr_definition)| fr_definition.name.is_some());
-
-        for (fr, fr_definition) in free_region_definitions {
-            self.check_free_region(infcx, fr, fr_definition);
-        }
-    }
-
-    fn check_free_region(
-        &self,
-        infcx: &InferCtxt<'_, '_, 'tcx>,
-        fr: RegionVid,
-        fr_definition: &RegionDefinition<'tcx>,
-    ) {
-        let inferred_values = self.inferred_values.as_ref().unwrap();
-        let fr_name = fr_definition.name.unwrap();
-        let fr_value = inferred_values.iter(fr.index());
-
-        // Find every region `o` such that `fr: o`
-        // (because `fr` includes `end(o)`).
-        for outlived_fr in fr_value.take_while(|&i| i < self.num_free_regions) {
-            // `fr` includes `end(fr)`, that's not especially
-            // interesting.
-            if fr.index() == outlived_fr {
-                continue;
-            }
-
-            let outlived_fr_definition = &self.definitions[RegionVid::new(outlived_fr)];
-            let outlived_fr_name = outlived_fr_definition.name.unwrap();
-
-            // Check that `o <= fr`. If not, report an error.
-            if !self.free_region_map
-                .sub_free_regions(outlived_fr_name, fr_name)
-            {
-                // FIXME: worst error msg ever
-                let blame_span = self.blame_span(fr, RegionVid::new(outlived_fr));
-                infcx.tcx.sess.span_err(
-                    blame_span,
-                    &format!(
-                        "free region `{}` does not outlive `{}`",
-                        fr_name,
-                        outlived_fr_name
-                    ),
-                );
-            }
-        }
-    }
-
-    /// Propagate the region constraints: this will grow the values
-    /// for each region variable until all the constraints are
-    /// satisfied. Note that some values may grow **too** large to be
-    /// feasible, but we check this later.
-    fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
-        let mut changed = true;
-
-        debug!("propagate_constraints()");
-        debug!("propagate_constraints: constraints={:#?}", {
-            let mut constraints: Vec<_> = self.constraints.iter().collect();
-            constraints.sort();
-            constraints
-        });
-
-        // The initial values for each region are derived from the liveness
-        // constraints we have accumulated.
-        let mut inferred_values = self.liveness_constraints.clone();
-
-        while changed {
-            changed = false;
-            debug!("propagate_constraints: --------------------");
-            for constraint in &self.constraints {
-                debug!("propagate_constraints: constraint={:?}", constraint);
-
-                // Grow the value as needed to accommodate the
-                // outlives constraint.
-
-                if self.copy(
-                    &mut inferred_values,
-                    mir,
-                    constraint.sub,
-                    constraint.sup,
-                    constraint.point,
-                ) {
-                    debug!("propagate_constraints:   sub={:?}", constraint.sub);
-                    debug!("propagate_constraints:   sup={:?}", constraint.sup);
-                    changed = true;
-                }
-            }
-            debug!("\n");
-        }
-
-        self.inferred_values = Some(inferred_values);
-    }
-
-    fn copy(
-        &self,
-        inferred_values: &mut BitMatrix,
-        mir: &Mir<'tcx>,
-        from_region: RegionVid,
-        to_region: RegionVid,
-        start_point: Location,
-    ) -> bool {
-        let mut changed = false;
-
-        let mut stack = vec![];
-        let mut visited = FxHashSet();
-
-        stack.push(start_point);
-        while let Some(p) = stack.pop() {
-            debug!("        copy: p={:?}", p);
-
-            if !self.region_contains_point_in_matrix(inferred_values, from_region, p) {
-                debug!("            not in from-region");
-                continue;
-            }
-
-            if !visited.insert(p) {
-                debug!("            already visited");
-                continue;
-            }
-
-            let point_index = self.point_indices.get(&p).unwrap();
-            changed |= inferred_values.add(to_region.index(), *point_index);
-
-            let block_data = &mir[p.block];
-            let successor_points = if p.statement_index < block_data.statements.len() {
-                vec![
-                    Location {
-                        statement_index: p.statement_index + 1,
-                        ..p
-                    },
-                ]
-            } else {
-                block_data
-                    .terminator()
-                    .successors()
-                    .iter()
-                    .map(|&basic_block| {
-                        Location {
-                            statement_index: 0,
-                            block: basic_block,
-                        }
-                    })
-                    .collect::<Vec<_>>()
-            };
-
-            if successor_points.is_empty() {
-                // If we reach the END point in the graph, then copy
-                // over any skolemized end points in the `from_region`
-                // and make sure they are included in the `to_region`.
-                let free_region_indices = inferred_values
-                    .iter(from_region.index())
-                    .take_while(|&i| i < self.num_free_regions)
-                    .collect::<Vec<_>>();
-                for fr in &free_region_indices {
-                    changed |= inferred_values.add(to_region.index(), *fr);
-                }
-            } else {
-                stack.extend(successor_points);
-            }
-        }
-
-        changed
-    }
-
-    /// Tries to finds a good span to blame for the fact that `fr1`
-    /// contains `fr2`.
-    fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
-        // Find everything that influenced final value of `fr`.
-        let influenced_fr1 = self.dependencies(fr1);
-
-        // Try to find some outlives constraint `'X: fr2` where `'X`
-        // influenced `fr1`. Blame that.
-        //
-        // NB, this is a pretty bad choice most of the time. In
-        // particular, the connection between `'X` and `fr1` may not
-        // be obvious to the user -- not to mention the naive notion
-        // of dependencies, which doesn't account for the locations of
-        // contraints at all. But it will do for now.
-        for constraint in &self.constraints {
-            if constraint.sub == fr2 && influenced_fr1[constraint.sup] {
-                return constraint.span;
-            }
-        }
-
-        bug!(
-            "could not find any constraint to blame for {:?}: {:?}",
-            fr1,
-            fr2
-        );
-    }
-
-    /// Finds all regions whose values `'a` may depend on in some way.
-    /// Basically if there exists a constraint `'a: 'b @ P`, then `'b`
-    /// and `dependencies('b)` will be in the final set.
-    ///
-    /// Used during error reporting, extremely naive and inefficient.
-    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, bool> {
-        let mut result_set = IndexVec::from_elem(false, &self.definitions);
-        let mut changed = true;
-        result_set[r0] = true;
-
-        while changed {
-            changed = false;
-            for constraint in &self.constraints {
-                if result_set[constraint.sup] {
-                    if !result_set[constraint.sub] {
-                        result_set[constraint.sub] = true;
-                        changed = true;
-                    }
-                }
-            }
-        }
-
-        result_set
-    }
-}
-
-impl<'tcx> RegionDefinition<'tcx> {
-    fn new(origin: RegionVariableOrigin) -> Self {
-        // Create a new region definition. Note that, for free
-        // regions, these fields get updated later in
-        // `init_free_regions`.
-        Self { origin, name: None }
-    }
-}
-
-impl fmt::Debug for Constraint {
-    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        write!(
-            formatter,
-            "({:?}: {:?} @ {:?}) due to {:?}",
-            self.sup,
-            self.sub,
-            self.point,
-            self.span
-        )
-    }
-}
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
new file mode 100644 (file)
index 0000000..b304ea4
--- /dev/null
@@ -0,0 +1,542 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::free_regions::FreeRegions;
+use rustc::infer::InferCtxt;
+use rustc::infer::RegionVariableOrigin;
+use rustc::infer::NLLRegionVariableOrigin;
+use rustc::infer::region_constraints::VarOrigins;
+use rustc::infer::outlives::free_region_map::FreeRegionMap;
+use rustc::mir::{Location, Mir};
+use rustc::ty::{self, RegionVid};
+use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::bitvec::BitMatrix;
+use rustc_data_structures::indexed_vec::Idx;
+use std::collections::BTreeMap;
+use std::fmt;
+use syntax_pos::Span;
+
+pub struct RegionInferenceContext<'tcx> {
+    /// Contains the definition for every region variable.  Region
+    /// variables are identified by their index (`RegionVid`). The
+    /// definition contains information about where the region came
+    /// from as well as its final inferred value.
+    definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
+
+    /// The liveness constraints added to each region. For most
+    /// regions, these start out empty and steadily grow, though for
+    /// each free region R they start out containing the entire CFG
+    /// and `end(R)`.
+    ///
+    /// In this `BitMatrix` representation, the rows are the region
+    /// variables and the columns are the free regions and MIR locations.
+    liveness_constraints: BitMatrix,
+
+    /// The final inferred values of the inference variables; `None`
+    /// until `solve` is invoked.
+    inferred_values: Option<BitMatrix>,
+
+    /// The constraints we have accumulated and used during solving.
+    constraints: Vec<Constraint>,
+
+    /// A map from each MIR Location to its column index in
+    /// `liveness_constraints`/`inferred_values`. (The first N columns are
+    /// the free regions.)
+    point_indices: BTreeMap<Location, usize>,
+
+    num_free_regions: usize,
+
+    free_region_map: &'tcx FreeRegionMap<'tcx>,
+}
+
+struct RegionDefinition<'tcx> {
+    /// Why we created this variable. Mostly these will be
+    /// `RegionVariableOrigin::NLL`, but some variables get created
+    /// elsewhere in the code with other causes (e.g., instantiation
+    /// late-bound-regions).
+    origin: RegionVariableOrigin,
+
+    /// If this is a free-region, then this is `Some(X)` where `X` is
+    /// the name of the region.
+    name: Option<ty::Region<'tcx>>,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Constraint {
+    // NB. The ordering here is not significant for correctness, but
+    // it is for convenience. Before we dump the constraints in the
+    // debugging logs, we sort them, and we'd like the "super region"
+    // to be first, etc. (In particular, span should remain last.)
+    /// The region SUP must outlive SUB...
+    sup: RegionVid,
+
+    /// Region that must be outlived.
+    sub: RegionVid,
+
+    /// At this location.
+    point: Location,
+
+    /// Where did this constraint arise?
+    span: Span,
+}
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+    /// Creates a new region inference context with a total of
+    /// `num_region_variables` valid inference variables; the first N
+    /// of those will be constant regions representing the free
+    /// regions defined in `free_regions`.
+    pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self {
+        let num_region_variables = var_origins.len();
+        let num_free_regions = free_regions.indices.len();
+
+        let mut num_points = 0;
+        let mut point_indices = BTreeMap::new();
+
+        for (block, block_data) in mir.basic_blocks().iter_enumerated() {
+            for statement_index in 0..block_data.statements.len() + 1 {
+                let location = Location {
+                    block,
+                    statement_index,
+                };
+                point_indices.insert(location, num_free_regions + num_points);
+                num_points += 1;
+            }
+        }
+
+        // Create a RegionDefinition for each inference variable.
+        let definitions = var_origins
+            .into_iter()
+            .map(|origin| RegionDefinition::new(origin))
+            .collect();
+
+        let mut result = Self {
+            definitions,
+            liveness_constraints: BitMatrix::new(
+                num_region_variables,
+                num_free_regions + num_points,
+            ),
+            inferred_values: None,
+            constraints: Vec::new(),
+            point_indices,
+            num_free_regions,
+            free_region_map: free_regions.free_region_map,
+        };
+
+        result.init_free_regions(free_regions);
+
+        result
+    }
+
+    /// Initializes the region variables for each free region
+    /// (lifetime parameter). The first N variables always correspond
+    /// to the free regions appearing in the function signature (both
+    /// named and anonymous) and where clauses. This function iterates
+    /// over those regions and initializes them with minimum values.
+    ///
+    /// For example:
+    ///
+    ///     fn foo<'a, 'b>(..) where 'a: 'b
+    ///
+    /// would initialize two variables like so:
+    ///
+    ///     R0 = { CFG, R0 } // 'a
+    ///     R1 = { CFG, R0, R1 } // 'b
+    ///
+    /// Here, R0 represents `'a`, and it contains (a) the entire CFG
+    /// and (b) any free regions that it outlives, which in this case
+    /// is just itself. R1 (`'b`) in contrast also outlives `'a` and
+    /// hence contains R0 and R1.
+    fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>) {
+        let FreeRegions {
+            indices,
+            free_region_map: _,
+        } = free_regions;
+
+        // For each free region X:
+        for (free_region, &variable) in indices {
+            // These should be free-region variables.
+            assert!(match self.definitions[variable].origin {
+                RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
+                _ => false,
+            });
+
+            // Initialize the name and a few other details.
+            self.definitions[variable].name = Some(free_region);
+
+            // Add all nodes in the CFG to liveness constraints
+            for (_location, point_index) in &self.point_indices {
+                self.liveness_constraints
+                    .add(variable.index(), *point_index);
+            }
+
+            // Add `end(X)` into the set for X.
+            self.liveness_constraints
+                .add(variable.index(), variable.index());
+        }
+    }
+
+    /// Returns an iterator over all the region indices.
+    pub fn regions(&self) -> impl Iterator<Item = RegionVid> {
+        self.definitions.indices()
+    }
+
+    /// Returns true if the region `r` contains the point `p`.
+    ///
+    /// Panics if called before `solve()` executes,
+    pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool {
+        let inferred_values = self.inferred_values
+            .as_ref()
+            .expect("region values not yet inferred");
+        self.region_contains_point_in_matrix(inferred_values, r, p)
+    }
+
+    /// True if given region `r` contains the point `p`, when
+    /// evaluated in the set of region values `matrix`.
+    fn region_contains_point_in_matrix(
+        &self,
+        matrix: &BitMatrix,
+        r: RegionVid,
+        p: Location,
+    ) -> bool {
+        let point_index = self.point_indices
+            .get(&p)
+            .expect("point index should be known");
+        matrix.contains(r.index(), *point_index)
+    }
+
+    /// True if given region `r` contains the `end(s)`, when
+    /// evaluated in the set of region values `matrix`.
+    fn region_contains_region_in_matrix(
+        &self,
+        matrix: &BitMatrix,
+        r: RegionVid,
+        s: RegionVid
+    ) -> bool {
+        matrix.contains(r.index(), s.index())
+    }
+
+    /// Returns access to the value of `r` for debugging purposes.
+    pub(super) fn region_value_str(&self, r: RegionVid) -> String {
+        let inferred_values = self.inferred_values
+            .as_ref()
+            .expect("region values not yet inferred");
+
+        let mut result = String::new();
+        result.push_str("{");
+        let mut sep = "";
+
+        for &point in self.point_indices.keys() {
+            if self.region_contains_point_in_matrix(inferred_values, r, point) {
+                result.push_str(&format!("{}{:?}", sep, point));
+                sep = ", ";
+            }
+        }
+
+        for fr in (0 .. self.num_free_regions).map(RegionVid::new) {
+            if self.region_contains_region_in_matrix(inferred_values, r, fr) {
+                result.push_str(&format!("{}{:?}", sep, fr));
+                sep = ", ";
+            }
+        }
+
+        result.push_str("}");
+
+        result
+    }
+
+    /// Indicates that the region variable `v` is live at the point `point`.
+    pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) -> bool {
+        debug!("add_live_point({:?}, {:?})", v, point);
+        assert!(self.inferred_values.is_none(), "values already inferred");
+        let point_index = self.point_indices
+            .get(&point)
+            .expect("point index should be known");
+        self.liveness_constraints.add(v.index(), *point_index)
+    }
+
+    /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
+    pub(super) fn add_outlives(
+        &mut self,
+        span: Span,
+        sup: RegionVid,
+        sub: RegionVid,
+        point: Location,
+    ) {
+        debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
+        assert!(self.inferred_values.is_none(), "values already inferred");
+        self.constraints.push(Constraint {
+            span,
+            sup,
+            sub,
+            point,
+        });
+    }
+
+    /// Perform region inference.
+    pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
+        assert!(self.inferred_values.is_none(), "values already inferred");
+
+        // Find the minimal regions that can solve the constraints. This is infallible.
+        self.propagate_constraints(mir);
+
+        // Now, see whether any of the constraints were too strong. In particular,
+        // we want to check for a case where a free region exceeded its bounds.
+        // Consider:
+        //
+        //     fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
+        //
+        // In this case, returning `x` requires `&'a u32 <: &'b u32`
+        // and hence we establish (transitively) a constraint that
+        // `'a: 'b`. The `propagate_constraints` code above will
+        // therefore add `end('a)` into the region for `'b` -- but we
+        // have no evidence that `'b` outlives `'a`, so we want to report
+        // an error.
+
+        // The free regions are always found in a prefix of the full list.
+        let free_region_definitions = self.definitions
+            .iter_enumerated()
+            .take_while(|(_, fr_definition)| fr_definition.name.is_some());
+
+        for (fr, fr_definition) in free_region_definitions {
+            self.check_free_region(infcx, fr, fr_definition);
+        }
+    }
+
+    fn check_free_region(
+        &self,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        fr: RegionVid,
+        fr_definition: &RegionDefinition<'tcx>,
+    ) {
+        let inferred_values = self.inferred_values.as_ref().unwrap();
+        let fr_name = fr_definition.name.unwrap();
+        let fr_value = inferred_values.iter(fr.index());
+
+        // Find every region `o` such that `fr: o`
+        // (because `fr` includes `end(o)`).
+        for outlived_fr in fr_value.take_while(|&i| i < self.num_free_regions) {
+            // `fr` includes `end(fr)`, that's not especially
+            // interesting.
+            if fr.index() == outlived_fr {
+                continue;
+            }
+
+            let outlived_fr_definition = &self.definitions[RegionVid::new(outlived_fr)];
+            let outlived_fr_name = outlived_fr_definition.name.unwrap();
+
+            // Check that `o <= fr`. If not, report an error.
+            if !self.free_region_map
+                .sub_free_regions(outlived_fr_name, fr_name)
+            {
+                // FIXME: worst error msg ever
+                let blame_span = self.blame_span(fr, RegionVid::new(outlived_fr));
+                infcx.tcx.sess.span_err(
+                    blame_span,
+                    &format!(
+                        "free region `{}` does not outlive `{}`",
+                        fr_name,
+                        outlived_fr_name
+                    ),
+                );
+            }
+        }
+    }
+
+    /// Propagate the region constraints: this will grow the values
+    /// for each region variable until all the constraints are
+    /// satisfied. Note that some values may grow **too** large to be
+    /// feasible, but we check this later.
+    fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
+        let mut changed = true;
+
+        debug!("propagate_constraints()");
+        debug!("propagate_constraints: constraints={:#?}", {
+            let mut constraints: Vec<_> = self.constraints.iter().collect();
+            constraints.sort();
+            constraints
+        });
+
+        // The initial values for each region are derived from the liveness
+        // constraints we have accumulated.
+        let mut inferred_values = self.liveness_constraints.clone();
+
+        while changed {
+            changed = false;
+            debug!("propagate_constraints: --------------------");
+            for constraint in &self.constraints {
+                debug!("propagate_constraints: constraint={:?}", constraint);
+
+                // Grow the value as needed to accommodate the
+                // outlives constraint.
+
+                if self.copy(
+                    &mut inferred_values,
+                    mir,
+                    constraint.sub,
+                    constraint.sup,
+                    constraint.point,
+                ) {
+                    debug!("propagate_constraints:   sub={:?}", constraint.sub);
+                    debug!("propagate_constraints:   sup={:?}", constraint.sup);
+                    changed = true;
+                }
+            }
+            debug!("\n");
+        }
+
+        self.inferred_values = Some(inferred_values);
+    }
+
+    fn copy(
+        &self,
+        inferred_values: &mut BitMatrix,
+        mir: &Mir<'tcx>,
+        from_region: RegionVid,
+        to_region: RegionVid,
+        start_point: Location,
+    ) -> bool {
+        let mut changed = false;
+
+        let mut stack = vec![];
+        let mut visited = FxHashSet();
+
+        stack.push(start_point);
+        while let Some(p) = stack.pop() {
+            debug!("        copy: p={:?}", p);
+
+            if !self.region_contains_point_in_matrix(inferred_values, from_region, p) {
+                debug!("            not in from-region");
+                continue;
+            }
+
+            if !visited.insert(p) {
+                debug!("            already visited");
+                continue;
+            }
+
+            let point_index = self.point_indices.get(&p).unwrap();
+            changed |= inferred_values.add(to_region.index(), *point_index);
+
+            let block_data = &mir[p.block];
+            let successor_points = if p.statement_index < block_data.statements.len() {
+                vec![
+                    Location {
+                        statement_index: p.statement_index + 1,
+                        ..p
+                    },
+                ]
+            } else {
+                block_data
+                    .terminator()
+                    .successors()
+                    .iter()
+                    .map(|&basic_block| {
+                        Location {
+                            statement_index: 0,
+                            block: basic_block,
+                        }
+                    })
+                    .collect::<Vec<_>>()
+            };
+
+            if successor_points.is_empty() {
+                // If we reach the END point in the graph, then copy
+                // over any skolemized end points in the `from_region`
+                // and make sure they are included in the `to_region`.
+                let free_region_indices = inferred_values
+                    .iter(from_region.index())
+                    .take_while(|&i| i < self.num_free_regions)
+                    .collect::<Vec<_>>();
+                for fr in &free_region_indices {
+                    changed |= inferred_values.add(to_region.index(), *fr);
+                }
+            } else {
+                stack.extend(successor_points);
+            }
+        }
+
+        changed
+    }
+
+    /// Tries to finds a good span to blame for the fact that `fr1`
+    /// contains `fr2`.
+    fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
+        // Find everything that influenced final value of `fr`.
+        let influenced_fr1 = self.dependencies(fr1);
+
+        // Try to find some outlives constraint `'X: fr2` where `'X`
+        // influenced `fr1`. Blame that.
+        //
+        // NB, this is a pretty bad choice most of the time. In
+        // particular, the connection between `'X` and `fr1` may not
+        // be obvious to the user -- not to mention the naive notion
+        // of dependencies, which doesn't account for the locations of
+        // contraints at all. But it will do for now.
+        for constraint in &self.constraints {
+            if constraint.sub == fr2 && influenced_fr1[constraint.sup] {
+                return constraint.span;
+            }
+        }
+
+        bug!(
+            "could not find any constraint to blame for {:?}: {:?}",
+            fr1,
+            fr2
+        );
+    }
+
+    /// Finds all regions whose values `'a` may depend on in some way.
+    /// Basically if there exists a constraint `'a: 'b @ P`, then `'b`
+    /// and `dependencies('b)` will be in the final set.
+    ///
+    /// Used during error reporting, extremely naive and inefficient.
+    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, bool> {
+        let mut result_set = IndexVec::from_elem(false, &self.definitions);
+        let mut changed = true;
+        result_set[r0] = true;
+
+        while changed {
+            changed = false;
+            for constraint in &self.constraints {
+                if result_set[constraint.sup] {
+                    if !result_set[constraint.sub] {
+                        result_set[constraint.sub] = true;
+                        changed = true;
+                    }
+                }
+            }
+        }
+
+        result_set
+    }
+}
+
+impl<'tcx> RegionDefinition<'tcx> {
+    fn new(origin: RegionVariableOrigin) -> Self {
+        // Create a new region definition. Note that, for free
+        // regions, these fields get updated later in
+        // `init_free_regions`.
+        Self { origin, name: None }
+    }
+}
+
+impl fmt::Debug for Constraint {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(
+            formatter,
+            "({:?}: {:?} @ {:?}) due to {:?}",
+            self.sup,
+            self.sub,
+            self.point,
+            self.span
+        )
+    }
+}