Skip to content

Commit 34f3303

Browse files
author
Markus Westerlind
committed
Document obligation forest changes
1 parent f771696 commit 34f3303

File tree

8 files changed

+115
-71
lines changed

8 files changed

+115
-71
lines changed

compiler/rustc_data_structures/src/logged_unification_table.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub struct LoggedUnificationTableStorage<K: ut::UnifyKey, I: Idx = K> {
4848
modified_set: ms::ModifiedSet<I>,
4949
}
5050

51-
/// UnificationTableStorage which logs which variables has been unfified with a value, allowing watchers
51+
/// UnificationTableStorage which logs which variables has been unified with a value, allowing watchers
5252
/// to only iterate over the changed variables instead of all variables
5353
pub struct LoggedUnificationTable<'a, K: ut::UnifyKey, I: Idx, L> {
5454
storage: &'a mut LoggedUnificationTableStorage<K, I>,
@@ -200,11 +200,11 @@ where
200200
self.storage.unify_log.unwatch_variable(index)
201201
}
202202

203-
/// Iterates through all unified variables since the last call to `drain_modified_set`
203+
/// Iterates through all unified variables since the last call to `notify_watcher`
204204
/// passing the unified variable to `f`
205-
pub fn drain_modified_set(&mut self, offset: &ms::Offset<I>, mut f: impl FnMut(I)) {
205+
pub fn notify_watcher(&mut self, offset: &ms::Offset<I>, mut f: impl FnMut(I)) {
206206
let unify_log = &self.storage.unify_log;
207-
self.storage.modified_set.drain(&mut self.undo_log, offset, |vid| {
207+
self.storage.modified_set.notify_watcher(&mut self.undo_log, offset, |vid| {
208208
for &unified_vid in unify_log.get(vid) {
209209
f(unified_vid);
210210
}

compiler/rustc_data_structures/src/modified_set.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ enum UndoInner {
1313
#[derive(Copy, Clone, Debug)]
1414
pub struct Undo<I>(UndoInner, PhantomData<I>);
1515

16+
/// Tracks which indices have been modified and allows watchers to registered and notified of these
17+
/// changes.
1618
#[derive(Clone, Debug)]
1719
pub struct ModifiedSet<T: Idx> {
1820
modified: Vec<T>,
@@ -26,32 +28,40 @@ impl<T: Idx> Default for ModifiedSet<T> {
2628
}
2729

2830
impl<T: Idx> ModifiedSet<T> {
31+
/// Creates a new `ModifiedSet`
2932
pub fn new() -> Self {
3033
Self::default()
3134
}
3235

36+
/// Marks `index` as "modified". A subsequent call to `drain` will notify the callback with
37+
/// `index`
3338
pub fn set(&mut self, undo_log: &mut impl UndoLogs<Undo<T>>, index: T) {
3439
self.modified.push(index);
3540
undo_log.push(Undo(UndoInner::Add, PhantomData));
3641
}
3742

38-
pub fn drain(
43+
/// Calls `f` with all the indices that have been modified since the last call to
44+
/// `notify_watcher`
45+
pub fn notify_watcher(
3946
&mut self,
4047
undo_log: &mut impl UndoLogs<Undo<T>>,
41-
index: &Offset<T>,
48+
watcher_offset: &Offset<T>,
4249
mut f: impl FnMut(T),
4350
) {
44-
let offset = &mut self.offsets[index.index];
51+
let offset = &mut self.offsets[watcher_offset.index];
4552
if *offset < self.modified.len() {
4653
for &index in &self.modified[*offset..] {
4754
f(index);
4855
}
49-
undo_log
50-
.push(Undo(UndoInner::Drain { index: index.index, offset: *offset }, PhantomData));
56+
undo_log.push(Undo(
57+
UndoInner::Drain { index: watcher_offset.index, offset: *offset },
58+
PhantomData,
59+
));
5160
*offset = self.modified.len();
5261
}
5362
}
5463

64+
/// Clears the set of all modifications that have been drained by all watchers
5565
pub fn clear(&mut self) {
5666
let min = self.offsets.iter().copied().min().unwrap_or_else(|| self.modified.len());
5767
self.modified.drain(..min);
@@ -60,14 +70,24 @@ impl<T: Idx> ModifiedSet<T> {
6070
}
6171
}
6272

73+
/// Registers a new watcher on this set.
74+
///
75+
/// NOTE: Watchers must be removed in the reverse order that they were registered
6376
pub fn register(&mut self) -> Offset<T> {
6477
let index = self.offsets.len();
6578
self.offsets.push(self.modified.len());
6679
Offset { index, _marker: PhantomData }
6780
}
6881

82+
/// De-registers a watcher on this set.
83+
///
84+
/// NOTE: Watchers must be removed in the reverse order that they were registered
6985
pub fn deregister(&mut self, offset: Offset<T>) {
70-
assert_eq!(offset.index, self.offsets.len() - 1);
86+
assert_eq!(
87+
offset.index,
88+
self.offsets.len() - 1,
89+
"Watchers must be removed in the reverse order that they were registered"
90+
);
7191
self.offsets.pop();
7292
std::mem::forget(offset);
7393
}
@@ -88,6 +108,8 @@ impl<I: Idx> Rollback<Undo<I>> for ModifiedSet<I> {
88108
}
89109
}
90110

111+
/// A registered offset into a `ModifiedSet`. Tracks how much a watcher has seen so far to avoid
112+
/// being notified of the same event twice.
91113
#[must_use]
92114
pub struct Offset<T> {
93115
index: usize,

compiler/rustc_data_structures/src/obligation_forest/mod.rs

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//! The `ObligationForest` is a utility data structure used in trait
12
//! matching to track the set of outstanding obligations (those not yet
23
//! resolved to success or error). It also tracks the "backtrace" of each
34
//! pending obligation (why we are trying to figure this out in the first
@@ -41,7 +42,7 @@
4142
//! now considered to be in error.
4243
//!
4344
//! When the call to `process_obligations` completes, you get back an `Outcome`,
44-
//! which includes three bits of information:
45+
//! which includes two bits of information:
4546
//!
4647
//! - `completed`: a list of obligations where processing was fully
4748
//! completed without error (meaning that all transitive subobligations
@@ -52,13 +53,6 @@
5253
//! all the obligations in `C` have been found completed.
5354
//! - `errors`: a list of errors that occurred and associated backtraces
5455
//! at the time of error, which can be used to give context to the user.
55-
//! - `stalled`: if true, then none of the existing obligations were
56-
//! *shallowly successful* (that is, no callback returned `Changed(_)`).
57-
//! This implies that all obligations were either errors or returned an
58-
//! ambiguous result, which means that any further calls to
59-
//! `process_obligations` would simply yield back further ambiguous
60-
//! results. This is used by the `FulfillmentContext` to decide when it
61-
//! has reached a steady state.
6256
//!
6357
//! ### Implementation details
6458
//!
@@ -88,17 +82,17 @@ mod graphviz;
8882
mod tests;
8983

9084
pub trait ForestObligation: Clone + Debug {
91-
/// A key used to avoid evaluating the same obligation twice
85+
/// A key used to avoid evaluating the same obligation twice.
9286
type CacheKey: Clone + hash::Hash + Eq + Debug;
93-
/// The variable type used in the obligation when it could not yet be fulfilled
87+
/// The variable type used in the obligation when it could not yet be fulfilled.
9488
type Variable: Clone + hash::Hash + Eq + Debug;
95-
/// A type which tracks which variables has been unified
89+
/// A type which tracks which variables has been unified.
9690
type WatcherOffset;
9791

9892
/// Converts this `ForestObligation` suitable for use as a cache key.
9993
/// If two distinct `ForestObligations`s return the same cache key,
10094
/// then it must be sound to use the result of processing one obligation
101-
/// (e.g. success for error) for the other obligation
95+
/// (e.g. success for error) for the other obligation.
10296
fn as_cache_key(&self) -> Self::CacheKey;
10397

10498
/// Returns which variables this obligation is currently stalled on. If the slice is empty then
@@ -127,7 +121,9 @@ pub trait ObligationProcessor {
127121
where
128122
I: Clone + Iterator<Item = &'c Self::Obligation>;
129123

130-
fn unblocked(
124+
/// Calls `f` with all the variables that have been unblocked (instantiated) since the last call
125+
/// to `notify_unblocked`.
126+
fn notify_unblocked(
131127
&self,
132128
offset: &<Self::Obligation as ForestObligation>::WatcherOffset,
133129
f: impl FnMut(<Self::Obligation as ForestObligation>::Variable),
@@ -175,14 +171,15 @@ pub struct ObligationForest<O: ForestObligation> {
175171
/// number allowing them to be ordered when processing them.
176172
node_number: u32,
177173

178-
/// Stores the indices of the nodes currently in the pending state
174+
/// Stores the indices of the nodes currently in the pending state.
179175
pending_nodes: Vec<NodeIndex>,
180176

181177
/// Stores the indices of the nodes currently in the success or waiting states.
182178
/// Can also contain `Done` or `Error` nodes as `process_cycles` does not remove a node
183179
/// immediately but instead upon the next time that node is processed.
184180
success_or_waiting_nodes: Vec<NodeIndex>,
185-
/// Stores the indices of the nodes currently in the error or done states
181+
182+
/// Stores the indices of the nodes currently in the error or done states.
186183
error_or_done_nodes: RefCell<Vec<NodeIndex>>,
187184

188185
/// Nodes that have been removed and are ready to be reused (pure optimization to reuse
@@ -205,22 +202,26 @@ pub struct ObligationForest<O: ForestObligation> {
205202
/// [details]: https://github.com/rust-lang/rust/pull/53255#issuecomment-421184780
206203
error_cache: FxHashMap<ObligationTreeId, FxHashSet<O::CacheKey>>,
207204

208-
/// Stores which nodes would be unblocked once `O::Variable` is unified
205+
/// Stores which nodes would be unblocked once `O::Variable` is unified.
209206
stalled_on: FxHashMap<O::Variable, Vec<NodeIndex>>,
210-
/// Stores the node indices that are unblocked and should be processed at the next opportunity
207+
208+
/// Stores the node indices that are unblocked and should be processed at the next opportunity.
211209
unblocked: BinaryHeap<Unblocked>,
210+
212211
/// Stores nodes which should be processed on the next iteration since the variables they are
213-
/// actually blocked on are unknown
212+
/// actually blocked on are unknown.
214213
stalled_on_unknown: Vec<NodeIndex>,
214+
215215
/// The offset that this `ObligationForest` has registered. Should be de-registered before
216216
/// dropping this forest.
217-
offset: Option<O::WatcherOffset>,
218-
/// Reusable vector for storing unblocked nodes whose watch should be removed
217+
///
218+
watcher_offset: Option<O::WatcherOffset>,
219+
/// Reusable vector for storing unblocked nodes whose watch should be removed.
219220
temp_unblocked_nodes: Vec<O::Variable>,
220221
}
221222

222223
/// Helper struct for use with `BinaryHeap` to process nodes in the order that they were added to
223-
/// the forest
224+
/// the forest.
224225
struct Unblocked {
225226
index: NodeIndex,
226227
order: u32,
@@ -248,13 +249,14 @@ struct Node<O: ForestObligation> {
248249
obligation: O,
249250
state: Cell<NodeState>,
250251

251-
/// A predicate (and its key) can changed during processing. If it does we need to register the
252+
/// A predicate (and its key) can change during processing. If it does we need to register the
252253
/// old predicate so that we can remove or mark it as done if this node errors or is done.
253254
alternative_predicates: Vec<O::CacheKey>,
254255

255256
/// Obligations that depend on this obligation for their completion. They
256257
/// must all be in a non-pending state.
257258
dependents: Vec<NodeIndex>,
259+
258260
/// Obligations that this obligation depends on for their completion.
259261
reverse_dependents: Vec<NodeIndex>,
260262

@@ -267,6 +269,9 @@ struct Node<O: ForestObligation> {
267269

268270
/// Identifier of the obligation tree to which this node belongs.
269271
obligation_tree_id: ObligationTreeId,
272+
273+
/// Nodes must be processed in the order that they were added so we give each node a unique
274+
/// number allowing them to be ordered when processing them.
270275
node_number: u32,
271276
}
272277

@@ -292,8 +297,9 @@ where
292297
}
293298
}
294299

295-
/// Initializes a node, reusing the existing allocations
296-
fn init(
300+
/// Initializes a node, reusing the existing allocations. Used when removing a node from the
301+
/// dead_nodes list
302+
fn reinit(
297303
&mut self,
298304
parent: Option<NodeIndex>,
299305
obligation: O,
@@ -441,16 +447,19 @@ impl<O: ForestObligation> ObligationForest<O> {
441447
unblocked: Default::default(),
442448
stalled_on_unknown: Default::default(),
443449
temp_unblocked_nodes: Default::default(),
444-
offset: None,
450+
watcher_offset: None,
445451
}
446452
}
447453

448-
pub fn offset(&self) -> Option<&O::WatcherOffset> {
449-
self.offset.as_ref()
454+
/// Returns the `WatcherOffset` regsitered with the notification table. See the field
455+
/// `ObligationForest::watcher_offset`.
456+
pub fn watcher_offset(&self) -> Option<&O::WatcherOffset> {
457+
self.watcher_offset.as_ref()
450458
}
451459

452-
pub fn take_offset(&mut self) -> Option<O::WatcherOffset> {
453-
self.offset.take()
460+
/// Removes the watcher_offset, allowing it to be deregistered
461+
pub fn take_watcher_offset(&mut self) -> Option<O::WatcherOffset> {
462+
self.watcher_offset.take()
454463
}
455464

456465
/// Returns the total number of nodes in the forest that have not
@@ -523,7 +532,7 @@ impl<O: ForestObligation> ObligationForest<O> {
523532
// otherwise allocate a new node
524533
let new_index = if let Some(new_index) = self.dead_nodes.pop() {
525534
let node = &mut self.nodes[new_index];
526-
node.init(parent, obligation, obligation_tree_id, node_number);
535+
node.reinit(parent, obligation, obligation_tree_id, node_number);
527536
new_index
528537
} else {
529538
let new_index = self.nodes.len();
@@ -565,11 +574,7 @@ impl<O: ForestObligation> ObligationForest<O> {
565574
where
566575
F: Fn(&O) -> P,
567576
{
568-
self.pending_nodes
569-
.iter()
570-
.map(|&index| &self.nodes[index])
571-
.map(|node| f(&node.obligation))
572-
.collect()
577+
self.pending_nodes.iter().map(|&index| f(&self.nodes[index].obligation)).collect()
573578
}
574579

575580
fn insert_into_error_cache(&mut self, index: NodeIndex) {
@@ -589,8 +594,8 @@ impl<O: ForestObligation> ObligationForest<O> {
589594
P: ObligationProcessor<Obligation = O>,
590595
OUT: OutcomeTrait<Obligation = O, Error = Error<O, P::Error>>,
591596
{
592-
if self.offset.is_none() {
593-
self.offset = Some(processor.register_variable_watcher());
597+
if self.watcher_offset.is_none() {
598+
self.watcher_offset = Some(processor.register_variable_watcher());
594599
}
595600
let mut errors = vec![];
596601
let mut stalled = true;
@@ -688,7 +693,10 @@ impl<O: ForestObligation> ObligationForest<O> {
688693
Outcome { completed, errors }
689694
}
690695

691-
#[inline(never)]
696+
/// Checks which nodes have been unblocked since the last time this was called. All nodes that
697+
/// were unblocked are added to the `unblocked` queue and all watches associated with the
698+
/// variables blocking those nodes are deregistered (since they are now instantiated, they will
699+
/// neither block a node, nor be instantiated again)
692700
fn unblock_nodes<P>(&mut self, processor: &mut P)
693701
where
694702
P: ObligationProcessor<Obligation = O>,
@@ -698,7 +706,7 @@ impl<O: ForestObligation> ObligationForest<O> {
698706
let unblocked = &mut self.unblocked;
699707
let temp_unblocked_nodes = &mut self.temp_unblocked_nodes;
700708
temp_unblocked_nodes.clear();
701-
processor.unblocked(self.offset.as_ref().unwrap(), |var| {
709+
processor.notify_unblocked(self.watcher_offset.as_ref().unwrap(), |var| {
702710
if let Some(unblocked_nodes) = stalled_on.remove(&var) {
703711
for node_index in unblocked_nodes {
704712
let node = &nodes[node_index];
@@ -861,9 +869,8 @@ impl<O: ForestObligation> ObligationForest<O> {
861869
}
862870
}
863871

864-
/// Compresses the vector, removing all popped nodes. This adjusts the
865-
/// indices and hence invalidates any outstanding indices. `process_cycles`
866-
/// must be run beforehand to remove any cycles on `Success` nodes.
872+
/// Compresses the forest, moving all nodes marked `Done` or `Error` into `dead_nodes` for later reuse
873+
/// `process_cycles` must be run beforehand to remove any cycles on `Success` nodes.
867874
#[inline(never)]
868875
fn compress(&mut self, do_completed: DoCompleted) -> Option<Vec<O>> {
869876
let mut removed_done_obligations: Vec<O> = vec![];
@@ -872,6 +879,8 @@ impl<O: ForestObligation> ObligationForest<O> {
872879
let mut error_or_done_nodes = mem::take(self.error_or_done_nodes.get_mut());
873880
for &index in &error_or_done_nodes {
874881
let node = &mut self.nodes[index];
882+
883+
// Remove this node from all the nodes that depends on it
875884
let reverse_dependents = mem::take(&mut node.reverse_dependents);
876885
for &reverse_index in &reverse_dependents {
877886
let reverse_node = &mut self.nodes[reverse_index];
@@ -892,6 +901,8 @@ impl<O: ForestObligation> ObligationForest<O> {
892901
if let Some(opt) = self.active_cache.get_mut(&node.obligation.as_cache_key()) {
893902
*opt = CacheState::Done;
894903
}
904+
// If the node's predicate changed at some point we mark all its alternate
905+
// predicates as done as well
895906
for alt in &node.alternative_predicates {
896907
if let Some(opt) = self.active_cache.get_mut(alt) {
897908
*opt = CacheState::Done;
@@ -903,13 +914,17 @@ impl<O: ForestObligation> ObligationForest<O> {
903914
removed_done_obligations.push(node.obligation.clone());
904915
}
905916

917+
// Store the node so it and its allocations can be used when another node is
918+
// allocated
906919
self.dead_nodes.push(index);
907920
}
908921
NodeState::Error => {
909922
// We *intentionally* remove the node from the cache at this point. Otherwise
910923
// tests must come up with a different type on every type error they
911924
// check against.
912925
self.active_cache.remove(&node.obligation.as_cache_key());
926+
// If the node's predicate changed at some point we remove all its alternate
927+
// predicates as well
913928
for alt in &node.alternative_predicates {
914929
self.active_cache.remove(alt);
915930
}

compiler/rustc_data_structures/src/obligation_forest/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ where
9393
{
9494
}
9595

96-
fn unblocked(
96+
fn notify_unblocked(
9797
&self,
9898
_offset: &<Self::Obligation as ForestObligation>::WatcherOffset,
9999
_f: impl FnMut(<Self::Obligation as ForestObligation>::Variable),

0 commit comments

Comments
 (0)