Skip to content

Commit

Permalink
rewrite free_region/region_inference to use newly minted
Browse files Browse the repository at this point in the history
`TransitiveRelation`
  • Loading branch information
nikomatsakis committed Aug 18, 2015
1 parent 5e126e4 commit 4b1d3b7
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 59 deletions.
83 changes: 57 additions & 26 deletions src/librustc/middle/free_region.rs
Expand Up @@ -10,24 +10,18 @@

//! This file defines

use middle::ty::{self, FreeRegion, Region};
use middle::wf::ImpliedBound;
use middle::ty::{self, FreeRegion};
use util::common::can_reach;
use util::nodemap::{FnvHashMap, FnvHashSet};
use rustc_data_structures::transitive_relation::TransitiveRelation;

#[derive(Clone)]
pub struct FreeRegionMap {
/// `map` maps from a free region `a` to a list of
/// free regions `bs` such that `a <= b for all b in bs`
map: FnvHashMap<FreeRegion, Vec<FreeRegion>>,
/// regions that are required to outlive (and therefore be
/// equal to) 'static.
statics: FnvHashSet<FreeRegion>
relation: TransitiveRelation<Region>
}

impl FreeRegionMap {
pub fn new() -> FreeRegionMap {
FreeRegionMap { map: FnvHashMap(), statics: FnvHashSet() }
FreeRegionMap { relation: TransitiveRelation::new() }
}

pub fn relate_free_regions_from_implied_bounds<'tcx>(&mut self,
Expand Down Expand Up @@ -84,22 +78,38 @@ impl FreeRegionMap {
}

fn relate_to_static(&mut self, sup: FreeRegion) {
self.statics.insert(sup);
self.relation.add(ty::ReStatic, ty::ReFree(sup));
}

fn relate_free_regions(&mut self, sub: FreeRegion, sup: FreeRegion) {
let mut sups = self.map.entry(sub).or_insert(Vec::new());
if !sups.contains(&sup) {
sups.push(sup);
}
self.relation.add(ty::ReFree(sub), ty::ReFree(sup))
}

/// Determines whether two free regions have a subregion relationship
/// by walking the graph encoded in `map`. Note that
/// it is possible that `sub != sup` and `sub <= sup` and `sup <= sub`
/// (that is, the user can give two different names to the same lifetime).
pub fn sub_free_region(&self, sub: FreeRegion, sup: FreeRegion) -> bool {
can_reach(&self.map, sub, sup) || self.is_static(&sup)
let result = sub == sup || {
let sub = ty::ReFree(sub);
let sup = ty::ReFree(sup);
self.relation.contains(&sub, &sup) || self.relation.contains(&sup, &ty::ReStatic)
};
debug!("sub_free_region(sub={:?}, sup={:?}) = {:?}", sub, sup, result);
result
}

pub fn lub_free_regions(&self, fr_a: FreeRegion, fr_b: FreeRegion) -> Region {
let r_a = ty::ReFree(fr_a);
let r_b = ty::ReFree(fr_b);
let result = if fr_a == fr_b { r_a } else {
match self.relation.best_upper_bound(&r_a, &r_b) {
None => ty::ReStatic,
Some(r) => *r,
}
};
debug!("lub_free_regions(fr_a={:?}, fr_b={:?}) = {:?}", fr_a, fr_b, result);
result
}

/// Determines whether one region is a subregion of another. This is intended to run *after
Expand All @@ -109,10 +119,7 @@ impl FreeRegionMap {
sub_region: ty::Region,
super_region: ty::Region)
-> bool {
debug!("is_subregion_of(sub_region={:?}, super_region={:?})",
sub_region, super_region);

sub_region == super_region || {
let result = sub_region == super_region || {
match (sub_region, super_region) {
(ty::ReEmpty, _) |
(_, ty::ReStatic) =>
Expand All @@ -121,23 +128,47 @@ impl FreeRegionMap {
(ty::ReScope(sub_scope), ty::ReScope(super_scope)) =>
tcx.region_maps.is_subscope_of(sub_scope, super_scope),

(ty::ReScope(sub_scope), ty::ReFree(ref fr)) =>
tcx.region_maps.is_subscope_of(sub_scope, fr.scope.to_code_extent()),
(ty::ReScope(sub_scope), ty::ReFree(fr)) =>
tcx.region_maps.is_subscope_of(sub_scope, fr.scope.to_code_extent()) ||
self.is_static(fr),

(ty::ReFree(sub_fr), ty::ReFree(super_fr)) =>
self.sub_free_region(sub_fr, super_fr),

(ty::ReStatic, ty::ReFree(ref sup_fr)) => self.is_static(sup_fr),
(ty::ReStatic, ty::ReFree(sup_fr)) =>
self.is_static(sup_fr),

_ =>
false,
}
}
};
debug!("is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
sub_region, super_region, result);
result
}

/// Determines whether this free-region is required to be 'static
pub fn is_static(&self, super_region: &ty::FreeRegion) -> bool {
pub fn is_static(&self, super_region: ty::FreeRegion) -> bool {
debug!("is_static(super_region={:?})", super_region);
self.statics.iter().any(|s| can_reach(&self.map, *s, *super_region))
self.relation.contains(&ty::ReStatic, &ty::ReFree(super_region))
}
}

#[cfg(test)]
fn free_region(index: u32) -> FreeRegion {
use middle::region::DestructionScopeData;
FreeRegion { scope: DestructionScopeData::new(0),
bound_region: ty::BoundRegion::BrAnon(index) }
}

#[test]
fn lub() {
// a very VERY basic test, but see the tests in
// TransitiveRelation, which are much more thorough.
let frs: Vec<_> = (0..3).map(|i| free_region(i)).collect();
let mut map = FreeRegionMap::new();
map.relate_free_regions(frs[0], frs[2]);
map.relate_free_regions(frs[1], frs[2]);
assert_eq!(map.lub_free_regions(frs[0], frs[1]), ty::ReFree(frs[2]));
}

37 changes: 4 additions & 33 deletions src/librustc/middle/infer/region_inference/mod.rs
Expand Up @@ -812,8 +812,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
ReScope(self.tcx.region_maps.nearest_common_ancestor(a_id, b_id))
}

(ReFree(ref a_fr), ReFree(ref b_fr)) => {
self.lub_free_regions(free_regions, a_fr, b_fr)
(ReFree(a_fr), ReFree(b_fr)) => {
free_regions.lub_free_regions(a_fr, b_fr)
}

// For these types, we cannot define any additional
Expand All @@ -825,35 +825,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
}

/// Computes a region that encloses both free region arguments. Guarantee that if the same two
/// regions are given as argument, in any order, a consistent result is returned.
fn lub_free_regions(&self,
free_regions: &FreeRegionMap,
a: &FreeRegion,
b: &FreeRegion)
-> ty::Region
{
return match a.cmp(b) {
Less => helper(self, free_regions, a, b),
Greater => helper(self, free_regions, b, a),
Equal => ty::ReFree(*a)
};

fn helper(_this: &RegionVarBindings,
free_regions: &FreeRegionMap,
a: &FreeRegion,
b: &FreeRegion) -> ty::Region
{
if free_regions.sub_free_region(*a, *b) {
ty::ReFree(*b)
} else if free_regions.sub_free_region(*b, *a) {
ty::ReFree(*a)
} else {
ty::ReStatic
}
}
}

fn glb_concrete_regions(&self,
free_regions: &FreeRegionMap,
a: Region,
Expand Down Expand Up @@ -892,8 +863,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
b));
}

(ReFree(ref fr), ReScope(s_id)) |
(ReScope(s_id), ReFree(ref fr)) => {
(ReFree(fr), ReScope(s_id)) |
(ReScope(s_id), ReFree(fr)) => {
let s = ReScope(s_id);
// Free region is something "at least as big as
// `fr.scope_id`." If we find that the scope `fr.scope_id` is bigger
Expand Down

0 comments on commit 4b1d3b7

Please sign in to comment.