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
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
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;
8882mod tests;
8983
9084pub 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.
224225struct 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 }
0 commit comments