1
- //! The `ObligationForest` is a utility data structure used in trait
2
1
//! matching to track the set of outstanding obligations (those not yet
3
2
//! resolved to success or error). It also tracks the "backtrace" of each
4
3
//! pending obligation (why we are trying to figure this out in the first
@@ -133,8 +132,11 @@ pub trait ObligationProcessor {
133
132
offset : & <Self :: Obligation as ForestObligation >:: WatcherOffset ,
134
133
f : impl FnMut ( <Self :: Obligation as ForestObligation >:: Variable ) ,
135
134
) ;
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
+ ) ;
138
140
fn watch_variable ( & self , var : <Self :: Obligation as ForestObligation >:: Variable ) ;
139
141
fn unwatch_variable ( & self , var : <Self :: Obligation as ForestObligation >:: Variable ) ;
140
142
}
@@ -159,6 +161,11 @@ type ObligationTreeIdGenerator =
159
161
/// significant, and space considerations are not important.
160
162
type NodeIndex = usize ;
161
163
164
+ enum CacheState {
165
+ Active ( NodeIndex ) ,
166
+ Done ,
167
+ }
168
+
162
169
pub struct ObligationForest < O : ForestObligation > {
163
170
/// The list of obligations. In between calls to `process_obligations`,
164
171
/// this list only contains nodes in the `Pending` or `Waiting` state.
@@ -171,18 +178,21 @@ pub struct ObligationForest<O: ForestObligation> {
171
178
/// Stores the indices of the nodes currently in the pending state
172
179
pending_nodes : Vec < NodeIndex > ,
173
180
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.
175
184
success_or_waiting_nodes : Vec < NodeIndex > ,
176
185
/// Stores the indices of the nodes currently in the error or done states
177
186
error_or_done_nodes : RefCell < Vec < NodeIndex > > ,
178
187
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)
180
190
dead_nodes : Vec < NodeIndex > ,
181
191
182
192
/// A cache of the nodes in `nodes`, indexed by predicate. Unfortunately,
183
193
/// its contents are not guaranteed to match those of `nodes`. See the
184
194
/// comments in `process_obligation` for details.
185
- active_cache : FxHashMap < O :: CacheKey , Option < NodeIndex > > ,
195
+ active_cache : FxHashMap < O :: CacheKey , CacheState > ,
186
196
187
197
obligation_tree_id_generator : ObligationTreeIdGenerator ,
188
198
@@ -201,10 +211,12 @@ pub struct ObligationForest<O: ForestObligation> {
201
211
unblocked : BinaryHeap < Unblocked > ,
202
212
/// Stores nodes which should be processed on the next iteration since the variables they are
203
213
/// actually blocked on are unknown
204
- check_next : Vec < NodeIndex > ,
214
+ stalled_on_unknown : Vec < NodeIndex > ,
205
215
/// The offset that this `ObligationForest` has registered. Should be de-registered before
206
216
/// dropping this forest.
207
217
offset : Option < O :: WatcherOffset > ,
218
+ /// Reusable vector for storing unblocked nodes whose watch should be removed
219
+ temp_unblocked_nodes : Vec < O :: Variable > ,
208
220
}
209
221
210
222
/// 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> {
427
439
error_cache : Default :: default ( ) ,
428
440
stalled_on : Default :: default ( ) ,
429
441
unblocked : Default :: default ( ) ,
430
- check_next : Default :: default ( ) ,
442
+ stalled_on_unknown : Default :: default ( ) ,
443
+ temp_unblocked_nodes : Default :: default ( ) ,
431
444
offset : None ,
432
445
}
433
446
}
@@ -462,8 +475,8 @@ impl<O: ForestObligation> ObligationForest<O> {
462
475
match self . active_cache . entry ( obligation. as_cache_key ( ) . clone ( ) ) {
463
476
Entry :: Occupied ( o) => {
464
477
let index = match o. get ( ) {
465
- Some ( index) => * index,
466
- None => {
478
+ CacheState :: Active ( index) => * index,
479
+ CacheState :: Done => {
467
480
debug ! (
468
481
"register_obligation_at: ignoring already done obligation: {:?}" ,
469
482
obligation
@@ -497,14 +510,15 @@ impl<O: ForestObligation> ObligationForest<O> {
497
510
. get ( & obligation_tree_id)
498
511
. map ( |errors| errors. contains ( & obligation. as_cache_key ( ) ) )
499
512
. 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 ;
504
513
505
514
if already_failed {
506
515
Err ( ( ) )
507
516
} 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
+
508
522
// If we have a dead node we can reuse it and it's associated allocations,
509
523
// otherwise allocate a new node
510
524
let new_index = if let Some ( new_index) = self . dead_nodes . pop ( ) {
@@ -524,9 +538,10 @@ impl<O: ForestObligation> ObligationForest<O> {
524
538
if let Some ( parent_index) = parent {
525
539
self . nodes [ parent_index] . reverse_dependents . push ( new_index) ;
526
540
}
541
+
527
542
self . pending_nodes . push ( new_index) ;
528
543
self . unblocked . push ( Unblocked { index : new_index, order : node_number } ) ;
529
- v. insert ( Some ( new_index) ) ;
544
+ v. insert ( CacheState :: Active ( new_index) ) ;
530
545
Ok ( ( ) )
531
546
}
532
547
}
@@ -575,101 +590,84 @@ impl<O: ForestObligation> ObligationForest<O> {
575
590
OUT : OutcomeTrait < Obligation = O , Error = Error < O , P :: Error > > ,
576
591
{
577
592
if self . offset . is_none ( ) {
578
- self . offset = Some ( processor. register ( ) ) ;
593
+ self . offset = Some ( processor. register_variable_watcher ( ) ) ;
579
594
}
580
595
let mut errors = vec ! [ ] ;
581
596
let mut stalled = true ;
582
597
583
598
self . unblock_nodes ( processor) ;
584
599
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
+ }
599
611
600
- if node. state . get ( ) != NodeState :: Pending {
601
- continue ;
602
- }
612
+ let node = & mut self . nodes [ index] ;
603
613
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
+ }
618
617
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
+ }
630
629
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 ( ) {
635
636
// We stalled but the variables that caused it are unknown so we run
636
637
// `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) ;
651
649
}
652
- // No change in state.
653
650
}
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) ;
667
665
}
668
666
}
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 ) } ) ;
673
671
}
674
672
}
675
673
}
@@ -698,7 +696,8 @@ impl<O: ForestObligation> ObligationForest<O> {
698
696
let nodes = & mut self . nodes ;
699
697
let stalled_on = & mut self . stalled_on ;
700
698
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 ( ) ;
702
701
processor. unblocked ( self . offset . as_ref ( ) . unwrap ( ) , |var| {
703
702
if let Some ( unblocked_nodes) = stalled_on. remove ( & var) {
704
703
for node_index in unblocked_nodes {
@@ -710,10 +709,10 @@ impl<O: ForestObligation> ObligationForest<O> {
710
709
) ;
711
710
unblocked. push ( Unblocked { index : node_index, order : node. node_number } ) ;
712
711
}
713
- temp . push ( var) ;
712
+ temp_unblocked_nodes . push ( var) ;
714
713
}
715
714
} ) ;
716
- for var in temp {
715
+ for var in temp_unblocked_nodes . drain ( .. ) {
717
716
processor. unwatch_variable ( var) ;
718
717
}
719
718
}
@@ -876,10 +875,13 @@ impl<O: ForestObligation> ObligationForest<O> {
876
875
let reverse_dependents = mem:: take ( & mut node. reverse_dependents ) ;
877
876
for & reverse_index in & reverse_dependents {
878
877
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
+ }
881
884
}
882
- reverse_node. dependents . retain ( |& i| i != index) ;
883
885
}
884
886
let node = & mut self . nodes [ index] ;
885
887
node. reverse_dependents = reverse_dependents;
@@ -888,11 +890,11 @@ impl<O: ForestObligation> ObligationForest<O> {
888
890
NodeState :: Done => {
889
891
// Mark as done
890
892
if let Some ( opt) = self . active_cache . get_mut ( & node. obligation . as_cache_key ( ) ) {
891
- * opt = None ;
893
+ * opt = CacheState :: Done ;
892
894
}
893
895
for alt in & node. alternative_predicates {
894
896
if let Some ( opt) = self . active_cache . get_mut ( alt) {
895
- * opt = None
897
+ * opt = CacheState :: Done ;
896
898
}
897
899
}
898
900
0 commit comments