Skip to content

Commit 5e5b99f

Browse files
committed
Auto merge of #27892 - nikomatsakis:issue-27583, r=pnkfelix
Issue #27583 was caused by the fact that `LUB('a,'b)` yielded `'static`, even if there existed a region `'tcx:'a+'b`. This PR replaces the old very hacky code for computing how free regions relate to one another with something rather more robust. This solves the issue for #27583, though I think that similar bizarro bugs can no doubt arise in other ways -- the root of the problem is that the region-inference code was written in an era when a LUB always existed, but that hasn't held for some time. To *truly* solve this problem, it needs to be generalized to cope with that reality. But let's leave that battle for another day. r? @aturon
2 parents 94ee3b5 + 81eab1c commit 5e5b99f

File tree

9 files changed

+925
-116
lines changed

9 files changed

+925
-116
lines changed

Diff for: src/librustc/middle/free_region.rs

+65-28
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,26 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
//! This file defines
12-
11+
//! This file handles the relationships between free regions --
12+
//! meaning lifetime parameters. Ordinarily, free regions are
13+
//! unrelated to one another, but they can be related vai implied or
14+
//! explicit bounds. In that case, we track the bounds using the
15+
//! `TransitiveRelation` type and use that to decide when one free
16+
//! region outlives another and so forth.
17+
18+
use middle::ty::{self, FreeRegion, Region};
1319
use middle::wf::ImpliedBound;
14-
use middle::ty::{self, FreeRegion};
15-
use util::common::can_reach;
16-
use util::nodemap::{FnvHashMap, FnvHashSet};
20+
use rustc_data_structures::transitive_relation::TransitiveRelation;
1721

1822
#[derive(Clone)]
1923
pub struct FreeRegionMap {
20-
/// `map` maps from a free region `a` to a list of
21-
/// free regions `bs` such that `a <= b for all b in bs`
22-
map: FnvHashMap<FreeRegion, Vec<FreeRegion>>,
23-
/// regions that are required to outlive (and therefore be
24-
/// equal to) 'static.
25-
statics: FnvHashSet<FreeRegion>
24+
// Stores the relation `a < b`, where `a` and `b` are regions.
25+
relation: TransitiveRelation<Region>
2626
}
2727

2828
impl FreeRegionMap {
2929
pub fn new() -> FreeRegionMap {
30-
FreeRegionMap { map: FnvHashMap(), statics: FnvHashSet() }
30+
FreeRegionMap { relation: TransitiveRelation::new() }
3131
}
3232

3333
pub fn relate_free_regions_from_implied_bounds<'tcx>(&mut self,
@@ -84,22 +84,38 @@ impl FreeRegionMap {
8484
}
8585

8686
fn relate_to_static(&mut self, sup: FreeRegion) {
87-
self.statics.insert(sup);
87+
self.relation.add(ty::ReStatic, ty::ReFree(sup));
8888
}
8989

9090
fn relate_free_regions(&mut self, sub: FreeRegion, sup: FreeRegion) {
91-
let mut sups = self.map.entry(sub).or_insert(Vec::new());
92-
if !sups.contains(&sup) {
93-
sups.push(sup);
94-
}
91+
self.relation.add(ty::ReFree(sub), ty::ReFree(sup))
9592
}
9693

9794
/// Determines whether two free regions have a subregion relationship
9895
/// by walking the graph encoded in `map`. Note that
9996
/// it is possible that `sub != sup` and `sub <= sup` and `sup <= sub`
10097
/// (that is, the user can give two different names to the same lifetime).
10198
pub fn sub_free_region(&self, sub: FreeRegion, sup: FreeRegion) -> bool {
102-
can_reach(&self.map, sub, sup) || self.is_static(&sup)
99+
let result = sub == sup || {
100+
let sub = ty::ReFree(sub);
101+
let sup = ty::ReFree(sup);
102+
self.relation.contains(&sub, &sup) || self.relation.contains(&ty::ReStatic, &sup)
103+
};
104+
debug!("sub_free_region(sub={:?}, sup={:?}) = {:?}", sub, sup, result);
105+
result
106+
}
107+
108+
pub fn lub_free_regions(&self, fr_a: FreeRegion, fr_b: FreeRegion) -> Region {
109+
let r_a = ty::ReFree(fr_a);
110+
let r_b = ty::ReFree(fr_b);
111+
let result = if fr_a == fr_b { r_a } else {
112+
match self.relation.postdom_upper_bound(&r_a, &r_b) {
113+
None => ty::ReStatic,
114+
Some(r) => *r,
115+
}
116+
};
117+
debug!("lub_free_regions(fr_a={:?}, fr_b={:?}) = {:?}", fr_a, fr_b, result);
118+
result
103119
}
104120

105121
/// Determines whether one region is a subregion of another. This is intended to run *after
@@ -109,10 +125,7 @@ impl FreeRegionMap {
109125
sub_region: ty::Region,
110126
super_region: ty::Region)
111127
-> bool {
112-
debug!("is_subregion_of(sub_region={:?}, super_region={:?})",
113-
sub_region, super_region);
114-
115-
sub_region == super_region || {
128+
let result = sub_region == super_region || {
116129
match (sub_region, super_region) {
117130
(ty::ReEmpty, _) |
118131
(_, ty::ReStatic) =>
@@ -121,23 +134,47 @@ impl FreeRegionMap {
121134
(ty::ReScope(sub_scope), ty::ReScope(super_scope)) =>
122135
tcx.region_maps.is_subscope_of(sub_scope, super_scope),
123136

124-
(ty::ReScope(sub_scope), ty::ReFree(ref fr)) =>
125-
tcx.region_maps.is_subscope_of(sub_scope, fr.scope.to_code_extent()),
137+
(ty::ReScope(sub_scope), ty::ReFree(fr)) =>
138+
tcx.region_maps.is_subscope_of(sub_scope, fr.scope.to_code_extent()) ||
139+
self.is_static(fr),
126140

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

130-
(ty::ReStatic, ty::ReFree(ref sup_fr)) => self.is_static(sup_fr),
144+
(ty::ReStatic, ty::ReFree(sup_fr)) =>
145+
self.is_static(sup_fr),
131146

132147
_ =>
133148
false,
134149
}
135-
}
150+
};
151+
debug!("is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
152+
sub_region, super_region, result);
153+
result
136154
}
137155

138156
/// Determines whether this free-region is required to be 'static
139-
pub fn is_static(&self, super_region: &ty::FreeRegion) -> bool {
157+
pub fn is_static(&self, super_region: ty::FreeRegion) -> bool {
140158
debug!("is_static(super_region={:?})", super_region);
141-
self.statics.iter().any(|s| can_reach(&self.map, *s, *super_region))
159+
self.relation.contains(&ty::ReStatic, &ty::ReFree(super_region))
142160
}
143161
}
162+
163+
#[cfg(test)]
164+
fn free_region(index: u32) -> FreeRegion {
165+
use middle::region::DestructionScopeData;
166+
FreeRegion { scope: DestructionScopeData::new(0),
167+
bound_region: ty::BoundRegion::BrAnon(index) }
168+
}
169+
170+
#[test]
171+
fn lub() {
172+
// a very VERY basic test, but see the tests in
173+
// TransitiveRelation, which are much more thorough.
174+
let frs: Vec<_> = (0..3).map(|i| free_region(i)).collect();
175+
let mut map = FreeRegionMap::new();
176+
map.relate_free_regions(frs[0], frs[2]);
177+
map.relate_free_regions(frs[1], frs[2]);
178+
assert_eq!(map.lub_free_regions(frs[0], frs[1]), ty::ReFree(frs[2]));
179+
}
180+

Diff for: src/librustc/middle/infer/region_inference/mod.rs

+4-33
Original file line numberDiff line numberDiff line change
@@ -812,8 +812,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
812812
ReScope(self.tcx.region_maps.nearest_common_ancestor(a_id, b_id))
813813
}
814814

815-
(ReFree(ref a_fr), ReFree(ref b_fr)) => {
816-
self.lub_free_regions(free_regions, a_fr, b_fr)
815+
(ReFree(a_fr), ReFree(b_fr)) => {
816+
free_regions.lub_free_regions(a_fr, b_fr)
817817
}
818818

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

828-
/// Computes a region that encloses both free region arguments. Guarantee that if the same two
829-
/// regions are given as argument, in any order, a consistent result is returned.
830-
fn lub_free_regions(&self,
831-
free_regions: &FreeRegionMap,
832-
a: &FreeRegion,
833-
b: &FreeRegion)
834-
-> ty::Region
835-
{
836-
return match a.cmp(b) {
837-
Less => helper(self, free_regions, a, b),
838-
Greater => helper(self, free_regions, b, a),
839-
Equal => ty::ReFree(*a)
840-
};
841-
842-
fn helper(_this: &RegionVarBindings,
843-
free_regions: &FreeRegionMap,
844-
a: &FreeRegion,
845-
b: &FreeRegion) -> ty::Region
846-
{
847-
if free_regions.sub_free_region(*a, *b) {
848-
ty::ReFree(*b)
849-
} else if free_regions.sub_free_region(*b, *a) {
850-
ty::ReFree(*a)
851-
} else {
852-
ty::ReStatic
853-
}
854-
}
855-
}
856-
857828
fn glb_concrete_regions(&self,
858829
free_regions: &FreeRegionMap,
859830
a: Region,
@@ -892,8 +863,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
892863
b));
893864
}
894865

895-
(ReFree(ref fr), ReScope(s_id)) |
896-
(ReScope(s_id), ReFree(ref fr)) => {
866+
(ReFree(fr), ReScope(s_id)) |
867+
(ReScope(s_id), ReFree(fr)) => {
897868
let s = ReScope(s_id);
898869
// Free region is something "at least as big as
899870
// `fr.scope_id`." If we find that the scope `fr.scope_id` is bigger

Diff for: src/librustc/util/common.rs

-43
Original file line numberDiff line numberDiff line change
@@ -203,49 +203,6 @@ pub fn block_query<P>(b: &ast::Block, p: P) -> bool where P: FnMut(&ast::Expr) -
203203
return v.flag;
204204
}
205205

206-
/// K: Eq + Hash<S>, V, S, H: Hasher<S>
207-
///
208-
/// Determines whether there exists a path from `source` to `destination`. The
209-
/// graph is defined by the `edges_map`, which maps from a node `S` to a list of
210-
/// its adjacent nodes `T`.
211-
///
212-
/// Efficiency note: This is implemented in an inefficient way because it is
213-
/// typically invoked on very small graphs. If the graphs become larger, a more
214-
/// efficient graph representation and algorithm would probably be advised.
215-
pub fn can_reach<T, S>(edges_map: &HashMap<T, Vec<T>, S>, source: T,
216-
destination: T) -> bool
217-
where S: HashState, T: Hash + Eq + Clone,
218-
{
219-
if source == destination {
220-
return true;
221-
}
222-
223-
// Do a little breadth-first-search here. The `queue` list
224-
// doubles as a way to detect if we've seen a particular FR
225-
// before. Note that we expect this graph to be an *extremely
226-
// shallow* tree.
227-
let mut queue = vec!(source);
228-
let mut i = 0;
229-
while i < queue.len() {
230-
match edges_map.get(&queue[i]) {
231-
Some(edges) => {
232-
for target in edges {
233-
if *target == destination {
234-
return true;
235-
}
236-
237-
if !queue.iter().any(|x| x == target) {
238-
queue.push((*target).clone());
239-
}
240-
}
241-
}
242-
None => {}
243-
}
244-
i += 1;
245-
}
246-
return false;
247-
}
248-
249206
/// Memoizes a one-argument closure using the given RefCell containing
250207
/// a type implementing MutableMap to serve as a cache.
251208
///

0 commit comments

Comments
 (0)