1
Fork 0

separate the Collector from the Data it is collecting

This commit is contained in:
Niko Matsakis 2017-11-05 11:59:49 -05:00
parent adf1519941
commit f6037f2816
4 changed files with 123 additions and 119 deletions

View file

@ -25,7 +25,7 @@ use middle::free_region::RegionRelations;
use middle::region;
use super::Constraint;
use infer::SubregionOrigin;
use infer::region_constraints::RegionConstraintCollector;
use infer::region_constraints::RegionConstraintData;
use util::nodemap::{FxHashMap, FxHashSet};
use std::borrow::Cow;
@ -57,7 +57,7 @@ graphs will be printed. \n\
}
pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>(
region_constraints: &RegionConstraintCollector<'tcx>,
region_data: &RegionConstraintData<'tcx>,
region_rels: &RegionRelations<'a, 'gcx, 'tcx>)
{
let tcx = region_rels.tcx;
@ -113,7 +113,7 @@ pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>(
}
};
match dump_region_constraints_to(region_rels, &region_constraints.constraints, &output_path) {
match dump_region_data_to(region_rels, &region_data.constraints, &output_path) {
Ok(()) => {}
Err(e) => {
let msg = format!("io error dumping region constraints: {}", e);
@ -267,15 +267,15 @@ impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
pub type ConstraintMap<'tcx> = BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>;
fn dump_region_constraints_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>,
map: &ConstraintMap<'tcx>,
path: &str)
-> io::Result<()> {
debug!("dump_region_constraints map (len: {}) path: {}",
fn dump_region_data_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>,
map: &ConstraintMap<'tcx>,
path: &str)
-> io::Result<()> {
debug!("dump_region_data map (len: {}) path: {}",
map.len(),
path);
let g = ConstraintGraph::new(format!("region_constraints"), region_rels, map);
debug!("dump_region_constraints calling render");
let g = ConstraintGraph::new(format!("region_data"), region_rels, map);
debug!("dump_region_data calling render");
let mut v = Vec::new();
dot::render(&g, &mut v).unwrap();
File::create(path).and_then(|mut f| f.write_all(&v))

View file

@ -14,7 +14,7 @@ use infer::SubregionOrigin;
use infer::RegionVariableOrigin;
use infer::region_constraints::Constraint;
use infer::region_constraints::GenericKind;
use infer::region_constraints::RegionConstraintCollector;
use infer::region_constraints::RegionConstraintData;
use infer::region_constraints::VerifyBound;
use middle::free_region::RegionRelations;
use rustc_data_structures::fx::FxHashSet;
@ -73,20 +73,20 @@ struct RegionAndOrigin<'tcx> {
type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>;
impl<'tcx> RegionConstraintCollector<'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,
mut self,
region_rels: &RegionRelations<'_, '_, 'tcx>,
) -> (
LexicalRegionResolutions<'tcx>,
Vec<RegionResolutionError<'tcx>>,
) {
debug!("RegionConstraintCollector: resolve_regions()");
debug!("RegionConstraintData: resolve_regions()");
let mut errors = vec![];
let values = self.infer_variable_values(region_rels, &mut errors);
(values, errors)
@ -642,7 +642,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
return (result, dup_found);
fn process_edges<'tcx>(
this: &RegionConstraintCollector<'tcx>,
this: &RegionConstraintData<'tcx>,
state: &mut WalkState<'tcx>,
graph: &RegionGraph<'tcx>,
source_vid: RegionVid,

View file

@ -1132,10 +1132,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
region_context,
region_map,
free_regions);
let mut region_constraints = self.region_constraints.borrow_mut()
.take()
.expect("regions already resolved");
let (lexical_region_resolutions, errors) = region_constraints.resolve_regions(&region_rels);
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 old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions));
assert!(old_value.is_none());

View file

@ -30,6 +30,69 @@ use std::u32;
mod taint;
pub struct RegionConstraintCollector<'tcx> {
data: RegionConstraintData<'tcx>,
lubs: CombineMap<'tcx>,
glbs: CombineMap<'tcx>,
skolemization_count: u32,
bound_count: u32,
/// The undo log records actions that might later be undone.
///
/// Note: when the undo_log is empty, we are not actively
/// snapshotting. When the `start_snapshot()` method is called, we
/// push an OpenSnapshot entry onto the list to indicate that we
/// are now actively snapshotting. The reason for this is that
/// otherwise we end up adding entries for things like the lower
/// bound on a variable and so forth, which can never be rolled
/// back.
undo_log: Vec<UndoLogEntry<'tcx>>,
unification_table: UnificationTable<ty::RegionVid>,
}
/// 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.
#[derive(Default)]
pub struct RegionConstraintData<'tcx> {
/// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
pub var_origins: Vec<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>>,
/// A "verify" is something that we need to verify after inference
/// is done, but which does not directly affect inference in any
/// way.
///
/// An example is a `A <= B` where neither `A` nor `B` are
/// inference variables.
pub verifys: Vec<Verify<'tcx>>,
/// A "given" is a relationship that is known to hold. In
/// particular, we often know from closure fn signatures that a
/// particular free region must be a subregion of a region
/// variable:
///
/// foo.iter().filter(<'a> |x: &'a &'b T| ...)
///
/// In situations like this, `'b` is in fact a region variable
/// introduced by the call to `iter()`, and `'a` is a bound region
/// on the closure (as indicated by the `<'a>` prefix). If we are
/// naive, we wind up inferring that `'b` must be `'static`,
/// because we require that it be greater than `'a` and we do not
/// know what `'a` is precisely.
///
/// This hashmap is used to avoid that naive scenario. Basically
/// we record the fact that `'a <= 'b` is implied by the fn
/// signature, and then ignore the constraint when solving
/// equations. This is a bit of a hack but seems to work.
pub givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>,
}
/// A constraint that influences the inference process.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub enum Constraint<'tcx> {
@ -143,65 +206,6 @@ enum CombineMapType {
type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
pub struct RegionConstraintCollector<'tcx> {
pub(in infer) var_origins: Vec<RegionVariableOrigin>,
/// Constraints of the form `A <= B` introduced by the region
/// checker. Here at least one of `A` and `B` must be a region
/// variable.
///
/// Using `BTreeMap` because the order in which we iterate over
/// these constraints can affect the way we build the region graph,
/// which in turn affects the way that region errors are reported,
/// leading to small variations in error output across runs and
/// platforms.
pub(in infer) constraints: BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
/// A "verify" is something that we need to verify after inference is
/// done, but which does not directly affect inference in any way.
///
/// An example is a `A <= B` where neither `A` nor `B` are
/// inference variables.
pub(in infer) verifys: Vec<Verify<'tcx>>,
/// A "given" is a relationship that is known to hold. In particular,
/// we often know from closure fn signatures that a particular free
/// region must be a subregion of a region variable:
///
/// foo.iter().filter(<'a> |x: &'a &'b T| ...)
///
/// In situations like this, `'b` is in fact a region variable
/// introduced by the call to `iter()`, and `'a` is a bound region
/// on the closure (as indicated by the `<'a>` prefix). If we are
/// naive, we wind up inferring that `'b` must be `'static`,
/// because we require that it be greater than `'a` and we do not
/// know what `'a` is precisely.
///
/// This hashmap is used to avoid that naive scenario. Basically we
/// record the fact that `'a <= 'b` is implied by the fn signature,
/// and then ignore the constraint when solving equations. This is
/// a bit of a hack but seems to work.
pub(in infer) givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>,
lubs: CombineMap<'tcx>,
glbs: CombineMap<'tcx>,
skolemization_count: u32,
bound_count: u32,
/// The undo log records actions that might later be undone.
///
/// Note: when the undo_log is empty, we are not actively
/// snapshotting. When the `start_snapshot()` method is called, we
/// push an OpenSnapshot entry onto the list to indicate that we
/// are now actively snapshotting. The reason for this is that
/// otherwise we end up adding entries for things like the lower
/// bound on a variable and so forth, which can never be rolled
/// back.
undo_log: Vec<UndoLogEntry<'tcx>>,
unification_table: UnificationTable<ty::RegionVid>,
}
pub struct RegionSnapshot {
length: usize,
region_snapshot: unify::Snapshot<ty::RegionVid>,
@ -245,10 +249,7 @@ impl TaintDirections {
impl<'tcx> RegionConstraintCollector<'tcx> {
pub fn new() -> RegionConstraintCollector<'tcx> {
RegionConstraintCollector {
var_origins: Vec::new(),
constraints: BTreeMap::new(),
verifys: Vec::new(),
givens: FxHashSet(),
data: RegionConstraintData::default(),
lubs: FxHashMap(),
glbs: FxHashMap(),
skolemization_count: 0,
@ -258,6 +259,11 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
}
}
/// Once all the constraints have been gathered, extract out the final data.
pub fn into_data(self) -> RegionConstraintData<'tcx> {
self.data
}
fn in_snapshot(&self) -> bool {
!self.undo_log.is_empty()
}
@ -289,8 +295,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
} else {
(*self.undo_log)[snapshot.length] = CommitedSnapshot;
}
self.unification_table
.commit(snapshot.region_snapshot);
self.unification_table.commit(snapshot.region_snapshot);
}
pub fn rollback_to(&mut self, snapshot: RegionSnapshot) {
@ -304,8 +309,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
let c = self.undo_log.pop().unwrap();
assert!(c == OpenSnapshot);
self.skolemization_count = snapshot.skolemization_count;
self.unification_table
.rollback_to(snapshot.region_snapshot);
self.unification_table.rollback_to(snapshot.region_snapshot);
}
fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) {
@ -317,18 +321,18 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
// nothing to do here
}
AddVar(vid) => {
self.var_origins.pop().unwrap();
assert_eq!(self.var_origins.len(), vid.index as usize);
self.data.var_origins.pop().unwrap();
assert_eq!(self.data.var_origins.len(), vid.index as usize);
}
AddConstraint(ref constraint) => {
self.constraints.remove(constraint);
self.data.constraints.remove(constraint);
}
AddVerify(index) => {
self.verifys.pop();
assert_eq!(self.verifys.len(), index);
self.data.verifys.pop();
assert_eq!(self.data.verifys.len(), index);
}
AddGiven(sub, sup) => {
self.givens.remove(&(sub, sup));
self.data.givens.remove(&(sub, sup));
}
AddCombination(Glb, ref regions) => {
self.glbs.remove(regions);
@ -339,18 +343,11 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
}
}
pub fn num_vars(&self) -> u32 {
let len = self.var_origins.len();
// enforce no overflow
assert!(len as u32 as usize == len);
len as u32
}
pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid {
let vid = RegionVid {
index: self.num_vars(),
index: self.data.num_vars(),
};
self.var_origins.push(origin.clone());
self.data.var_origins.push(origin.clone());
let u_vid = self.unification_table
.new_key(unify_key::RegionVidKey { min_vid: vid });
@ -367,7 +364,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
}
pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin {
self.var_origins[vid.index as usize].clone()
self.data.var_origins[vid.index as usize].clone()
}
/// Creates a new skolemized region. Skolemized regions are fresh
@ -523,21 +520,22 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) {
// cannot add constraints once regions are resolved
debug!("RegionConstraintCollector: add_constraint({:?})", constraint);
debug!(
"RegionConstraintCollector: add_constraint({:?})",
constraint
);
// never overwrite an existing (constraint, origin) - only insert one if it isn't
// present in the map yet. This prevents origins from outside the snapshot being
// replaced with "less informative" origins e.g. during calls to `can_eq`
let in_snapshot = self.in_snapshot();
let undo_log = &mut self.undo_log;
self.constraints
.entry(constraint)
.or_insert_with(|| {
if in_snapshot {
undo_log.push(AddConstraint(constraint));
}
origin
});
self.data.constraints.entry(constraint).or_insert_with(|| {
if in_snapshot {
undo_log.push(AddConstraint(constraint));
}
origin
});
}
fn add_verify(&mut self, verify: Verify<'tcx>) {
@ -552,8 +550,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
_ => {}
}
let index = self.verifys.len();
self.verifys.push(verify);
let index = self.data.verifys.len();
self.data.verifys.push(verify);
if self.in_snapshot() {
self.undo_log.push(AddVerify(index));
}
@ -561,7 +559,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) {
// cannot add givens once regions are resolved
if self.givens.insert((sub, sup)) {
if self.data.givens.insert((sub, sup)) {
debug!("add_given({:?} <= {:?})", sub, sup);
self.undo_log.push(AddGiven(sub, sup));
@ -767,11 +765,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
// edges and add any new regions we find to result_set. This
// is not a terribly efficient implementation.
let mut taint_set = taint::TaintSet::new(directions, r0);
taint_set.fixed_point(
tcx,
&self.undo_log[mark.length..],
&self.verifys,
);
taint_set.fixed_point(tcx, &self.undo_log[mark.length..], &self.data.verifys);
debug!("tainted: result={:?}", taint_set);
return taint_set.into_set();
}
@ -866,3 +860,12 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> {
}
}
}
impl<'tcx> RegionConstraintData<'tcx> {
pub fn num_vars(&self) -> u32 {
let len = self.var_origins.len();
// enforce no overflow
assert!(len as u32 as usize == len);
len as u32
}
}