1//! Routine to compute the strongly connected components (SCCs) of a graph.
2//!
3//! Also computes as the resulting DAG if each SCC is replaced with a
4//! node in the graph. This uses [Tarjan's algorithm](
5//! https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)
6//! that completes in *O*(*n*) time.
7//! Optionally, also annotate the SCC nodes with some commutative data.
8//! Typical examples would include: minimum element in SCC, maximum element
9//! reachable from it, etc.
1011use std::assert_matches::debug_assert_matches;
12use std::fmt::Debug;
13use std::marker::PhantomData;
14use std::ops::Range;
1516use rustc_index::{Idx, IndexSlice, IndexVec};
17use tracing::{debug, instrument, trace};
1819use crate::fx::FxHashSet;
20use crate::graph::vec_graph::VecGraph;
21use crate::graph::{DirectedGraph, NumEdges, Successors};
2223#[cfg(test)]
24mod tests;
2526/// An annotation for an SCC. This can be a representative,
27/// the max/min element of the SCC, or all of the above.
28///
29/// Concretely, the both merge operations must commute, e.g. where `merge`
30/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)`
31///
32/// In general, what you want is probably always min/max according
33/// to some ordering, potentially with side constraints (min x such
34/// that P holds).
35pub trait Annotation: Debug + Copy {
36/// Merge two existing annotations into one during
37 /// path compression.o
38fn merge_scc(self, other: Self) -> Self;
3940/// Merge a successor into this annotation.
41fn merge_reached(self, other: Self) -> Self;
4243fn update_scc(&mut self, other: Self) {
44*self = self.merge_scc(other)
45 }
4647fn update_reachable(&mut self, other: Self) {
48*self = self.merge_reached(other)
49 }
50}
5152/// An accumulator for annotations.
53pub trait Annotations<N: Idx> {
54type Ann: Annotation;
55type SccIdx: Idx + Ord;
5657fn new(&self, element: N) -> Self::Ann;
58fn annotate_scc(&mut self, scc: Self::SccIdx, annotation: Self::Ann);
59}
6061/// The nil annotation accumulator, which does nothing.
62struct NoAnnotations<S: Idx + Ord>(PhantomData<S>);
6364impl<N: Idx, S: Idx + Ord> Annotations<N> for NoAnnotations<S> {
65type SccIdx = S;
66type Ann = ();
67fn new(&self, _element: N) {}
68fn annotate_scc(&mut self, _scc: S, _annotation: ()) {}
69}
7071/// The empty annotation, which does nothing.
72impl Annotationfor () {
73fn merge_reached(self, _other: Self) -> Self {
74 ()
75 }
76fn merge_scc(self, _other: Self) -> Self {
77 ()
78 }
79}
8081/// Strongly connected components (SCC) of a graph. The type `N` is
82/// the index type for the graph nodes and `S` is the index type for
83/// the SCCs. We can map from each node to the SCC that it
84/// participates in, and we also have the successors of each SCC.
85pub struct Sccs<N: Idx, S: Idx> {
86/// For each node, what is the SCC index of the SCC to which it
87 /// belongs.
88scc_indices: IndexVec<N, S>,
8990/// Data about all the SCCs.
91scc_data: SccData<S>,
92}
9394/// Information about an invidividual SCC node.
95struct SccDetails {
96/// For this SCC, the range of `all_successors` where its
97 /// successors can be found.
98range: Range<usize>,
99}
100101// The name of this struct should discourage you from making it public and leaking
102// its representation. This message was left here by one who came before you,
103// who learnt the hard way that making even small changes in representation
104// is difficult when it's publicly inspectable.
105//
106// Obey the law of Demeter!
107struct SccData<S: Idx> {
108/// Maps SCC indices to their metadata, including
109 /// offsets into `all_successors`.
110scc_details: IndexVec<S, SccDetails>,
111112/// Contains the successors for all the Sccs, concatenated. The
113 /// range of indices corresponding to a given SCC is found in its
114 /// `scc_details.range`.
115all_successors: Vec<S>,
116}
117118impl<N: Idx, S: Idx + Ord> Sccs<N, S> {
119/// Compute SCCs without annotations.
120pub fn new(graph: &impl Successors<Node = N>) -> Self {
121Self::new_with_annotation(graph, &mut NoAnnotations(PhantomData::<S>))
122 }
123124/// Compute SCCs and annotate them with a user-supplied annotation
125pub fn new_with_annotation<A: Annotations<N, SccIdx = S>>(
126 graph: &impl Successors<Node = N>,
127 annotations: &mut A,
128 ) -> Self {
129SccsConstruction::construct(graph, annotations)
130 }
131132pub fn scc_indices(&self) -> &IndexSlice<N, S> {
133&self.scc_indices
134 }
135136/// Returns the number of SCCs in the graph.
137pub fn num_sccs(&self) -> usize {
138self.scc_data.len()
139 }
140141/// Returns an iterator over the SCCs in the graph.
142 ///
143 /// The SCCs will be iterated in **dependency order** (or **post order**),
144 /// meaning that if `S1 -> S2`, we will visit `S2` first and `S1` after.
145 /// This is convenient when the edges represent dependencies: when you visit
146 /// `S1`, the value for `S2` will already have been computed.
147pub fn all_sccs(&self) -> impl Iterator<Item = S> + 'static {
148 (0..self.scc_data.len()).map(S::new)
149 }
150151/// Returns the SCC to which a node `r` belongs.
152pub fn scc(&self, r: N) -> S {
153self.scc_indices[r]
154 }
155156/// Returns the successors of the given SCC.
157pub fn successors(&self, scc: S) -> &[S] {
158self.scc_data.successors(scc)
159 }
160161/// Construct the reverse graph of the SCC graph.
162pub fn reverse(&self) -> VecGraph<S> {
163VecGraph::new(
164self.num_sccs(),
165self.all_sccs()
166 .flat_map(|source| {
167self.successors(source).iter().map(move |&target| (target, source))
168 })
169 .collect(),
170 )
171 }
172}
173174impl<N: Idx, S: Idx + Ord> DirectedGraphfor Sccs<N, S> {
175type Node = S;
176177fn num_nodes(&self) -> usize {
178self.num_sccs()
179 }
180}
181182impl<N: Idx, S: Idx + Ord> NumEdgesfor Sccs<N, S> {
183fn num_edges(&self) -> usize {
184self.scc_data.all_successors.len()
185 }
186}
187188impl<N: Idx, S: Idx + Ord> Successorsfor Sccs<N, S> {
189fn successors(&self, node: S) -> impl Iterator<Item = Self::Node> {
190self.successors(node).iter().cloned()
191 }
192}
193194impl<S: Idx> SccData<S> {
195/// Number of SCCs,
196fn len(&self) -> usize {
197self.scc_details.len()
198 }
199200/// Returns the successors of the given SCC.
201fn successors(&self, scc: S) -> &[S] {
202&self.all_successors[self.scc_details[scc].range.clone()]
203 }
204205/// Creates a new SCC with `successors` as its successors and
206 /// returns the resulting index.
207fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S {
208// Store the successors on `scc_successors_vec`, remembering
209 // the range of indices.
210let all_successors_start = self.all_successors.len();
211self.all_successors.extend(successors);
212let all_successors_end = self.all_successors.len();
213214{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:214",
"rustc_data_structures::graph::scc",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(214u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("create_scc({0:?}) successors={1:?}",
self.len(),
&self.all_successors[all_successors_start..all_successors_end])
as &dyn Value))])
});
} else { ; }
};debug!(
215"create_scc({:?}) successors={:?}",
216self.len(),
217&self.all_successors[all_successors_start..all_successors_end],
218 );
219220let range = all_successors_start..all_successors_end;
221let metadata = SccDetails { range };
222self.scc_details.push(metadata)
223 }
224}
225226struct SccsConstruction<'c, 'a, G, A>
227where
228G: DirectedGraph + Successors,
229 A: Annotations<G::Node>,
230{
231 graph: &'c G,
232233/// The state of each node; used during walk to record the stack
234 /// and after walk to record what cycle each node ended up being
235 /// in.
236node_states: IndexVec<G::Node, NodeState<G::Node, A::SccIdx, A::Ann>>,
237238/// The stack of nodes that we are visiting as part of the DFS.
239node_stack: Vec<G::Node>,
240241/// The stack of successors: as we visit a node, we mark our
242 /// position in this stack, and when we encounter a successor SCC,
243 /// we push it on the stack. When we complete an SCC, we can pop
244 /// everything off the stack that was found along the way.
245successors_stack: Vec<A::SccIdx>,
246247/// A set used to strip duplicates. As we accumulate successors
248 /// into the successors_stack, we sometimes get duplicate entries.
249 /// We use this set to remove those -- we also keep its storage
250 /// around between successors to amortize memory allocation costs.
251duplicate_set: FxHashSet<A::SccIdx>,
252253 scc_data: SccData<A::SccIdx>,
254255 annotations: &'a mut A,
256}
257258#[derive(#[automatically_derived]
impl<N: ::core::marker::Copy, S: ::core::marker::Copy,
A: ::core::marker::Copy + Annotation> ::core::marker::Copy for
NodeState<N, S, A> {
}Copy, #[automatically_derived]
impl<N: ::core::clone::Clone, S: ::core::clone::Clone,
A: ::core::clone::Clone + Annotation> ::core::clone::Clone for
NodeState<N, S, A> {
#[inline]
fn clone(&self) -> NodeState<N, S, A> {
match self {
NodeState::NotVisited => NodeState::NotVisited,
NodeState::BeingVisited { depth: __self_0, annotation: __self_1 }
=>
NodeState::BeingVisited {
depth: ::core::clone::Clone::clone(__self_0),
annotation: ::core::clone::Clone::clone(__self_1),
},
NodeState::InCycle { scc_index: __self_0, annotation: __self_1 }
=>
NodeState::InCycle {
scc_index: ::core::clone::Clone::clone(__self_0),
annotation: ::core::clone::Clone::clone(__self_1),
},
NodeState::InCycleWith { parent: __self_0 } =>
NodeState::InCycleWith {
parent: ::core::clone::Clone::clone(__self_0),
},
}
}
}Clone, #[automatically_derived]
impl<N: ::core::fmt::Debug, S: ::core::fmt::Debug, A: ::core::fmt::Debug +
Annotation> ::core::fmt::Debug for NodeState<N, S, A> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
NodeState::NotVisited =>
::core::fmt::Formatter::write_str(f, "NotVisited"),
NodeState::BeingVisited { depth: __self_0, annotation: __self_1 }
=>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"BeingVisited", "depth", __self_0, "annotation", &__self_1),
NodeState::InCycle { scc_index: __self_0, annotation: __self_1 }
=>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"InCycle", "scc_index", __self_0, "annotation", &__self_1),
NodeState::InCycleWith { parent: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"InCycleWith", "parent", &__self_0),
}
}
}Debug)]
259enum NodeState<N, S, A: Annotation> {
260/// This node has not yet been visited as part of the DFS.
261 ///
262 /// After SCC construction is complete, this state ought to be
263 /// impossible.
264NotVisited,
265266/// This node is currently being walked as part of our DFS. It is on
267 /// the stack at the depth `depth` and its current annotation is
268 /// `annotation`.
269 ///
270 /// After SCC construction is complete, this state ought to be
271 /// impossible.
272BeingVisited { depth: usize, annotation: A },
273274/// Indicates that this node is a member of the given cycle where
275 /// the merged annotation is `annotation`.
276 /// Note that an SCC can have several cycles, so its final annotation
277 /// is the merged value of all its member annotations.
278InCycle { scc_index: S, annotation: A },
279280/// Indicates that this node is a member of whatever cycle
281 /// `parent` is a member of. This state is transient: whenever we
282 /// see it, we try to overwrite it with the current state of
283 /// `parent` (this is the "path compression" step of a union-find
284 /// algorithm).
285InCycleWith { parent: N },
286}
287288/// The state of walking a given node.
289#[derive(#[automatically_derived]
impl<S: ::core::marker::Copy, A: ::core::marker::Copy + Annotation>
::core::marker::Copy for WalkReturn<S, A> {
}Copy, #[automatically_derived]
impl<S: ::core::clone::Clone, A: ::core::clone::Clone + Annotation>
::core::clone::Clone for WalkReturn<S, A> {
#[inline]
fn clone(&self) -> WalkReturn<S, A> {
match self {
WalkReturn::Cycle { min_depth: __self_0, annotation: __self_1 } =>
WalkReturn::Cycle {
min_depth: ::core::clone::Clone::clone(__self_0),
annotation: ::core::clone::Clone::clone(__self_1),
},
WalkReturn::Complete { scc_index: __self_0, annotation: __self_1 }
=>
WalkReturn::Complete {
scc_index: ::core::clone::Clone::clone(__self_0),
annotation: ::core::clone::Clone::clone(__self_1),
},
}
}
}Clone, #[automatically_derived]
impl<S: ::core::fmt::Debug, A: ::core::fmt::Debug + Annotation>
::core::fmt::Debug for WalkReturn<S, A> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
WalkReturn::Cycle { min_depth: __self_0, annotation: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f, "Cycle",
"min_depth", __self_0, "annotation", &__self_1),
WalkReturn::Complete { scc_index: __self_0, annotation: __self_1 }
=>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"Complete", "scc_index", __self_0, "annotation", &__self_1),
}
}
}Debug)]
290enum WalkReturn<S, A: Annotation> {
291/// The walk found a cycle, but the entire component is not known to have
292 /// been fully walked yet. We only know the minimum depth of this
293 /// component in a minimum spanning tree of the graph. This component
294 /// is tentatively represented by the state of the first node of this
295 /// cycle we met, which is at `min_depth`.
296Cycle { min_depth: usize, annotation: A },
297/// The SCC and everything reachable from it have been fully walked.
298 /// At this point we know what is inside the SCC as we have visited every
299 /// node reachable from it. The SCC can now be fully represented by its ID.
300Complete { scc_index: S, annotation: A },
301}
302303impl<'c, 'a, G, A> SccsConstruction<'c, 'a, G, A>
304where
305G: DirectedGraph + Successors,
306 A: Annotations<G::Node>,
307{
308/// Identifies SCCs in the graph `G` and computes the resulting
309 /// DAG. This uses a variant of [Tarjan's
310 /// algorithm][wikipedia]. The high-level summary of the algorithm
311 /// is that we do a depth-first search. Along the way, we keep a
312 /// stack of each node whose successors are being visited. We
313 /// track the depth of each node on this stack (there is no depth
314 /// if the node is not on the stack). When we find that some node
315 /// N with depth D can reach some other node N' with lower depth
316 /// D' (i.e., D' < D), we know that N, N', and all nodes in
317 /// between them on the stack are part of an SCC.
318 ///
319 /// Additionally, we keep track of a current annotation of the SCC.
320 ///
321 /// [wikipedia]: https://bit.ly/2EZIx84
322fn construct(graph: &'c G, annotations: &'a mut A) -> Sccs<G::Node, A::SccIdx> {
323let num_nodes = graph.num_nodes();
324325let mut this = Self {
326graph,
327 node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes),
328 node_stack: Vec::with_capacity(num_nodes),
329 successors_stack: Vec::new(),
330 scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() },
331 duplicate_set: FxHashSet::default(),
332annotations,
333 };
334335let scc_indices = graph336 .iter_nodes()
337 .map(|node| match this.start_walk_from(node) {
338 WalkReturn::Complete { scc_index, .. } => scc_index,
339 WalkReturn::Cycle { min_depth, .. } => {
340{
::core::panicking::panic_fmt(format_args!("`start_walk_node({0:?})` returned cycle with depth {1:?}",
node, min_depth));
}panic!("`start_walk_node({node:?})` returned cycle with depth {min_depth:?}")341 }
342 })
343 .collect();
344345Sccs { scc_indices, scc_data: this.scc_data }
346 }
347348fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<A::SccIdx, A::Ann> {
349self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node))
350 }
351352/// Inspect a node during the DFS. We first examine its current
353 /// state -- if it is not yet visited (`NotVisited`), return `None` so
354 /// that the caller might push it onto the stack and start walking its
355 /// successors.
356 ///
357 /// If it is already on the DFS stack it will be in the state
358 /// `BeingVisited`. In that case, we have found a cycle and we
359 /// return the depth from the stack.
360 ///
361 /// Otherwise, we are looking at a node that has already been
362 /// completely visited. We therefore return `WalkReturn::Complete`
363 /// with its associated SCC index.
364fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<A::SccIdx, A::Ann>> {
365Some(match self.find_state(node) {
366 NodeState::InCycle { scc_index, annotation } => {
367 WalkReturn::Complete { scc_index, annotation }
368 }
369370 NodeState::BeingVisited { depth: min_depth, annotation } => {
371 WalkReturn::Cycle { min_depth, annotation }
372 }
373374 NodeState::NotVisited => return None,
375376 NodeState::InCycleWith { parent } => {
::core::panicking::panic_fmt(format_args!("`find_state` returned `InCycleWith({0:?})`, which ought to be impossible",
parent));
}panic!(
377"`find_state` returned `InCycleWith({parent:?})`, which ought to be impossible"
378),
379 })
380 }
381382/// Fetches the state of the node `r`. If `r` is recorded as being
383 /// in a cycle with some other node `r2`, then fetches the state
384 /// of `r2` (and updates `r` to reflect current result). This is
385 /// basically the "find" part of a standard union-find algorithm
386 /// (with path compression).
387fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, A::SccIdx, A::Ann> {
388// To avoid recursion we temporarily reuse the `parent` of each
389 // InCycleWith link to encode a downwards link while compressing
390 // the path. After we have found the root or deepest node being
391 // visited, we traverse the reverse links and correct the node
392 // states on the way.
393 //
394 // **Note**: This mutation requires that this is a leaf function
395 // or at least that none of the called functions inspects the
396 // current node states. Luckily, we are a leaf.
397398 // Remember one previous link. The termination condition when
399 // following links downwards is then simply as soon as we have
400 // found the initial self-loop.
401let mut previous_node = node;
402403// Ultimately propagated to all the transitive parents when following
404 // `InCycleWith` upwards.
405 // This loop performs the downward link encoding mentioned above. Details below!
406 // Note that there are two different states being assigned: the root state, and
407 // a potentially derived version of the root state for non-root nodes in the chain.
408let (root_state, assigned_state) = {
409loop {
410{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:410",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(410u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("find_state(r = {1:?} in state {0:?})",
self.node_states[node], node) as &dyn Value))])
});
} else { ; }
};trace!("find_state(r = {node:?} in state {:?})", self.node_states[node]);
411match self.node_states[node] {
412// This must have been the first and only state since it is unexplored*;
413 // no update needed! * Unless there is a bug :')
414s @ NodeState::NotVisited => return s,
415// We are in a completely discovered SCC; every node on our path is in that SCC:
416s @ NodeState::InCycle { .. } => break (s, s),
417// The Interesting Third Base Case: we are a path back to a root node
418 // still being explored. Now we need that node to keep its state and
419 // every other node to be recorded as being in whatever component that
420 // ends up in.
421s @ NodeState::BeingVisited { depth, .. } => {
422break (s, NodeState::InCycleWith { parent: self.node_stack[depth] });
423 }
424// We are not at the head of a path; keep compressing it!
425NodeState::InCycleWith { parent } => {
426// We test this, to be extremely sure that we never
427 // ever break our termination condition for the
428 // reverse iteration loop.
429if !(node != parent) {
{
::core::panicking::panic_fmt(format_args!("Node can not be in cycle with itself"));
}
};assert!(node != parent, "Node can not be in cycle with itself");
430431// Store the previous node as an inverted list link
432self.node_states[node] = NodeState::InCycleWith { parent: previous_node };
433// Update to parent node.
434previous_node = node;
435node = parent;
436 }
437 }
438 }
439 };
440441// The states form a graph where up to one outgoing link is stored at
442 // each node. Initially in general,
443 //
444 // E
445 // ^
446 // |
447 // InCycleWith/BeingVisited/NotVisited
448 // |
449 // A-InCycleWith->B-InCycleWith…>C-InCycleWith->D-+
450 // |
451 // = node, previous_node
452 //
453 // After the first loop, this will look like
454 // E
455 // ^
456 // |
457 // InCycleWith/BeingVisited/NotVisited
458 // |
459 // +>A<-InCycleWith-B<…InCycleWith-C<-InCycleWith-D-+
460 // | | | |
461 // | InCycleWith | = node
462 // +-+ =previous_node
463 //
464 // Note in particular that A will be linked to itself in a self-cycle
465 // and no other self-cycles occur due to how InCycleWith is assigned in
466 // the find phase implemented by `walk_unvisited_node`.
467 //
468 // We now want to compress the path, that is assign the state of the
469 // link D-E to all other links.
470 //
471 // We can then walk backwards, starting from `previous_node`, and assign
472 // each node in the list with the updated state. The loop terminates
473 // when we reach the self-cycle.
474475 // Move backwards until we found the node where we started. We
476 // will know when we hit the state where previous_node == node.
477loop {
478// Back at the beginning, we can return. Note that we return the root state.
479 // This is because for components being explored, we would otherwise get a
480 // `node_state[n] = InCycleWith{ parent: n }` and that's wrong.
481if previous_node == node {
482return root_state;
483 }
484{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:484",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(484u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Compressing {0:?} down to {1:?} with state {2:?}",
node, previous_node, assigned_state) as &dyn Value))])
});
} else { ; }
};trace!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}");
485486// Update to previous node in the link.
487match self.node_states[previous_node] {
488 NodeState::InCycleWith { parent: previous } => {
489node = previous_node;
490previous_node = previous;
491 }
492// Only InCycleWith nodes were added to the reverse linked list.
493 other => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Invalid previous link while compressing cycle: {0:?}",
other)));
}unreachable!("Invalid previous link while compressing cycle: {other:?}"),
494 }
495496// Update the node state to the (potentially derived) state.
497 // If the root is still being explored, this is
498 // `InCycleWith{ parent: <root node>}`, otherwise
499 // `assigned_state == root_state`.
500self.node_states[node] = assigned_state;
501 }
502 }
503504/// Walks a node that has never been visited before.
505 ///
506 /// Call this method when `inspect_node` has returned `None`. Having the
507 /// caller decide avoids mutual recursion between the two methods and allows
508 /// us to maintain an allocated stack for nodes on the path between calls.
509#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("walk_unvisited_node",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(509u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&[],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{ meta.fields().value_set(&[]) })
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: WalkReturn<A::SccIdx, A::Ann> =
loop {};
return __tracing_attr_fake_return;
}
{
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:511",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(511u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Walk unvisited node: {0:?}",
initial) as &dyn Value))])
});
} else { ; }
};
struct VisitingNodeFrame<G: DirectedGraph, Successors, A> {
node: G::Node,
successors: Option<Successors>,
depth: usize,
min_depth: usize,
successors_len: usize,
min_cycle_root: G::Node,
successor_node: G::Node,
#[doc =
" The annotation for the SCC starting in `node`. It may or may"]
#[doc = " not contain other nodes."]
current_component_annotation: A,
}
let mut successors_stack =
core::mem::take(&mut self.successors_stack);
if true {
match (&successors_stack.len(), &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};
let mut stack: Vec<VisitingNodeFrame<G, _, _>> =
<[_]>::into_vec(::alloc::boxed::box_new([VisitingNodeFrame {
node: initial,
depth: 0,
min_depth: 0,
successors: None,
successors_len: 0,
min_cycle_root: initial,
successor_node: initial,
current_component_annotation: self.annotations.new(initial),
}]));
let mut return_value = None;
'recurse:
while let Some(frame) = stack.last_mut() {
let VisitingNodeFrame {
node,
depth,
successors,
successors_len,
min_depth,
min_cycle_root,
successor_node,
current_component_annotation } = frame;
let node = *node;
let depth = *depth;
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:558",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(558u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Visiting {0:?} at depth {1:?}, annotation: {2:?}",
node, depth, current_component_annotation) as &dyn Value))])
});
} else { ; }
};
let successors =
match successors {
Some(successors) => successors,
None => {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:566",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(566u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["depth", "node"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&depth) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&node) as
&dyn Value))])
});
} else { ; }
};
if true {
match self.node_states[node] {
NodeState::NotVisited => {}
ref left_val => {
::core::panicking::assert_matches_failed(left_val,
"NodeState::NotVisited", ::core::option::Option::None);
}
};
};
self.node_states[node] =
NodeState::BeingVisited {
depth,
annotation: *current_component_annotation,
};
self.node_stack.push(node);
*successors_len = successors_stack.len();
successors.get_or_insert(self.graph.successors(node))
}
};
let successors_len = *successors_len;
let returned_walk =
return_value.take().into_iter().map(|walk|
(*successor_node, Some(walk)));
let successor_walk =
successors.map(|successor_node|
{
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:596",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(596u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["node",
"successor_node"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&node) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&successor_node)
as &dyn Value))])
});
} else { ; }
};
(successor_node, self.inspect_node(successor_node))
});
for (successor_node, walk) in
returned_walk.chain(successor_walk) {
match walk {
Some(WalkReturn::Cycle {
min_depth: successor_min_depth,
annotation: successor_annotation }) => {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:607",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(607u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Cycle found from {0:?}, minimum depth: {1:?}, annotation: {2:?}",
node, successor_min_depth, successor_annotation) as
&dyn Value))])
});
} else { ; }
};
if !(successor_min_depth <= depth) {
::core::panicking::panic("assertion failed: successor_min_depth <= depth")
};
if successor_min_depth < *min_depth {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:613",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(613u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["node",
"successor_min_depth"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&node) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&successor_min_depth)
as &dyn Value))])
});
} else { ; }
};
*min_depth = successor_min_depth;
*min_cycle_root = successor_node;
}
current_component_annotation.update_scc(successor_annotation);
}
Some(WalkReturn::Complete {
scc_index: successor_scc_index,
annotation: successor_annotation }) => {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:625",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(625u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Complete; {0:?} is root of complete-visited SCC idx {1:?} with annotation {2:?}",
node, successor_scc_index, successor_annotation) as
&dyn Value))])
});
} else { ; }
};
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:630",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(630u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["node",
"successor_scc_index"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&node) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&successor_scc_index)
as &dyn Value))])
});
} else { ; }
};
successors_stack.push(successor_scc_index);
current_component_annotation.update_reachable(successor_annotation);
}
None => {
let depth = depth + 1;
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:637",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(637u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Recursing down into {0:?} at depth {1:?}",
successor_node, depth) as &dyn Value))])
});
} else { ; }
};
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:638",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(638u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["depth",
"successor_node"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&depth) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&successor_node)
as &dyn Value))])
});
} else { ; }
};
frame.successor_node = successor_node;
stack.push(VisitingNodeFrame {
node: successor_node,
depth,
successors: None,
successors_len: 0,
min_depth: depth,
min_cycle_root: successor_node,
successor_node,
current_component_annotation: self.annotations.new(successor_node),
});
continue 'recurse;
}
}
}
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:657",
"rustc_data_structures::graph::scc",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(657u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Finished walk from {0:?} with annotation: {1:?}",
node, current_component_annotation) as &dyn Value))])
});
} else { ; }
};
let r = self.node_stack.pop();
if true {
match (&r, &Some(node)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};
let frame = stack.pop().unwrap();
let current_component_annotation =
frame.current_component_annotation;
if true {
match (&frame.node, &node) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};
if true {
if !return_value.is_none() {
::core::panicking::panic("assertion failed: return_value.is_none()")
};
};
return_value =
Some(if frame.min_depth == depth {
let deduplicated_successors =
{
let duplicate_set = &mut self.duplicate_set;
duplicate_set.clear();
successors_stack.drain(successors_len..).filter(move |&i|
duplicate_set.insert(i))
};
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_data_structures/src/graph/scc/mod.rs:687",
"rustc_data_structures::graph::scc",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_data_structures/src/graph/scc/mod.rs"),
::tracing_core::__macro_support::Option::Some(687u32),
::tracing_core::__macro_support::Option::Some("rustc_data_structures::graph::scc"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Creating SCC rooted in {1:?} with successor {0:?}",
frame.successor_node, node) as &dyn Value))])
});
} else { ; }
};
let scc_index =
self.scc_data.create_scc(deduplicated_successors);
self.annotations.annotate_scc(scc_index,
current_component_annotation);
self.node_states[node] =
NodeState::InCycle {
scc_index,
annotation: current_component_annotation,
};
WalkReturn::Complete {
scc_index,
annotation: current_component_annotation,
}
} else {
self.node_states[node] =
NodeState::InCycleWith { parent: frame.min_cycle_root };
WalkReturn::Cycle {
min_depth: frame.min_depth,
annotation: current_component_annotation,
}
});
}
self.successors_stack = successors_stack;
if true {
match (&self.successors_stack.len(), &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};
return_value.unwrap()
}
}
}#[instrument(skip(self, initial), level = "trace")]510fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<A::SccIdx, A::Ann> {
511trace!("Walk unvisited node: {initial:?}");
512struct VisitingNodeFrame<G: DirectedGraph, Successors, A> {
513 node: G::Node,
514 successors: Option<Successors>,
515 depth: usize,
516 min_depth: usize,
517 successors_len: usize,
518 min_cycle_root: G::Node,
519 successor_node: G::Node,
520/// The annotation for the SCC starting in `node`. It may or may
521 /// not contain other nodes.
522current_component_annotation: A,
523 }
524525// Move the stack to a local variable. We want to utilize the existing allocation and
526 // mutably borrow it without borrowing self at the same time.
527let mut successors_stack = core::mem::take(&mut self.successors_stack);
528529debug_assert_eq!(successors_stack.len(), 0);
530531let mut stack: Vec<VisitingNodeFrame<G, _, _>> = vec![VisitingNodeFrame {
532 node: initial,
533 depth: 0,
534 min_depth: 0,
535 successors: None,
536 successors_len: 0,
537 min_cycle_root: initial,
538 successor_node: initial,
539 current_component_annotation: self.annotations.new(initial),
540 }];
541542let mut return_value = None;
543544'recurse: while let Some(frame) = stack.last_mut() {
545let VisitingNodeFrame {
546 node,
547 depth,
548 successors,
549 successors_len,
550 min_depth,
551 min_cycle_root,
552 successor_node,
553 current_component_annotation,
554 } = frame;
555let node = *node;
556let depth = *depth;
557558trace!(
559"Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}"
560);
561562let successors = match successors {
563Some(successors) => successors,
564None => {
565// This None marks that we still have the initialize this node's frame.
566trace!(?depth, ?node);
567568debug_assert_matches!(self.node_states[node], NodeState::NotVisited);
569570// Push `node` onto the stack.
571self.node_states[node] = NodeState::BeingVisited {
572 depth,
573 annotation: *current_component_annotation,
574 };
575self.node_stack.push(node);
576577// Walk each successor of the node, looking to see if any of
578 // them can reach a node that is presently on the stack. If
579 // so, that means they can also reach us.
580*successors_len = successors_stack.len();
581// Set and return a reference, this is currently empty.
582successors.get_or_insert(self.graph.successors(node))
583 }
584 };
585586// Now that the successors iterator is initialized, this is a constant for this frame.
587let successors_len = *successors_len;
588589// Construct iterators for the nodes and walk results. There are two cases:
590 // * The walk of a successor node returned.
591 // * The remaining successor nodes.
592let returned_walk =
593 return_value.take().into_iter().map(|walk| (*successor_node, Some(walk)));
594595let successor_walk = successors.map(|successor_node| {
596trace!(?node, ?successor_node);
597 (successor_node, self.inspect_node(successor_node))
598 });
599for (successor_node, walk) in returned_walk.chain(successor_walk) {
600match walk {
601// The starting node `node` leads to a cycle whose earliest node,
602 // `successor_node`, is at `min_depth`. There may be more cycles.
603Some(WalkReturn::Cycle {
604 min_depth: successor_min_depth,
605 annotation: successor_annotation,
606 }) => {
607trace!(
608"Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}"
609);
610// Track the minimum depth we can reach.
611assert!(successor_min_depth <= depth);
612if successor_min_depth < *min_depth {
613trace!(?node, ?successor_min_depth);
614*min_depth = successor_min_depth;
615*min_cycle_root = successor_node;
616 }
617 current_component_annotation.update_scc(successor_annotation);
618 }
619// The starting node `node` is succeeded by a fully identified SCC
620 // which is now added to the set under `scc_index`.
621Some(WalkReturn::Complete {
622 scc_index: successor_scc_index,
623 annotation: successor_annotation,
624 }) => {
625trace!(
626"Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}"
627);
628// Push the completed SCC indices onto
629 // the `successors_stack` for later.
630trace!(?node, ?successor_scc_index);
631 successors_stack.push(successor_scc_index);
632 current_component_annotation.update_reachable(successor_annotation);
633 }
634// `node` has no more (direct) successors; search recursively.
635None => {
636let depth = depth + 1;
637trace!("Recursing down into {successor_node:?} at depth {depth:?}");
638trace!(?depth, ?successor_node);
639// Remember which node the return value will come from.
640frame.successor_node = successor_node;
641// Start a new stack frame, then step into it.
642stack.push(VisitingNodeFrame {
643 node: successor_node,
644 depth,
645 successors: None,
646 successors_len: 0,
647 min_depth: depth,
648 min_cycle_root: successor_node,
649 successor_node,
650 current_component_annotation: self.annotations.new(successor_node),
651 });
652continue 'recurse;
653 }
654 }
655 }
656657trace!("Finished walk from {node:?} with annotation: {current_component_annotation:?}");
658659// Completed walk, remove `node` from the stack.
660let r = self.node_stack.pop();
661debug_assert_eq!(r, Some(node));
662663// Remove the frame, it's done.
664let frame = stack.pop().unwrap();
665let current_component_annotation = frame.current_component_annotation;
666debug_assert_eq!(frame.node, node);
667668// If `min_depth == depth`, then we are the root of the
669 // cycle: we can't reach anyone further down the stack.
670671 // Pass the 'return value' down the stack.
672 // We return one frame at a time so there can't be another return value.
673debug_assert!(return_value.is_none());
674 return_value = Some(if frame.min_depth == depth {
675// We are at the head of the component.
676677 // Note that successor stack may have duplicates, so we
678 // want to remove those:
679let deduplicated_successors = {
680let duplicate_set = &mut self.duplicate_set;
681 duplicate_set.clear();
682 successors_stack
683 .drain(successors_len..)
684 .filter(move |&i| duplicate_set.insert(i))
685 };
686687debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node);
688689let scc_index = self.scc_data.create_scc(deduplicated_successors);
690691self.annotations.annotate_scc(scc_index, current_component_annotation);
692693self.node_states[node] =
694 NodeState::InCycle { scc_index, annotation: current_component_annotation };
695696 WalkReturn::Complete { scc_index, annotation: current_component_annotation }
697 } else {
698// We are not the head of the cycle. Return back to our
699 // caller. They will take ownership of the
700 // `self.successors` data that we pushed.
701self.node_states[node] = NodeState::InCycleWith { parent: frame.min_cycle_root };
702 WalkReturn::Cycle {
703 min_depth: frame.min_depth,
704 annotation: current_component_annotation,
705 }
706 });
707 }
708709// Keep the allocation we used for successors_stack.
710self.successors_stack = successors_stack;
711debug_assert_eq!(self.successors_stack.len(), 0);
712713 return_value.unwrap()
714 }
715}