@@ -302,7 +302,20 @@ enum ContinueTraversal {
302302 SkipSelfAndChildren ,
303303}
304304
305+ #[ derive( Clone , Copy ) ]
306+ pub enum ChildrenVisitMode {
307+ VisitChildrenOfAccessed ,
308+ SkipChildrenOfAccessed ,
309+ }
310+
311+ enum RecursionState {
312+ BeforeChildren ,
313+ AfterChildren ,
314+ }
315+
305316/// 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.
306319struct TreeVisitorStack < NodeContinue , NodeApp , ErrHandler > {
307320 /// Identifier of the original access.
308321 initial : UniIndex ,
@@ -316,10 +329,12 @@ struct TreeVisitorStack<NodeContinue, NodeApp, ErrHandler> {
316329 /// Mutable state of the visit: the tags left to handle.
317330 /// Every tag pushed should eventually be handled,
318331 /// 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 ) > ,
323338}
324339
325340impl < NodeContinue , NodeApp , InnErr , OutErr , ErrHandler >
@@ -362,64 +377,85 @@ where
362377 & mut self ,
363378 this : & mut TreeVisitor < ' _ > ,
364379 accessed_node : UniIndex ,
365- push_children_of_accessed : bool ,
380+ visit_children : ChildrenVisitMode ,
366381 ) -> 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.
367386 assert ! ( self . stack. is_empty( ) ) ;
368387 // First, handle accessed node. A bunch of things need to
369388 // be handled differently here compared to the further parents
370389 // of `accesssed_node`.
371390 {
372391 self . propagate_at ( this, accessed_node, AccessRelatedness :: This ) ?;
373- if push_children_of_accessed {
392+ if matches ! ( visit_children , ChildrenVisitMode :: VisitChildrenOfAccessed ) {
374393 let accessed_node = this. nodes . get ( accessed_node) . unwrap ( ) ;
394+ // We `rev()` here because we reverse the entire stack later.
375395 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+ ) ) ;
377401 }
378402 }
379403 }
380- // Then, handle the accessed node's parent . Here, we need to
404+ // Then, handle the accessed node's parents . Here, we need to
381405 // make sure we only mark the "cousin" subtrees for later visitation,
382406 // not the subtree that contains the accessed node.
383407 let mut last_node = accessed_node;
384408 while let Some ( current) = this. nodes . get ( last_node) . unwrap ( ) . parent {
385409 self . propagate_at ( this, current, AccessRelatedness :: StrictChildAccess ) ?;
386410 let node = this. nodes . get ( current) . unwrap ( ) ;
411+ // We `rev()` here because we reverse the entire stack later.
387412 for & child in node. children . iter ( ) . rev ( ) {
388413 if last_node == child {
389414 continue ;
390415 }
391- self . stack . push ( ( child, AccessRelatedness :: DistantAccess , false ) ) ;
416+ self . stack . push ( (
417+ child,
418+ AccessRelatedness :: DistantAccess ,
419+ RecursionState :: BeforeChildren ,
420+ ) ) ;
392421 }
393422 last_node = current;
394423 }
424+ // Reverse the stack, as discussed above.
395425 self . stack . reverse ( ) ;
396426 Ok ( ( ) )
397427 }
398428
399429 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 ( ) {
401431 let idx = * idx;
402432 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 ;
416452 }
417453 }
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 ) ? ;
423459 }
424460 }
425461 }
@@ -437,19 +473,42 @@ where
437473}
438474
439475impl < ' 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.
453512 fn traverse_this_parents_children_other < InnErr , OutErr > (
454513 mut self ,
455514 start : BorTag ,
@@ -463,14 +522,18 @@ impl<'tree> TreeVisitor<'tree> {
463522 // undergoing a child access. Also pushes the children and the other
464523 // cousin nodes (i.e. all nodes undergoing a foreign access) to the stack
465524 // 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+ ) ?;
467530 // Now visit all the foreign nodes we remembered earlier.
468531 // For this we go bottom-up, but also allow f_continue to skip entire
469532 // subtrees from being visited if it would be a NOP.
470533 stack. finish_foreign_accesses ( & mut self )
471534 }
472535
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`.
474537 fn traverse_nonchildren < InnErr , OutErr > (
475538 mut self ,
476539 start : BorTag ,
@@ -483,7 +546,11 @@ impl<'tree> TreeVisitor<'tree> {
483546 // Visits the accessed node itself, and all its parents, i.e. all nodes
484547 // undergoing a child access. Also pushes the other cousin nodes to the
485548 // 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+ ) ?;
487554 // Now visit all the foreign nodes we remembered earlier.
488555 // For this we go bottom-up, but also allow f_continue to skip entire
489556 // subtrees from being visited if it would be a NOP.
0 commit comments