diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 213cf52a8eb36..f0d425f3f1766 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -160,7 +160,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => for region in regioncx.regions() { - writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?; + writeln!(out, "| {:?}: {}", region, regioncx.region_value_str(region))?; }, // Before each basic block, dump out the values diff --git a/src/librustc_mir/borrow_check/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer.rs index f60bd3c6ecec5..aac341380a00a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer.rs @@ -18,7 +18,9 @@ use rustc::mir::{Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashSet; -use std::collections::BTreeSet; +use rustc_data_structures::bitvec::BitMatrix; +use rustc_data_structures::indexed_vec::Idx; +use std::collections::BTreeMap; use std::fmt; use syntax_pos::Span; @@ -33,15 +35,25 @@ pub struct RegionInferenceContext<'tcx> { /// regions, these start out empty and steadily grow, though for /// each free region R they start out containing the entire CFG /// and `end(R)`. - liveness_constraints: IndexVec, + /// + /// 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>, + inferred_values: Option, /// The constraints we have accumulated and used during solving. constraints: Vec, + /// 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, + + num_free_regions: usize, + free_region_map: &'tcx FreeRegionMap<'tcx>, } @@ -57,42 +69,6 @@ struct RegionDefinition<'tcx> { name: Option>, } -/// The value of an individual region variable. Region variables -/// consist of a set of points in the CFG as well as a set of "free -/// regions", which are sometimes written as `end(R)`. These -/// correspond to the named lifetimes and refer to portions of the -/// caller's control-flow graph -- specifically some portion that can -/// be reached after we return. -#[derive(Clone, Default, PartialEq, Eq)] -struct Region { - points: BTreeSet, - free_regions: BTreeSet, -} - -impl fmt::Debug for Region { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { - formatter - .debug_set() - .entries(&self.points) - .entries(&self.free_regions) - .finish() - } -} - -impl Region { - fn add_point(&mut self, point: Location) -> bool { - self.points.insert(point) - } - - fn add_free_region(&mut self, region: RegionVid) -> bool { - self.free_regions.insert(region) - } - - fn contains_point(&self, point: Location) -> bool { - self.points.contains(&point) - } -} - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Constraint { // NB. The ordering here is not significant for correctness, but @@ -119,6 +95,21 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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 @@ -127,14 +118,19 @@ impl<'tcx> RegionInferenceContext<'tcx> { .collect(); let mut result = Self { - definitions: definitions, - liveness_constraints: IndexVec::from_elem_n(Region::default(), num_region_variables), + 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, mir); + result.init_free_regions(free_regions); result } @@ -158,7 +154,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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>, mir: &Mir<'tcx>) { + fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>) { let FreeRegions { indices, free_region_map: _, @@ -175,27 +171,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Initialize the name and a few other details. self.definitions[variable].name = Some(free_region); - // Add all nodes in the CFG to `definition.value`. - for (block, block_data) in mir.basic_blocks().iter_enumerated() { - let liveness_constraint = &mut self.liveness_constraints[variable]; - for statement_index in 0..block_data.statements.len() + 1 { - let location = Location { - block, - statement_index, - }; - liveness_constraint.add_point(location); - } + // 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[variable].add_free_region(variable); - - debug!( - "init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`", - free_region, - variable, - self.liveness_constraints[variable], - ); + self.liveness_constraints + .add(variable.index(), variable.index()); } } @@ -206,27 +190,76 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Returns true if the region `r` contains the point `p`. /// - /// Until `solve()` executes, this value is not particularly meaningful. + /// 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"); - inferred_values[r].contains_point(p) + 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(&self, r: RegionVid) -> &fmt::Debug { + pub(super) fn region_value_str(&self, r: RegionVid) -> String { let inferred_values = self.inferred_values .as_ref() .expect("region values not yet inferred"); - &inferred_values[r] + + 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) { + 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"); - self.liveness_constraints[v].add_point(point); + 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`. @@ -285,26 +318,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) { let inferred_values = self.inferred_values.as_ref().unwrap(); let fr_name = fr_definition.name.unwrap(); - let fr_value = &inferred_values[fr]; + 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.free_regions { + 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 == outlived_fr { + if fr.index() == outlived_fr { continue; } - let outlived_fr_definition = &self.definitions[outlived_fr]; + 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) { - // worst error msg ever - let blame_span = self.blame_span(fr, outlived_fr); + // FIXME: worst error msg ever + let blame_span = self.blame_span(fr, RegionVid::new(outlived_fr)); infcx.tcx.sess.span_err( blame_span, &format!( @@ -323,7 +356,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// feasible, but we check this later. fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { let mut changed = true; - let mut dfs = Dfs::new(mir); debug!("propagate_constraints()"); debug!("propagate_constraints: constraints={:#?}", { @@ -342,15 +374,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { for constraint in &self.constraints { debug!("propagate_constraints: constraint={:?}", constraint); - let sub = &inferred_values[constraint.sub].clone(); - let sup_value = &mut inferred_values[constraint.sup]; - // Grow the value as needed to accommodate the // outlives constraint. - if dfs.copy(sub, sup_value, constraint.point) { - debug!("propagate_constraints: sub={:?}", sub); - debug!("propagate_constraints: sup={:?}", sup_value); + 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; } } @@ -360,72 +395,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.inferred_values = Some(inferred_values); } - /// 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 { - 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 - } -} - -struct Dfs<'a, 'tcx: 'a> { - mir: &'a Mir<'tcx>, -} - -impl<'a, 'tcx> Dfs<'a, 'tcx> { - fn new(mir: &'a Mir<'tcx>) -> Self { - Self { mir } - } - fn copy( - &mut self, - from_region: &Region, - to_region: &mut Region, + &self, + inferred_values: &mut BitMatrix, + mir: &Mir<'tcx>, + from_region: RegionVid, + to_region: RegionVid, start_point: Location, ) -> bool { let mut changed = false; @@ -435,9 +410,9 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { stack.push(start_point); while let Some(p) = stack.pop() { - debug!(" dfs: p={:?}", p); + debug!(" copy: p={:?}", p); - if !from_region.contains_point(p) { + if !self.region_contains_point_in_matrix(inferred_values, from_region, p) { debug!(" not in from-region"); continue; } @@ -447,9 +422,10 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { continue; } - changed |= to_region.add_point(p); + let point_index = self.point_indices.get(&p).unwrap(); + changed |= inferred_values.add(to_region.index(), *point_index); - let block_data = &self.mir[p.block]; + let block_data = &mir[p.block]; let successor_points = if p.statement_index < block_data.statements.len() { vec![ Location { @@ -475,10 +451,12 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { // 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`. - - debug!(" dfs: free_regions={:?}", from_region.free_regions); - for &fr in &from_region.free_regions { - changed |= to_region.free_regions.insert(fr); + let free_region_indices = inferred_values + .iter(from_region.index()) + .take_while(|&i| i < self.num_free_regions) + .collect::>(); + for fr in &free_region_indices { + changed |= inferred_values.add(to_region.index(), *fr); } } else { stack.extend(successor_points); @@ -487,6 +465,58 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { 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 { + 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> {