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>,
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> {
"----() 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);
}
}
// 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
let succ_vid = RegionVid::new(succ_index);
// Add `'c <= '1`.
- self.givens.insert((r, succ_vid));
+ self.data.givens.insert((r, succ_vid));
}
}
}
fn expansion(
&self,
- region_rels: &RegionRelations<'_, '_, 'tcx>,
var_values: &mut LexicalRegionResolutions<'tcx>,
) {
self.iterate_until_fixed_point("Expansion", |constraint, origin| {
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(..) => {
fn expand_node(
&self,
- region_rels: &RegionRelations<'_, '_, 'tcx>,
a_region: Region<'tcx>,
b_vid: RegionVid,
b_data: &mut VarValue<'tcx>,
// 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;
}
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;
}
}
}
+
+ 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,
}
Constraint::RegSubReg(sub, sup) => {
- if region_rels.is_subregion_of(sub, sup) {
+ if self.region_rels.is_subregion_of(sub, sup) {
continue;
}
// 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 {:?}={:?} <= {:?}",
}
}
- for verify in &self.verifys {
+ for verify in &self.data.verifys {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(verify.region);
continue;
}
- if verify.bound.is_met(region_rels, var_data, sub) {
+ if self.bound_is_met(&verify.bound, var_data, sub) {
continue;
}
/// 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>>,
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,
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(
fn collect_error_for_expanding_node(
&self,
- region_rels: &RegionRelations<'_, '_, 'tcx>,
graph: &RegionGraph<'tcx>,
dup_vec: &mut [u32],
node_idx: RegionVid,
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: {:?} \
// 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();
node_idx
);
- process_edges(self, &mut state, graph, node_idx, dir);
+ process_edges(&self.data, &mut state, graph, node_idx, dir);
}
let WalkState {
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);
}
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 {
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.
/// 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>>,
impl<'tcx> RegionConstraintCollector<'tcx> {
pub fn new() -> RegionConstraintCollector<'tcx> {
RegionConstraintCollector {
+ var_origins: VarOrigins::default(),
data: RegionConstraintData::default(),
lubs: FxHashMap(),
glbs: FxHashMap(),
}
/// 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 {
// 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);
}
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 });
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
}
}
}
-
-impl<'tcx> RegionConstraintData<'tcx> {
- pub fn num_vars(&self) -> usize {
- self.var_origins.len()
- }
-}