Skip to content

Commit d54e7e3

Browse files
committed
introduce ConstraintGraph, stop mutating constraints in place
Encapsulate the dependencies more cleanly.
1 parent 8fa24bb commit d54e7e3

File tree

6 files changed

+76
-94
lines changed

6 files changed

+76
-94
lines changed

src/librustc_mir/borrow_check/nll/constraint_set.rs

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,39 +32,6 @@ impl ConstraintSet {
3232
}
3333
self.constraints.push(constraint);
3434
}
35-
36-
/// Once all constraints have been added, `link()` is used to thread together the constraints
37-
/// based on which would be affected when a particular region changes. See the next field of
38-
/// `OutlivesContraint` for more details.
39-
/// link returns a map that is needed later by `each_affected_by_dirty`.
40-
pub fn link(&mut self, len: usize) -> IndexVec<RegionVid, Option<ConstraintIndex>> {
41-
let mut map = IndexVec::from_elem_n(None, len);
42-
43-
for (idx, constraint) in self.constraints.iter_enumerated_mut().rev() {
44-
let mut head = &mut map[constraint.sub];
45-
debug_assert!(constraint.next.is_none());
46-
constraint.next = *head;
47-
*head = Some(idx);
48-
}
49-
50-
map
51-
}
52-
53-
/// When a region R1 changes, we need to reprocess all constraints R2: R1 to take into account
54-
/// any new elements that R1 now has. This method will quickly enumerate all such constraints
55-
/// (that is, constraints where R1 is in the "subregion" position).
56-
/// To use it, invoke with `map[R1]` where map is the map returned by `link`;
57-
/// the callback op will be invoked for each affected constraint.
58-
pub fn each_affected_by_dirty(
59-
&self,
60-
mut opt_dep_idx: Option<ConstraintIndex>,
61-
mut op: impl FnMut(ConstraintIndex),
62-
) {
63-
while let Some(dep_idx) = opt_dep_idx {
64-
op(dep_idx);
65-
opt_dep_idx = self.constraints[dep_idx].next;
66-
}
67-
}
6835
}
6936

7037
impl Deref for ConstraintSet {
@@ -85,16 +52,6 @@ pub struct OutlivesConstraint {
8552
/// Region that must be outlived.
8653
pub sub: RegionVid,
8754

88-
/// Later on, we thread the constraints onto a linked list
89-
/// grouped by their `sub` field. So if you had:
90-
///
91-
/// Index | Constraint | Next Field
92-
/// ----- | ---------- | ----------
93-
/// 0 | `'a: 'b` | Some(2)
94-
/// 1 | `'b: 'c` | None
95-
/// 2 | `'c: 'b` | None
96-
pub next: Option<ConstraintIndex>,
97-
9855
/// Where did this constraint arise?
9956
pub locations: Locations,
10057
}
@@ -110,3 +67,46 @@ impl fmt::Debug for OutlivesConstraint {
11067
}
11168

11269
newtype_index!(ConstraintIndex { DEBUG_FORMAT = "ConstraintIndex({})" });
70+
71+
crate struct ConstraintGraph {
72+
first_constraints: IndexVec<RegionVid, Option<ConstraintIndex>>,
73+
next_constraints: IndexVec<ConstraintIndex, Option<ConstraintIndex>>,
74+
}
75+
76+
impl ConstraintGraph {
77+
/// Constraint a graph where each region constraint `R1: R2` is
78+
/// treated as an edge `R2 -> R1`. This is useful for cheaply
79+
/// finding dirty constraints.
80+
crate fn new(set: &ConstraintSet, num_region_vars: usize) -> Self {
81+
let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars);
82+
let mut next_constraints = IndexVec::from_elem(None, &set.constraints);
83+
84+
for (idx, constraint) in set.constraints.iter_enumerated().rev() {
85+
let mut head = &mut first_constraints[constraint.sub];
86+
let mut next = &mut next_constraints[idx];
87+
debug_assert!(next.is_none());
88+
*next = *head;
89+
*head = Some(idx);
90+
}
91+
92+
ConstraintGraph { first_constraints, next_constraints }
93+
}
94+
95+
/// Invokes `op` with the index of any constraints of the form
96+
/// `region_sup: region_sub`. These are the constraints that must
97+
/// be reprocessed when the value of `R1` changes. If you think of
98+
/// each constraint `R1: R2` as an edge `R2 -> R1`, then this
99+
/// gives the set of successors to R2.
100+
crate fn for_each_dependent(
101+
&self,
102+
region_sub: RegionVid,
103+
mut op: impl FnMut(ConstraintIndex),
104+
) {
105+
let mut p = self.first_constraints[region_sub];
106+
while let Some(dep_idx) = p {
107+
op(dep_idx);
108+
p = self.next_constraints[dep_idx];
109+
}
110+
}
111+
}
112+

src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
8383
sup,
8484
sub,
8585
locations,
86-
next: _,
8786
} = constraint;
8887
with_msg(&format!(
8988
"{:?}: {:?} due to {:?}",

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
8989
stack: &mut Vec<ConstraintIndex>,
9090
results: &mut Vec<Vec<ConstraintIndex>>,
9191
) {
92-
let dependency_map = self.dependency_map.as_ref().unwrap();
93-
9492
// Check if we already visited this region.
9593
if !visited.insert(current_region) {
9694
return;
@@ -105,20 +103,19 @@ impl<'tcx> RegionInferenceContext<'tcx> {
105103
return;
106104
}
107105

108-
self.constraints
109-
.each_affected_by_dirty(dependency_map[current_region], |constraint| {
110-
assert_eq!(self.constraints[constraint].sub, current_region);
111-
stack.push(constraint);
112-
self.find_constraint_paths_between_regions_helper(
113-
from_region,
114-
self.constraints[constraint].sup,
115-
target_test,
116-
visited,
117-
stack,
118-
results,
119-
);
120-
stack.pop();
121-
});
106+
self.constraint_graph.for_each_dependent(current_region, |constraint| {
107+
assert_eq!(self.constraints[constraint].sub, current_region);
108+
stack.push(constraint);
109+
self.find_constraint_paths_between_regions_helper(
110+
from_region,
111+
self.constraints[constraint].sup,
112+
target_test,
113+
visited,
114+
stack,
115+
results,
116+
);
117+
stack.pop();
118+
});
122119
}
123120

124121
/// This function will return true if a constraint is interesting and false if a constraint

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
// except according to those terms.
1010

1111
use super::universal_regions::UniversalRegions;
12-
use borrow_check::nll::constraint_set::{ConstraintIndex, ConstraintSet, OutlivesConstraint};
12+
use borrow_check::nll::constraint_set::{
13+
ConstraintIndex, ConstraintGraph, ConstraintSet, OutlivesConstraint
14+
};
1315
use borrow_check::nll::type_check::Locations;
1416
use rustc::hir::def_id::DefId;
1517
use rustc::infer::canonical::QueryRegionConstraint;
@@ -57,16 +59,12 @@ pub struct RegionInferenceContext<'tcx> {
5759
/// until `solve` is invoked.
5860
inferred_values: Option<RegionValues>,
5961

60-
/// For each variable, stores the index of the first constraint
61-
/// where that variable appears on the RHS. This is the start of a
62-
/// 'linked list' threaded by the `next` field in `Constraint`.
63-
///
64-
/// This map is build when values are inferred.
65-
dependency_map: Option<IndexVec<RegionVid, Option<ConstraintIndex>>>,
66-
6762
/// The constraints we have accumulated and used during solving.
6863
constraints: ConstraintSet,
6964

65+
/// The constraint-set, but organized by regions.
66+
constraint_graph: ConstraintGraph,
67+
7068
/// Type constraints that we check after solving.
7169
type_tests: Vec<TypeTest<'tcx>>,
7270

@@ -203,27 +201,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
203201
outlives_constraints: ConstraintSet,
204202
type_tests: Vec<TypeTest<'tcx>>,
205203
) -> Self {
206-
// The `next` field should not yet have been initialized:
207-
debug_assert!(outlives_constraints.iter().all(|c| c.next.is_none()));
208-
209204
let num_region_variables = var_infos.len();
210205
let num_universal_regions = universal_regions.len();
211206

212207
let elements = &Rc::new(RegionValueElements::new(mir, num_universal_regions));
213208

214209
// Create a RegionDefinition for each inference variable.
215-
let definitions = var_infos
210+
let definitions: IndexVec<_, _> = var_infos
216211
.into_iter()
217212
.map(|info| RegionDefinition::new(info.origin))
218213
.collect();
219214

215+
let constraint_graph = ConstraintGraph::new(&outlives_constraints, definitions.len());
216+
220217
let mut result = Self {
221218
definitions,
222219
elements: elements.clone(),
223220
liveness_constraints: RegionValues::new(elements, num_region_variables),
224221
inferred_values: None,
225-
dependency_map: None,
226222
constraints: outlives_constraints,
223+
constraint_graph,
227224
type_tests,
228225
universal_regions,
229226
};
@@ -392,7 +389,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
392389
/// satisfied. Note that some values may grow **too** large to be
393390
/// feasible, but we check this later.
394391
fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
395-
self.dependency_map = Some(self.build_dependency_map());
392+
assert!(self.inferred_values.is_none());
396393
let inferred_values = self.compute_region_values(mir);
397394
self.inferred_values = Some(inferred_values);
398395
}
@@ -409,8 +406,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
409406
// constraints we have accumulated.
410407
let mut inferred_values = self.liveness_constraints.clone();
411408

412-
let dependency_map = self.dependency_map.as_ref().unwrap();
413-
414409
// Constraints that may need to be repropagated (initially all):
415410
let mut dirty_list: Vec<_> = self.constraints.indices().collect();
416411

@@ -428,14 +423,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
428423
debug!("propagate_constraints: sub={:?}", constraint.sub);
429424
debug!("propagate_constraints: sup={:?}", constraint.sup);
430425

431-
self.constraints.each_affected_by_dirty(
432-
dependency_map[constraint.sup],
433-
|dep_idx| {
434-
if clean_bit_vec.remove(dep_idx.index()) {
435-
dirty_list.push(dep_idx);
436-
}
437-
},
438-
);
426+
// The region of `constraint.sup` changed, so find all
427+
// constraints of the form `R: constriant.sup` and
428+
// enqueue them as dirty. We will have to reprocess
429+
// them.
430+
self.constraint_graph.for_each_dependent(constraint.sup, |dep_idx| {
431+
if clean_bit_vec.remove(dep_idx.index()) {
432+
dirty_list.push(dep_idx);
433+
}
434+
});
439435
}
440436

441437
debug!("\n");
@@ -444,14 +440,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
444440
inferred_values
445441
}
446442

447-
/// Builds up a map from each region variable X to a vector with the
448-
/// indices of constraints that need to be re-evaluated when X changes.
449-
/// These are constraints like Y: X @ P -- so if X changed, we may
450-
/// need to grow Y.
451-
fn build_dependency_map(&mut self) -> IndexVec<RegionVid, Option<ConstraintIndex>> {
452-
self.constraints.link(self.definitions.len())
453-
}
454-
455443
/// Once regions have been propagated, this method is used to see
456444
/// whether the "type tests" produced by typeck were satisfied;
457445
/// type tests encode type-outlives relationships like `T:

src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
186186
locations: self.locations,
187187
sub,
188188
sup,
189-
next: None,
190189
});
191190
}
192191

src/librustc_mir/borrow_check/nll/type_check/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
15371537
sup: ref_region.to_region_vid(),
15381538
sub: borrow_region.to_region_vid(),
15391539
locations: location.boring(),
1540-
next: None,
15411540
});
15421541

15431542
if let Some(all_facts) = all_facts {

0 commit comments

Comments
 (0)