diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index 94edaa746f915..80cdb0e441790 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -51,6 +51,17 @@ impl BitVector { new_value != value } + /// Returns true if the bit has changed. + #[inline] + pub fn remove(&mut self, bit: usize) -> bool { + let (word, mask) = word_mask(bit); + let data = &mut self.data[word]; + let value = *data; + let new_value = value & !mask; + *data = new_value; + new_value != value + } + #[inline] pub fn insert_all(&mut self, all: &BitVector) -> bool { assert!(self.data.len() == all.data.len()); diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 9a2f98d4622f7..f316a8b480db4 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::collections::HashMap; + use super::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; @@ -22,6 +24,7 @@ use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegi use rustc::traits::ObligationCause; use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; use rustc::util::common::ErrorReported; +use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::IndexVec; use rustc_errors::DiagnosticBuilder; use std::fmt; @@ -452,8 +455,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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(); @@ -465,37 +466,60 @@ impl<'tcx> RegionInferenceContext<'tcx> { // 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. - let Ok(made_changes) = self.dfs( - mir, - CopyFromSourceToTarget { - source_region: constraint.sub, - target_region: constraint.sup, - inferred_values: &mut inferred_values, - constraint_point: constraint.point, - constraint_span: constraint.span, - }, - ); + let dependency_map = self.build_dependency_map(); + let mut dirty_list: Vec<_> = (0..self.constraints.len()).collect(); + let mut dirty_bit_vec = BitVector::new(dirty_list.len()); + + debug!("propagate_constraints: --------------------"); + while let Some(constraint_idx) = dirty_list.pop() { + dirty_bit_vec.remove(constraint_idx); + + let constraint = &self.constraints[constraint_idx]; + debug!("propagate_constraints: constraint={:?}", constraint); + + // Grow the value as needed to accommodate the + // outlives constraint. + let Ok(made_changes) = self.dfs( + mir, + CopyFromSourceToTarget { + source_region: constraint.sub, + target_region: constraint.sup, + inferred_values: &mut inferred_values, + constraint_point: constraint.point, + constraint_span: constraint.span, + }, + ); + + if made_changes { + debug!("propagate_constraints: sub={:?}", constraint.sub); + debug!("propagate_constraints: sup={:?}", constraint.sup); - if made_changes { - debug!("propagate_constraints: sub={:?}", constraint.sub); - debug!("propagate_constraints: sup={:?}", constraint.sup); - changed = true; + for &dep_idx in dependency_map.get(&constraint.sup).unwrap_or(&vec![]) { + if dirty_bit_vec.insert(dep_idx) { + dirty_list.push(dep_idx); + } } } + debug!("\n"); } self.inferred_values = Some(inferred_values); } + /// Builds up a map from each region variable X to a vector with the indices of constraints that + /// need to be re-evaluated when X changes. These are constraints like Y: X @ P -- so if X + /// changed, we may need to grow Y. + fn build_dependency_map(&self) -> HashMap> { + let mut map = HashMap::new(); + + for (idx, constraint) in self.constraints.iter().enumerate() { + map.entry(constraint.sub).or_insert(Vec::new()).push(idx); + } + + map + } + /// Once regions have been propagated, this method is used to see /// whether the "type tests" produced by typeck were satisfied; /// type tests encode type-outlives relationships like `T: