Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 618d7a1

Browse files
committedMay 1, 2025
Auto merge of #139965 - amandasystems:marginally-improved-scc-annotations, r=lcnr
Decouple SCC annotations from SCCs This rewires SCC annotations to have them be a separate, visitor-type data structure. It was broken out of #130227, which needed them to be able to remove unused annotations after computation without recomputing the SCCs themselves. As a drive-by it also removes some redundant code from the hot loop in SCC construction for a performance improvement. r? lcnr
2 parents 0c33fe2 + b660ab9 commit 618d7a1

File tree

4 files changed

+239
-171
lines changed

4 files changed

+239
-171
lines changed
 

‎compiler/rustc_borrowck/src/constraints/mod.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
77
use rustc_span::Span;
88
use tracing::{debug, instrument};
99

10-
use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker};
10+
use crate::region_infer::{AnnotatedSccs, ConstraintSccs, RegionDefinition, SccAnnotations};
1111
use crate::type_check::Locations;
1212
use crate::universal_regions::UniversalRegions;
1313

@@ -61,12 +61,14 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
6161
&self,
6262
static_region: RegionVid,
6363
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
64-
) -> ConstraintSccs {
64+
) -> AnnotatedSccs {
6565
let constraint_graph = self.graph(definitions.len());
6666
let region_graph = &constraint_graph.region_graph(self, static_region);
67-
ConstraintSccs::new_with_annotation(&region_graph, |r| {
68-
RegionTracker::new(r, &definitions[r])
69-
})
67+
let mut annotation_visitor = SccAnnotations::new(definitions);
68+
(
69+
ConstraintSccs::new_with_annotation(&region_graph, &mut annotation_visitor),
70+
annotation_visitor.scc_to_annotation,
71+
)
7072
}
7173

7274
/// This method handles Universe errors by rewriting the constraint
@@ -79,12 +81,12 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
7981
/// eventually go away.
8082
///
8183
/// For a more precise definition, see the documentation for
82-
/// [`RegionTracker::has_incompatible_universes()`].
84+
/// [`crate::region_infer::RegionTracker`].
8385
///
8486
/// This edge case used to be handled during constraint propagation
8587
/// by iterating over the strongly connected components in the constraint
8688
/// graph while maintaining a set of bookkeeping mappings similar
87-
/// to what is stored in `RegionTracker` and manually adding 'sttaic as
89+
/// to what is stored in `RegionTracker` and manually adding 'static as
8890
/// needed.
8991
///
9092
/// It was rewritten as part of the Polonius project with the goal of moving
@@ -108,9 +110,9 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
108110
&mut self,
109111
universal_regions: &UniversalRegions<'tcx>,
110112
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
111-
) -> ConstraintSccs {
113+
) -> AnnotatedSccs {
112114
let fr_static = universal_regions.fr_static;
113-
let sccs = self.compute_sccs(fr_static, definitions);
115+
let (sccs, annotations) = self.compute_sccs(fr_static, definitions);
114116

115117
// Changed to `true` if we added any constraints to `self` and need to
116118
// recompute SCCs.
@@ -124,7 +126,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
124126
continue;
125127
}
126128

127-
let annotation = sccs.annotation(scc);
129+
let annotation = annotations[scc];
128130

129131
// If this SCC participates in a universe violation,
130132
// e.g. if it reaches a region with a universe smaller than
@@ -154,7 +156,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
154156
self.compute_sccs(fr_static, definitions)
155157
} else {
156158
// If we didn't add any back-edges; no more work needs doing
157-
sccs
159+
(sccs, annotations)
158160
}
159161
}
160162
}

‎compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ mod reverse_sccs;
4747

4848
pub(crate) mod values;
4949

50-
pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex, RegionTracker>;
50+
pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex>;
51+
pub(crate) type AnnotatedSccs = (ConstraintSccs, IndexVec<ConstraintSccIndex, RegionTracker>);
5152

5253
/// An annotation for region graph SCCs that tracks
53-
/// the values of its elements.
54+
/// the values of its elements. This annotates a single SCC.
5455
#[derive(Copy, Debug, Clone)]
55-
pub struct RegionTracker {
56+
pub(crate) struct RegionTracker {
5657
/// The largest universe of a placeholder reached from this SCC.
5758
/// This includes placeholders within this SCC.
5859
max_placeholder_universe_reached: UniverseIndex,
@@ -97,6 +98,32 @@ impl scc::Annotation for RegionTracker {
9798
}
9899
}
99100

101+
/// A Visitor for SCC annotation construction.
102+
pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> {
103+
pub(crate) scc_to_annotation: IndexVec<ConstraintSccIndex, A>,
104+
definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>,
105+
}
106+
107+
impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> {
108+
pub(crate) fn new(definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>) -> Self {
109+
Self { scc_to_annotation: IndexVec::new(), definitions }
110+
}
111+
}
112+
113+
impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
114+
fn new(&self, element: RegionVid) -> RegionTracker {
115+
RegionTracker::new(element, &self.definitions[element])
116+
}
117+
118+
fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) {
119+
let idx = self.scc_to_annotation.push(annotation);
120+
assert!(idx == scc);
121+
}
122+
123+
type Ann = RegionTracker;
124+
type SccIdx = ConstraintSccIndex;
125+
}
126+
100127
impl RegionTracker {
101128
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
102129
let (representative_is_placeholder, representative_is_existential) = match definition.origin
@@ -166,6 +193,8 @@ pub struct RegionInferenceContext<'tcx> {
166193
/// compute the values of each region.
167194
constraint_sccs: ConstraintSccs,
168195

196+
scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
197+
169198
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
170199
/// `B: A`. This is used to compute the universal regions that are required
171200
/// to outlive a given SCC. Computed lazily.
@@ -446,7 +475,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
446475

447476
let definitions = create_definitions(infcx, &universal_regions);
448477

449-
let constraint_sccs =
478+
let (constraint_sccs, scc_annotations) =
450479
outlives_constraints.add_outlives_static(&universal_regions, &definitions);
451480
let constraints = Frozen::freeze(outlives_constraints);
452481
let constraint_graph = Frozen::freeze(constraints.graph(definitions.len()));
@@ -472,6 +501,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
472501
constraints,
473502
constraint_graph,
474503
constraint_sccs,
504+
scc_annotations,
475505
rev_scc_graph: None,
476506
member_constraints,
477507
member_constraints_applied: Vec::new(),
@@ -798,7 +828,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
798828

799829
// If the member region lives in a higher universe, we currently choose
800830
// the most conservative option by leaving it unchanged.
801-
if !self.constraint_sccs().annotation(scc).min_universe().is_root() {
831+
if !self.scc_universe(scc).is_root() {
802832
return;
803833
}
804834

@@ -874,8 +904,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
874904
/// in `scc_a`. Used during constraint propagation, and only once
875905
/// the value of `scc_b` has been computed.
876906
fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
877-
let a_annotation = self.constraint_sccs().annotation(scc_a);
878-
let b_annotation = self.constraint_sccs().annotation(scc_b);
907+
let a_annotation = self.scc_annotations[scc_a];
908+
let b_annotation = self.scc_annotations[scc_b];
879909
let a_universe = a_annotation.min_universe();
880910

881911
// If scc_b's declared universe is a subset of
@@ -991,7 +1021,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
9911021
"lower_bound = {:?} r_scc={:?} universe={:?}",
9921022
lower_bound,
9931023
r_scc,
994-
self.constraint_sccs.annotation(r_scc).min_universe()
1024+
self.scc_universe(r_scc)
9951025
);
9961026
// If the type test requires that `T: 'a` where `'a` is a
9971027
// placeholder from another universe, that effectively requires
@@ -1472,7 +1502,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
14721502
/// The minimum universe of any variable reachable from this
14731503
/// SCC, inside or outside of it.
14741504
fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex {
1475-
self.constraint_sccs().annotation(scc).min_universe()
1505+
self.scc_annotations[scc].min_universe()
14761506
}
14771507

14781508
/// Checks the final value for the free region `fr` to see if it
@@ -2216,7 +2246,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
22162246
/// they *must* be equal (though not having the same repr does not
22172247
/// mean they are unequal).
22182248
fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid {
2219-
self.constraint_sccs.annotation(scc).representative
2249+
self.scc_annotations[scc].representative
22202250
}
22212251

22222252
pub(crate) fn liveness_constraints(&self) -> &LivenessValues {

‎compiler/rustc_data_structures/src/graph/scc/mod.rs

Lines changed: 74 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
1111
use std::assert_matches::debug_assert_matches;
1212
use std::fmt::Debug;
13+
use std::marker::PhantomData;
1314
use std::ops::Range;
1415

1516
use rustc_index::{Idx, IndexSlice, IndexVec};
16-
use tracing::{debug, instrument};
17+
use tracing::{debug, instrument, trace};
1718

1819
use crate::fx::FxHashSet;
1920
use crate::graph::vec_graph::VecGraph;
@@ -48,6 +49,25 @@ pub trait Annotation: Debug + Copy {
4849
}
4950
}
5051

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+
5171
/// The empty annotation, which does nothing.
5272
impl Annotation for () {
5373
fn merge_reached(self, _other: Self) -> Self {
@@ -62,23 +82,20 @@ impl Annotation for () {
6282
/// the index type for the graph nodes and `S` is the index type for
6383
/// the SCCs. We can map from each node to the SCC that it
6484
/// 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> {
6686
/// For each node, what is the SCC index of the SCC to which it
6787
/// belongs.
6888
scc_indices: IndexVec<N, S>,
6989

7090
/// Data about all the SCCs.
71-
scc_data: SccData<S, A>,
91+
scc_data: SccData<S>,
7292
}
7393

7494
/// Information about an invidividual SCC node.
75-
struct SccDetails<A: Annotation> {
95+
struct SccDetails {
7696
/// For this SCC, the range of `all_successors` where its
7797
/// successors can be found.
7898
range: Range<usize>,
79-
80-
/// User-specified metadata about the SCC.
81-
annotation: A,
8299
}
83100

84101
// The name of this struct should discourage you from making it public and leaking
@@ -87,35 +104,29 @@ struct SccDetails<A: Annotation> {
87104
// is difficult when it's publicly inspectable.
88105
//
89106
// Obey the law of Demeter!
90-
struct SccData<S: Idx, A: Annotation> {
107+
struct SccData<S: Idx> {
91108
/// Maps SCC indices to their metadata, including
92109
/// offsets into `all_successors`.
93-
scc_details: IndexVec<S, SccDetails<A>>,
110+
scc_details: IndexVec<S, SccDetails>,
94111

95112
/// Contains the successors for all the Sccs, concatenated. The
96113
/// range of indices corresponding to a given SCC is found in its
97114
/// `scc_details.range`.
98115
all_successors: Vec<S>,
99116
}
100117

101-
impl<N: Idx, S: Idx + Ord> Sccs<N, S, ()> {
118+
impl<N: Idx, S: Idx + Ord> Sccs<N, S> {
102119
/// Compute SCCs without annotations.
103120
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>))
105122
}
106-
}
107123

108-
impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> {
109124
/// 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>>(
111126
graph: &impl Successors<Node = N>,
112-
to_annotation: F,
127+
annotations: &mut A,
113128
) -> 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)
119130
}
120131

121132
pub fn scc_indices(&self) -> &IndexSlice<N, S> {
@@ -160,27 +171,27 @@ impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> {
160171
}
161172
}
162173

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> {
164175
type Node = S;
165176

166177
fn num_nodes(&self) -> usize {
167178
self.num_sccs()
168179
}
169180
}
170181

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> {
172183
fn num_edges(&self) -> usize {
173184
self.scc_data.all_successors.len()
174185
}
175186
}
176187

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> {
178189
fn successors(&self, node: S) -> impl Iterator<Item = Self::Node> {
179190
self.successors(node).iter().cloned()
180191
}
181192
}
182193

183-
impl<S: Idx, A: Annotation> SccData<S, A> {
194+
impl<S: Idx> SccData<S> {
184195
/// Number of SCCs,
185196
fn len(&self) -> usize {
186197
self.scc_details.len()
@@ -192,45 +203,37 @@ impl<S: Idx, A: Annotation> SccData<S, A> {
192203
}
193204

194205
/// Creates a new SCC with `successors` as its successors and
195-
/// the maximum weight of its internal nodes `scc_max_weight` and
196206
/// 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 {
198208
// Store the successors on `scc_successors_vec`, remembering
199209
// the range of indices.
200210
let all_successors_start = self.all_successors.len();
201211
self.all_successors.extend(successors);
202212
let all_successors_end = self.all_successors.len();
203213

204214
debug!(
205-
"create_scc({:?}) successors={:?}, annotation={:?}",
215+
"create_scc({:?}) successors={:?}",
206216
self.len(),
207217
&self.all_successors[all_successors_start..all_successors_end],
208-
annotation
209218
);
210219

211220
let range = all_successors_start..all_successors_end;
212-
let metadata = SccDetails { range, annotation };
221+
let metadata = SccDetails { range };
213222
self.scc_details.push(metadata)
214223
}
215-
216-
fn annotation(&self, scc: S) -> A {
217-
self.scc_details[scc].annotation
218-
}
219224
}
220225

221-
struct SccsConstruction<'c, G, S, A, F>
226+
struct SccsConstruction<'c, 'a, G, A>
222227
where
223228
G: DirectedGraph + Successors,
224-
S: Idx,
225-
A: Annotation,
226-
F: Fn(G::Node) -> A,
229+
A: Annotations<G::Node>,
227230
{
228231
graph: &'c G,
229232

230233
/// The state of each node; used during walk to record the stack
231234
/// and after walk to record what cycle each node ended up being
232235
/// 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>>,
234237

235238
/// The stack of nodes that we are visiting as part of the DFS.
236239
node_stack: Vec<G::Node>,
@@ -239,23 +242,21 @@ where
239242
/// position in this stack, and when we encounter a successor SCC,
240243
/// we push it on the stack. When we complete an SCC, we can pop
241244
/// everything off the stack that was found along the way.
242-
successors_stack: Vec<S>,
245+
successors_stack: Vec<A::SccIdx>,
243246

244247
/// A set used to strip duplicates. As we accumulate successors
245248
/// into the successors_stack, we sometimes get duplicate entries.
246249
/// We use this set to remove those -- we also keep its storage
247250
/// around between successors to amortize memory allocation costs.
248-
duplicate_set: FxHashSet<S>,
251+
duplicate_set: FxHashSet<A::SccIdx>,
249252

250-
scc_data: SccData<S, A>,
253+
scc_data: SccData<A::SccIdx>,
251254

252-
/// A function that constructs an initial SCC annotation
253-
/// out of a single node.
254-
to_annotation: F,
255+
annotations: &'a mut A,
255256
}
256257

257258
#[derive(Copy, Clone, Debug)]
258-
enum NodeState<N, S, A> {
259+
enum NodeState<N, S, A: Annotation> {
259260
/// This node has not yet been visited as part of the DFS.
260261
///
261262
/// After SCC construction is complete, this state ought to be
@@ -286,7 +287,7 @@ enum NodeState<N, S, A> {
286287

287288
/// The state of walking a given node.
288289
#[derive(Copy, Clone, Debug)]
289-
enum WalkReturn<S, A> {
290+
enum WalkReturn<S, A: Annotation> {
290291
/// The walk found a cycle, but the entire component is not known to have
291292
/// been fully walked yet. We only know the minimum depth of this
292293
/// component in a minimum spanning tree of the graph. This component
@@ -299,12 +300,10 @@ enum WalkReturn<S, A> {
299300
Complete { scc_index: S, annotation: A },
300301
}
301302

302-
impl<'c, G, S, A, F> SccsConstruction<'c, G, S, A, F>
303+
impl<'c, 'a, G, A> SccsConstruction<'c, 'a, G, A>
303304
where
304305
G: DirectedGraph + Successors,
305-
S: Idx,
306-
F: Fn(G::Node) -> A,
307-
A: Annotation,
306+
A: Annotations<G::Node>,
308307
{
309308
/// Identifies SCCs in the graph `G` and computes the resulting
310309
/// DAG. This uses a variant of [Tarjan's
@@ -320,7 +319,7 @@ where
320319
/// Additionally, we keep track of a current annotation of the SCC.
321320
///
322321
/// [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> {
324323
let num_nodes = graph.num_nodes();
325324

326325
let mut this = Self {
@@ -330,7 +329,7 @@ where
330329
successors_stack: Vec::new(),
331330
scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() },
332331
duplicate_set: FxHashSet::default(),
333-
to_annotation,
332+
annotations,
334333
};
335334

336335
let scc_indices = graph
@@ -346,7 +345,7 @@ where
346345
Sccs { scc_indices, scc_data: this.scc_data }
347346
}
348347

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> {
350349
self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node))
351350
}
352351

@@ -362,7 +361,7 @@ where
362361
/// Otherwise, we are looking at a node that has already been
363362
/// completely visited. We therefore return `WalkReturn::Complete`
364363
/// 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>> {
366365
Some(match self.find_state(node) {
367366
NodeState::InCycle { scc_index, annotation } => {
368367
WalkReturn::Complete { scc_index, annotation }
@@ -385,7 +384,7 @@ where
385384
/// of `r2` (and updates `r` to reflect current result). This is
386385
/// basically the "find" part of a standard union-find algorithm
387386
/// (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> {
389388
// To avoid recursion we temporarily reuse the `parent` of each
390389
// InCycleWith link to encode a downwards link while compressing
391390
// the path. After we have found the root or deepest node being
@@ -408,7 +407,7 @@ where
408407
// a potentially derived version of the root state for non-root nodes in the chain.
409408
let (root_state, assigned_state) = {
410409
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]);
412411
match self.node_states[node] {
413412
// This must have been the first and only state since it is unexplored*;
414413
// no update needed! * Unless there is a bug :')
@@ -482,7 +481,7 @@ where
482481
if previous_node == node {
483482
return root_state;
484483
}
485-
debug!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}");
484+
trace!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}");
486485

487486
// Update to previous node in the link.
488487
match self.node_states[previous_node] {
@@ -507,9 +506,9 @@ where
507506
/// Call this method when `inspect_node` has returned `None`. Having the
508507
/// caller decide avoids mutual recursion between the two methods and allows
509508
/// 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:?}");
513512
struct VisitingNodeFrame<G: DirectedGraph, Successors, A> {
514513
node: G::Node,
515514
successors: Option<Successors>,
@@ -537,7 +536,7 @@ where
537536
successors_len: 0,
538537
min_cycle_root: initial,
539538
successor_node: initial,
540-
current_component_annotation: (self.to_annotation)(initial),
539+
current_component_annotation: self.annotations.new(initial),
541540
}];
542541

543542
let mut return_value = None;
@@ -556,19 +555,15 @@ where
556555
let node = *node;
557556
let depth = *depth;
558557

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!(
564559
"Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}"
565560
);
566561

567562
let successors = match successors {
568563
Some(successors) => successors,
569564
None => {
570565
// This None marks that we still have the initialize this node's frame.
571-
debug!(?depth, ?node);
566+
trace!(?depth, ?node);
572567

573568
debug_assert_matches!(self.node_states[node], NodeState::NotVisited);
574569

@@ -598,7 +593,7 @@ where
598593
return_value.take().into_iter().map(|walk| (*successor_node, Some(walk)));
599594

600595
let successor_walk = successors.map(|successor_node| {
601-
debug!(?node, ?successor_node);
596+
trace!(?node, ?successor_node);
602597
(successor_node, self.inspect_node(successor_node))
603598
});
604599
for (successor_node, walk) in returned_walk.chain(successor_walk) {
@@ -609,13 +604,13 @@ where
609604
min_depth: successor_min_depth,
610605
annotation: successor_annotation,
611606
}) => {
612-
debug!(
607+
trace!(
613608
"Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}"
614609
);
615610
// Track the minimum depth we can reach.
616611
assert!(successor_min_depth <= depth);
617612
if successor_min_depth < *min_depth {
618-
debug!(?node, ?successor_min_depth);
613+
trace!(?node, ?successor_min_depth);
619614
*min_depth = successor_min_depth;
620615
*min_cycle_root = successor_node;
621616
}
@@ -627,20 +622,20 @@ where
627622
scc_index: successor_scc_index,
628623
annotation: successor_annotation,
629624
}) => {
630-
debug!(
625+
trace!(
631626
"Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}"
632627
);
633628
// Push the completed SCC indices onto
634629
// the `successors_stack` for later.
635-
debug!(?node, ?successor_scc_index);
630+
trace!(?node, ?successor_scc_index);
636631
successors_stack.push(successor_scc_index);
637632
current_component_annotation.update_reachable(successor_annotation);
638633
}
639634
// `node` has no more (direct) successors; search recursively.
640635
None => {
641636
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);
644639
// Remember which node the return value will come from.
645640
frame.successor_node = successor_node;
646641
// Start a new stack frame, then step into it.
@@ -652,14 +647,14 @@ where
652647
min_depth: depth,
653648
min_cycle_root: successor_node,
654649
successor_node,
655-
current_component_annotation: (self.to_annotation)(successor_node),
650+
current_component_annotation: self.annotations.new(successor_node),
656651
});
657652
continue 'recurse;
658653
}
659654
}
660655
}
661656

662-
debug!("Finished walk from {node:?} with annotation: {current_component_annotation:?}");
657+
trace!("Finished walk from {node:?} with annotation: {current_component_annotation:?}");
663658

664659
// Completed walk, remove `node` from the stack.
665660
let r = self.node_stack.pop();
@@ -691,8 +686,9 @@ where
691686

692687
debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node);
693688

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);
696692

697693
self.node_states[node] =
698694
NodeState::InCycle { scc_index, annotation: current_component_annotation };

‎compiler/rustc_data_structures/src/graph/scc/tests.rs

Lines changed: 112 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,39 @@ use crate::graph::tests::TestGraph;
55

66
#[derive(Copy, Clone, Debug)]
77
struct MaxReached(usize);
8-
type UsizeSccs = Sccs<usize, usize, ()>;
9-
type MaxReachedSccs = Sccs<usize, usize, MaxReached>;
8+
struct Maxes(IndexVec<usize, MaxReached>, fn(usize) -> usize);
9+
type UsizeSccs = Sccs<usize, usize>;
10+
11+
impl Annotations<usize> for Maxes {
12+
fn new(&self, element: usize) -> MaxReached {
13+
MaxReached(self.1(element))
14+
}
15+
16+
fn annotate_scc(&mut self, scc: usize, annotation: MaxReached) {
17+
let i = self.0.push(annotation);
18+
assert!(i == scc);
19+
}
20+
21+
type Ann = MaxReached;
22+
type SccIdx = usize;
23+
}
24+
25+
impl Maxes {
26+
fn annotation(&self, scc: usize) -> MaxReached {
27+
self.0[scc]
28+
}
29+
fn new(mapping: fn(usize) -> usize) -> Self {
30+
Self(IndexVec::new(), mapping)
31+
}
32+
}
1033

1134
impl Annotation for MaxReached {
1235
fn merge_scc(self, other: Self) -> Self {
1336
Self(std::cmp::max(other.0, self.0))
1437
}
1538

1639
fn merge_reached(self, other: Self) -> Self {
17-
self.merge_scc(other)
40+
Self(std::cmp::max(other.0, self.0))
1841
}
1942
}
2043

@@ -24,17 +47,32 @@ impl PartialEq<usize> for MaxReached {
2447
}
2548
}
2649

27-
impl MaxReached {
28-
fn from_usize(nr: usize) -> Self {
29-
Self(nr)
30-
}
31-
}
32-
3350
#[derive(Copy, Clone, Debug)]
3451
struct MinMaxIn {
3552
min: usize,
3653
max: usize,
3754
}
55+
struct MinMaxes(IndexVec<usize, MinMaxIn>, fn(usize) -> MinMaxIn);
56+
57+
impl MinMaxes {
58+
fn annotation(&self, scc: usize) -> MinMaxIn {
59+
self.0[scc]
60+
}
61+
}
62+
63+
impl Annotations<usize> for MinMaxes {
64+
fn new(&self, element: usize) -> MinMaxIn {
65+
self.1(element)
66+
}
67+
68+
fn annotate_scc(&mut self, scc: usize, annotation: MinMaxIn) {
69+
let i = self.0.push(annotation);
70+
assert!(i == scc);
71+
}
72+
73+
type Ann = MinMaxIn;
74+
type SccIdx = usize;
75+
}
3876

3977
impl Annotation for MinMaxIn {
4078
fn merge_scc(self, other: Self) -> Self {
@@ -261,67 +299,68 @@ fn bench_sccc(b: &mut test::Bencher) {
261299
#[test]
262300
fn test_max_self_loop() {
263301
let graph = TestGraph::new(0, &[(0, 0)]);
264-
let sccs: MaxReachedSccs =
265-
Sccs::new_with_annotation(&graph, |n| if n == 0 { MaxReached(17) } else { MaxReached(0) });
266-
assert_eq!(sccs.annotation(0), 17);
302+
let mut annotations = Maxes(IndexVec::new(), |n| if n == 0 { 17 } else { 0 });
303+
Sccs::new_with_annotation(&graph, &mut annotations);
304+
assert_eq!(annotations.0[0], 17);
267305
}
268306

269307
#[test]
270308
fn test_max_branch() {
271309
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]);
272-
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
273-
assert_eq!(sccs.annotation(sccs.scc(0)), 4);
274-
assert_eq!(sccs.annotation(sccs.scc(1)), 3);
275-
assert_eq!(sccs.annotation(sccs.scc(2)), 4);
310+
let mut annotations = Maxes(IndexVec::new(), |n| n);
311+
let sccs = Sccs::new_with_annotation(&graph, &mut annotations);
312+
assert_eq!(annotations.0[sccs.scc(0)], 4);
313+
assert_eq!(annotations.0[sccs.scc(1)], 3);
314+
assert_eq!(annotations.0[sccs.scc(2)], 4);
276315
}
316+
277317
#[test]
278318
fn test_single_cycle_max() {
279319
let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]);
280-
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
281-
assert_eq!(sccs.annotation(sccs.scc(2)), 4);
282-
assert_eq!(sccs.annotation(sccs.scc(0)), 4);
283-
}
284-
285-
#[test]
286-
fn test_simple_cycle_max() {
287-
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 0)]);
288-
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
289-
assert_eq!(sccs.num_sccs(), 1);
320+
let mut annotations = Maxes(IndexVec::new(), |n| n);
321+
let sccs = Sccs::new_with_annotation(&graph, &mut annotations);
322+
assert_eq!(annotations.0[sccs.scc(2)], 4);
323+
assert_eq!(annotations.0[sccs.scc(0)], 4);
290324
}
291325

292326
#[test]
293327
fn test_double_cycle_max() {
294328
let graph =
295329
TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]);
296-
let sccs: MaxReachedSccs =
297-
Sccs::new_with_annotation(&graph, |n| if n == 5 { MaxReached(2) } else { MaxReached(1) });
330+
let mut annotations = Maxes(IndexVec::new(), |n| if n == 5 { 2 } else { 1 });
331+
332+
let sccs = Sccs::new_with_annotation(&graph, &mut annotations);
298333

299-
assert_eq!(sccs.annotation(sccs.scc(0)).0, 2);
334+
assert_eq!(annotations.0[sccs.scc(0)].0, 2);
300335
}
301336

302337
#[test]
303338
fn test_bug_minimised() {
304339
let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]);
305-
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |n| match n {
306-
3 => MaxReached(1),
307-
_ => MaxReached(0),
340+
let mut annotations = Maxes(IndexVec::new(), |n| match n {
341+
3 => 1,
342+
_ => 0,
308343
});
309-
assert_eq!(sccs.annotation(sccs.scc(2)), 1);
310-
assert_eq!(sccs.annotation(sccs.scc(1)), 0);
311-
assert_eq!(sccs.annotation(sccs.scc(4)), 0);
344+
345+
let sccs = Sccs::new_with_annotation(&graph, &mut annotations);
346+
assert_eq!(annotations.annotation(sccs.scc(2)), 1);
347+
assert_eq!(annotations.annotation(sccs.scc(1)), 0);
348+
assert_eq!(annotations.annotation(sccs.scc(4)), 0);
312349
}
313350

314351
#[test]
315352
fn test_bug_max_leak_minimised() {
316353
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]);
317-
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
318-
4 => MaxReached(1),
319-
_ => MaxReached(0),
354+
let mut annotations = Maxes(IndexVec::new(), |w| match w {
355+
4 => 1,
356+
_ => 0,
320357
});
321358

322-
assert_eq!(sccs.annotation(sccs.scc(2)), 0);
323-
assert_eq!(sccs.annotation(sccs.scc(3)), 1);
324-
assert_eq!(sccs.annotation(sccs.scc(0)), 1);
359+
let sccs = Sccs::new_with_annotation(&graph, &mut annotations);
360+
361+
assert_eq!(annotations.annotation(sccs.scc(2)), 0);
362+
assert_eq!(annotations.annotation(sccs.scc(3)), 1);
363+
assert_eq!(annotations.annotation(sccs.scc(0)), 1);
325364
}
326365

327366
#[test]
@@ -369,48 +408,49 @@ fn test_bug_max_leak() {
369408
(23, 24),
370409
],
371410
);
372-
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
373-
22 => MaxReached(1),
374-
24 => MaxReached(2),
375-
27 => MaxReached(2),
376-
_ => MaxReached(0),
411+
let mut annotations = Maxes::new(|w| match w {
412+
22 => 1,
413+
24 => 2,
414+
27 => 2,
415+
_ => 0,
377416
});
378-
379-
assert_eq!(sccs.annotation(sccs.scc(2)), 0);
380-
assert_eq!(sccs.annotation(sccs.scc(7)), 0);
381-
assert_eq!(sccs.annotation(sccs.scc(8)), 2);
382-
assert_eq!(sccs.annotation(sccs.scc(23)), 2);
383-
assert_eq!(sccs.annotation(sccs.scc(3)), 2);
384-
assert_eq!(sccs.annotation(sccs.scc(0)), 2);
417+
let sccs = Sccs::new_with_annotation(&graph, &mut annotations);
418+
419+
assert_eq!(annotations.annotation(sccs.scc(2)), 0);
420+
assert_eq!(annotations.annotation(sccs.scc(7)), 0);
421+
assert_eq!(annotations.annotation(sccs.scc(8)), 2);
422+
assert_eq!(annotations.annotation(sccs.scc(23)), 2);
423+
assert_eq!(annotations.annotation(sccs.scc(3)), 2);
424+
assert_eq!(annotations.annotation(sccs.scc(0)), 2);
385425
}
386426

387427
#[test]
388428
fn test_bug_max_zero_stick_shape() {
389429
let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]);
390-
391-
let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
392-
4 => MaxReached(1),
393-
_ => MaxReached(0),
430+
let mut annotations = Maxes::new(|w| match w {
431+
4 => 1,
432+
_ => 0,
394433
});
434+
let sccs = Sccs::new_with_annotation(&graph, &mut annotations);
395435

396-
assert_eq!(sccs.annotation(sccs.scc(0)), 1);
397-
assert_eq!(sccs.annotation(sccs.scc(1)), 1);
398-
assert_eq!(sccs.annotation(sccs.scc(2)), 1);
399-
assert_eq!(sccs.annotation(sccs.scc(3)), 1);
400-
assert_eq!(sccs.annotation(sccs.scc(4)), 1);
436+
assert_eq!(annotations.annotation(sccs.scc(0)), 1);
437+
assert_eq!(annotations.annotation(sccs.scc(1)), 1);
438+
assert_eq!(annotations.annotation(sccs.scc(2)), 1);
439+
assert_eq!(annotations.annotation(sccs.scc(3)), 1);
440+
assert_eq!(annotations.annotation(sccs.scc(4)), 1);
401441
}
402442

403443
#[test]
404444
fn test_min_max_in() {
405445
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]);
406-
let sccs: Sccs<usize, usize, MinMaxIn> =
407-
Sccs::new_with_annotation(&graph, |w| MinMaxIn { min: w, max: w });
408-
409-
assert_eq!(sccs.annotation(sccs.scc(2)).min, 2);
410-
assert_eq!(sccs.annotation(sccs.scc(2)).max, 2);
411-
assert_eq!(sccs.annotation(sccs.scc(0)).min, 0);
412-
assert_eq!(sccs.annotation(sccs.scc(0)).max, 4);
413-
assert_eq!(sccs.annotation(sccs.scc(3)).min, 0);
414-
assert_eq!(sccs.annotation(sccs.scc(3)).max, 4);
415-
assert_eq!(sccs.annotation(sccs.scc(5)).min, 5);
446+
let mut annotations = MinMaxes(IndexVec::new(), |w| MinMaxIn { min: w, max: w });
447+
let sccs = Sccs::new_with_annotation(&graph, &mut annotations);
448+
449+
assert_eq!(annotations.annotation(sccs.scc(2)).min, 2);
450+
assert_eq!(annotations.annotation(sccs.scc(2)).max, 2);
451+
assert_eq!(annotations.annotation(sccs.scc(0)).min, 0);
452+
assert_eq!(annotations.annotation(sccs.scc(0)).max, 4);
453+
assert_eq!(annotations.annotation(sccs.scc(3)).min, 0);
454+
assert_eq!(annotations.annotation(sccs.scc(3)).max, 4);
455+
assert_eq!(annotations.annotation(sccs.scc(5)).min, 5);
416456
}

0 commit comments

Comments
 (0)
Please sign in to comment.