Skip to content

Commit a8a33cf

Browse files
committed
Auto merge of rust-lang#99702 - SparrowLii:transtive_relation, r=oli-obk
get rid of `RefCell` in `TransitiveRelation` This is one of the jobs in `Pending refactorings` in rust-lang#48685. The parallel-compiler's work has been suspended for quite some time, but I think I can pick it up gradually. I think this PR should be a start. Regarding the refactoring of `TransitiveRelation`, `@nikomatsakis` has proposed [two(three?) schemes](rust-lang#48587 (comment)). In order to satisfy both compilation efficiency and robustness, I think adding the `freeze` method may be the best solution, although it requires relatively more code changes.
2 parents ee8c31e + a01ac5a commit a8a33cf

File tree

13 files changed

+224
-179
lines changed

13 files changed

+224
-179
lines changed

Diff for: compiler/rustc_borrowck/src/type_check/free_region_relations.rs

+22-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustc_data_structures::frozen::Frozen;
2-
use rustc_data_structures::transitive_relation::TransitiveRelation;
2+
use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
33
use rustc_infer::infer::canonical::QueryRegionConstraints;
44
use rustc_infer::infer::outlives;
55
use rustc_infer::infer::outlives::env::RegionBoundPairs;
@@ -61,25 +61,13 @@ pub(crate) fn create<'tcx>(
6161
constraints,
6262
universal_regions: universal_regions.clone(),
6363
region_bound_pairs: Default::default(),
64-
relations: UniversalRegionRelations {
65-
universal_regions: universal_regions.clone(),
66-
outlives: Default::default(),
67-
inverse_outlives: Default::default(),
68-
},
64+
outlives: Default::default(),
65+
inverse_outlives: Default::default(),
6966
}
7067
.create()
7168
}
7269

7370
impl UniversalRegionRelations<'_> {
74-
/// Records in the `outlives_relation` (and
75-
/// `inverse_outlives_relation`) that `fr_a: fr_b`. Invoked by the
76-
/// builder below.
77-
fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
78-
debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b);
79-
self.outlives.add(fr_a, fr_b);
80-
self.inverse_outlives.add(fr_b, fr_a);
81-
}
82-
8371
/// Given two universal regions, returns the postdominating
8472
/// upper-bound (effectively the least upper bound).
8573
///
@@ -216,11 +204,20 @@ struct UniversalRegionRelationsBuilder<'this, 'tcx> {
216204
constraints: &'this mut MirTypeckRegionConstraints<'tcx>,
217205

218206
// outputs:
219-
relations: UniversalRegionRelations<'tcx>,
207+
outlives: TransitiveRelationBuilder<RegionVid>,
208+
inverse_outlives: TransitiveRelationBuilder<RegionVid>,
220209
region_bound_pairs: RegionBoundPairs<'tcx>,
221210
}
222211

223212
impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
213+
/// Records in the `outlives_relation` (and
214+
/// `inverse_outlives_relation`) that `fr_a: fr_b`.
215+
fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
216+
debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b);
217+
self.outlives.add(fr_a, fr_b);
218+
self.inverse_outlives.add(fr_b, fr_a);
219+
}
220+
224221
pub(crate) fn create(mut self) -> CreateResult<'tcx> {
225222
let unnormalized_input_output_tys = self
226223
.universal_regions
@@ -292,9 +289,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
292289
let fr_fn_body = self.universal_regions.fr_fn_body;
293290
for fr in self.universal_regions.universal_regions() {
294291
debug!("build: relating free region {:?} to itself and to 'static", fr);
295-
self.relations.relate_universal_regions(fr, fr);
296-
self.relations.relate_universal_regions(fr_static, fr);
297-
self.relations.relate_universal_regions(fr, fr_fn_body);
292+
self.relate_universal_regions(fr, fr);
293+
self.relate_universal_regions(fr_static, fr);
294+
self.relate_universal_regions(fr, fr_fn_body);
298295
}
299296

300297
for data in &constraint_sets {
@@ -313,7 +310,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
313310
}
314311

315312
CreateResult {
316-
universal_region_relations: Frozen::freeze(self.relations),
313+
universal_region_relations: Frozen::freeze(UniversalRegionRelations {
314+
universal_regions: self.universal_regions,
315+
outlives: self.outlives.freeze(),
316+
inverse_outlives: self.inverse_outlives.freeze(),
317+
}),
317318
region_bound_pairs: self.region_bound_pairs,
318319
normalized_inputs_and_output,
319320
}
@@ -356,7 +357,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
356357
// The bound says that `r1 <= r2`; we store `r2: r1`.
357358
let r1 = self.universal_regions.to_region_vid(r1);
358359
let r2 = self.universal_regions.to_region_vid(r2);
359-
self.relations.relate_universal_regions(r2, r1);
360+
self.relate_universal_regions(r2, r1);
360361
}
361362

362363
OutlivesBound::RegionSubParam(r_a, param_b) => {

Diff for: compiler/rustc_data_structures/src/transitive_relation.rs

+67-54
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,57 @@
1+
use crate::frozen::Frozen;
12
use crate::fx::FxIndexSet;
2-
use crate::sync::Lock;
33
use rustc_index::bit_set::BitMatrix;
44
use std::fmt::Debug;
55
use std::hash::Hash;
66
use std::mem;
7+
use std::ops::Deref;
78

89
#[cfg(test)]
910
mod tests;
1011

1112
#[derive(Clone, Debug)]
12-
pub struct TransitiveRelation<T> {
13+
pub struct TransitiveRelationBuilder<T> {
1314
// List of elements. This is used to map from a T to a usize.
1415
elements: FxIndexSet<T>,
1516

1617
// List of base edges in the graph. Require to compute transitive
1718
// closure.
1819
edges: Vec<Edge>,
20+
}
21+
22+
#[derive(Debug)]
23+
pub struct TransitiveRelation<T> {
24+
// Frozen transitive relation elements and edges.
25+
builder: Frozen<TransitiveRelationBuilder<T>>,
1926

20-
// This is a cached transitive closure derived from the edges.
21-
// Currently, we build it lazily and just throw out any existing
22-
// copy whenever a new edge is added. (The Lock is to permit
23-
// the lazy computation.) This is kind of silly, except for the
24-
// fact its size is tied to `self.elements.len()`, so I wanted to
25-
// wait before building it up to avoid reallocating as new edges
26-
// are added with new elements. Perhaps better would be to ask the
27-
// user for a batch of edges to minimize this effect, but I
28-
// already wrote the code this way. :P -nmatsakis
29-
closure: Lock<Option<BitMatrix<usize, usize>>>,
27+
// Cached transitive closure derived from the edges.
28+
closure: Frozen<BitMatrix<usize, usize>>,
3029
}
3130

32-
// HACK(eddyb) manual impl avoids `Default` bound on `T`.
33-
impl<T: Eq + Hash> Default for TransitiveRelation<T> {
34-
fn default() -> Self {
31+
impl<T> Deref for TransitiveRelation<T> {
32+
type Target = Frozen<TransitiveRelationBuilder<T>>;
33+
34+
fn deref(&self) -> &Self::Target {
35+
&self.builder
36+
}
37+
}
38+
39+
impl<T: Clone> Clone for TransitiveRelation<T> {
40+
fn clone(&self) -> Self {
3541
TransitiveRelation {
36-
elements: Default::default(),
37-
edges: Default::default(),
38-
closure: Default::default(),
42+
builder: Frozen::freeze(self.builder.deref().clone()),
43+
closure: Frozen::freeze(self.closure.deref().clone()),
3944
}
4045
}
4146
}
4247

48+
// HACK(eddyb) manual impl avoids `Default` bound on `T`.
49+
impl<T: Eq + Hash> Default for TransitiveRelationBuilder<T> {
50+
fn default() -> Self {
51+
TransitiveRelationBuilder { elements: Default::default(), edges: Default::default() }
52+
}
53+
}
54+
4355
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
4456
struct Index(usize);
4557

@@ -49,7 +61,7 @@ struct Edge {
4961
target: Index,
5062
}
5163

52-
impl<T: Eq + Hash + Copy> TransitiveRelation<T> {
64+
impl<T: Eq + Hash + Copy> TransitiveRelationBuilder<T> {
5365
pub fn is_empty(&self) -> bool {
5466
self.edges.is_empty()
5567
}
@@ -63,23 +75,19 @@ impl<T: Eq + Hash + Copy> TransitiveRelation<T> {
6375
}
6476

6577
fn add_index(&mut self, a: T) -> Index {
66-
let (index, added) = self.elements.insert_full(a);
67-
if added {
68-
// if we changed the dimensions, clear the cache
69-
*self.closure.get_mut() = None;
70-
}
78+
let (index, _added) = self.elements.insert_full(a);
7179
Index(index)
7280
}
7381

7482
/// Applies the (partial) function to each edge and returns a new
75-
/// relation. If `f` returns `None` for any end-point, returns
76-
/// `None`.
77-
pub fn maybe_map<F, U>(&self, mut f: F) -> Option<TransitiveRelation<U>>
83+
/// relation builder. If `f` returns `None` for any end-point,
84+
/// returns `None`.
85+
pub fn maybe_map<F, U>(&self, mut f: F) -> Option<TransitiveRelationBuilder<U>>
7886
where
7987
F: FnMut(T) -> Option<U>,
8088
U: Clone + Debug + Eq + Hash + Copy,
8189
{
82-
let mut result = TransitiveRelation::default();
90+
let mut result = TransitiveRelationBuilder::default();
8391
for edge in &self.edges {
8492
result.add(f(self.elements[edge.source.0])?, f(self.elements[edge.target.0])?);
8593
}
@@ -93,10 +101,38 @@ impl<T: Eq + Hash + Copy> TransitiveRelation<T> {
93101
let edge = Edge { source: a, target: b };
94102
if !self.edges.contains(&edge) {
95103
self.edges.push(edge);
104+
}
105+
}
106+
107+
/// Compute the transitive closure derived from the edges, and converted to
108+
/// the final result. After this, all elements will be immutable to maintain
109+
/// the correctness of the result.
110+
pub fn freeze(self) -> TransitiveRelation<T> {
111+
let mut matrix = BitMatrix::new(self.elements.len(), self.elements.len());
112+
let mut changed = true;
113+
while changed {
114+
changed = false;
115+
for edge in &self.edges {
116+
// add an edge from S -> T
117+
changed |= matrix.insert(edge.source.0, edge.target.0);
96118

97-
// added an edge, clear the cache
98-
*self.closure.get_mut() = None;
119+
// add all outgoing edges from T into S
120+
changed |= matrix.union_rows(edge.target.0, edge.source.0);
121+
}
99122
}
123+
TransitiveRelation { builder: Frozen::freeze(self), closure: Frozen::freeze(matrix) }
124+
}
125+
}
126+
127+
impl<T: Eq + Hash + Copy> TransitiveRelation<T> {
128+
/// Applies the (partial) function to each edge and returns a new
129+
/// relation including transitive closures.
130+
pub fn maybe_map<F, U>(&self, f: F) -> Option<TransitiveRelation<U>>
131+
where
132+
F: FnMut(T) -> Option<U>,
133+
U: Clone + Debug + Eq + Hash + Copy,
134+
{
135+
Some(self.builder.maybe_map(f)?.freeze())
100136
}
101137

102138
/// Checks whether `a < target` (transitively)
@@ -322,30 +358,7 @@ impl<T: Eq + Hash + Copy> TransitiveRelation<T> {
322358
where
323359
OP: FnOnce(&BitMatrix<usize, usize>) -> R,
324360
{
325-
let mut closure_cell = self.closure.borrow_mut();
326-
let mut closure = closure_cell.take();
327-
if closure.is_none() {
328-
closure = Some(self.compute_closure());
329-
}
330-
let result = op(closure.as_ref().unwrap());
331-
*closure_cell = closure;
332-
result
333-
}
334-
335-
fn compute_closure(&self) -> BitMatrix<usize, usize> {
336-
let mut matrix = BitMatrix::new(self.elements.len(), self.elements.len());
337-
let mut changed = true;
338-
while changed {
339-
changed = false;
340-
for edge in &self.edges {
341-
// add an edge from S -> T
342-
changed |= matrix.insert(edge.source.0, edge.target.0);
343-
344-
// add all outgoing edges from T into S
345-
changed |= matrix.union_rows(edge.target.0, edge.source.0);
346-
}
347-
}
348-
matrix
361+
op(&self.closure)
349362
}
350363

351364
/// Lists all the base edges in the graph: the initial _non-transitive_ set of element

0 commit comments

Comments
 (0)