From da545cee6057eddc0cf64767870a1cdc087ade1f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 26 Jan 2018 02:14:54 -0300 Subject: [PATCH 1/4] Make region inference use a dirty list Fixes #47602 --- src/librustc_data_structures/bitvec.rs | 11 +++ .../borrow_check/nll/region_infer/mod.rs | 72 ++++++++++++------- 2 files changed, 59 insertions(+), 24 deletions(-) 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: From 44b666816b7b027b358843080f11232c53a8b929 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 26 Jan 2018 19:58:54 -0500 Subject: [PATCH 2/4] Adjust comment spacing I suspect the lines would be long for tidy. --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 f316a8b480db4..b9acb837dbaa9 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -507,9 +507,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { 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. + /// 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(); From b57ca9d97f286fce6b11335af710c7e3a2a6b6d9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 26 Jan 2018 20:13:51 -0500 Subject: [PATCH 3/4] change from `dirty_bit_vec` to `clean_bit_vec` Otherwise the vector is initially out of sync --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 b9acb837dbaa9..b8258c7667662 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -467,12 +467,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut inferred_values = self.liveness_constraints.clone(); let dependency_map = self.build_dependency_map(); + + // Constraints that may need to be repropagated (initially all): let mut dirty_list: Vec<_> = (0..self.constraints.len()).collect(); - let mut dirty_bit_vec = BitVector::new(dirty_list.len()); + + // Set to 0 for each constraint that is on the dirty list: + let mut clean_bit_vec = BitVector::new(dirty_list.len()); debug!("propagate_constraints: --------------------"); while let Some(constraint_idx) = dirty_list.pop() { - dirty_bit_vec.remove(constraint_idx); + clean_bit_vec.insert(constraint_idx); let constraint = &self.constraints[constraint_idx]; debug!("propagate_constraints: constraint={:?}", constraint); @@ -495,7 +499,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("propagate_constraints: sup={:?}", constraint.sup); for &dep_idx in dependency_map.get(&constraint.sup).unwrap_or(&vec![]) { - if dirty_bit_vec.insert(dep_idx) { + if clean_bit_vec.remove(dep_idx) { dirty_list.push(dep_idx); } } From 205eba83e2373c81ed136997a0af004630853e10 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 26 Jan 2018 20:51:40 -0500 Subject: [PATCH 4/4] pacify the mercilous tidy --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b8258c7667662..9a338947f4772 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -467,10 +467,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut inferred_values = self.liveness_constraints.clone(); let dependency_map = self.build_dependency_map(); - + // Constraints that may need to be repropagated (initially all): let mut dirty_list: Vec<_> = (0..self.constraints.len()).collect(); - + // Set to 0 for each constraint that is on the dirty list: let mut clean_bit_vec = BitVector::new(dirty_list.len());