Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit 67625bf

Browse files
author
Hendrik van Antwerpen
committed
Replicate old partial path behavior based on minimal partial paths set
- Drop the feature flag and provide the `ForwardPartialPathStitcher::find_all_locally_complete_paths` that mimics the old behavior. - Drop `PartialPath::{is_complete_as_possible,as_complete_as_necessary}` and make that logic local to the path finding algorithms instead. - Allow `root` to be used as a start node for path stitching. - Many alpha-equivalent updates to test expectations.
1 parent 7ae64b2 commit 67625bf

17 files changed

+349
-229
lines changed

stack-graphs/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ edition = "2018"
1515
[features]
1616
copious-debugging = []
1717
json = ["serde", "serde_json"]
18-
partial-path-finding-v1 = []
1918

2019
[lib]
2120
# All of our tests are in the tests/it "integration" test executable.

stack-graphs/src/arena.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,15 @@ impl<H, T> SupplementalArena<H, T> {
315315
pub fn len(&self) -> usize {
316316
self.items.len()
317317
}
318+
319+
/// Iterate over the items in this arena.
320+
pub(crate) fn iter(&self) -> impl Iterator<Item = (Handle<T>, &T)> {
321+
self.items
322+
.iter()
323+
.enumerate()
324+
.skip(1)
325+
.map(|(i, x)| (Handle::from_some(i as u32), unsafe { &*(x.as_ptr()) }))
326+
}
318327
}
319328

320329
impl<H, T> SupplementalArena<H, T>

stack-graphs/src/assert.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,16 @@ impl Assertion {
159159

160160
let mut actual_paths = Vec::new();
161161
for reference in &references {
162-
let reference_paths = ForwardPartialPathStitcher::find_all_complete_partial_paths(
162+
let mut reference_paths = Vec::new();
163+
ForwardPartialPathStitcher::find_all_complete_partial_paths(
163164
graph,
164165
partials,
165166
db,
166167
vec![*reference],
167168
cancellation_flag,
169+
|_, _, p| {
170+
reference_paths.push(p.clone());
171+
},
168172
)?;
169173
for reference_path in &reference_paths {
170174
if reference_paths

stack-graphs/src/c.rs

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,17 +1576,44 @@ pub extern "C" fn sg_partial_path_arena_find_partial_paths_in_file(
15761576
let partial_path_list = unsafe { &mut *partial_path_list };
15771577
let cancellation_flag: Option<&AtomicUsize> =
15781578
unsafe { std::mem::transmute(cancellation_flag.as_ref()) };
1579-
partials
1580-
.find_all_partial_paths_in_file(
1581-
graph,
1582-
file,
1583-
&AtomicUsizeCancellationFlag(cancellation_flag),
1584-
|_graph, partials, mut path| {
1585-
path.ensure_both_directions(partials);
1586-
partial_path_list.partial_paths.push(path);
1587-
},
1588-
)
1589-
.into()
1579+
sg_partial_path_arena_find_partial_paths_in_file_inner(
1580+
graph,
1581+
partials,
1582+
file,
1583+
partial_path_list,
1584+
&AtomicUsizeCancellationFlag(cancellation_flag),
1585+
)
1586+
.into()
1587+
}
1588+
1589+
fn sg_partial_path_arena_find_partial_paths_in_file_inner(
1590+
graph: &StackGraph,
1591+
partials: &mut PartialPaths,
1592+
file: Handle<File>,
1593+
partial_path_list: &mut sg_partial_path_list,
1594+
cancellation_flag: &dyn CancellationFlag,
1595+
) -> Result<(), CancellationError> {
1596+
let mut db = Database::new();
1597+
partials.find_minimal_partial_paths_set_in_file(
1598+
graph,
1599+
file,
1600+
cancellation_flag,
1601+
|graph, partials, path| {
1602+
db.add_partial_path(graph, partials, path);
1603+
},
1604+
)?;
1605+
ForwardPartialPathStitcher::find_locally_complete_partial_paths(
1606+
graph,
1607+
partials,
1608+
&mut db,
1609+
cancellation_flag,
1610+
|_graph, partials, path| {
1611+
let mut path = path.clone();
1612+
path.ensure_both_directions(partials);
1613+
partial_path_list.partial_paths.push(path);
1614+
},
1615+
)?;
1616+
Ok(())
15901617
}
15911618

15921619
/// A handle to a partial path in a partial path database. A zero handle represents a missing

stack-graphs/src/partial.rs

Lines changed: 16 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,7 +1967,7 @@ impl PartialPath {
19671967
&mut symbol_stack_postcondition,
19681968
&mut scope_stack_postcondition,
19691969
)
1970-
.expect("");
1970+
.expect("lifting single nodes to partial paths should not fail");
19711971

19721972
PartialPath {
19731973
start_node: node,
@@ -2056,56 +2056,11 @@ impl PartialPath {
20562056
}
20572057

20582058
pub fn starts_at_endpoint(&self, graph: &StackGraph) -> bool {
2059-
let node = &graph[self.start_node];
2060-
node.is_endpoint()
2059+
graph[self.start_node].is_endpoint()
20612060
}
20622061

20632062
pub fn ends_at_endpoint(&self, graph: &StackGraph) -> bool {
2064-
let node = &graph[self.end_node];
2065-
node.is_endpoint() || node.is_jump_to()
2066-
}
2067-
2068-
#[cfg(not(feature = "partial-path-finding-v1"))]
2069-
pub fn as_complete_as_necessary(&self, graph: &StackGraph) -> bool {
2070-
self.starts_at_endpoint(graph) && self.ends_at_endpoint(graph)
2071-
}
2072-
2073-
/// A partial path is _as complete as possible_ if we cannot extend it any further within the
2074-
/// current file. This represents the maximal amount of work that we can pre-compute at index
2075-
/// time.
2076-
#[cfg(feature = "partial-path-finding-v1")]
2077-
pub fn is_complete_as_possible(&self, graph: &StackGraph) -> bool {
2078-
match &graph[self.start_node] {
2079-
Node::Root(_) => (),
2080-
node @ Node::Scope(_) => {
2081-
if !node.is_exported_scope() {
2082-
return false;
2083-
}
2084-
}
2085-
node @ Node::PushScopedSymbol(_) | node @ Node::PushSymbol(_) => {
2086-
if !node.is_reference() {
2087-
return false;
2088-
} else if !self.symbol_stack_precondition.can_match_empty() {
2089-
return false;
2090-
}
2091-
}
2092-
_ => return false,
2093-
}
2094-
2095-
match &graph[self.end_node] {
2096-
Node::Root(_) => (),
2097-
Node::JumpTo(_) => (),
2098-
node @ Node::PopScopedSymbol(_) | node @ Node::PopSymbol(_) => {
2099-
if !node.is_definition() {
2100-
return false;
2101-
} else if !self.symbol_stack_postcondition.can_match_empty() {
2102-
return false;
2103-
}
2104-
}
2105-
_ => return false,
2106-
}
2107-
2108-
true
2063+
graph[self.end_node].is_endpoint()
21092064
}
21102065

21112066
/// Returns whether a partial path is "productive" — that is, whether it adds useful
@@ -2505,15 +2460,16 @@ impl Node {
25052460
}
25062461
}
25072462

2508-
#[cfg(feature = "partial-path-finding-v1")]
25092463
impl PartialPaths {
2510-
/// Finds all partial paths in a file, calling the `visit` closure for each one.
2464+
/// Finds a minimal set of partial paths in a file, calling the `visit` closure for each one.
25112465
///
2512-
/// This function ensures that the set of visited partial paths covers all complete
2513-
/// paths, from references to definitions, when used for path stitching. Callers are
2514-
/// advised _not_ to filter this set in the visitor using functions like
2515-
/// [`PartialPath::is_complete_as_possible`][] or [`PartialPath::is_productive`][] as
2516-
/// that may interfere with implementation changes of this function.
2466+
/// This function ensures that the set of visited partial paths
2467+
/// (a) is minimal, no path can be constructed by stitching other paths in the set, and
2468+
/// (b) covers all complete paths, from references to definitions, when used for path stitching
2469+
///
2470+
/// Callers are advised _not_ to filter this set in the visitor using functions like
2471+
/// [`PartialPath::is_productive`][] as that may interfere with implementation changes of this
2472+
/// function.
25172473
///
25182474
/// This function will not return until all reachable partial paths have been processed, so
25192475
/// `graph` must already contain a complete stack graph. If you have a very large stack graph
@@ -2522,7 +2478,7 @@ impl PartialPaths {
25222478
/// [`PartialPath::extend`][] manually.
25232479
///
25242480
/// [`PartialPath::extend`]: struct.PartialPath.html#method.extend
2525-
pub fn find_all_partial_paths_in_file<F>(
2481+
pub fn find_minimal_partial_paths_set_in_file<F>(
25262482
&mut self,
25272483
graph: &StackGraph,
25282484
file: Handle<File>,
@@ -2532,64 +2488,11 @@ impl PartialPaths {
25322488
where
25332489
F: FnMut(&StackGraph, &mut PartialPaths, PartialPath),
25342490
{
2535-
let mut cycle_detector = CycleDetector::new();
2536-
let mut queue = VecDeque::new();
2537-
queue.push_back(PartialPath::from_node(graph, self, StackGraph::root_node()));
2538-
queue.extend(
2539-
graph
2540-
.nodes_for_file(file)
2541-
.filter(|node| match &graph[*node] {
2542-
node @ Node::PushScopedSymbol(_) => node.is_reference(),
2543-
node @ Node::PushSymbol(_) => node.is_reference(),
2544-
node @ Node::Scope(_) => node.is_exported_scope(),
2545-
_ => false,
2546-
})
2547-
.map(|node| PartialPath::from_node(graph, self, node)),
2548-
);
2549-
while let Some(path) = queue.pop_front() {
2550-
cancellation_flag.check("finding partial paths in file")?;
2551-
if !cycle_detector.should_process_path(&path, |probe| probe.cmp(graph, self, &path)) {
2552-
continue;
2553-
}
2554-
let should_extend = path.end_node == path.start_node || !graph[path.end_node].is_root();
2555-
if should_extend {
2556-
path.extend_from_file(graph, self, file, &mut queue);
2557-
}
2558-
if path.is_complete_as_possible(graph) && path.is_productive(self) {
2559-
visit(graph, self, path);
2560-
}
2491+
fn as_complete_as_necessary(graph: &StackGraph, path: &PartialPath) -> bool {
2492+
path.starts_at_endpoint(graph)
2493+
&& (path.ends_at_endpoint(graph) || graph[path.end_node].is_jump_to())
25612494
}
2562-
Ok(())
2563-
}
2564-
}
25652495

2566-
#[cfg(not(feature = "partial-path-finding-v1"))]
2567-
impl PartialPaths {
2568-
/// Finds all partial paths in a file, calling the `visit` closure for each one.
2569-
///
2570-
/// This function ensures that the set of visited partial paths covers all complete
2571-
/// paths, from references to definitions, when used for path stitching. Callers are
2572-
/// advised _not_ to filter this set in the visitor using functions like
2573-
/// [`PartialPath::is_complete_as_possible`][] or [`PartialPath::is_productive`][] as
2574-
/// that may interfere with implementation changes of this function.
2575-
///
2576-
/// This function will not return until all reachable partial paths have been processed, so
2577-
/// `graph` must already contain a complete stack graph. If you have a very large stack graph
2578-
/// stored in some other storage system, and want more control over lazily loading only the
2579-
/// necessary pieces, then you should code up your own loop that calls
2580-
/// [`PartialPath::extend`][] manually.
2581-
///
2582-
/// [`PartialPath::extend`]: struct.PartialPath.html#method.extend
2583-
pub fn find_all_partial_paths_in_file<F>(
2584-
&mut self,
2585-
graph: &StackGraph,
2586-
file: Handle<File>,
2587-
cancellation_flag: &dyn CancellationFlag,
2588-
mut visit: F,
2589-
) -> Result<(), CancellationError>
2590-
where
2591-
F: FnMut(&StackGraph, &mut PartialPaths, PartialPath),
2592-
{
25932496
copious_debugging!("Find all partial paths in {}", graph[file]);
25942497
let mut cycle_detector = CycleDetector::new();
25952498
let mut queue = VecDeque::new();
@@ -2604,7 +2507,7 @@ impl PartialPaths {
26042507
cancellation_flag.check("finding partial paths in file")?;
26052508
let is_seed = path.start_node == path.end_node && path.edges.len() == 0;
26062509
copious_debugging!(" => {}", path.display(graph, self));
2607-
if !is_seed && path.as_complete_as_necessary(graph) {
2510+
if !is_seed && as_complete_as_necessary(graph, &path) {
26082511
copious_debugging!(" * as complete as necessary");
26092512
if !path.is_productive(self) {
26102513
copious_debugging!(" * not productive");

0 commit comments

Comments
 (0)