@@ -402,13 +402,25 @@ where
402
402
// Ultimately propagated to all the transitive parents when following
403
403
// `InCycleWith` upwards.
404
404
// This loop performs the downward link encoding mentioned above. Details below!
405
- let node_state = {
405
+ // Note that there are two different states being assigned: the root state, and
406
+ // a potentially derived version of the root state for non-root nodes in the chain.
407
+ let ( root_state, assigned_state) = {
406
408
loop {
407
409
debug ! ( "find_state(r = {node:?} in state {:?})" , self . node_states[ node] ) ;
408
410
match self . node_states [ node] {
409
- s @ ( NodeState :: NotVisited
410
- | NodeState :: BeingVisited { .. }
411
- | NodeState :: InCycle { .. } ) => break s,
411
+ // This must have been the first and only state since it is unexplored*;
412
+ // no update needed! * Unless there is a bug :')
413
+ s @ NodeState :: NotVisited => return s,
414
+ // We are in a completely discovered SCC; every node on our path is in that SCC:
415
+ s @ NodeState :: InCycle { .. } => break ( s, s) ,
416
+ // The Interesting Third Base Case: we are a path back to a root node
417
+ // still being explored. Now we need that node to keep its state and
418
+ // every other node to be recorded as being in whatever component that
419
+ // ends up in.
420
+ s @ NodeState :: BeingVisited { depth, .. } => {
421
+ break ( s, NodeState :: InCycleWith { parent : self . node_stack [ depth] } ) ;
422
+ }
423
+ // We are not at the head of a path; keep compressing it!
412
424
NodeState :: InCycleWith { parent } => {
413
425
// We test this, to be extremely sure that we never
414
426
// ever break our termination condition for the
@@ -462,11 +474,13 @@ where
462
474
// Move backwards until we found the node where we started. We
463
475
// will know when we hit the state where previous_node == node.
464
476
loop {
465
- // Back at the beginning, we can return.
477
+ // Back at the beginning, we can return. Note that we return the root state.
478
+ // This is becuse for components being explored, we would otherwise get a
479
+ // `node_state[n] = InCycleWith{ parent: n }` and that's wrong.
466
480
if previous_node == node {
467
- return node_state ;
481
+ return root_state ;
468
482
}
469
- debug ! ( "Compressing {node:?} down to {previous_node:?} with state {node_state :?}" ) ;
483
+ debug ! ( "Compressing {node:?} down to {previous_node:?} with state {assigned_state :?}" ) ;
470
484
471
485
// Update to previous node in the link.
472
486
match self . node_states [ previous_node] {
@@ -475,25 +489,14 @@ where
475
489
previous_node = previous;
476
490
}
477
491
// Only InCycleWith nodes were added to the reverse linked list.
478
- other => panic ! ( "Invalid previous link while compressing cycle: {other:?}" ) ,
492
+ other => unreachable ! ( "Invalid previous link while compressing cycle: {other:?}" ) ,
479
493
}
480
494
481
- debug ! ( "find_state: parent_state = {:?}" , node_state) ;
482
-
483
- let new_state = match node_state {
484
- // Still visiting nodes, compress the cycle to the root node
485
- // at that depth.
486
- NodeState :: BeingVisited { depth, .. } => {
487
- let parent = self . node_stack [ depth] ;
488
- NodeState :: InCycleWith { parent }
489
- }
490
- // Already fully visited; we just transfer the state of the parent.
491
- s @ NodeState :: InCycle { .. } => s,
492
- // These cannot be the root nodes of a path being compressed
493
- NodeState :: NotVisited | NodeState :: InCycleWith { .. } => unreachable ! ( ) ,
494
- } ;
495
-
496
- self . node_states [ node] = new_state;
495
+ // Update the node state to the (potentially derived) state.
496
+ // If the root is still being explored, this is
497
+ // `InCycleWith{ parent: <root node>}`, otherwise
498
+ // `assigned_state == root_state`.
499
+ self . node_states [ node] = assigned_state;
497
500
}
498
501
}
499
502
0 commit comments