Skip to content

Commit 22abfb6

Browse files
author
Markus Westerlind
committed
refactor: Simplify obligation_forest
1 parent f5dbb04 commit 22abfb6

File tree

5 files changed

+139
-125
lines changed

5 files changed

+139
-125
lines changed

compiler/rustc_data_structures/src/logged_unification_table.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ where
107107
K::Value: ut::UnifyValue<Error = ut::NoError>,
108108
{
109109
if self.storage.unify_log.needs_log(vid) {
110-
warn!("ModifiedSet {:?} => {:?}", vid, ty);
111110
self.storage.modified_set.set(&mut self.undo_log, vid);
112111
}
113112
let vid = vid.into();

compiler/rustc_data_structures/src/obligation_forest/mod.rs

Lines changed: 104 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//! The `ObligationForest` is a utility data structure used in trait
21
//! matching to track the set of outstanding obligations (those not yet
32
//! resolved to success or error). It also tracks the "backtrace" of each
43
//! pending obligation (why we are trying to figure this out in the first
@@ -133,8 +132,11 @@ pub trait ObligationProcessor {
133132
offset: &<Self::Obligation as ForestObligation>::WatcherOffset,
134133
f: impl FnMut(<Self::Obligation as ForestObligation>::Variable),
135134
);
136-
fn register(&self) -> <Self::Obligation as ForestObligation>::WatcherOffset;
137-
fn deregister(&self, offset: <Self::Obligation as ForestObligation>::WatcherOffset);
135+
fn register_variable_watcher(&self) -> <Self::Obligation as ForestObligation>::WatcherOffset;
136+
fn deregister_variable_watcher(
137+
&self,
138+
offset: <Self::Obligation as ForestObligation>::WatcherOffset,
139+
);
138140
fn watch_variable(&self, var: <Self::Obligation as ForestObligation>::Variable);
139141
fn unwatch_variable(&self, var: <Self::Obligation as ForestObligation>::Variable);
140142
}
@@ -159,6 +161,11 @@ type ObligationTreeIdGenerator =
159161
/// significant, and space considerations are not important.
160162
type NodeIndex = usize;
161163

164+
enum CacheState {
165+
Active(NodeIndex),
166+
Done,
167+
}
168+
162169
pub struct ObligationForest<O: ForestObligation> {
163170
/// The list of obligations. In between calls to `process_obligations`,
164171
/// this list only contains nodes in the `Pending` or `Waiting` state.
@@ -171,18 +178,21 @@ pub struct ObligationForest<O: ForestObligation> {
171178
/// Stores the indices of the nodes currently in the pending state
172179
pending_nodes: Vec<NodeIndex>,
173180

174-
/// Stores the indices of the nodes currently in the success or waiting states
181+
/// Stores the indices of the nodes currently in the success or waiting states.
182+
/// Can also contain `Done` or `Error` nodes as `process_cycles` does not remove a node
183+
/// immediately but instead upon the next time that node is processed.
175184
success_or_waiting_nodes: Vec<NodeIndex>,
176185
/// Stores the indices of the nodes currently in the error or done states
177186
error_or_done_nodes: RefCell<Vec<NodeIndex>>,
178187

179-
/// Nodes that have been removed and are ready to be reused
188+
/// Nodes that have been removed and are ready to be reused (pure optimization to reuse
189+
/// allocations)
180190
dead_nodes: Vec<NodeIndex>,
181191

182192
/// A cache of the nodes in `nodes`, indexed by predicate. Unfortunately,
183193
/// its contents are not guaranteed to match those of `nodes`. See the
184194
/// comments in `process_obligation` for details.
185-
active_cache: FxHashMap<O::CacheKey, Option<NodeIndex>>,
195+
active_cache: FxHashMap<O::CacheKey, CacheState>,
186196

187197
obligation_tree_id_generator: ObligationTreeIdGenerator,
188198

@@ -201,10 +211,12 @@ pub struct ObligationForest<O: ForestObligation> {
201211
unblocked: BinaryHeap<Unblocked>,
202212
/// Stores nodes which should be processed on the next iteration since the variables they are
203213
/// actually blocked on are unknown
204-
check_next: Vec<NodeIndex>,
214+
stalled_on_unknown: Vec<NodeIndex>,
205215
/// The offset that this `ObligationForest` has registered. Should be de-registered before
206216
/// dropping this forest.
207217
offset: Option<O::WatcherOffset>,
218+
/// Reusable vector for storing unblocked nodes whose watch should be removed
219+
temp_unblocked_nodes: Vec<O::Variable>,
208220
}
209221

210222
/// Helper struct for use with `BinaryHeap` to process nodes in the order that they were added to
@@ -427,7 +439,8 @@ impl<O: ForestObligation> ObligationForest<O> {
427439
error_cache: Default::default(),
428440
stalled_on: Default::default(),
429441
unblocked: Default::default(),
430-
check_next: Default::default(),
442+
stalled_on_unknown: Default::default(),
443+
temp_unblocked_nodes: Default::default(),
431444
offset: None,
432445
}
433446
}
@@ -462,8 +475,8 @@ impl<O: ForestObligation> ObligationForest<O> {
462475
match self.active_cache.entry(obligation.as_cache_key().clone()) {
463476
Entry::Occupied(o) => {
464477
let index = match o.get() {
465-
Some(index) => *index,
466-
None => {
478+
CacheState::Active(index) => *index,
479+
CacheState::Done => {
467480
debug!(
468481
"register_obligation_at: ignoring already done obligation: {:?}",
469482
obligation
@@ -497,14 +510,15 @@ impl<O: ForestObligation> ObligationForest<O> {
497510
.get(&obligation_tree_id)
498511
.map(|errors| errors.contains(&obligation.as_cache_key()))
499512
.unwrap_or(false);
500-
// Retrieves a fresh number for the new node so that each node are processed in the
501-
// order that they were created
502-
let node_number = self.node_number;
503-
self.node_number += 1;
504513

505514
if already_failed {
506515
Err(())
507516
} else {
517+
// Retrieves a fresh number for the new node so that each node are processed in the
518+
// order that they were created
519+
let node_number = self.node_number;
520+
self.node_number += 1;
521+
508522
// If we have a dead node we can reuse it and it's associated allocations,
509523
// otherwise allocate a new node
510524
let new_index = if let Some(new_index) = self.dead_nodes.pop() {
@@ -524,9 +538,10 @@ impl<O: ForestObligation> ObligationForest<O> {
524538
if let Some(parent_index) = parent {
525539
self.nodes[parent_index].reverse_dependents.push(new_index);
526540
}
541+
527542
self.pending_nodes.push(new_index);
528543
self.unblocked.push(Unblocked { index: new_index, order: node_number });
529-
v.insert(Some(new_index));
544+
v.insert(CacheState::Active(new_index));
530545
Ok(())
531546
}
532547
}
@@ -575,101 +590,84 @@ impl<O: ForestObligation> ObligationForest<O> {
575590
OUT: OutcomeTrait<Obligation = O, Error = Error<O, P::Error>>,
576591
{
577592
if self.offset.is_none() {
578-
self.offset = Some(processor.register());
593+
self.offset = Some(processor.register_variable_watcher());
579594
}
580595
let mut errors = vec![];
581596
let mut stalled = true;
582597

583598
self.unblock_nodes(processor);
584599

585-
let mut run_again = true;
586-
while !self.unblocked.is_empty() || (!self.check_next.is_empty() && run_again) {
587-
run_again = false;
588-
let nodes = &self.nodes;
589-
self.unblocked.extend(
590-
self.check_next
591-
.drain(..)
592-
.map(|index| Unblocked { index, order: nodes[index].node_number }),
593-
);
594-
while let Some(Unblocked { index, .. }) = self.unblocked.pop() {
595-
if self.unblocked.peek().map(|u| u.index) == Some(index) {
596-
continue;
597-
}
598-
let node = &mut self.nodes[index];
600+
let nodes = &self.nodes;
601+
self.unblocked.extend(
602+
self.stalled_on_unknown
603+
.drain(..)
604+
.map(|index| Unblocked { index, order: nodes[index].node_number }),
605+
);
606+
while let Some(Unblocked { index, .. }) = self.unblocked.pop() {
607+
// Skip any duplicates since we only need to processes the node once
608+
if self.unblocked.peek().map(|u| u.index) == Some(index) {
609+
continue;
610+
}
599611

600-
if node.state.get() != NodeState::Pending {
601-
continue;
602-
}
612+
let node = &mut self.nodes[index];
603613

604-
// Any variables we were stalled on are now resolved so remove the watches
605-
for var in node.obligation.stalled_on() {
606-
match self.stalled_on.entry(var.clone()) {
607-
Entry::Vacant(_) => (),
608-
Entry::Occupied(mut entry) => {
609-
let nodes = entry.get_mut();
610-
nodes.retain(|node_index| *node_index != index);
611-
if nodes.is_empty() {
612-
processor.unwatch_variable(var.clone());
613-
entry.remove();
614-
}
615-
}
616-
}
617-
}
614+
if node.state.get() != NodeState::Pending {
615+
continue;
616+
}
618617

619-
// `processor.process_obligation` can modify the predicate within
620-
// `node.obligation`, and that predicate is the key used for
621-
// `self.active_cache`. This means that `self.active_cache` can get
622-
// out of sync with `nodes`. It's not very common, but it does
623-
// happen, and code in `compress` has to allow for it.
624-
let before = node.obligation.as_cache_key();
625-
let result = processor.process_obligation(&mut node.obligation);
626-
let after = node.obligation.as_cache_key();
627-
if before != after {
628-
node.alternative_predicates.push(before);
629-
}
618+
// `processor.process_obligation` can modify the predicate within
619+
// `node.obligation`, and that predicate is the key used for
620+
// `self.active_cache`. This means that `self.active_cache` can get
621+
// out of sync with `nodes`. It's not very common, but it does
622+
// happen, and code in `compress` has to allow for it.
623+
let before = node.obligation.as_cache_key();
624+
let result = processor.process_obligation(&mut node.obligation);
625+
let after = node.obligation.as_cache_key();
626+
if before != after {
627+
node.alternative_predicates.push(before);
628+
}
630629

631-
self.unblock_nodes(processor);
632-
let node = &mut self.nodes[index];
633-
match result {
634-
ProcessResult::Unchanged => {
630+
self.unblock_nodes(processor);
631+
let node = &mut self.nodes[index];
632+
match result {
633+
ProcessResult::Unchanged => {
634+
let stalled_on = node.obligation.stalled_on();
635+
if stalled_on.is_empty() {
635636
// We stalled but the variables that caused it are unknown so we run
636637
// `index` again at the next opportunity
637-
let stalled_on = node.obligation.stalled_on();
638-
if stalled_on.is_empty() {
639-
self.check_next.push(index);
640-
} else {
641-
// Register every variable that we stal
642-
for var in stalled_on {
643-
self.stalled_on
644-
.entry(var.clone())
645-
.or_insert_with(|| {
646-
processor.watch_variable(var.clone());
647-
Vec::new()
648-
})
649-
.push(index);
650-
}
638+
self.stalled_on_unknown.push(index);
639+
} else {
640+
// Register every variable that we stalled on
641+
for var in stalled_on {
642+
self.stalled_on
643+
.entry(var.clone())
644+
.or_insert_with(|| {
645+
processor.watch_variable(var.clone());
646+
Vec::new()
647+
})
648+
.push(index);
651649
}
652-
// No change in state.
653650
}
654-
ProcessResult::Changed(children) => {
655-
// We are not (yet) stalled.
656-
stalled = false;
657-
node.state.set(NodeState::Success);
658-
self.success_or_waiting_nodes.push(index);
659-
660-
for child in children {
661-
let st = self.register_obligation_at(child, Some(index));
662-
if let Err(()) = st {
663-
// Error already reported - propagate it
664-
// to our node.
665-
self.error_at(index);
666-
}
651+
// No change in state.
652+
}
653+
ProcessResult::Changed(children) => {
654+
// We are not (yet) stalled.
655+
stalled = false;
656+
node.state.set(NodeState::Success);
657+
self.success_or_waiting_nodes.push(index);
658+
659+
for child in children {
660+
let st = self.register_obligation_at(child, Some(index));
661+
if let Err(()) = st {
662+
// Error already reported - propagate it
663+
// to our node.
664+
self.error_at(index);
667665
}
668666
}
669-
ProcessResult::Error(err) => {
670-
stalled = false;
671-
errors.push(Error { error: err, backtrace: self.error_at(index) });
672-
}
667+
}
668+
ProcessResult::Error(err) => {
669+
stalled = false;
670+
errors.push(Error { error: err, backtrace: self.error_at(index) });
673671
}
674672
}
675673
}
@@ -698,7 +696,8 @@ impl<O: ForestObligation> ObligationForest<O> {
698696
let nodes = &mut self.nodes;
699697
let stalled_on = &mut self.stalled_on;
700698
let unblocked = &mut self.unblocked;
701-
let mut temp = Vec::new();
699+
let temp_unblocked_nodes = &mut self.temp_unblocked_nodes;
700+
temp_unblocked_nodes.clear();
702701
processor.unblocked(self.offset.as_ref().unwrap(), |var| {
703702
if let Some(unblocked_nodes) = stalled_on.remove(&var) {
704703
for node_index in unblocked_nodes {
@@ -710,10 +709,10 @@ impl<O: ForestObligation> ObligationForest<O> {
710709
);
711710
unblocked.push(Unblocked { index: node_index, order: node.node_number });
712711
}
713-
temp.push(var);
712+
temp_unblocked_nodes.push(var);
714713
}
715714
});
716-
for var in temp {
715+
for var in temp_unblocked_nodes.drain(..) {
717716
processor.unwatch_variable(var);
718717
}
719718
}
@@ -876,10 +875,13 @@ impl<O: ForestObligation> ObligationForest<O> {
876875
let reverse_dependents = mem::take(&mut node.reverse_dependents);
877876
for &reverse_index in &reverse_dependents {
878877
let reverse_node = &mut self.nodes[reverse_index];
879-
if reverse_node.dependents.first() == Some(&index) {
880-
reverse_node.has_parent = false;
878+
879+
if let Some(i) = reverse_node.dependents.iter().position(|x| *x == index) {
880+
reverse_node.dependents.swap_remove(i);
881+
if i == 0 {
882+
reverse_node.has_parent = false;
883+
}
881884
}
882-
reverse_node.dependents.retain(|&i| i != index);
883885
}
884886
let node = &mut self.nodes[index];
885887
node.reverse_dependents = reverse_dependents;
@@ -888,11 +890,11 @@ impl<O: ForestObligation> ObligationForest<O> {
888890
NodeState::Done => {
889891
// Mark as done
890892
if let Some(opt) = self.active_cache.get_mut(&node.obligation.as_cache_key()) {
891-
*opt = None;
893+
*opt = CacheState::Done;
892894
}
893895
for alt in &node.alternative_predicates {
894896
if let Some(opt) = self.active_cache.get_mut(alt) {
895-
*opt = None
897+
*opt = CacheState::Done;
896898
}
897899
}
898900

compiler/rustc_data_structures/src/obligation_forest/tests.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,12 @@ where
9999
_f: impl FnMut(<Self::Obligation as ForestObligation>::Variable),
100100
) {
101101
}
102-
fn register(&self) -> <Self::Obligation as ForestObligation>::WatcherOffset {}
103-
fn deregister(&self, _offset: <Self::Obligation as ForestObligation>::WatcherOffset) {}
102+
fn register_variable_watcher(&self) -> <Self::Obligation as ForestObligation>::WatcherOffset {}
103+
fn deregister_variable_watcher(
104+
&self,
105+
_offset: <Self::Obligation as ForestObligation>::WatcherOffset,
106+
) {
107+
}
104108
fn watch_variable(&self, _var: <Self::Obligation as ForestObligation>::Variable) {}
105109
fn unwatch_variable(&self, _var: <Self::Obligation as ForestObligation>::Variable) {}
106110
}

0 commit comments

Comments
 (0)