@@ -402,13 +402,25 @@ where
402402 // Ultimately propagated to all the transitive parents when following
403403 // `InCycleWith` upwards.
404404 // 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) = {
406408 loop {
407409 debug ! ( "find_state(r = {node:?} in state {:?})" , self . node_states[ node] ) ;
408410 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!
412424 NodeState :: InCycleWith { parent } => {
413425 // We test this, to be extremely sure that we never
414426 // ever break our termination condition for the
@@ -462,11 +474,13 @@ where
462474 // Move backwards until we found the node where we started. We
463475 // will know when we hit the state where previous_node == node.
464476 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.
466480 if previous_node == node {
467- return node_state ;
481+ return root_state ;
468482 }
469- debug ! ( "Compressing {node:?} down to {previous_node:?} with state {node_state :?}" ) ;
483+ debug ! ( "Compressing {node:?} down to {previous_node:?} with state {assigned_state :?}" ) ;
470484
471485 // Update to previous node in the link.
472486 match self . node_states [ previous_node] {
@@ -475,25 +489,14 @@ where
475489 previous_node = previous;
476490 }
477491 // 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:?}" ) ,
479493 }
480494
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;
497500 }
498501 }
499502
0 commit comments