]> git.lizzy.rs Git - rust.git/commitdiff
split the `var_origins` from the `RegionConstraintData`
authorNiko Matsakis <niko@alum.mit.edu>
Sun, 5 Nov 2017 19:37:55 +0000 (14:37 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Thu, 16 Nov 2017 10:57:44 +0000 (05:57 -0500)
src/librustc/infer/lexical_region_resolve/mod.rs
src/librustc/infer/mod.rs
src/librustc/infer/region_constraints/mod.rs

index 3aeecaf166ac6d66815048b3178343579bfe48bb..e097bf27c473a637ead48d203e9d20a7e63fe111 100644 (file)
@@ -15,6 +15,7 @@
 use infer::region_constraints::Constraint;
 use infer::region_constraints::GenericKind;
 use infer::region_constraints::RegionConstraintData;
+use infer::region_constraints::VarOrigins;
 use infer::region_constraints::VerifyBound;
 use middle::free_region::RegionRelations;
 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 
 mod graphviz;
 
+/// This function performs lexical region resolution given a complete
+/// set of constraints and variable origins. It performs a fixed-point
+/// iteration to find region values which satisfy all constraints,
+/// assuming such values can be found. It returns the final values of
+/// all the variables as well as a set of errors that must be reported.
+pub fn resolve<'tcx>(
+    region_rels: &RegionRelations<'_, '_, 'tcx>,
+    var_origins: VarOrigins,
+    data: RegionConstraintData<'tcx>
+) -> (
+    LexicalRegionResolutions<'tcx>,
+    Vec<RegionResolutionError<'tcx>>,
+) {
+    debug!("RegionConstraintData: resolve_regions()");
+    let mut errors = vec![];
+    let mut resolver = LexicalResolver { region_rels, var_origins, data };
+    let values = resolver.infer_variable_values(&mut errors);
+    (values, errors)
+}
+
+/// Contains the result of lexical region resolution. Offers methods
+/// to lookup up the final value of a region variable.
 pub struct LexicalRegionResolutions<'tcx> {
     values: IndexVec<RegionVid, VarValue<'tcx>>,
     error_region: ty::Region<'tcx>,
@@ -74,139 +97,40 @@ struct RegionAndOrigin<'tcx> {
 
 type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>;
 
-impl<'tcx> RegionConstraintData<'tcx> {
-    /// This function performs the actual region resolution.  It must be
-    /// called after all constraints have been added.  It performs a
-    /// fixed-point iteration to find region values which satisfy all
-    /// constraints, assuming such values can be found; if they cannot,
-    /// errors are reported.
-    pub fn resolve_regions(
-        mut self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
-    ) -> (
-        LexicalRegionResolutions<'tcx>,
-        Vec<RegionResolutionError<'tcx>>,
-    ) {
-        debug!("RegionConstraintData: resolve_regions()");
-        let mut errors = vec![];
-        let values = self.infer_variable_values(region_rels, &mut errors);
-        (values, errors)
-    }
-
-    fn lub_concrete_regions(
-        &self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
-        a: Region<'tcx>,
-        b: Region<'tcx>,
-    ) -> Region<'tcx> {
-        let tcx = region_rels.tcx;
-        match (a, b) {
-            (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => {
-                bug!("cannot relate region: LUB({:?}, {:?})", a, b);
-            }
-
-            (r @ &ReStatic, _) | (_, r @ &ReStatic) => {
-                r // nothing lives longer than static
-            }
-
-            (&ReEmpty, r) | (r, &ReEmpty) => {
-                r // everything lives longer than empty
-            }
-
-            (&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
-                span_bug!(
-                    self.var_origins[v_id].span(),
-                    "lub_concrete_regions invoked with non-concrete \
-                     regions: {:?}, {:?}",
-                    a,
-                    b
-                );
-            }
-
-            (&ReEarlyBound(_), &ReScope(s_id)) |
-            (&ReScope(s_id), &ReEarlyBound(_)) |
-            (&ReFree(_), &ReScope(s_id)) |
-            (&ReScope(s_id), &ReFree(_)) => {
-                // A "free" region can be interpreted as "some region
-                // at least as big as fr.scope".  So, we can
-                // reasonably compare free regions and scopes:
-                let fr_scope = match (a, b) {
-                    (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => {
-                        region_rels.region_scope_tree.early_free_scope(region_rels.tcx, br)
-                    }
-                    (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => {
-                        region_rels.region_scope_tree.free_scope(region_rels.tcx, fr)
-                    }
-                    _ => bug!(),
-                };
-                let r_id = region_rels
-                    .region_scope_tree
-                    .nearest_common_ancestor(fr_scope, s_id);
-                if r_id == fr_scope {
-                    // if the free region's scope `fr.scope` is bigger than
-                    // the scope region `s_id`, then the LUB is the free
-                    // region itself:
-                    match (a, b) {
-                        (_, &ReScope(_)) => return a,
-                        (&ReScope(_), _) => return b,
-                        _ => bug!(),
-                    }
-                }
-
-                // otherwise, we don't know what the free region is,
-                // so we must conservatively say the LUB is static:
-                tcx.types.re_static
-            }
-
-            (&ReScope(a_id), &ReScope(b_id)) => {
-                // The region corresponding to an outer block is a
-                // subtype of the region corresponding to an inner
-                // block.
-                let lub = region_rels
-                    .region_scope_tree
-                    .nearest_common_ancestor(a_id, b_id);
-                tcx.mk_region(ReScope(lub))
-            }
-
-            (&ReEarlyBound(_), &ReEarlyBound(_)) |
-            (&ReFree(_), &ReEarlyBound(_)) |
-            (&ReEarlyBound(_), &ReFree(_)) |
-            (&ReFree(_), &ReFree(_)) => region_rels.lub_free_regions(a, b),
-
-            // For these types, we cannot define any additional
-            // relationship:
-            (&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b {
-                a
-            } else {
-                tcx.types.re_static
-            },
-        }
-    }
+struct LexicalResolver<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    region_rels: &'cx RegionRelations<'cx, 'gcx, 'tcx>,
+    var_origins: VarOrigins,
+    data: RegionConstraintData<'tcx>
+}
 
+impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
     fn infer_variable_values(
         &mut self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
         errors: &mut Vec<RegionResolutionError<'tcx>>,
     ) -> LexicalRegionResolutions<'tcx> {
-        let mut var_data = self.construct_var_data(region_rels.tcx);
+        let mut var_data = self.construct_var_data(self.region_rels.tcx);
 
         // Dorky hack to cause `dump_constraints` to only get called
         // if debug mode is enabled:
         debug!(
             "----() End constraint listing (context={:?}) {:?}---",
-            region_rels.context,
-            self.dump_constraints(region_rels)
+            self.region_rels.context,
+            self.dump_constraints(self.region_rels)
         );
-        graphviz::maybe_print_constraints_for(self, region_rels);
+        graphviz::maybe_print_constraints_for(&self.data, self.region_rels);
 
         let graph = self.construct_graph();
         self.expand_givens(&graph);
-        self.expansion(region_rels, &mut var_data);
-        self.collect_errors(region_rels, &mut var_data, errors);
-        self.collect_var_errors(region_rels, &var_data, &graph, errors);
+        self.expansion(&mut var_data);
+        self.collect_errors(&mut var_data, errors);
+        self.collect_var_errors(&var_data, &graph, errors);
         var_data
     }
 
+    fn num_vars(&self) -> usize {
+        self.var_origins.len()
+    }
+
     /// Initially, the value for all variables is set to `'empty`, the
     /// empty region. The `expansion` phase will grow this larger.
     fn construct_var_data(&self, tcx: TyCtxt<'_, '_, 'tcx>) -> LexicalRegionResolutions<'tcx> {
@@ -223,7 +147,7 @@ fn dump_constraints(&self, free_regions: &RegionRelations<'_, '_, 'tcx>) {
             "----() Start constraint listing (context={:?}) ()----",
             free_regions.context
         );
-        for (idx, (constraint, _)) in self.constraints.iter().enumerate() {
+        for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() {
             debug!("Constraint {} => {:?}", idx, constraint);
         }
     }
@@ -239,7 +163,7 @@ fn expand_givens(&mut self, graph: &RegionGraph) {
         //     and   '0 <= '1
         //     then  'c <= '1
 
-        let seeds: Vec<_> = self.givens.iter().cloned().collect();
+        let seeds: Vec<_> = self.data.givens.iter().cloned().collect();
         for (r, vid) in seeds {
 
             // While all things transitively reachable in the graph
@@ -255,7 +179,7 @@ fn expand_givens(&mut self, graph: &RegionGraph) {
                     let succ_vid = RegionVid::new(succ_index);
 
                     // Add `'c <= '1`.
-                    self.givens.insert((r, succ_vid));
+                    self.data.givens.insert((r, succ_vid));
                 }
             }
         }
@@ -263,7 +187,6 @@ fn expand_givens(&mut self, graph: &RegionGraph) {
 
     fn expansion(
         &self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
         var_values: &mut LexicalRegionResolutions<'tcx>,
     ) {
         self.iterate_until_fixed_point("Expansion", |constraint, origin| {
@@ -271,13 +194,13 @@ fn expansion(
             match *constraint {
                 Constraint::RegSubVar(a_region, b_vid) => {
                     let b_data = var_values.value_mut(b_vid);
-                    self.expand_node(region_rels, a_region, b_vid, b_data)
+                    self.expand_node(a_region, b_vid, b_data)
                 }
                 Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) {
                     VarValue::ErrorValue => false,
                     VarValue::Value(a_region) => {
                         let b_node = var_values.value_mut(b_vid);
-                        self.expand_node(region_rels, a_region, b_vid, b_node)
+                        self.expand_node(a_region, b_vid, b_node)
                     }
                 },
                 Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => {
@@ -291,7 +214,6 @@ fn expansion(
 
     fn expand_node(
         &self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
         a_region: Region<'tcx>,
         b_vid: RegionVid,
         b_data: &mut VarValue<'tcx>,
@@ -301,7 +223,7 @@ fn expand_node(
         // Check if this relationship is implied by a given.
         match *a_region {
             ty::ReEarlyBound(_) | ty::ReFree(_) => {
-                if self.givens.contains(&(a_region, b_vid)) {
+                if self.data.givens.contains(&(a_region, b_vid)) {
                     debug!("given");
                     return false;
                 }
@@ -311,7 +233,7 @@ fn expand_node(
 
         match *b_data {
             VarValue::Value(cur_region) => {
-                let lub = self.lub_concrete_regions(region_rels, a_region, cur_region);
+                let lub = self.lub_concrete_regions(a_region, cur_region);
                 if lub == cur_region {
                     return false;
                 }
@@ -333,16 +255,105 @@ fn expand_node(
         }
     }
 
+
+    fn lub_concrete_regions(
+        &self,
+        a: Region<'tcx>,
+        b: Region<'tcx>,
+    ) -> Region<'tcx> {
+        let tcx = self.region_rels.tcx;
+        match (a, b) {
+            (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => {
+                bug!("cannot relate region: LUB({:?}, {:?})", a, b);
+            }
+
+            (r @ &ReStatic, _) | (_, r @ &ReStatic) => {
+                r // nothing lives longer than static
+            }
+
+            (&ReEmpty, r) | (r, &ReEmpty) => {
+                r // everything lives longer than empty
+            }
+
+            (&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
+                span_bug!(
+                    self.var_origins[v_id].span(),
+                    "lub_concrete_regions invoked with non-concrete \
+                     regions: {:?}, {:?}",
+                    a,
+                    b
+                );
+            }
+
+            (&ReEarlyBound(_), &ReScope(s_id)) |
+            (&ReScope(s_id), &ReEarlyBound(_)) |
+            (&ReFree(_), &ReScope(s_id)) |
+            (&ReScope(s_id), &ReFree(_)) => {
+                // A "free" region can be interpreted as "some region
+                // at least as big as fr.scope".  So, we can
+                // reasonably compare free regions and scopes:
+                let fr_scope = match (a, b) {
+                    (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => {
+                        self.region_rels.region_scope_tree.early_free_scope(self.region_rels.tcx, br)
+                    }
+                    (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => {
+                        self.region_rels.region_scope_tree.free_scope(self.region_rels.tcx, fr)
+                    }
+                    _ => bug!(),
+                };
+                let r_id = self.region_rels
+                    .region_scope_tree
+                    .nearest_common_ancestor(fr_scope, s_id);
+                if r_id == fr_scope {
+                    // if the free region's scope `fr.scope` is bigger than
+                    // the scope region `s_id`, then the LUB is the free
+                    // region itself:
+                    match (a, b) {
+                        (_, &ReScope(_)) => return a,
+                        (&ReScope(_), _) => return b,
+                        _ => bug!(),
+                    }
+                }
+
+                // otherwise, we don't know what the free region is,
+                // so we must conservatively say the LUB is static:
+                tcx.types.re_static
+            }
+
+            (&ReScope(a_id), &ReScope(b_id)) => {
+                // The region corresponding to an outer block is a
+                // subtype of the region corresponding to an inner
+                // block.
+                let lub = self.region_rels
+                    .region_scope_tree
+                    .nearest_common_ancestor(a_id, b_id);
+                tcx.mk_region(ReScope(lub))
+            }
+
+            (&ReEarlyBound(_), &ReEarlyBound(_)) |
+            (&ReFree(_), &ReEarlyBound(_)) |
+            (&ReEarlyBound(_), &ReFree(_)) |
+            (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b),
+
+            // For these types, we cannot define any additional
+            // relationship:
+            (&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b {
+                a
+            } else {
+                tcx.types.re_static
+            },
+        }
+    }
+
     /// After expansion is complete, go and check upper bounds (i.e.,
     /// cases where the region cannot grow larger than a fixed point)
     /// and check that they are satisfied.
     fn collect_errors(
         &self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
         var_data: &mut LexicalRegionResolutions<'tcx>,
         errors: &mut Vec<RegionResolutionError<'tcx>>,
     ) {
-        for (constraint, origin) in &self.constraints {
+        for (constraint, origin) in &self.data.constraints {
             debug!(
                 "collect_errors: constraint={:?} origin={:?}",
                 constraint,
@@ -354,7 +365,7 @@ fn collect_errors(
                 }
 
                 Constraint::RegSubReg(sub, sup) => {
-                    if region_rels.is_subregion_of(sub, sup) {
+                    if self.region_rels.is_subregion_of(sub, sup) {
                         continue;
                     }
 
@@ -385,7 +396,7 @@ fn collect_errors(
                     // Do not report these errors immediately:
                     // instead, set the variable value to error and
                     // collect them later.
-                    if !region_rels.is_subregion_of(a_region, b_region) {
+                    if !self.region_rels.is_subregion_of(a_region, b_region) {
                         debug!(
                             "collect_errors: region error at {:?}: \
                              cannot verify that {:?}={:?} <= {:?}",
@@ -400,7 +411,7 @@ fn collect_errors(
             }
         }
 
-        for verify in &self.verifys {
+        for verify in &self.data.verifys {
             debug!("collect_errors: verify={:?}", verify);
             let sub = var_data.normalize(verify.region);
 
@@ -410,7 +421,7 @@ fn collect_errors(
                 continue;
             }
 
-            if verify.bound.is_met(region_rels, var_data, sub) {
+            if self.bound_is_met(&verify.bound, var_data, sub) {
                 continue;
             }
 
@@ -434,7 +445,6 @@ fn collect_errors(
     /// and create a `RegionResolutionError` for each of them.
     fn collect_var_errors(
         &self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
         var_data: &LexicalRegionResolutions<'tcx>,
         graph: &RegionGraph<'tcx>,
         errors: &mut Vec<RegionResolutionError<'tcx>>,
@@ -481,7 +491,6 @@ fn collect_var_errors(
                        starts to create problems we'll have to revisit
                        this portion of the code and think hard about it. =) */
                     self.collect_error_for_expanding_node(
-                        region_rels,
                         graph,
                         &mut dup_vec,
                         node_vid,
@@ -509,7 +518,7 @@ fn construct_graph(&self) -> RegionGraph<'tcx> {
         let dummy_source = graph.add_node(());
         let dummy_sink = graph.add_node(());
 
-        for (constraint, _) in &self.constraints {
+        for (constraint, _) in &self.data.constraints {
             match *constraint {
                 Constraint::VarSubVar(a_id, b_id) => {
                     graph.add_edge(
@@ -536,7 +545,6 @@ fn construct_graph(&self) -> RegionGraph<'tcx> {
 
     fn collect_error_for_expanding_node(
         &self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
         graph: &RegionGraph<'tcx>,
         dup_vec: &mut [u32],
         node_idx: RegionVid,
@@ -568,7 +576,7 @@ fn region_order_key(x: &RegionAndOrigin) -> u8 {
 
         for lower_bound in &lower_bounds {
             for upper_bound in &upper_bounds {
-                if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) {
+                if !self.region_rels.is_subregion_of(lower_bound.region, upper_bound.region) {
                     let origin = self.var_origins[node_idx].clone();
                     debug!(
                         "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
@@ -624,7 +632,7 @@ struct WalkState<'tcx> {
 
         // to start off the process, walk the source node in the
         // direction specified
-        process_edges(self, &mut state, graph, orig_node_idx, dir);
+        process_edges(&self.data, &mut state, graph, orig_node_idx, dir);
 
         while !state.stack.is_empty() {
             let node_idx = state.stack.pop().unwrap();
@@ -642,7 +650,7 @@ struct WalkState<'tcx> {
                 node_idx
             );
 
-            process_edges(self, &mut state, graph, node_idx, dir);
+            process_edges(&self.data, &mut state, graph, node_idx, dir);
         }
 
         let WalkState {
@@ -699,7 +707,7 @@ fn iterate_until_fixed_point<F>(&self, tag: &str, mut body: F)
             changed = false;
             iteration += 1;
             debug!("---- {} Iteration {}{}", "#", tag, iteration);
-            for (constraint, origin) in &self.constraints {
+            for (constraint, origin) in &self.data.constraints {
                 let edge_changed = body(constraint, origin);
                 if edge_changed {
                     debug!("Updated due to constraint {:?}", constraint);
@@ -709,38 +717,36 @@ fn iterate_until_fixed_point<F>(&self, tag: &str, mut body: F)
         }
         debug!("---- {} Complete after {} iteration(s)", tag, iteration);
     }
-}
 
-impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin)
-    }
-}
-
-
-impl<'tcx> VerifyBound<'tcx> {
-    fn is_met(
+    fn bound_is_met(
         &self,
-        region_rels: &RegionRelations<'_, '_, 'tcx>,
+        bound: &VerifyBound<'tcx>,
         var_values: &LexicalRegionResolutions<'tcx>,
         min: ty::Region<'tcx>,
     ) -> bool {
-        match self {
+        match bound {
             VerifyBound::AnyRegion(rs) => rs.iter()
                 .map(|&r| var_values.normalize(r))
-                .any(|r| region_rels.is_subregion_of(min, r)),
+                .any(|r| self.region_rels.is_subregion_of(min, r)),
 
             VerifyBound::AllRegions(rs) => rs.iter()
                 .map(|&r| var_values.normalize(r))
-                .all(|r| region_rels.is_subregion_of(min, r)),
+                .all(|r| self.region_rels.is_subregion_of(min, r)),
 
-            VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.is_met(region_rels, var_values, min)),
+            VerifyBound::AnyBound(bs) => bs.iter().any(|b| self.bound_is_met(b, var_values, min)),
 
-            VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.is_met(region_rels, var_values, min)),
+            VerifyBound::AllBounds(bs) => bs.iter().all(|b| self.bound_is_met(b, var_values, min)),
         }
     }
 }
 
+impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin)
+    }
+}
+
+
 impl<'tcx> LexicalRegionResolutions<'tcx> {
     fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
index 53112bcd88073ea81a5a924d9cd544570ec53dab..a1ad65a6c4a48ae74eae0198b70dd7cf3aeb8891 100644 (file)
@@ -1128,15 +1128,16 @@ pub fn resolve_regions_and_report_errors(&self,
                 "region_obligations not empty: {:#?}",
                 self.region_obligations.borrow());
 
-        let region_rels = RegionRelations::new(self.tcx,
-                                               region_context,
-                                               region_map,
-                                               free_regions);
-        let region_data = self.region_constraints.borrow_mut()
-                                                 .take()
-                                                 .expect("regions already resolved")
-                                                 .into_data();
-        let (lexical_region_resolutions, errors) = region_data.resolve_regions(&region_rels);
+        let region_rels = &RegionRelations::new(self.tcx,
+                                                region_context,
+                                                region_map,
+                                                free_regions);
+        let (var_origins, data) = self.region_constraints.borrow_mut()
+                                                         .take()
+                                                         .expect("regions already resolved")
+                                                         .into_origins_and_data();
+        let (lexical_region_resolutions, errors) =
+            lexical_region_resolve::resolve(region_rels, var_origins, data);
 
         let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions));
         assert!(old_value.is_none());
index 749a47004da311ce238fe1ff3ab851e940e50730..36e5e3039572ada804f6c0130b9c8c9405c26839 100644 (file)
 mod taint;
 
 pub struct RegionConstraintCollector<'tcx> {
+    /// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
+    pub var_origins: IndexVec<RegionVid, RegionVariableOrigin>,
+
     data: RegionConstraintData<'tcx>,
+
+    /// For a given pair of regions (R1, R2), maps to a region R3 that
+    /// is designated as their LUB (edges R1 <= R3 and R2 <= R3
+    /// exist). This prevents us from making many such regions.
     lubs: CombineMap<'tcx>,
+
+    /// For a given pair of regions (R1, R2), maps to a region R3 that
+    /// is designated as their GLB (edges R3 <= R1 and R3 <= R2
+    /// exist). This prevents us from making many such regions.
     glbs: CombineMap<'tcx>,
+
+    /// Number of skolemized variables currently active.
     skolemization_count: u32,
+
+    /// Global counter used during the GLB algorithm to create unique
+    /// names for fresh bound regions
     bound_count: u32,
 
     /// The undo log records actions that might later be undone.
@@ -49,20 +65,25 @@ pub struct RegionConstraintCollector<'tcx> {
     /// back.
     undo_log: Vec<UndoLogEntry<'tcx>>,
 
+    /// When we add a R1 == R2 constriant, we currently add (a) edges
+    /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this
+    /// table. You can then call `opportunistic_resolve_var` early
+    /// which will map R1 and R2 to some common region (i.e., either
+    /// R1 or R2). This is important when dropck and other such code
+    /// is iterating to a fixed point, because otherwise we sometimes
+    /// would wind up with a fresh stream of region variables that
+    /// have been equated but appear distinct.
     unification_table: UnificationTable<ty::RegionVid>,
 }
 
 pub type VarOrigins = IndexVec<RegionVid, RegionVariableOrigin>;
 
 /// The full set of region constraints gathered up by the collector.
-/// Describes a set of region variables ranging from 0..N (where N is
-/// the length of the `var_origins` vector), and various constraints
-/// between them.
+/// Describes constraints between the region variables and other
+/// regions, as well as other conditions that must be verified, or
+/// assumptions that can be made.
 #[derive(Default)]
 pub struct RegionConstraintData<'tcx> {
-    /// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
-    pub var_origins: IndexVec<RegionVid, RegionVariableOrigin>,
-
     /// Constraints of the form `A <= B`, where either `A` or `B` can
     /// be a region variable (or neither, as it happens).
     pub constraints: BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
@@ -252,6 +273,7 @@ pub fn both() -> Self {
 impl<'tcx> RegionConstraintCollector<'tcx> {
     pub fn new() -> RegionConstraintCollector<'tcx> {
         RegionConstraintCollector {
+            var_origins: VarOrigins::default(),
             data: RegionConstraintData::default(),
             lubs: FxHashMap(),
             glbs: FxHashMap(),
@@ -263,8 +285,8 @@ pub fn new() -> RegionConstraintCollector<'tcx> {
     }
 
     /// Once all the constraints have been gathered, extract out the final data.
-    pub fn into_data(self) -> RegionConstraintData<'tcx> {
-        self.data
+    pub fn into_origins_and_data(self) -> (VarOrigins, RegionConstraintData<'tcx>) {
+        (self.var_origins, self.data)
     }
 
     fn in_snapshot(&self) -> bool {
@@ -324,8 +346,8 @@ fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) {
                 // nothing to do here
             }
             AddVar(vid) => {
-                self.data.var_origins.pop().unwrap();
-                assert_eq!(self.data.var_origins.len(), vid.index as usize);
+                self.var_origins.pop().unwrap();
+                assert_eq!(self.var_origins.len(), vid.index as usize);
             }
             AddConstraint(ref constraint) => {
                 self.data.constraints.remove(constraint);
@@ -347,7 +369,7 @@ fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) {
     }
 
     pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid {
-        let vid = self.data.var_origins.push(origin.clone());
+        let vid = self.var_origins.push(origin.clone());
 
         let u_vid = self.unification_table
             .new_key(unify_key::RegionVidKey { min_vid: vid });
@@ -363,8 +385,9 @@ pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid {
         return vid;
     }
 
+    /// Returns the origin for the given variable.
     pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin {
-        self.data.var_origins[vid].clone()
+        self.var_origins[vid].clone()
     }
 
     /// Creates a new skolemized region. Skolemized regions are fresh
@@ -862,9 +885,3 @@ pub fn and(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> {
         }
     }
 }
-
-impl<'tcx> RegionConstraintData<'tcx> {
-    pub fn num_vars(&self) -> usize {
-        self.var_origins.len()
-    }
-}