1
+ //! The `ObligationForest` is a utility data structure used in trait
1
2
//! matching to track the set of outstanding obligations (those not yet
2
3
//! resolved to success or error). It also tracks the "backtrace" of each
3
4
//! pending obligation (why we are trying to figure this out in the first
41
42
//! now considered to be in error.
42
43
//!
43
44
//! 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:
45
46
//!
46
47
//! - `completed`: a list of obligations where processing was fully
47
48
//! completed without error (meaning that all transitive subobligations
52
53
//! all the obligations in `C` have been found completed.
53
54
//! - `errors`: a list of errors that occurred and associated backtraces
54
55
//! 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.
62
56
//!
63
57
//! ### Implementation details
64
58
//!
@@ -88,17 +82,17 @@ mod graphviz;
88
82
mod tests;
89
83
90
84
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.
92
86
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.
94
88
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.
96
90
type WatcherOffset ;
97
91
98
92
/// Converts this `ForestObligation` suitable for use as a cache key.
99
93
/// If two distinct `ForestObligations`s return the same cache key,
100
94
/// 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.
102
96
fn as_cache_key ( & self ) -> Self :: CacheKey ;
103
97
104
98
/// Returns which variables this obligation is currently stalled on. If the slice is empty then
@@ -127,7 +121,9 @@ pub trait ObligationProcessor {
127
121
where
128
122
I : Clone + Iterator < Item = & ' c Self :: Obligation > ;
129
123
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 (
131
127
& self ,
132
128
offset : & <Self :: Obligation as ForestObligation >:: WatcherOffset ,
133
129
f : impl FnMut ( <Self :: Obligation as ForestObligation >:: Variable ) ,
@@ -175,14 +171,15 @@ pub struct ObligationForest<O: ForestObligation> {
175
171
/// number allowing them to be ordered when processing them.
176
172
node_number : u32 ,
177
173
178
- /// Stores the indices of the nodes currently in the pending state
174
+ /// Stores the indices of the nodes currently in the pending state.
179
175
pending_nodes : Vec < NodeIndex > ,
180
176
181
177
/// Stores the indices of the nodes currently in the success or waiting states.
182
178
/// Can also contain `Done` or `Error` nodes as `process_cycles` does not remove a node
183
179
/// immediately but instead upon the next time that node is processed.
184
180
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.
186
183
error_or_done_nodes : RefCell < Vec < NodeIndex > > ,
187
184
188
185
/// Nodes that have been removed and are ready to be reused (pure optimization to reuse
@@ -205,22 +202,26 @@ pub struct ObligationForest<O: ForestObligation> {
205
202
/// [details]: https://github.com/rust-lang/rust/pull/53255#issuecomment-421184780
206
203
error_cache : FxHashMap < ObligationTreeId , FxHashSet < O :: CacheKey > > ,
207
204
208
- /// Stores which nodes would be unblocked once `O::Variable` is unified
205
+ /// Stores which nodes would be unblocked once `O::Variable` is unified.
209
206
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.
211
209
unblocked : BinaryHeap < Unblocked > ,
210
+
212
211
/// 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.
214
213
stalled_on_unknown : Vec < NodeIndex > ,
214
+
215
215
/// The offset that this `ObligationForest` has registered. Should be de-registered before
216
216
/// 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.
219
220
temp_unblocked_nodes : Vec < O :: Variable > ,
220
221
}
221
222
222
223
/// Helper struct for use with `BinaryHeap` to process nodes in the order that they were added to
223
- /// the forest
224
+ /// the forest.
224
225
struct Unblocked {
225
226
index : NodeIndex ,
226
227
order : u32 ,
@@ -248,13 +249,14 @@ struct Node<O: ForestObligation> {
248
249
obligation : O ,
249
250
state : Cell < NodeState > ,
250
251
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
252
253
/// old predicate so that we can remove or mark it as done if this node errors or is done.
253
254
alternative_predicates : Vec < O :: CacheKey > ,
254
255
255
256
/// Obligations that depend on this obligation for their completion. They
256
257
/// must all be in a non-pending state.
257
258
dependents : Vec < NodeIndex > ,
259
+
258
260
/// Obligations that this obligation depends on for their completion.
259
261
reverse_dependents : Vec < NodeIndex > ,
260
262
@@ -267,6 +269,9 @@ struct Node<O: ForestObligation> {
267
269
268
270
/// Identifier of the obligation tree to which this node belongs.
269
271
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.
270
275
node_number : u32 ,
271
276
}
272
277
@@ -292,8 +297,9 @@ where
292
297
}
293
298
}
294
299
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 (
297
303
& mut self ,
298
304
parent : Option < NodeIndex > ,
299
305
obligation : O ,
@@ -441,16 +447,19 @@ impl<O: ForestObligation> ObligationForest<O> {
441
447
unblocked : Default :: default ( ) ,
442
448
stalled_on_unknown : Default :: default ( ) ,
443
449
temp_unblocked_nodes : Default :: default ( ) ,
444
- offset : None ,
450
+ watcher_offset : None ,
445
451
}
446
452
}
447
453
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 ( )
450
458
}
451
459
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 ( )
454
463
}
455
464
456
465
/// Returns the total number of nodes in the forest that have not
@@ -523,7 +532,7 @@ impl<O: ForestObligation> ObligationForest<O> {
523
532
// otherwise allocate a new node
524
533
let new_index = if let Some ( new_index) = self . dead_nodes . pop ( ) {
525
534
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) ;
527
536
new_index
528
537
} else {
529
538
let new_index = self . nodes . len ( ) ;
@@ -565,11 +574,7 @@ impl<O: ForestObligation> ObligationForest<O> {
565
574
where
566
575
F : Fn ( & O ) -> P ,
567
576
{
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 ( )
573
578
}
574
579
575
580
fn insert_into_error_cache ( & mut self , index : NodeIndex ) {
@@ -589,8 +594,8 @@ impl<O: ForestObligation> ObligationForest<O> {
589
594
P : ObligationProcessor < Obligation = O > ,
590
595
OUT : OutcomeTrait < Obligation = O , Error = Error < O , P :: Error > > ,
591
596
{
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 ( ) ) ;
594
599
}
595
600
let mut errors = vec ! [ ] ;
596
601
let mut stalled = true ;
@@ -688,7 +693,10 @@ impl<O: ForestObligation> ObligationForest<O> {
688
693
Outcome { completed, errors }
689
694
}
690
695
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)
692
700
fn unblock_nodes < P > ( & mut self , processor : & mut P )
693
701
where
694
702
P : ObligationProcessor < Obligation = O > ,
@@ -698,7 +706,7 @@ impl<O: ForestObligation> ObligationForest<O> {
698
706
let unblocked = & mut self . unblocked ;
699
707
let temp_unblocked_nodes = & mut self . temp_unblocked_nodes ;
700
708
temp_unblocked_nodes. clear ( ) ;
701
- processor. unblocked ( self . offset . as_ref ( ) . unwrap ( ) , |var| {
709
+ processor. notify_unblocked ( self . watcher_offset . as_ref ( ) . unwrap ( ) , |var| {
702
710
if let Some ( unblocked_nodes) = stalled_on. remove ( & var) {
703
711
for node_index in unblocked_nodes {
704
712
let node = & nodes[ node_index] ;
@@ -861,9 +869,8 @@ impl<O: ForestObligation> ObligationForest<O> {
861
869
}
862
870
}
863
871
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.
867
874
#[ inline( never) ]
868
875
fn compress ( & mut self , do_completed : DoCompleted ) -> Option < Vec < O > > {
869
876
let mut removed_done_obligations: Vec < O > = vec ! [ ] ;
@@ -872,6 +879,8 @@ impl<O: ForestObligation> ObligationForest<O> {
872
879
let mut error_or_done_nodes = mem:: take ( self . error_or_done_nodes . get_mut ( ) ) ;
873
880
for & index in & error_or_done_nodes {
874
881
let node = & mut self . nodes [ index] ;
882
+
883
+ // Remove this node from all the nodes that depends on it
875
884
let reverse_dependents = mem:: take ( & mut node. reverse_dependents ) ;
876
885
for & reverse_index in & reverse_dependents {
877
886
let reverse_node = & mut self . nodes [ reverse_index] ;
@@ -892,6 +901,8 @@ impl<O: ForestObligation> ObligationForest<O> {
892
901
if let Some ( opt) = self . active_cache . get_mut ( & node. obligation . as_cache_key ( ) ) {
893
902
* opt = CacheState :: Done ;
894
903
}
904
+ // If the node's predicate changed at some point we mark all its alternate
905
+ // predicates as done as well
895
906
for alt in & node. alternative_predicates {
896
907
if let Some ( opt) = self . active_cache . get_mut ( alt) {
897
908
* opt = CacheState :: Done ;
@@ -903,13 +914,17 @@ impl<O: ForestObligation> ObligationForest<O> {
903
914
removed_done_obligations. push ( node. obligation . clone ( ) ) ;
904
915
}
905
916
917
+ // Store the node so it and its allocations can be used when another node is
918
+ // allocated
906
919
self . dead_nodes . push ( index) ;
907
920
}
908
921
NodeState :: Error => {
909
922
// We *intentionally* remove the node from the cache at this point. Otherwise
910
923
// tests must come up with a different type on every type error they
911
924
// check against.
912
925
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
913
928
for alt in & node. alternative_predicates {
914
929
self . active_cache . remove ( alt) ;
915
930
}
0 commit comments