@@ -302,7 +302,20 @@ enum ContinueTraversal {
302
302
SkipSelfAndChildren ,
303
303
}
304
304
305
+ #[ derive( Clone , Copy ) ]
306
+ pub enum ChildrenVisitMode {
307
+ VisitChildrenOfAccessed ,
308
+ SkipChildrenOfAccessed ,
309
+ }
310
+
311
+ enum RecursionState {
312
+ BeforeChildren ,
313
+ AfterChildren ,
314
+ }
315
+
305
316
/// Stack of nodes left to explore in a tree traversal.
317
+ /// See the docs of `traverse_this_parents_children_other` for details on the
318
+ /// traversal order.
306
319
struct TreeVisitorStack < NodeContinue , NodeApp , ErrHandler > {
307
320
/// Identifier of the original access.
308
321
initial : UniIndex ,
@@ -316,10 +329,12 @@ struct TreeVisitorStack<NodeContinue, NodeApp, ErrHandler> {
316
329
/// Mutable state of the visit: the tags left to handle.
317
330
/// Every tag pushed should eventually be handled,
318
331
/// and the precise order is relevant for diagnostics.
319
- /// Since the traversal is bottom-up, we need to remember
320
- /// whether we're here initially (false) or after visiting all
321
- /// children (true). The bool indicates this.
322
- stack : Vec < ( UniIndex , AccessRelatedness , bool ) > ,
332
+ /// Since the traversal is piecewise bottom-up, we need to
333
+ /// remember whether we're here initially, or after visiting all children.
334
+ /// The last element indicates this.
335
+ /// This is just an artifact of how you hand-roll recursion,
336
+ /// it does not have a deeper meaning otherwise.
337
+ stack : Vec < ( UniIndex , AccessRelatedness , RecursionState ) > ,
323
338
}
324
339
325
340
impl < NodeContinue , NodeApp , InnErr , OutErr , ErrHandler >
@@ -362,64 +377,85 @@ where
362
377
& mut self ,
363
378
this : & mut TreeVisitor < ' _ > ,
364
379
accessed_node : UniIndex ,
365
- push_children_of_accessed : bool ,
380
+ visit_children : ChildrenVisitMode ,
366
381
) -> Result < ( ) , OutErr > {
382
+ // We want to visit the accessed node's children first.
383
+ // However, we will below walk up our parents and push their children (our cousins)
384
+ // onto the stack. To ensure correct iteration order, this method thus finishes
385
+ // by reversing the stack. This only works if the stack is empty initially.
367
386
assert ! ( self . stack. is_empty( ) ) ;
368
387
// First, handle accessed node. A bunch of things need to
369
388
// be handled differently here compared to the further parents
370
389
// of `accesssed_node`.
371
390
{
372
391
self . propagate_at ( this, accessed_node, AccessRelatedness :: This ) ?;
373
- if push_children_of_accessed {
392
+ if matches ! ( visit_children , ChildrenVisitMode :: VisitChildrenOfAccessed ) {
374
393
let accessed_node = this. nodes . get ( accessed_node) . unwrap ( ) ;
394
+ // We `rev()` here because we reverse the entire stack later.
375
395
for & child in accessed_node. children . iter ( ) . rev ( ) {
376
- self . stack . push ( ( child, AccessRelatedness :: AncestorAccess , false ) ) ;
396
+ self . stack . push ( (
397
+ child,
398
+ AccessRelatedness :: AncestorAccess ,
399
+ RecursionState :: BeforeChildren ,
400
+ ) ) ;
377
401
}
378
402
}
379
403
}
380
- // Then, handle the accessed node's parent . Here, we need to
404
+ // Then, handle the accessed node's parents . Here, we need to
381
405
// make sure we only mark the "cousin" subtrees for later visitation,
382
406
// not the subtree that contains the accessed node.
383
407
let mut last_node = accessed_node;
384
408
while let Some ( current) = this. nodes . get ( last_node) . unwrap ( ) . parent {
385
409
self . propagate_at ( this, current, AccessRelatedness :: StrictChildAccess ) ?;
386
410
let node = this. nodes . get ( current) . unwrap ( ) ;
411
+ // We `rev()` here because we reverse the entire stack later.
387
412
for & child in node. children . iter ( ) . rev ( ) {
388
413
if last_node == child {
389
414
continue ;
390
415
}
391
- self . stack . push ( ( child, AccessRelatedness :: DistantAccess , false ) ) ;
416
+ self . stack . push ( (
417
+ child,
418
+ AccessRelatedness :: DistantAccess ,
419
+ RecursionState :: BeforeChildren ,
420
+ ) ) ;
392
421
}
393
422
last_node = current;
394
423
}
424
+ // Reverse the stack, as discussed above.
395
425
self . stack . reverse ( ) ;
396
426
Ok ( ( ) )
397
427
}
398
428
399
429
fn finish_foreign_accesses ( & mut self , this : & mut TreeVisitor < ' _ > ) -> Result < ( ) , OutErr > {
400
- while let Some ( ( idx, rel_pos, is_final ) ) = self . stack . last_mut ( ) {
430
+ while let Some ( ( idx, rel_pos, step ) ) = self . stack . last_mut ( ) {
401
431
let idx = * idx;
402
432
let rel_pos = * rel_pos;
403
- if * is_final {
404
- self . stack . pop ( ) ;
405
- self . propagate_at ( this, idx, rel_pos) ?;
406
- } else {
407
- * is_final = true ;
408
- let handle_children = self . should_continue_at ( this, idx, rel_pos) ;
409
- match handle_children {
410
- ContinueTraversal :: Recurse => {
411
- // add all children, and also leave the node itself
412
- // on the stack so that it can be visited later.
413
- let node = this. nodes . get ( idx) . unwrap ( ) ;
414
- for & child in node. children . iter ( ) {
415
- self . stack . push ( ( child, rel_pos, false ) ) ;
433
+ match * step {
434
+ // How to do bottom-up traversal, 101: Before you handle a node, you handle all children.
435
+ // For this, you must first find the children, which is what this code here does.
436
+ RecursionState :: BeforeChildren => {
437
+ // Next time we come back will be when all the children are handled.
438
+ * step = RecursionState :: AfterChildren ;
439
+ // Now push the children, except if we are told to skip this subtree.
440
+ let handle_children = self . should_continue_at ( this, idx, rel_pos) ;
441
+ match handle_children {
442
+ ContinueTraversal :: Recurse => {
443
+ let node = this. nodes . get ( idx) . unwrap ( ) ;
444
+ for & child in node. children . iter ( ) {
445
+ self . stack . push ( ( child, rel_pos, RecursionState :: BeforeChildren ) ) ;
446
+ }
447
+ }
448
+ ContinueTraversal :: SkipSelfAndChildren => {
449
+ // skip self
450
+ self . stack . pop ( ) ;
451
+ continue ;
416
452
}
417
453
}
418
- ContinueTraversal :: SkipSelfAndChildren => {
419
- // skip self
420
- self . stack . pop ( ) ;
421
- continue ;
422
- }
454
+ }
455
+ // All the children are handled, let's actually visit this node
456
+ RecursionState :: AfterChildren => {
457
+ self . stack . pop ( ) ;
458
+ self . propagate_at ( this , idx , rel_pos ) ? ;
423
459
}
424
460
}
425
461
}
@@ -437,19 +473,42 @@ where
437
473
}
438
474
439
475
impl < ' tree > TreeVisitor < ' tree > {
440
- // Applies `f_propagate` to every vertex of the tree bottom-up in the following order: first
441
- // all ancestors of `start` (starting with `start` itself), then children of `start`, then the rest,
442
- // always going bottom-up.
443
- // This ensures that errors are triggered in the following order
444
- // - first invalid accesses with insufficient permissions, closest to the accessed node first,
445
- // - then protector violations, bottom-up, starting with the children of the accessed node, and then
446
- // going upwards and outwards.
447
- //
448
- // `f_propagate` should follow the following format: for a given `Node` it updates its
449
- // `Permission` depending on the position relative to `start` (given by an
450
- // `AccessRelatedness`).
451
- // `f_continue` is called before on foreign nodes, and describes whether to continue
452
- // with the subtree at that node.
476
+ /// Applies `f_propagate` to every vertex of the tree in a piecewise bottom-up way: First, visit
477
+ /// all ancestors of `start` (starting with `start` itself), then children of `start`, then the rest,
478
+ /// going bottom-up in each of these two "pieces" / sections.
479
+ /// This ensures that errors are triggered in the following order
480
+ /// - first invalid accesses with insufficient permissions, closest to the accessed node first,
481
+ /// - then protector violations, bottom-up, starting with the children of the accessed node, and then
482
+ /// going upwards and outwards.
483
+ ///
484
+ /// The following graphic visualizes it, with numbers indicating visitation order and `start` being
485
+ /// the node that is visited first ("1"):
486
+ ///
487
+ /// ```text
488
+ /// 3
489
+ /// /|
490
+ /// / |
491
+ /// 9 2
492
+ /// | |\
493
+ /// | | \
494
+ /// 8 1 7
495
+ /// / \
496
+ /// 4 6
497
+ /// |
498
+ /// 5
499
+ /// ```
500
+ ///
501
+ /// `f_propagate` should follow the following format: for a given `Node` it updates its
502
+ /// `Permission` depending on the position relative to `start` (given by an
503
+ /// `AccessRelatedness`).
504
+ /// `f_continue` is called earlier on foreign nodes, and describes whether to even start
505
+ /// visiting the subtree at that node. If it e.g. returns `SkipSelfAndChildren` on node 6
506
+ /// above, then nodes 5 _and_ 6 would not be visited by `f_propagate`. It is not used for
507
+ /// notes having a child access (nodes 1, 2, 3).
508
+ ///
509
+ /// Finally, remember that the iteration order is not relevant for UB, it only affects
510
+ /// diagnostics. It also affects tree traversal optimizations built on top of this, so
511
+ /// those need to be reviewed carefully as well whenever this changes.
453
512
fn traverse_this_parents_children_other < InnErr , OutErr > (
454
513
mut self ,
455
514
start : BorTag ,
@@ -463,14 +522,18 @@ impl<'tree> TreeVisitor<'tree> {
463
522
// undergoing a child access. Also pushes the children and the other
464
523
// cousin nodes (i.e. all nodes undergoing a foreign access) to the stack
465
524
// to be processed later.
466
- stack. go_upwards_from_accessed ( & mut self , start_idx, true ) ?;
525
+ stack. go_upwards_from_accessed (
526
+ & mut self ,
527
+ start_idx,
528
+ ChildrenVisitMode :: VisitChildrenOfAccessed ,
529
+ ) ?;
467
530
// Now visit all the foreign nodes we remembered earlier.
468
531
// For this we go bottom-up, but also allow f_continue to skip entire
469
532
// subtrees from being visited if it would be a NOP.
470
533
stack. finish_foreign_accesses ( & mut self )
471
534
}
472
535
473
- // Like `traverse_this_parents_children_other`, but skips the children of `start`.
536
+ /// Like `traverse_this_parents_children_other`, but skips the children of `start`.
474
537
fn traverse_nonchildren < InnErr , OutErr > (
475
538
mut self ,
476
539
start : BorTag ,
@@ -483,7 +546,11 @@ impl<'tree> TreeVisitor<'tree> {
483
546
// Visits the accessed node itself, and all its parents, i.e. all nodes
484
547
// undergoing a child access. Also pushes the other cousin nodes to the
485
548
// stack, but not the children of the accessed node.
486
- stack. go_upwards_from_accessed ( & mut self , start_idx, false ) ?;
549
+ stack. go_upwards_from_accessed (
550
+ & mut self ,
551
+ start_idx,
552
+ ChildrenVisitMode :: SkipChildrenOfAccessed ,
553
+ ) ?;
487
554
// Now visit all the foreign nodes we remembered earlier.
488
555
// For this we go bottom-up, but also allow f_continue to skip entire
489
556
// subtrees from being visited if it would be a NOP.
0 commit comments