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.
160162type NodeIndex = usize ;
161163
164+ enum CacheState {
165+ Active ( NodeIndex ) ,
166+ Done ,
167+ }
168+
162169pub 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
0 commit comments