8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
+ //! The `ObligationForest` is a utility data structure used in trait
12
+ //! matching to track the set of outstanding obligations (those not
13
+ //! yet resolved to success or error). It also tracks the "backtrace"
14
+ //! of each pending obligation (why we are trying to figure this out
15
+ //! in the first place). See README.md for a general overview of how
16
+ //! to use this class.
17
+
11
18
use std:: fmt:: Debug ;
12
19
use std:: mem;
13
20
@@ -17,6 +24,18 @@ mod node_index;
17
24
mod test;
18
25
19
26
pub struct ObligationForest < O > {
27
+ /// The list of obligations. In between calls to
28
+ /// `process_obligations`, this list only contains nodes in the
29
+ /// `Pending` or `Success` state (with a non-zero number of
30
+ /// incomplete children). During processing, some of those nodes
31
+ /// may be changed to the error state, or we may find that they
32
+ /// are completed (That is, `num_incomplete_children` drops to 0).
33
+ /// At the end of processing, those nodes will be removed by a
34
+ /// call to `compress`.
35
+ ///
36
+ /// At all times we maintain the invariant that every node appears
37
+ /// at a higher index than its parent. This is needed by the
38
+ /// backtrace iterator (which uses `split_at`).
20
39
nodes : Vec < Node < O > > ,
21
40
snapshots : Vec < usize >
22
41
}
@@ -33,28 +52,44 @@ struct Node<O> {
33
52
root : NodeIndex , // points to the root, which may be the current node
34
53
}
35
54
55
+ /// The state of one node in some tree within the forest. This
56
+ /// represents the current state of processing for the obligation (of
57
+ /// type `O`) associated with this node.
36
58
#[ derive( Debug ) ]
37
59
enum NodeState < O > {
38
- Leaf { obligation : O } ,
39
- Success { obligation : O , num_children : usize } ,
60
+ /// Obligation not yet resolved to success or error.
61
+ Pending { obligation : O } ,
62
+
63
+ /// Obligation resolved to success; `num_incomplete_children`
64
+ /// indicates the number of children still in an "incomplete"
65
+ /// state. Incomplete means that either the child is still
66
+ /// pending, or it has children which are incomplete. (Basically,
67
+ /// there is pending work somewhere in the subtree of the child.)
68
+ ///
69
+ /// Once all children have completed, success nodes are removed
70
+ /// from the vector by the compression step.
71
+ Success { obligation : O , num_incomplete_children : usize } ,
72
+
73
+ /// This obligation was resolved to an error. Error nodes are
74
+ /// removed from the vector by the compression step.
40
75
Error ,
41
76
}
42
77
43
78
#[ derive( Debug ) ]
44
79
pub struct Outcome < O , E > {
45
80
/// Obligations that were completely evaluated, including all
46
81
/// (transitive) subobligations.
47
- pub successful : Vec < O > ,
82
+ pub completed : Vec < O > ,
48
83
49
84
/// Backtrace of obligations that were found to be in error.
50
85
pub errors : Vec < Error < O , E > > ,
51
86
52
87
/// If true, then we saw no successful obligations, which means
53
88
/// there is no point in further iteration. This is based on the
54
- /// assumption that `Err` and `Ok(None)` results do not affect
55
- /// environmental inference state. (Note that if we invoke
56
- /// `process_obligations` with no pending obligations, stalled
57
- /// will be true.)
89
+ /// assumption that when trait matching returns `Err` or
90
+ /// `Ok(None)`, those results do not affect environmental
91
+ /// inference state. (Note that if we invoke `process_obligations`
92
+ /// with no pending obligations, stalled will be true.)
58
93
pub stalled : bool ,
59
94
}
60
95
@@ -90,13 +125,15 @@ impl<O: Debug> ObligationForest<O> {
90
125
}
91
126
92
127
pub fn rollback_snapshot ( & mut self , snapshot : Snapshot ) {
93
- // check that we are obeying stack discipline
128
+ // Check that we are obeying stack discipline.
94
129
assert_eq ! ( snapshot. len, self . snapshots. len( ) ) ;
95
130
let nodes_len = self . snapshots . pop ( ) . unwrap ( ) ;
96
131
97
- // the only action permitted while in a snapshot is to push new roots
132
+ // The only action permitted while in a snapshot is to push
133
+ // new root obligations. Because no processing will have been
134
+ // done, those roots should still be in the pending state.
98
135
debug_assert ! ( self . nodes[ nodes_len..] . iter( ) . all( |n| match n. state {
99
- NodeState :: Leaf { .. } => true ,
136
+ NodeState :: Pending { .. } => true ,
100
137
_ => false ,
101
138
} ) ) ;
102
139
@@ -116,12 +153,15 @@ impl<O: Debug> ObligationForest<O> {
116
153
}
117
154
118
155
/// Convert all remaining obligations to the given error.
156
+ ///
157
+ /// This cannot be done during a snapshot.
119
158
pub fn to_errors < E : Clone > ( & mut self , error : E ) -> Vec < Error < O , E > > {
159
+ assert ! ( !self . in_snapshot( ) ) ;
120
160
let mut errors = vec ! [ ] ;
121
161
for index in 0 ..self . nodes . len ( ) {
122
162
debug_assert ! ( !self . nodes[ index] . is_popped( ) ) ;
123
163
self . inherit_error ( index) ;
124
- if let NodeState :: Leaf { .. } = self . nodes [ index] . state {
164
+ if let NodeState :: Pending { .. } = self . nodes [ index] . state {
125
165
let backtrace = self . backtrace ( index) ;
126
166
errors. push ( Error { error : error. clone ( ) , backtrace : backtrace } ) ;
127
167
}
@@ -131,11 +171,11 @@ impl<O: Debug> ObligationForest<O> {
131
171
errors
132
172
}
133
173
134
- /// Convert all remaining obligations to the given error .
174
+ /// Returns the set of obligations that are in a pending state .
135
175
pub fn pending_obligations ( & self ) -> Vec < O > where O : Clone {
136
176
self . nodes . iter ( )
137
177
. filter_map ( |n| match n. state {
138
- NodeState :: Leaf { ref obligation } => Some ( obligation) ,
178
+ NodeState :: Pending { ref obligation } => Some ( obligation) ,
139
179
_ => None ,
140
180
} )
141
181
. cloned ( )
@@ -174,9 +214,11 @@ impl<O: Debug> ObligationForest<O> {
174
214
let ( prefix, suffix) = self . nodes . split_at_mut ( index) ;
175
215
let backtrace = Backtrace :: new ( prefix, parent) ;
176
216
match suffix[ 0 ] . state {
177
- NodeState :: Error => continue ,
178
- NodeState :: Success { .. } => continue ,
179
- NodeState :: Leaf { ref mut obligation } => action ( obligation, backtrace) ,
217
+ NodeState :: Error |
218
+ NodeState :: Success { .. } =>
219
+ continue ,
220
+ NodeState :: Pending { ref mut obligation } =>
221
+ action ( obligation, backtrace) ,
180
222
}
181
223
} ;
182
224
@@ -204,7 +246,7 @@ impl<O: Debug> ObligationForest<O> {
204
246
debug ! ( "process_obligations: complete" ) ;
205
247
206
248
Outcome {
207
- successful : successful_obligations,
249
+ completed : successful_obligations,
208
250
errors : errors,
209
251
stalled : stalled,
210
252
}
@@ -219,9 +261,9 @@ impl<O: Debug> ObligationForest<O> {
219
261
fn success ( & mut self , index : usize , children : Vec < O > ) {
220
262
debug ! ( "success(index={}, children={:?})" , index, children) ;
221
263
222
- let num_children = children. len ( ) ;
264
+ let num_incomplete_children = children. len ( ) ;
223
265
224
- if num_children == 0 {
266
+ if num_incomplete_children == 0 {
225
267
// if there is no work left to be done, decrement parent's ref count
226
268
self . update_parent ( index) ;
227
269
} else {
@@ -233,13 +275,14 @@ impl<O: Debug> ObligationForest<O> {
233
275
. map ( |o| Node :: new ( root_index, Some ( node_index) , o) ) ) ;
234
276
}
235
277
236
- // change state from `Leaf ` to `Success`, temporarily swapping in `Error`
278
+ // change state from `Pending ` to `Success`, temporarily swapping in `Error`
237
279
let state = mem:: replace ( & mut self . nodes [ index] . state , NodeState :: Error ) ;
238
280
self . nodes [ index] . state = match state {
239
- NodeState :: Leaf { obligation } =>
281
+ NodeState :: Pending { obligation } =>
240
282
NodeState :: Success { obligation : obligation,
241
- num_children : num_children } ,
242
- NodeState :: Success { .. } | NodeState :: Error =>
283
+ num_incomplete_children : num_incomplete_children } ,
284
+ NodeState :: Success { .. } |
285
+ NodeState :: Error =>
243
286
unreachable ! ( )
244
287
} ;
245
288
}
@@ -251,9 +294,9 @@ impl<O: Debug> ObligationForest<O> {
251
294
if let Some ( parent) = self . nodes [ child] . parent {
252
295
let parent = parent. get ( ) ;
253
296
match self . nodes [ parent] . state {
254
- NodeState :: Success { ref mut num_children , .. } => {
255
- * num_children -= 1 ;
256
- if * num_children > 0 {
297
+ NodeState :: Success { ref mut num_incomplete_children , .. } => {
298
+ * num_incomplete_children -= 1 ;
299
+ if * num_incomplete_children > 0 {
257
300
return ;
258
301
}
259
302
}
@@ -263,8 +306,10 @@ impl<O: Debug> ObligationForest<O> {
263
306
}
264
307
}
265
308
266
- /// If the root of `child` is in an error error, places `child`
267
- /// into an error state.
309
+ /// If the root of `child` is in an error state, places `child`
310
+ /// into an error state. This is used during processing so that we
311
+ /// skip the remaining obligations from a tree once some other
312
+ /// node in the tree is found to be in error.
268
313
fn inherit_error ( & mut self , child : usize ) {
269
314
let root = self . nodes [ child] . root . get ( ) ;
270
315
if let NodeState :: Error = self . nodes [ root] . state {
@@ -274,12 +319,15 @@ impl<O: Debug> ObligationForest<O> {
274
319
275
320
/// Returns a vector of obligations for `p` and all of its
276
321
/// ancestors, putting them into the error state in the process.
322
+ /// The fact that the root is now marked as an error is used by
323
+ /// `inherit_error` above to propagate the error state to the
324
+ /// remainder of the tree.
277
325
fn backtrace ( & mut self , mut p : usize ) -> Vec < O > {
278
326
let mut trace = vec ! [ ] ;
279
327
loop {
280
328
let state = mem:: replace ( & mut self . nodes [ p] . state , NodeState :: Error ) ;
281
329
match state {
282
- NodeState :: Leaf { obligation } |
330
+ NodeState :: Pending { obligation } |
283
331
NodeState :: Success { obligation, .. } => {
284
332
trace. push ( obligation) ;
285
333
}
@@ -338,9 +386,9 @@ impl<O: Debug> ObligationForest<O> {
338
386
( 0 .. dead) . map ( |_| self . nodes . pop ( ) . unwrap ( ) )
339
387
. flat_map ( |node| match node. state {
340
388
NodeState :: Error => None ,
341
- NodeState :: Leaf { .. } => unreachable ! ( ) ,
342
- NodeState :: Success { obligation, num_children } => {
343
- assert_eq ! ( num_children , 0 ) ;
389
+ NodeState :: Pending { .. } => unreachable ! ( ) ,
390
+ NodeState :: Success { obligation, num_incomplete_children } => {
391
+ assert_eq ! ( num_incomplete_children , 0 ) ;
344
392
Some ( obligation)
345
393
}
346
394
} )
@@ -365,15 +413,15 @@ impl<O> Node<O> {
365
413
fn new ( root : NodeIndex , parent : Option < NodeIndex > , obligation : O ) -> Node < O > {
366
414
Node {
367
415
parent : parent,
368
- state : NodeState :: Leaf { obligation : obligation } ,
416
+ state : NodeState :: Pending { obligation : obligation } ,
369
417
root : root
370
418
}
371
419
}
372
420
373
421
fn is_popped ( & self ) -> bool {
374
422
match self . state {
375
- NodeState :: Leaf { .. } => false ,
376
- NodeState :: Success { num_children , .. } => num_children == 0 ,
423
+ NodeState :: Pending { .. } => false ,
424
+ NodeState :: Success { num_incomplete_children , .. } => num_incomplete_children == 0 ,
377
425
NodeState :: Error => true ,
378
426
}
379
427
}
@@ -399,7 +447,8 @@ impl<'b, O> Iterator for Backtrace<'b, O> {
399
447
if let Some ( p) = self . pointer {
400
448
self . pointer = self . nodes [ p. get ( ) ] . parent ;
401
449
match self . nodes [ p. get ( ) ] . state {
402
- NodeState :: Leaf { ref obligation } | NodeState :: Success { ref obligation, .. } => {
450
+ NodeState :: Pending { ref obligation } |
451
+ NodeState :: Success { ref obligation, .. } => {
403
452
Some ( obligation)
404
453
}
405
454
NodeState :: Error => {
0 commit comments