10
10
11
11
use std:: assert_matches:: debug_assert_matches;
12
12
use std:: fmt:: Debug ;
13
+ use std:: marker:: PhantomData ;
13
14
use std:: ops:: Range ;
14
15
15
16
use rustc_index:: { Idx , IndexSlice , IndexVec } ;
16
- use tracing:: { debug, instrument} ;
17
+ use tracing:: { debug, instrument, trace } ;
17
18
18
19
use crate :: fx:: FxHashSet ;
19
20
use crate :: graph:: vec_graph:: VecGraph ;
@@ -48,6 +49,25 @@ pub trait Annotation: Debug + Copy {
48
49
}
49
50
}
50
51
52
+ /// An accumulator for annotations.
53
+ pub trait Annotations < N : Idx > {
54
+ type Ann : Annotation ;
55
+ type SccIdx : Idx + Ord ;
56
+
57
+ fn new ( & self , element : N ) -> Self :: Ann ;
58
+ fn annotate_scc ( & mut self , scc : Self :: SccIdx , annotation : Self :: Ann ) ;
59
+ }
60
+
61
+ /// The nil annotation accumulator, which does nothing.
62
+ struct NoAnnotations < S : Idx + Ord > ( PhantomData < S > ) ;
63
+
64
+ impl < N : Idx , S : Idx + Ord > Annotations < N > for NoAnnotations < S > {
65
+ type SccIdx = S ;
66
+ type Ann = ( ) ;
67
+ fn new ( & self , _element : N ) { }
68
+ fn annotate_scc ( & mut self , _scc : S , _annotation : ( ) ) { }
69
+ }
70
+
51
71
/// The empty annotation, which does nothing.
52
72
impl Annotation for ( ) {
53
73
fn merge_reached ( self , _other : Self ) -> Self {
@@ -62,23 +82,20 @@ impl Annotation for () {
62
82
/// the index type for the graph nodes and `S` is the index type for
63
83
/// the SCCs. We can map from each node to the SCC that it
64
84
/// participates in, and we also have the successors of each SCC.
65
- pub struct Sccs < N : Idx , S : Idx , A : Annotation = ( ) > {
85
+ pub struct Sccs < N : Idx , S : Idx > {
66
86
/// For each node, what is the SCC index of the SCC to which it
67
87
/// belongs.
68
88
scc_indices : IndexVec < N , S > ,
69
89
70
90
/// Data about all the SCCs.
71
- scc_data : SccData < S , A > ,
91
+ scc_data : SccData < S > ,
72
92
}
73
93
74
94
/// Information about an invidividual SCC node.
75
- struct SccDetails < A : Annotation > {
95
+ struct SccDetails {
76
96
/// For this SCC, the range of `all_successors` where its
77
97
/// successors can be found.
78
98
range : Range < usize > ,
79
-
80
- /// User-specified metadata about the SCC.
81
- annotation : A ,
82
99
}
83
100
84
101
// The name of this struct should discourage you from making it public and leaking
@@ -87,35 +104,29 @@ struct SccDetails<A: Annotation> {
87
104
// is difficult when it's publicly inspectable.
88
105
//
89
106
// Obey the law of Demeter!
90
- struct SccData < S : Idx , A : Annotation > {
107
+ struct SccData < S : Idx > {
91
108
/// Maps SCC indices to their metadata, including
92
109
/// offsets into `all_successors`.
93
- scc_details : IndexVec < S , SccDetails < A > > ,
110
+ scc_details : IndexVec < S , SccDetails > ,
94
111
95
112
/// Contains the successors for all the Sccs, concatenated. The
96
113
/// range of indices corresponding to a given SCC is found in its
97
114
/// `scc_details.range`.
98
115
all_successors : Vec < S > ,
99
116
}
100
117
101
- impl < N : Idx , S : Idx + Ord > Sccs < N , S , ( ) > {
118
+ impl < N : Idx , S : Idx + Ord > Sccs < N , S > {
102
119
/// Compute SCCs without annotations.
103
120
pub fn new ( graph : & impl Successors < Node = N > ) -> Self {
104
- Self :: new_with_annotation ( graph, |_| ( ) )
121
+ Self :: new_with_annotation ( graph, & mut NoAnnotations ( PhantomData :: < S > ) )
105
122
}
106
- }
107
123
108
- impl < N : Idx , S : Idx + Ord , A : Annotation > Sccs < N , S , A > {
109
124
/// Compute SCCs and annotate them with a user-supplied annotation
110
- pub fn new_with_annotation < F : Fn ( N ) -> A > (
125
+ pub fn new_with_annotation < A : Annotations < N , SccIdx = S > > (
111
126
graph : & impl Successors < Node = N > ,
112
- to_annotation : F ,
127
+ annotations : & mut A ,
113
128
) -> Self {
114
- SccsConstruction :: construct ( graph, to_annotation)
115
- }
116
-
117
- pub fn annotation ( & self , scc : S ) -> A {
118
- self . scc_data . annotation ( scc)
129
+ SccsConstruction :: construct ( graph, annotations)
119
130
}
120
131
121
132
pub fn scc_indices ( & self ) -> & IndexSlice < N , S > {
@@ -160,27 +171,27 @@ impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> {
160
171
}
161
172
}
162
173
163
- impl < N : Idx , S : Idx + Ord , A : Annotation > DirectedGraph for Sccs < N , S , A > {
174
+ impl < N : Idx , S : Idx + Ord > DirectedGraph for Sccs < N , S > {
164
175
type Node = S ;
165
176
166
177
fn num_nodes ( & self ) -> usize {
167
178
self . num_sccs ( )
168
179
}
169
180
}
170
181
171
- impl < N : Idx , S : Idx + Ord , A : Annotation > NumEdges for Sccs < N , S , A > {
182
+ impl < N : Idx , S : Idx + Ord > NumEdges for Sccs < N , S > {
172
183
fn num_edges ( & self ) -> usize {
173
184
self . scc_data . all_successors . len ( )
174
185
}
175
186
}
176
187
177
- impl < N : Idx , S : Idx + Ord , A : Annotation > Successors for Sccs < N , S , A > {
188
+ impl < N : Idx , S : Idx + Ord > Successors for Sccs < N , S > {
178
189
fn successors ( & self , node : S ) -> impl Iterator < Item = Self :: Node > {
179
190
self . successors ( node) . iter ( ) . cloned ( )
180
191
}
181
192
}
182
193
183
- impl < S : Idx , A : Annotation > SccData < S , A > {
194
+ impl < S : Idx > SccData < S > {
184
195
/// Number of SCCs,
185
196
fn len ( & self ) -> usize {
186
197
self . scc_details . len ( )
@@ -192,45 +203,37 @@ impl<S: Idx, A: Annotation> SccData<S, A> {
192
203
}
193
204
194
205
/// Creates a new SCC with `successors` as its successors and
195
- /// the maximum weight of its internal nodes `scc_max_weight` and
196
206
/// returns the resulting index.
197
- fn create_scc ( & mut self , successors : impl IntoIterator < Item = S > , annotation : A ) -> S {
207
+ fn create_scc ( & mut self , successors : impl IntoIterator < Item = S > ) -> S {
198
208
// Store the successors on `scc_successors_vec`, remembering
199
209
// the range of indices.
200
210
let all_successors_start = self . all_successors . len ( ) ;
201
211
self . all_successors . extend ( successors) ;
202
212
let all_successors_end = self . all_successors . len ( ) ;
203
213
204
214
debug ! (
205
- "create_scc({:?}) successors={:?}, annotation={:?} " ,
215
+ "create_scc({:?}) successors={:?}" ,
206
216
self . len( ) ,
207
217
& self . all_successors[ all_successors_start..all_successors_end] ,
208
- annotation
209
218
) ;
210
219
211
220
let range = all_successors_start..all_successors_end;
212
- let metadata = SccDetails { range, annotation } ;
221
+ let metadata = SccDetails { range } ;
213
222
self . scc_details . push ( metadata)
214
223
}
215
-
216
- fn annotation ( & self , scc : S ) -> A {
217
- self . scc_details [ scc] . annotation
218
- }
219
224
}
220
225
221
- struct SccsConstruction < ' c , G , S , A , F >
226
+ struct SccsConstruction < ' c , ' a , G , A >
222
227
where
223
228
G : DirectedGraph + Successors ,
224
- S : Idx ,
225
- A : Annotation ,
226
- F : Fn ( G :: Node ) -> A ,
229
+ A : Annotations < G :: Node > ,
227
230
{
228
231
graph : & ' c G ,
229
232
230
233
/// The state of each node; used during walk to record the stack
231
234
/// and after walk to record what cycle each node ended up being
232
235
/// in.
233
- node_states : IndexVec < G :: Node , NodeState < G :: Node , S , A > > ,
236
+ node_states : IndexVec < G :: Node , NodeState < G :: Node , A :: SccIdx , A :: Ann > > ,
234
237
235
238
/// The stack of nodes that we are visiting as part of the DFS.
236
239
node_stack : Vec < G :: Node > ,
@@ -239,23 +242,21 @@ where
239
242
/// position in this stack, and when we encounter a successor SCC,
240
243
/// we push it on the stack. When we complete an SCC, we can pop
241
244
/// everything off the stack that was found along the way.
242
- successors_stack : Vec < S > ,
245
+ successors_stack : Vec < A :: SccIdx > ,
243
246
244
247
/// A set used to strip duplicates. As we accumulate successors
245
248
/// into the successors_stack, we sometimes get duplicate entries.
246
249
/// We use this set to remove those -- we also keep its storage
247
250
/// around between successors to amortize memory allocation costs.
248
- duplicate_set : FxHashSet < S > ,
251
+ duplicate_set : FxHashSet < A :: SccIdx > ,
249
252
250
- scc_data : SccData < S , A > ,
253
+ scc_data : SccData < A :: SccIdx > ,
251
254
252
- /// A function that constructs an initial SCC annotation
253
- /// out of a single node.
254
- to_annotation : F ,
255
+ annotations : & ' a mut A ,
255
256
}
256
257
257
258
#[ derive( Copy , Clone , Debug ) ]
258
- enum NodeState < N , S , A > {
259
+ enum NodeState < N , S , A : Annotation > {
259
260
/// This node has not yet been visited as part of the DFS.
260
261
///
261
262
/// After SCC construction is complete, this state ought to be
@@ -286,7 +287,7 @@ enum NodeState<N, S, A> {
286
287
287
288
/// The state of walking a given node.
288
289
#[ derive( Copy , Clone , Debug ) ]
289
- enum WalkReturn < S , A > {
290
+ enum WalkReturn < S , A : Annotation > {
290
291
/// The walk found a cycle, but the entire component is not known to have
291
292
/// been fully walked yet. We only know the minimum depth of this
292
293
/// component in a minimum spanning tree of the graph. This component
@@ -299,12 +300,10 @@ enum WalkReturn<S, A> {
299
300
Complete { scc_index : S , annotation : A } ,
300
301
}
301
302
302
- impl < ' c , G , S , A , F > SccsConstruction < ' c , G , S , A , F >
303
+ impl < ' c , ' a , G , A > SccsConstruction < ' c , ' a , G , A >
303
304
where
304
305
G : DirectedGraph + Successors ,
305
- S : Idx ,
306
- F : Fn ( G :: Node ) -> A ,
307
- A : Annotation ,
306
+ A : Annotations < G :: Node > ,
308
307
{
309
308
/// Identifies SCCs in the graph `G` and computes the resulting
310
309
/// DAG. This uses a variant of [Tarjan's
@@ -320,7 +319,7 @@ where
320
319
/// Additionally, we keep track of a current annotation of the SCC.
321
320
///
322
321
/// [wikipedia]: https://bit.ly/2EZIx84
323
- fn construct ( graph : & ' c G , to_annotation : F ) -> Sccs < G :: Node , S , A > {
322
+ fn construct ( graph : & ' c G , annotations : & ' a mut A ) -> Sccs < G :: Node , A :: SccIdx > {
324
323
let num_nodes = graph. num_nodes ( ) ;
325
324
326
325
let mut this = Self {
@@ -330,7 +329,7 @@ where
330
329
successors_stack : Vec :: new ( ) ,
331
330
scc_data : SccData { scc_details : IndexVec :: new ( ) , all_successors : Vec :: new ( ) } ,
332
331
duplicate_set : FxHashSet :: default ( ) ,
333
- to_annotation ,
332
+ annotations ,
334
333
} ;
335
334
336
335
let scc_indices = graph
@@ -346,7 +345,7 @@ where
346
345
Sccs { scc_indices, scc_data : this. scc_data }
347
346
}
348
347
349
- fn start_walk_from ( & mut self , node : G :: Node ) -> WalkReturn < S , A > {
348
+ fn start_walk_from ( & mut self , node : G :: Node ) -> WalkReturn < A :: SccIdx , A :: Ann > {
350
349
self . inspect_node ( node) . unwrap_or_else ( || self . walk_unvisited_node ( node) )
351
350
}
352
351
@@ -362,7 +361,7 @@ where
362
361
/// Otherwise, we are looking at a node that has already been
363
362
/// completely visited. We therefore return `WalkReturn::Complete`
364
363
/// with its associated SCC index.
365
- fn inspect_node ( & mut self , node : G :: Node ) -> Option < WalkReturn < S , A > > {
364
+ fn inspect_node ( & mut self , node : G :: Node ) -> Option < WalkReturn < A :: SccIdx , A :: Ann > > {
366
365
Some ( match self . find_state ( node) {
367
366
NodeState :: InCycle { scc_index, annotation } => {
368
367
WalkReturn :: Complete { scc_index, annotation }
@@ -385,7 +384,7 @@ where
385
384
/// of `r2` (and updates `r` to reflect current result). This is
386
385
/// basically the "find" part of a standard union-find algorithm
387
386
/// (with path compression).
388
- fn find_state ( & mut self , mut node : G :: Node ) -> NodeState < G :: Node , S , A > {
387
+ fn find_state ( & mut self , mut node : G :: Node ) -> NodeState < G :: Node , A :: SccIdx , A :: Ann > {
389
388
// To avoid recursion we temporarily reuse the `parent` of each
390
389
// InCycleWith link to encode a downwards link while compressing
391
390
// the path. After we have found the root or deepest node being
@@ -408,7 +407,7 @@ where
408
407
// a potentially derived version of the root state for non-root nodes in the chain.
409
408
let ( root_state, assigned_state) = {
410
409
loop {
411
- debug ! ( "find_state(r = {node:?} in state {:?})" , self . node_states[ node] ) ;
410
+ trace ! ( "find_state(r = {node:?} in state {:?})" , self . node_states[ node] ) ;
412
411
match self . node_states [ node] {
413
412
// This must have been the first and only state since it is unexplored*;
414
413
// no update needed! * Unless there is a bug :')
@@ -482,7 +481,7 @@ where
482
481
if previous_node == node {
483
482
return root_state;
484
483
}
485
- debug ! ( "Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}" ) ;
484
+ trace ! ( "Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}" ) ;
486
485
487
486
// Update to previous node in the link.
488
487
match self . node_states [ previous_node] {
@@ -507,9 +506,9 @@ where
507
506
/// Call this method when `inspect_node` has returned `None`. Having the
508
507
/// caller decide avoids mutual recursion between the two methods and allows
509
508
/// us to maintain an allocated stack for nodes on the path between calls.
510
- #[ instrument( skip( self , initial) , level = "debug " ) ]
511
- fn walk_unvisited_node ( & mut self , initial : G :: Node ) -> WalkReturn < S , A > {
512
- debug ! ( "Walk unvisited node: {initial:?}" ) ;
509
+ #[ instrument( skip( self , initial) , level = "trace " ) ]
510
+ fn walk_unvisited_node ( & mut self , initial : G :: Node ) -> WalkReturn < A :: SccIdx , A :: Ann > {
511
+ trace ! ( "Walk unvisited node: {initial:?}" ) ;
513
512
struct VisitingNodeFrame < G : DirectedGraph , Successors , A > {
514
513
node : G :: Node ,
515
514
successors : Option < Successors > ,
@@ -537,7 +536,7 @@ where
537
536
successors_len: 0 ,
538
537
min_cycle_root: initial,
539
538
successor_node: initial,
540
- current_component_annotation: ( self . to_annotation ) ( initial) ,
539
+ current_component_annotation: self . annotations . new ( initial) ,
541
540
} ] ;
542
541
543
542
let mut return_value = None ;
@@ -556,19 +555,15 @@ where
556
555
let node = * node;
557
556
let depth = * depth;
558
557
559
- // node is definitely in the current component, add it to the annotation.
560
- if node != initial {
561
- current_component_annotation. update_scc ( ( self . to_annotation ) ( node) ) ;
562
- }
563
- debug ! (
558
+ trace ! (
564
559
"Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}"
565
560
) ;
566
561
567
562
let successors = match successors {
568
563
Some ( successors) => successors,
569
564
None => {
570
565
// This None marks that we still have the initialize this node's frame.
571
- debug ! ( ?depth, ?node) ;
566
+ trace ! ( ?depth, ?node) ;
572
567
573
568
debug_assert_matches ! ( self . node_states[ node] , NodeState :: NotVisited ) ;
574
569
@@ -598,7 +593,7 @@ where
598
593
return_value. take ( ) . into_iter ( ) . map ( |walk| ( * successor_node, Some ( walk) ) ) ;
599
594
600
595
let successor_walk = successors. map ( |successor_node| {
601
- debug ! ( ?node, ?successor_node) ;
596
+ trace ! ( ?node, ?successor_node) ;
602
597
( successor_node, self . inspect_node ( successor_node) )
603
598
} ) ;
604
599
for ( successor_node, walk) in returned_walk. chain ( successor_walk) {
@@ -609,13 +604,13 @@ where
609
604
min_depth : successor_min_depth,
610
605
annotation : successor_annotation,
611
606
} ) => {
612
- debug ! (
607
+ trace ! (
613
608
"Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}"
614
609
) ;
615
610
// Track the minimum depth we can reach.
616
611
assert ! ( successor_min_depth <= depth) ;
617
612
if successor_min_depth < * min_depth {
618
- debug ! ( ?node, ?successor_min_depth) ;
613
+ trace ! ( ?node, ?successor_min_depth) ;
619
614
* min_depth = successor_min_depth;
620
615
* min_cycle_root = successor_node;
621
616
}
@@ -627,20 +622,20 @@ where
627
622
scc_index : successor_scc_index,
628
623
annotation : successor_annotation,
629
624
} ) => {
630
- debug ! (
625
+ trace ! (
631
626
"Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}"
632
627
) ;
633
628
// Push the completed SCC indices onto
634
629
// the `successors_stack` for later.
635
- debug ! ( ?node, ?successor_scc_index) ;
630
+ trace ! ( ?node, ?successor_scc_index) ;
636
631
successors_stack. push ( successor_scc_index) ;
637
632
current_component_annotation. update_reachable ( successor_annotation) ;
638
633
}
639
634
// `node` has no more (direct) successors; search recursively.
640
635
None => {
641
636
let depth = depth + 1 ;
642
- debug ! ( "Recursing down into {successor_node:?} at depth {depth:?}" ) ;
643
- debug ! ( ?depth, ?successor_node) ;
637
+ trace ! ( "Recursing down into {successor_node:?} at depth {depth:?}" ) ;
638
+ trace ! ( ?depth, ?successor_node) ;
644
639
// Remember which node the return value will come from.
645
640
frame. successor_node = successor_node;
646
641
// Start a new stack frame, then step into it.
@@ -652,14 +647,14 @@ where
652
647
min_depth : depth,
653
648
min_cycle_root : successor_node,
654
649
successor_node,
655
- current_component_annotation : ( self . to_annotation ) ( successor_node) ,
650
+ current_component_annotation : self . annotations . new ( successor_node) ,
656
651
} ) ;
657
652
continue ' recurse;
658
653
}
659
654
}
660
655
}
661
656
662
- debug ! ( "Finished walk from {node:?} with annotation: {current_component_annotation:?}" ) ;
657
+ trace ! ( "Finished walk from {node:?} with annotation: {current_component_annotation:?}" ) ;
663
658
664
659
// Completed walk, remove `node` from the stack.
665
660
let r = self . node_stack . pop ( ) ;
@@ -691,8 +686,9 @@ where
691
686
692
687
debug ! ( "Creating SCC rooted in {node:?} with successor {:?}" , frame. successor_node) ;
693
688
694
- let scc_index =
695
- self . scc_data . create_scc ( deduplicated_successors, current_component_annotation) ;
689
+ let scc_index = self . scc_data . create_scc ( deduplicated_successors) ;
690
+
691
+ self . annotations . annotate_scc ( scc_index, current_component_annotation) ;
696
692
697
693
self . node_states [ node] =
698
694
NodeState :: InCycle { scc_index, annotation : current_component_annotation } ;
0 commit comments