From 4b1d3b703629db036ce685a852b80bf196419bb5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 18 Aug 2015 17:41:20 -0400 Subject: [PATCH] rewrite `free_region`/`region_inference` to use newly minted `TransitiveRelation` --- src/librustc/middle/free_region.rs | 83 +++++++++++++------ .../middle/infer/region_inference/mod.rs | 37 +-------- 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 5af37e9530cce..787303231b039 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -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>, - /// regions that are required to outlive (and therefore be - /// equal to) 'static. - statics: FnvHashSet + relation: TransitiveRelation } 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, @@ -84,14 +78,11 @@ 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 @@ -99,7 +90,26 @@ impl FreeRegionMap { /// 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 @@ -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) => @@ -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])); +} + diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index e8f8dbfbb0e63..e04f2955ddc18 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -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 @@ -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, @@ -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