diff options
author | David Malcolm <dmalcolm@redhat.com> | 2021-03-11 17:43:39 -0500 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2021-03-11 17:43:39 -0500 |
commit | 3f958348e78f38d91f0611618bb909182170c0f3 (patch) | |
tree | 4825f1b0a8f3500fb176782a3474c3bf563bb310 | |
parent | c4f8e568aa66a8461ee39d5f85c2e2d41a833b7f (diff) | |
download | gcc-3f958348e78f38d91f0611618bb909182170c0f3.zip gcc-3f958348e78f38d91f0611618bb909182170c0f3.tar.gz gcc-3f958348e78f38d91f0611618bb909182170c0f3.tar.bz2 |
analyzer: gracefully handle impossible paths in shortest-paths.h
This bulletproofs the shortest_paths code against unreachable nodes,
gracefully handling them, rather than failing an assertion.
I've marked this as "analyzer" as this is the only code using
shortest-paths.h.
This patch is required by followup work to fix PR analyzer/96374.
gcc/ChangeLog:
* digraph.cc (selftest::test_shortest_paths): Add test coverage
for paths from B and C.
* shortest-paths.h (shortest_paths::shortest_paths): Handle
unreachable nodes, rather than asserting.
-rw-r--r-- | gcc/digraph.cc | 101 | ||||
-rw-r--r-- | gcc/shortest-paths.h | 6 |
2 files changed, 83 insertions, 24 deletions
diff --git a/gcc/digraph.cc b/gcc/digraph.cc index 163909b..3441a85 100644 --- a/gcc/digraph.cc +++ b/gcc/digraph.cc @@ -142,36 +142,93 @@ test_shortest_paths () test_edge *ac = g.add_test_edge (a, c); test_edge *cd = g.add_test_edge (c, d); test_edge *be = g.add_test_edge (b, e); - g.add_test_edge (e, f); + test_edge *ef = g.add_test_edge (e, f); test_edge *cf = g.add_test_edge (c, f); - shortest_paths<test_graph_traits, test_path> sp (g, a); + /* Use "A" as the origin; all nodes should be reachable. */ + { + shortest_paths<test_graph_traits, test_path> sp (g, a); + + test_path path_to_a = sp.get_shortest_path (a); + ASSERT_EQ (path_to_a.m_edges.length (), 0); /* Trivial path. */ + + test_path path_to_b = sp.get_shortest_path (b); + ASSERT_EQ (path_to_b.m_edges.length (), 1); + ASSERT_EQ (path_to_b.m_edges[0], ab); + + test_path path_to_c = sp.get_shortest_path (c); + ASSERT_EQ (path_to_c.m_edges.length (), 1); + ASSERT_EQ (path_to_c.m_edges[0], ac); + + test_path path_to_d = sp.get_shortest_path (d); + ASSERT_EQ (path_to_d.m_edges.length (), 2); + ASSERT_EQ (path_to_d.m_edges[0], ac); + ASSERT_EQ (path_to_d.m_edges[1], cd); + + test_path path_to_e = sp.get_shortest_path (e); + ASSERT_EQ (path_to_e.m_edges.length (), 2); + ASSERT_EQ (path_to_e.m_edges[0], ab); + ASSERT_EQ (path_to_e.m_edges[1], be); + + test_path path_to_f = sp.get_shortest_path (f); + ASSERT_EQ (path_to_f.m_edges.length (), 2); + ASSERT_EQ (path_to_f.m_edges[0], ac); + ASSERT_EQ (path_to_f.m_edges[1], cf); + } + + /* Verify that we gracefully handle an origin from which some nodes + aren't reachable. */ + + /* Use "B" as the origin, so only E and F are reachable. */ + { + shortest_paths<test_graph_traits, test_path> sp (g, b); - test_path path_to_a = sp.get_shortest_path (a); - ASSERT_EQ (path_to_a.m_edges.length (), 0); + test_path path_to_a = sp.get_shortest_path (a); + ASSERT_EQ (path_to_a.m_edges.length (), 0); /* No path. */ - test_path path_to_b = sp.get_shortest_path (b); - ASSERT_EQ (path_to_b.m_edges.length (), 1); - ASSERT_EQ (path_to_b.m_edges[0], ab); + test_path path_to_b = sp.get_shortest_path (b); + ASSERT_EQ (path_to_b.m_edges.length (), 0); /* Trivial path. */ - test_path path_to_c = sp.get_shortest_path (c); - ASSERT_EQ (path_to_c.m_edges.length (), 1); - ASSERT_EQ (path_to_c.m_edges[0], ac); + test_path path_to_c = sp.get_shortest_path (c); + ASSERT_EQ (path_to_c.m_edges.length (), 0); /* No path. */ - test_path path_to_d = sp.get_shortest_path (d); - ASSERT_EQ (path_to_d.m_edges.length (), 2); - ASSERT_EQ (path_to_d.m_edges[0], ac); - ASSERT_EQ (path_to_d.m_edges[1], cd); + test_path path_to_d = sp.get_shortest_path (d); + ASSERT_EQ (path_to_d.m_edges.length (), 0); /* No path. */ - test_path path_to_e = sp.get_shortest_path (e); - ASSERT_EQ (path_to_e.m_edges.length (), 2); - ASSERT_EQ (path_to_e.m_edges[0], ab); - ASSERT_EQ (path_to_e.m_edges[1], be); + test_path path_to_e = sp.get_shortest_path (e); + ASSERT_EQ (path_to_e.m_edges.length (), 1); + ASSERT_EQ (path_to_e.m_edges[0], be); - test_path path_to_f = sp.get_shortest_path (f); - ASSERT_EQ (path_to_f.m_edges.length (), 2); - ASSERT_EQ (path_to_f.m_edges[0], ac); - ASSERT_EQ (path_to_f.m_edges[1], cf); + test_path path_to_f = sp.get_shortest_path (f); + ASSERT_EQ (path_to_f.m_edges.length (), 2); + ASSERT_EQ (path_to_f.m_edges[0], be); + ASSERT_EQ (path_to_f.m_edges[1], ef); + } + + /* Use "C" as the origin, so only D and F are reachable. */ + { + shortest_paths<test_graph_traits, test_path> sp (g, c); + + test_path path_to_a = sp.get_shortest_path (a); + ASSERT_EQ (path_to_a.m_edges.length (), 0); /* No path. */ + + test_path path_to_b = sp.get_shortest_path (b); + ASSERT_EQ (path_to_b.m_edges.length (), 0); /* No path. */ + + test_path path_to_c = sp.get_shortest_path (c); + ASSERT_EQ (path_to_c.m_edges.length (), 0); /* Trivial path. */ + + test_path path_to_d = sp.get_shortest_path (d); + ASSERT_EQ (path_to_d.m_edges.length (), 1); + ASSERT_EQ (path_to_d.m_edges[0], cd); + + test_path path_to_e = sp.get_shortest_path (e); + ASSERT_EQ (path_to_e.m_edges.length (), 0); /* No path. */ + + test_path path_to_f = sp.get_shortest_path (f); + ASSERT_EQ (path_to_f.m_edges.length (), 1); + ASSERT_EQ (path_to_f.m_edges[0], cf); + } } /* Run all of the selftests within this file. */ diff --git a/gcc/shortest-paths.h b/gcc/shortest-paths.h index ded6285..5648a95 100644 --- a/gcc/shortest-paths.h +++ b/gcc/shortest-paths.h @@ -96,7 +96,8 @@ shortest_paths<GraphTraits, Path_t>::shortest_paths (const graph_t &graph, idx_in_queue_with_min_dist = i; } } - gcc_assert (idx_with_min_dist != -1); + if (idx_with_min_dist == -1) + break; gcc_assert (idx_in_queue_with_min_dist != -1); // FIXME: this is confusing: there are two indices here @@ -123,7 +124,8 @@ shortest_paths<GraphTraits, Path_t>::shortest_paths (const graph_t &graph, } /* Generate an Path_t instance giving the shortest path to the node - TO from the origin node. */ + TO from the origin node. + If no such path exists, return an empty path. */ template <typename GraphTraits, typename Path_t> inline Path_t |