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::fmt::Debug;
12use std::marker::PhantomData;
13use std::ops::Range;
1415use rustc_index::{Idx, IndexSlice, IndexVec};
16use tracing::{debug, instrument, trace};
1718use crate::debug_assert_matches;
19use 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 `update_scc` and `update_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.
38fn update_scc(&mut self, other: &Self);
3940/// Merge a successor into this annotation.
41fn update_reachable(&mut self, other: &Self);
42}
4344/// An accumulator for annotations.
45pub trait Annotations<N: Idx> {
46type Ann: Annotation;
47type SccIdx: Idx + Ord;
4849fn new(&self, element: N) -> Self::Ann;
50fn annotate_scc(&mut self, scc: Self::SccIdx, annotation: Self::Ann);
51}
5253/// The nil annotation accumulator, which does nothing.
54struct NoAnnotations<S: Idx + Ord>(PhantomData<S>);
5556impl<N: Idx, S: Idx + Ord> Annotations<N> for NoAnnotations<S> {
57type SccIdx = S;
58type Ann = ();
59fn new(&self, _element: N) {}
60fn annotate_scc(&mut self, _scc: S, _annotation: ()) {}
61}
6263/// The empty annotation, which does nothing.
64impl Annotationfor () {
65fn update_reachable(&mut self, _other: &Self) {}
66fn update_scc(&mut self, _other: &Self) {}
67}
6869/// Strongly connected components (SCC) of a graph. The type `N` is
70/// the index type for the graph nodes and `S` is the index type for
71/// the SCCs. We can map from each node to the SCC that it
72/// participates in, and we also have the successors of each SCC.
73pub struct Sccs<N: Idx, S: Idx> {
74/// For each node, what is the SCC index of the SCC to which it
75 /// belongs.
76scc_indices: IndexVec<N, S>,
7778/// Data about all the SCCs.
79scc_data: SccData<S>,
80}
8182/// Information about an invidividual SCC node.
83struct SccDetails {
84/// For this SCC, the range of `all_successors` where its
85 /// successors can be found.
86range: Range<usize>,
87}
8889// The name of this struct should discourage you from making it public and leaking
90// its representation. This message was left here by one who came before you,
91// who learnt the hard way that making even small changes in representation
92// is difficult when it's publicly inspectable.
93//
94// Obey the law of Demeter!
95struct SccData<S: Idx> {
96/// Maps SCC indices to their metadata, including
97 /// offsets into `all_successors`.
98scc_details: IndexVec<S, SccDetails>,
99100/// Contains the successors for all the Sccs, concatenated. The
101 /// range of indices corresponding to a given SCC is found in its
102 /// `scc_details.range`.
103all_successors: Vec<S>,
104}
105106impl<N: Idx, S: Idx + Ord> Sccs<N, S> {
107/// Compute SCCs without annotations.
108pub fn new(graph: &impl Successors<Node = N>) -> Self {
109Self::new_with_annotation(graph, &mut NoAnnotations(PhantomData::<S>))
110 }
111112/// Compute SCCs and annotate them with a user-supplied annotation
113pub fn new_with_annotation<A: Annotations<N, SccIdx = S>>(
114 graph: &impl Successors<Node = N>,
115 annotations: &mut A,
116 ) -> Self {
117SccsConstruction::construct(graph, annotations)
118 }
119120pub fn scc_indices(&self) -> &IndexSlice<N, S> {
121&self.scc_indices
122 }
123124/// Returns the number of SCCs in the graph.
125pub fn num_sccs(&self) -> usize {
126self.scc_data.len()
127 }
128129/// Returns an iterator over the SCCs in the graph.
130 ///
131 /// The SCCs will be iterated in **dependency order** (or **post order**),
132 /// meaning that if `S1 -> S2`, we will visit `S2` first and `S1` after.
133 /// This is convenient when the edges represent dependencies: when you visit
134 /// `S1`, the value for `S2` will already have been computed.
135pub fn all_sccs(&self) -> impl Iterator<Item = S> + 'static {
136 (0..self.scc_data.len()).map(S::new)
137 }
138139/// Returns the SCC to which a node `r` belongs.
140pub fn scc(&self, r: N) -> S {
141self.scc_indices[r]
142 }
143144/// Returns the successors of the given SCC.
145pub fn successors(&self, scc: S) -> &[S] {
146self.scc_data.successors(scc)
147 }
148149/// Construct the reverse graph of the SCC graph.
150pub fn reverse(&self) -> VecGraph<S> {
151VecGraph::new(
152self.num_sccs(),
153self.all_sccs()
154 .flat_map(|source| {
155self.successors(source).iter().map(move |&target| (target, source))
156 })
157 .collect(),
158 )
159 }
160}
161162impl<N: Idx, S: Idx + Ord> DirectedGraphfor Sccs<N, S> {
163type Node = S;
164165fn num_nodes(&self) -> usize {
166self.num_sccs()
167 }
168}
169170impl<N: Idx, S: Idx + Ord> NumEdgesfor Sccs<N, S> {
171fn num_edges(&self) -> usize {
172self.scc_data.all_successors.len()
173 }
174}
175176impl<N: Idx, S: Idx + Ord> Successorsfor Sccs<N, S> {
177fn successors(&self, node: S) -> impl Iterator<Item = Self::Node> {
178self.successors(node).iter().cloned()
179 }
180}
181182impl<S: Idx> SccData<S> {
183/// Number of SCCs,
184fn len(&self) -> usize {
185self.scc_details.len()
186 }
187188/// Returns the successors of the given SCC.
189fn successors(&self, scc: S) -> &[S] {
190&self.all_successors[self.scc_details[scc].range.clone()]
191 }
192193/// Creates a new SCC with `successors` as its successors and
194 /// returns the resulting index.
195fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S {
196// Store the successors on `scc_successors_vec`, remembering
197 // the range of indices.
198let all_successors_start = self.all_successors.len();
199self.all_successors.extend(successors);
200let all_successors_end = self.all_successors.len();
201202{
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:202",
"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(202u32),
::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!(
203"create_scc({:?}) successors={:?}",
204self.len(),
205&self.all_successors[all_successors_start..all_successors_end],
206 );
207208let range = all_successors_start..all_successors_end;
209let metadata = SccDetails { range };
210self.scc_details.push(metadata)
211 }
212}
213214struct SccsConstruction<'c, 'a, G, A>
215where
216G: DirectedGraph + Successors,
217 A: Annotations<G::Node>,
218{
219 graph: &'c G,
220221/// The state of each node; used during walk to record the stack
222 /// and after walk to record what cycle each node ended up being
223 /// in.
224node_states: IndexVec<G::Node, NodeState<G::Node, A::SccIdx, A::Ann>>,
225226/// The stack of nodes that we are visiting as part of the DFS.
227node_stack: Vec<G::Node>,
228229/// The stack of successors: as we visit a node, we mark our
230 /// position in this stack, and when we encounter a successor SCC,
231 /// we push it on the stack. When we complete an SCC, we can pop
232 /// everything off the stack that was found along the way.
233successors_stack: Vec<A::SccIdx>,
234235/// A set used to strip duplicates. As we accumulate successors
236 /// into the successors_stack, we sometimes get duplicate entries.
237 /// We use this set to remove those -- we also keep its storage
238 /// around between successors to amortize memory allocation costs.
239duplicate_set: FxHashSet<A::SccIdx>,
240241 scc_data: SccData<A::SccIdx>,
242243 annotations: &'a mut A,
244}
245246#[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)]
247enum NodeState<N, S, A: Annotation> {
248/// This node has not yet been visited as part of the DFS.
249 ///
250 /// After SCC construction is complete, this state ought to be
251 /// impossible.
252NotVisited,
253254/// This node is currently being walked as part of our DFS. It is on
255 /// the stack at the depth `depth` and its current annotation is
256 /// `annotation`.
257 ///
258 /// After SCC construction is complete, this state ought to be
259 /// impossible.
260BeingVisited { depth: usize, annotation: A },
261262/// Indicates that this node is a member of the given cycle where
263 /// the merged annotation is `annotation`.
264 /// Note that an SCC can have several cycles, so its final annotation
265 /// is the merged value of all its member annotations.
266InCycle { scc_index: S, annotation: A },
267268/// Indicates that this node is a member of whatever cycle
269 /// `parent` is a member of. This state is transient: whenever we
270 /// see it, we try to overwrite it with the current state of
271 /// `parent` (this is the "path compression" step of a union-find
272 /// algorithm).
273InCycleWith { parent: N },
274}
275276/// The state of walking a given node.
277#[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)]
278enum WalkReturn<S, A: Annotation> {
279/// The walk found a cycle, but the entire component is not known to have
280 /// been fully walked yet. We only know the minimum depth of this
281 /// component in a minimum spanning tree of the graph. This component
282 /// is tentatively represented by the state of the first node of this
283 /// cycle we met, which is at `min_depth`.
284Cycle { min_depth: usize, annotation: A },
285/// The SCC and everything reachable from it have been fully walked.
286 /// At this point we know what is inside the SCC as we have visited every
287 /// node reachable from it. The SCC can now be fully represented by its ID.
288Complete { scc_index: S, annotation: A },
289}
290291impl<'c, 'a, G, A> SccsConstruction<'c, 'a, G, A>
292where
293G: DirectedGraph + Successors,
294 A: Annotations<G::Node>,
295{
296/// Identifies SCCs in the graph `G` and computes the resulting
297 /// DAG. This uses a variant of [Tarjan's
298 /// algorithm][wikipedia]. The high-level summary of the algorithm
299 /// is that we do a depth-first search. Along the way, we keep a
300 /// stack of each node whose successors are being visited. We
301 /// track the depth of each node on this stack (there is no depth
302 /// if the node is not on the stack). When we find that some node
303 /// N with depth D can reach some other node N' with lower depth
304 /// D' (i.e., D' < D), we know that N, N', and all nodes in
305 /// between them on the stack are part of an SCC.
306 ///
307 /// Additionally, we keep track of a current annotation of the SCC.
308 ///
309 /// [wikipedia]: https://bit.ly/2EZIx84
310fn construct(graph: &'c G, annotations: &'a mut A) -> Sccs<G::Node, A::SccIdx> {
311let num_nodes = graph.num_nodes();
312313let mut this = Self {
314graph,
315 node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes),
316 node_stack: Vec::with_capacity(num_nodes),
317 successors_stack: Vec::new(),
318 scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() },
319 duplicate_set: FxHashSet::default(),
320annotations,
321 };
322323let scc_indices = graph324 .iter_nodes()
325 .map(|node| match this.start_walk_from(node) {
326 WalkReturn::Complete { scc_index, .. } => scc_index,
327 WalkReturn::Cycle { min_depth, .. } => {
328{
::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:?}")329 }
330 })
331 .collect();
332333Sccs { scc_indices, scc_data: this.scc_data }
334 }
335336fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<A::SccIdx, A::Ann> {
337self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node))
338 }
339340/// Inspect a node during the DFS. We first examine its current
341 /// state -- if it is not yet visited (`NotVisited`), return `None` so
342 /// that the caller might push it onto the stack and start walking its
343 /// successors.
344 ///
345 /// If it is already on the DFS stack it will be in the state
346 /// `BeingVisited`. In that case, we have found a cycle and we
347 /// return the depth from the stack.
348 ///
349 /// Otherwise, we are looking at a node that has already been
350 /// completely visited. We therefore return `WalkReturn::Complete`
351 /// with its associated SCC index.
352fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<A::SccIdx, A::Ann>> {
353Some(match self.find_state(node) {
354 NodeState::InCycle { scc_index, annotation } => {
355 WalkReturn::Complete { scc_index, annotation }
356 }
357358 NodeState::BeingVisited { depth: min_depth, annotation } => {
359 WalkReturn::Cycle { min_depth, annotation }
360 }
361362 NodeState::NotVisited => return None,
363364 NodeState::InCycleWith { parent } => {
::core::panicking::panic_fmt(format_args!("`find_state` returned `InCycleWith({0:?})`, which ought to be impossible",
parent));
}panic!(
365"`find_state` returned `InCycleWith({parent:?})`, which ought to be impossible"
366),
367 })
368 }
369370/// Fetches the state of the node `r`. If `r` is recorded as being
371 /// in a cycle with some other node `r2`, then fetches the state
372 /// of `r2` (and updates `r` to reflect current result). This is
373 /// basically the "find" part of a standard union-find algorithm
374 /// (with path compression).
375fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, A::SccIdx, A::Ann> {
376// To avoid recursion we temporarily reuse the `parent` of each
377 // InCycleWith link to encode a downwards link while compressing
378 // the path. After we have found the root or deepest node being
379 // visited, we traverse the reverse links and correct the node
380 // states on the way.
381 //
382 // **Note**: This mutation requires that this is a leaf function
383 // or at least that none of the called functions inspects the
384 // current node states. Luckily, we are a leaf.
385386 // Remember one previous link. The termination condition when
387 // following links downwards is then simply as soon as we have
388 // found the initial self-loop.
389let mut previous_node = node;
390391// Ultimately propagated to all the transitive parents when following
392 // `InCycleWith` upwards.
393 // This loop performs the downward link encoding mentioned above. Details below!
394 // Note that there are two different states being assigned: the root state, and
395 // a potentially derived version of the root state for non-root nodes in the chain.
396let (root_state, assigned_state) = {
397loop {
398{
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:398",
"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(398u32),
::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]);
399match self.node_states[node] {
400// This must have been the first and only state since it is unexplored*;
401 // no update needed! * Unless there is a bug :')
402s @ NodeState::NotVisited => return s,
403// We are in a completely discovered SCC; every node on our path is in that SCC:
404s @ NodeState::InCycle { .. } => break (s, s),
405// The Interesting Third Base Case: we are a path back to a root node
406 // still being explored. Now we need that node to keep its state and
407 // every other node to be recorded as being in whatever component that
408 // ends up in.
409s @ NodeState::BeingVisited { depth, .. } => {
410break (s, NodeState::InCycleWith { parent: self.node_stack[depth] });
411 }
412// We are not at the head of a path; keep compressing it!
413NodeState::InCycleWith { parent } => {
414// We test this, to be extremely sure that we never
415 // ever break our termination condition for the
416 // reverse iteration loop.
417if !(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");
418419// Store the previous node as an inverted list link
420self.node_states[node] = NodeState::InCycleWith { parent: previous_node };
421// Update to parent node.
422previous_node = node;
423node = parent;
424 }
425 }
426 }
427 };
428429// The states form a graph where up to one outgoing link is stored at
430 // each node. Initially in general,
431 //
432 // E
433 // ^
434 // |
435 // InCycleWith/BeingVisited/NotVisited
436 // |
437 // A-InCycleWith->B-InCycleWith…>C-InCycleWith->D-+
438 // |
439 // = node, previous_node
440 //
441 // After the first loop, this will look like
442 // E
443 // ^
444 // |
445 // InCycleWith/BeingVisited/NotVisited
446 // |
447 // +>A<-InCycleWith-B<…InCycleWith-C<-InCycleWith-D-+
448 // | | | |
449 // | InCycleWith | = node
450 // +-+ =previous_node
451 //
452 // Note in particular that A will be linked to itself in a self-cycle
453 // and no other self-cycles occur due to how InCycleWith is assigned in
454 // the find phase implemented by `walk_unvisited_node`.
455 //
456 // We now want to compress the path, that is assign the state of the
457 // link D-E to all other links.
458 //
459 // We can then walk backwards, starting from `previous_node`, and assign
460 // each node in the list with the updated state. The loop terminates
461 // when we reach the self-cycle.
462463 // Move backwards until we found the node where we started. We
464 // will know when we hit the state where previous_node == node.
465loop {
466// Back at the beginning, we can return. Note that we return the root state.
467 // This is because for components being explored, we would otherwise get a
468 // `node_state[n] = InCycleWith{ parent: n }` and that's wrong.
469if previous_node == node {
470return root_state;
471 }
472{
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:472",
"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(472u32),
::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:?}");
473474// Update to previous node in the link.
475match self.node_states[previous_node] {
476 NodeState::InCycleWith { parent: previous } => {
477node = previous_node;
478previous_node = previous;
479 }
480// Only InCycleWith nodes were added to the reverse linked list.
481 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:?}"),
482 }
483484// Update the node state to the (potentially derived) state.
485 // If the root is still being explored, this is
486 // `InCycleWith{ parent: <root node>}`, otherwise
487 // `assigned_state == root_state`.
488self.node_states[node] = assigned_state;
489 }
490 }
491492/// Walks a node that has never been visited before.
493 ///
494 /// Call this method when `inspect_node` has returned `None`. Having the
495 /// caller decide avoids mutual recursion between the two methods and allows
496 /// us to maintain an allocated stack for nodes on the path between calls.
497#[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(497u32),
::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:499",
"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(499u32),
::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:546",
"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(546u32),
::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:554",
"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(554u32),
::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:584",
"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(584u32),
::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:595",
"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(595u32),
::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:601",
"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(601u32),
::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: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(&["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:618",
"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(618u32),
::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: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!("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:626",
"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(626u32),
::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:645",
"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(645u32),
::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:675",
"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(675u32),
::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")]498fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<A::SccIdx, A::Ann> {
499trace!("Walk unvisited node: {initial:?}");
500struct VisitingNodeFrame<G: DirectedGraph, Successors, A> {
501 node: G::Node,
502 successors: Option<Successors>,
503 depth: usize,
504 min_depth: usize,
505 successors_len: usize,
506 min_cycle_root: G::Node,
507 successor_node: G::Node,
508/// The annotation for the SCC starting in `node`. It may or may
509 /// not contain other nodes.
510current_component_annotation: A,
511 }
512513// Move the stack to a local variable. We want to utilize the existing allocation and
514 // mutably borrow it without borrowing self at the same time.
515let mut successors_stack = core::mem::take(&mut self.successors_stack);
516517debug_assert_eq!(successors_stack.len(), 0);
518519let mut stack: Vec<VisitingNodeFrame<G, _, _>> = vec![VisitingNodeFrame {
520 node: initial,
521 depth: 0,
522 min_depth: 0,
523 successors: None,
524 successors_len: 0,
525 min_cycle_root: initial,
526 successor_node: initial,
527 current_component_annotation: self.annotations.new(initial),
528 }];
529530let mut return_value = None;
531532'recurse: while let Some(frame) = stack.last_mut() {
533let VisitingNodeFrame {
534 node,
535 depth,
536 successors,
537 successors_len,
538 min_depth,
539 min_cycle_root,
540 successor_node,
541 current_component_annotation,
542 } = frame;
543let node = *node;
544let depth = *depth;
545546trace!(
547"Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}"
548);
549550let successors = match successors {
551Some(successors) => successors,
552None => {
553// This None marks that we still have the initialize this node's frame.
554trace!(?depth, ?node);
555556debug_assert_matches!(self.node_states[node], NodeState::NotVisited);
557558// Push `node` onto the stack.
559self.node_states[node] = NodeState::BeingVisited {
560 depth,
561 annotation: *current_component_annotation,
562 };
563self.node_stack.push(node);
564565// Walk each successor of the node, looking to see if any of
566 // them can reach a node that is presently on the stack. If
567 // so, that means they can also reach us.
568*successors_len = successors_stack.len();
569// Set and return a reference, this is currently empty.
570successors.get_or_insert(self.graph.successors(node))
571 }
572 };
573574// Now that the successors iterator is initialized, this is a constant for this frame.
575let successors_len = *successors_len;
576577// Construct iterators for the nodes and walk results. There are two cases:
578 // * The walk of a successor node returned.
579 // * The remaining successor nodes.
580let returned_walk =
581 return_value.take().into_iter().map(|walk| (*successor_node, Some(walk)));
582583let successor_walk = successors.map(|successor_node| {
584trace!(?node, ?successor_node);
585 (successor_node, self.inspect_node(successor_node))
586 });
587for (successor_node, walk) in returned_walk.chain(successor_walk) {
588match walk {
589// The starting node `node` leads to a cycle whose earliest node,
590 // `successor_node`, is at `min_depth`. There may be more cycles.
591Some(WalkReturn::Cycle {
592 min_depth: successor_min_depth,
593 annotation: successor_annotation,
594 }) => {
595trace!(
596"Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}"
597);
598// Track the minimum depth we can reach.
599assert!(successor_min_depth <= depth);
600if successor_min_depth < *min_depth {
601trace!(?node, ?successor_min_depth);
602*min_depth = successor_min_depth;
603*min_cycle_root = successor_node;
604 }
605 current_component_annotation.update_scc(&successor_annotation);
606 }
607// The starting node `node` is succeeded by a fully identified SCC
608 // which is now added to the set under `scc_index`.
609Some(WalkReturn::Complete {
610 scc_index: successor_scc_index,
611 annotation: successor_annotation,
612 }) => {
613trace!(
614"Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}"
615);
616// Push the completed SCC indices onto
617 // the `successors_stack` for later.
618trace!(?node, ?successor_scc_index);
619 successors_stack.push(successor_scc_index);
620 current_component_annotation.update_reachable(&successor_annotation);
621 }
622// `node` has no more (direct) successors; search recursively.
623None => {
624let depth = depth + 1;
625trace!("Recursing down into {successor_node:?} at depth {depth:?}");
626trace!(?depth, ?successor_node);
627// Remember which node the return value will come from.
628frame.successor_node = successor_node;
629// Start a new stack frame, then step into it.
630stack.push(VisitingNodeFrame {
631 node: successor_node,
632 depth,
633 successors: None,
634 successors_len: 0,
635 min_depth: depth,
636 min_cycle_root: successor_node,
637 successor_node,
638 current_component_annotation: self.annotations.new(successor_node),
639 });
640continue 'recurse;
641 }
642 }
643 }
644645trace!("Finished walk from {node:?} with annotation: {current_component_annotation:?}");
646647// Completed walk, remove `node` from the stack.
648let r = self.node_stack.pop();
649debug_assert_eq!(r, Some(node));
650651// Remove the frame, it's done.
652let frame = stack.pop().unwrap();
653let current_component_annotation = frame.current_component_annotation;
654debug_assert_eq!(frame.node, node);
655656// If `min_depth == depth`, then we are the root of the
657 // cycle: we can't reach anyone further down the stack.
658659 // Pass the 'return value' down the stack.
660 // We return one frame at a time so there can't be another return value.
661debug_assert!(return_value.is_none());
662 return_value = Some(if frame.min_depth == depth {
663// We are at the head of the component.
664665 // Note that successor stack may have duplicates, so we
666 // want to remove those:
667let deduplicated_successors = {
668let duplicate_set = &mut self.duplicate_set;
669 duplicate_set.clear();
670 successors_stack
671 .drain(successors_len..)
672 .filter(move |&i| duplicate_set.insert(i))
673 };
674675debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node);
676677let scc_index = self.scc_data.create_scc(deduplicated_successors);
678679self.annotations.annotate_scc(scc_index, current_component_annotation);
680681self.node_states[node] =
682 NodeState::InCycle { scc_index, annotation: current_component_annotation };
683684 WalkReturn::Complete { scc_index, annotation: current_component_annotation }
685 } else {
686// We are not the head of the cycle. Return back to our
687 // caller. They will take ownership of the
688 // `self.successors` data that we pushed.
689self.node_states[node] = NodeState::InCycleWith { parent: frame.min_cycle_root };
690 WalkReturn::Cycle {
691 min_depth: frame.min_depth,
692 annotation: current_component_annotation,
693 }
694 });
695 }
696697// Keep the allocation we used for successors_stack.
698self.successors_stack = successors_stack;
699debug_assert_eq!(self.successors_stack.len(), 0);
700701 return_value.unwrap()
702 }
703}