From 3d1ecc50ed7e83bb63116bc53f97eee409c7922d Mon Sep 17 00:00:00 2001 From: Havvy Date: Tue, 1 Nov 2016 20:32:02 -0700 Subject: [PATCH 1/4] Normalize generic bounds in graph iterators Use where clasues and only where clauses for bounds in the iterators for Graph. The rest of the code uses bounds on the generic declarations for Debug, and we may want to change those to for consistency. I did not do that here because I don't know whether or not that's a good idea. But for the iterators, they were inconsistent causing confusion, at least for me. --- src/librustc_data_structures/graph/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index fdb629ca5a578..111f3a2cd87ef 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -336,7 +336,7 @@ impl<'g, N: Debug, E: Debug> Iterator for AdjacentEdges<'g, N, E> { } } -pub struct AdjacentTargets<'g, N: 'g, E: 'g> +pub struct AdjacentTargets<'g, N, E> where N: 'g, E: 'g { @@ -351,7 +351,7 @@ impl<'g, N: Debug, E: Debug> Iterator for AdjacentTargets<'g, N, E> { } } -pub struct AdjacentSources<'g, N: 'g, E: 'g> +pub struct AdjacentSources<'g, N, E> where N: 'g, E: 'g { @@ -366,7 +366,10 @@ impl<'g, N: Debug, E: Debug> Iterator for AdjacentSources<'g, N, E> { } } -pub struct DepthFirstTraversal<'g, N: 'g, E: 'g> { +pub struct DepthFirstTraversal<'g, N, E> + where N: 'g, + E: 'g +{ graph: &'g Graph, stack: Vec, visited: BitVector, From fcf02623ee59bb21b948aea63b41b62609a7e663 Mon Sep 17 00:00:00 2001 From: Havvy Date: Wed, 2 Nov 2016 01:35:44 -0700 Subject: [PATCH 2/4] Added general iterators for graph nodes and edges Also used those general iterators in other methods. --- src/librustc_data_structures/graph/mod.rs | 48 +++++++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 111f3a2cd87ef..a47374feecda6 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -231,18 +231,30 @@ impl Graph { // # Iterating over nodes, edges + pub fn all_nodes_enumerated(&self) -> Nodes { + Nodes { + iter: self.nodes.iter().enumerate() + } + } + + pub fn all_edges_enumerated(&self) -> Edges { + Edges { + iter: self.edges.iter().enumerate() + } + } + pub fn each_node<'a, F>(&'a self, mut f: F) -> bool where F: FnMut(NodeIndex, &'a Node) -> bool { //! Iterates over all edges defined in the graph. - self.nodes.iter().enumerate().all(|(i, node)| f(NodeIndex(i), node)) + self.all_nodes_enumerated().all(|(node_idx, node)| f(node_idx, node)) } pub fn each_edge<'a, F>(&'a self, mut f: F) -> bool where F: FnMut(EdgeIndex, &'a Edge) -> bool { //! Iterates over all edges defined in the graph - self.edges.iter().enumerate().all(|(i, edge)| f(EdgeIndex(i), edge)) + self.all_edges_enumerated().all(|(edge_idx, edge)| f(edge_idx, edge)) } pub fn outgoing_edges(&self, source: NodeIndex) -> AdjacentEdges { @@ -286,8 +298,8 @@ impl Graph { while changed { changed = false; iteration += 1; - for (i, edge) in self.edges.iter().enumerate() { - changed |= op(iteration, EdgeIndex(i), edge); + for (edge_index, edge) in self.all_edges_enumerated() { + changed |= op(iteration, edge_index, edge); } } } @@ -302,6 +314,34 @@ impl Graph { // # Iterators +pub struct Nodes<'g, N> + where N: 'g, +{ + iter: ::std::iter::Enumerate<::std::slice::Iter<'g, Node>> +} + +impl<'g, N: Debug> Iterator for Nodes<'g, N> { + type Item = (NodeIndex, &'g Node); + + fn next(&mut self) -> Option<(NodeIndex, &'g Node)> { + self.iter.next().map(|(idx, n)| (NodeIndex(idx), n)) + } +} + +pub struct Edges<'g, E> + where E: 'g, +{ + iter: ::std::iter::Enumerate<::std::slice::Iter<'g, Edge>> +} + +impl<'g, E: Debug> Iterator for Edges<'g, E> { + type Item = (EdgeIndex, &'g Edge); + + fn next(&mut self) -> Option<(EdgeIndex, &'g Edge)> { + self.iter.next().map(|(idx, e)| (EdgeIndex(idx), e)) + } +} + pub struct AdjacentEdges<'g, N, E> where N: 'g, E: 'g From 7d91581cca025fbf308ed3402d04a55ea0bb0267 Mon Sep 17 00:00:00 2001 From: Havvy Date: Wed, 2 Nov 2016 01:45:12 -0700 Subject: [PATCH 3/4] Change Make comment into doc comment on Graph::iterate_until_fixed_point --- src/librustc_data_structures/graph/mod.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index a47374feecda6..6b40837899760 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -282,14 +282,11 @@ impl Graph { self.incoming_edges(target).sources() } - // # Fixed-point iteration - // - // A common use for graphs in our compiler is to perform - // fixed-point iteration. In this case, each edge represents a - // constraint, and the nodes themselves are associated with - // variables or other bitsets. This method facilitates such a - // computation. - + /// A common use for graphs in our compiler is to perform + /// fixed-point iteration. In this case, each edge represents a + /// constraint, and the nodes themselves are associated with + /// variables or other bitsets. This method facilitates such a + /// computation. pub fn iterate_until_fixed_point<'a, F>(&'a self, mut op: F) where F: FnMut(usize, EdgeIndex, &'a Edge) -> bool { From 9ddbb9133c86df9aab7b98a0e6b641b847c800ec Mon Sep 17 00:00:00 2001 From: Havvy Date: Wed, 2 Nov 2016 02:38:36 -0700 Subject: [PATCH 4/4] Added Graph::is_cyclicic_node algorithm --- src/librustc_data_structures/graph/mod.rs | 51 ++++++++++++++++----- src/librustc_data_structures/graph/tests.rs | 46 +++++++++++++++++-- 2 files changed, 82 insertions(+), 15 deletions(-) diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 6b40837899760..f94ed6b720946 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -231,14 +231,14 @@ impl Graph { // # Iterating over nodes, edges - pub fn all_nodes_enumerated(&self) -> Nodes { - Nodes { + pub fn enumerated_nodes(&self) -> EnumeratedNodes { + EnumeratedNodes { iter: self.nodes.iter().enumerate() } } - pub fn all_edges_enumerated(&self) -> Edges { - Edges { + pub fn enumerated_edges(&self) -> EnumeratedEdges { + EnumeratedEdges { iter: self.edges.iter().enumerate() } } @@ -247,14 +247,14 @@ impl Graph { where F: FnMut(NodeIndex, &'a Node) -> bool { //! Iterates over all edges defined in the graph. - self.all_nodes_enumerated().all(|(node_idx, node)| f(node_idx, node)) + self.enumerated_nodes().all(|(node_idx, node)| f(node_idx, node)) } pub fn each_edge<'a, F>(&'a self, mut f: F) -> bool where F: FnMut(EdgeIndex, &'a Edge) -> bool { //! Iterates over all edges defined in the graph - self.all_edges_enumerated().all(|(edge_idx, edge)| f(edge_idx, edge)) + self.enumerated_edges().all(|(edge_idx, edge)| f(edge_idx, edge)) } pub fn outgoing_edges(&self, source: NodeIndex) -> AdjacentEdges { @@ -295,7 +295,7 @@ impl Graph { while changed { changed = false; iteration += 1; - for (edge_index, edge) in self.all_edges_enumerated() { + for (edge_index, edge) in self.enumerated_edges() { changed |= op(iteration, edge_index, edge); } } @@ -307,17 +307,46 @@ impl Graph { -> DepthFirstTraversal<'a, N, E> { DepthFirstTraversal::with_start_node(self, start, direction) } + + /// Whether or not a node can be reached from itself. + pub fn is_node_cyclic(&self, starting_node_index: NodeIndex) -> bool { + // This is similar to depth traversal below, but we + // can't use that, because depth traversal doesn't show + // the starting node a second time. + let mut visited = BitVector::new(self.len_nodes()); + let mut stack = vec![starting_node_index]; + + while let Some(current_node_index) = stack.pop() { + visited.insert(current_node_index.0); + + // Directionality doesn't change the answer, + // so just use outgoing edges. + for (_, edge) in self.outgoing_edges(current_node_index) { + let target_node_index = edge.target(); + + if target_node_index == starting_node_index { + return true; + } + + if !visited.contains(target_node_index.0) { + stack.push(target_node_index); + } + } + } + + false + } } // # Iterators -pub struct Nodes<'g, N> +pub struct EnumeratedNodes<'g, N> where N: 'g, { iter: ::std::iter::Enumerate<::std::slice::Iter<'g, Node>> } -impl<'g, N: Debug> Iterator for Nodes<'g, N> { +impl<'g, N: Debug> Iterator for EnumeratedNodes<'g, N> { type Item = (NodeIndex, &'g Node); fn next(&mut self) -> Option<(NodeIndex, &'g Node)> { @@ -325,13 +354,13 @@ impl<'g, N: Debug> Iterator for Nodes<'g, N> { } } -pub struct Edges<'g, E> +pub struct EnumeratedEdges<'g, E> where E: 'g, { iter: ::std::iter::Enumerate<::std::slice::Iter<'g, Edge>> } -impl<'g, E: Debug> Iterator for Edges<'g, E> { +impl<'g, E: Debug> Iterator for EnumeratedEdges<'g, E> { type Item = (EdgeIndex, &'g Edge); fn next(&mut self) -> Option<(EdgeIndex, &'g Edge)> { diff --git a/src/librustc_data_structures/graph/tests.rs b/src/librustc_data_structures/graph/tests.rs index be7f48d27e041..a87410e6e1c8c 100644 --- a/src/librustc_data_structures/graph/tests.rs +++ b/src/librustc_data_structures/graph/tests.rs @@ -20,10 +20,13 @@ fn create_graph() -> TestGraph { // Create a simple graph // - // A -+> B --> C - // | | ^ - // | v | - // F D --> E + // F + // | + // V + // A --> B --> C + // | ^ + // v | + // D --> E let a = graph.add_node("A"); let b = graph.add_node("B"); @@ -42,6 +45,29 @@ fn create_graph() -> TestGraph { return graph; } +fn create_graph_with_cycle() -> TestGraph { + let mut graph = Graph::new(); + + // Create a graph with a cycle. + // + // A --> B <-- + + // | | + // v | + // C --> D + + let a = graph.add_node("A"); + let b = graph.add_node("B"); + let c = graph.add_node("C"); + let d = graph.add_node("D"); + + graph.add_edge(a, b, "AB"); + graph.add_edge(b, c, "BC"); + graph.add_edge(c, d, "CD"); + graph.add_edge(d, b, "DB"); + + return graph; +} + #[test] fn each_node() { let graph = create_graph(); @@ -139,3 +165,15 @@ fn each_adjacent_from_d() { let graph = create_graph(); test_adjacent_edges(&graph, NodeIndex(3), "D", &[("BD", "B")], &[("DE", "E")]); } + +#[test] +fn is_node_cyclic_a() { + let graph = create_graph_with_cycle(); + assert!(!graph.is_node_cyclic(NodeIndex(0))); +} + +#[test] +fn is_node_cyclic_b() { + let graph = create_graph_with_cycle(); + assert!(graph.is_node_cyclic(NodeIndex(1))); +}