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

Commit 611fb4d

Browse files
committed
Allow stitching from arbitrary partial paths
You can now instantiate the forward partial path stitcher with an explicit list of initial partial paths. From there, the phased algorithm proceeds exactly as before. This lets us implement “find qualified definitions”, where we look for any definition that is reachable from a particular fully qualified name (expressed as a symbol stack).
1 parent d152e15 commit 611fb4d

8 files changed

Lines changed: 455 additions & 13 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ jobs:
3434
${{ runner.OS }}-cargo-
3535
- name: Install cargo-valgrind
3636
run: |
37+
sudo apt-get update
3738
sudo apt-get install -y valgrind
3839
cargo install cargo-valgrind
3940
- name: Build library

‎stack-graphs/CHANGELOG.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Unreleased
9+
10+
### Added
11+
12+
- The new `ForwardPartialPathStitcher::from_partial_paths` constructor lets you
13+
seed a path stitching search with a specific list of partial paths (which need
14+
not be in the `Database`). This can be used, for instance, to implement “find
15+
qualified definitions”, where we look for any definition of a fully qualified
16+
name (expressed as a symbol stack). There is a new test case that shows an
17+
example implementation.
18+
19+
### Changed
20+
21+
- The `ForwardPartialPathStitcher::new` constructor has been renamed to
22+
`from_nodes`, to be more consistent with the new `from_partial_paths`
23+
constructor.
24+
825
## stack-graphs 0.5.0 - 2022-02-17
926

1027
### Added

‎stack-graphs/include/stack-graphs.h‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,11 +1039,24 @@ void sg_forward_path_stitcher_free(struct sg_forward_path_stitcher *stitcher);
10391039
// must ensure that `db` contains all possible extensions of any of those initial partial paths.
10401040
// You can retrieve a list of those extensions via the `previous_phase_partial_paths` and
10411041
// `previous_phase_partial_paths_length` fields.
1042-
struct sg_forward_partial_path_stitcher *sg_forward_partial_path_stitcher_new(const struct sg_stack_graph *graph,
1043-
struct sg_partial_path_arena *partials,
1044-
struct sg_partial_path_database *db,
1045-
size_t count,
1046-
const sg_node_handle *starting_nodes);
1042+
struct sg_forward_partial_path_stitcher *sg_forward_partial_path_stitcher_from_nodes(const struct sg_stack_graph *graph,
1043+
struct sg_partial_path_arena *partials,
1044+
struct sg_partial_path_database *db,
1045+
size_t count,
1046+
const sg_node_handle *starting_nodes);
1047+
1048+
// Creates a new forward partial path stitcher that is "seeded" with a set of initial partial
1049+
// paths.
1050+
//
1051+
// Before calling `sg_forward_partial_path_stitcher_process_next_phase` for the first time, you
1052+
// must ensure that `db` contains all possible extensions of any of those initial partial paths.
1053+
// You can retrieve a list of those extensions via the `previous_phase_partial_paths` and
1054+
// `previous_phase_partial_paths_length` fields.
1055+
struct sg_forward_partial_path_stitcher *sg_forward_partial_path_stitcher_from_partial_paths(const struct sg_stack_graph *_graph,
1056+
struct sg_partial_path_arena *partials,
1057+
struct sg_partial_path_database *_db,
1058+
size_t count,
1059+
const struct sg_partial_path *initial_partial_paths);
10471060

10481061
// Sets the maximum amount of work that can be performed during each phase of the algorithm. By
10491062
// bounding our work this way, you can ensure that it's not possible for our CPU-bound algorithm

‎stack-graphs/src/c.rs‎

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,7 +1918,7 @@ impl InternalForwardPartialPathStitcher {
19181918
/// You can retrieve a list of those extensions via the `previous_phase_partial_paths` and
19191919
/// `previous_phase_partial_paths_length` fields.
19201920
#[no_mangle]
1921-
pub extern "C" fn sg_forward_partial_path_stitcher_new(
1921+
pub extern "C" fn sg_forward_partial_path_stitcher_from_nodes(
19221922
graph: *const sg_stack_graph,
19231923
partials: *mut sg_partial_path_arena,
19241924
db: *mut sg_partial_path_database,
@@ -1929,7 +1929,7 @@ pub extern "C" fn sg_forward_partial_path_stitcher_new(
19291929
let partials = unsafe { &mut (*partials).inner };
19301930
let db = unsafe { &mut (*db).inner };
19311931
let starting_nodes = unsafe { std::slice::from_raw_parts(starting_nodes, count) };
1932-
let stitcher = ForwardPartialPathStitcher::new(
1932+
let stitcher = ForwardPartialPathStitcher::from_nodes(
19331933
graph,
19341934
partials,
19351935
db,
@@ -1940,6 +1940,30 @@ pub extern "C" fn sg_forward_partial_path_stitcher_new(
19401940
))) as *mut _
19411941
}
19421942

1943+
/// Creates a new forward partial path stitcher that is "seeded" with a set of initial partial
1944+
/// paths.
1945+
///
1946+
/// Before calling `sg_forward_partial_path_stitcher_process_next_phase` for the first time, you
1947+
/// must ensure that `db` contains all possible extensions of any of those initial partial paths.
1948+
/// You can retrieve a list of those extensions via the `previous_phase_partial_paths` and
1949+
/// `previous_phase_partial_paths_length` fields.
1950+
#[no_mangle]
1951+
pub extern "C" fn sg_forward_partial_path_stitcher_from_partial_paths(
1952+
_graph: *const sg_stack_graph,
1953+
partials: *mut sg_partial_path_arena,
1954+
_db: *mut sg_partial_path_database,
1955+
count: usize,
1956+
initial_partial_paths: *const sg_partial_path,
1957+
) -> *mut sg_forward_partial_path_stitcher {
1958+
let partials = unsafe { &mut (*partials).inner };
1959+
let initial_partial_paths =
1960+
unsafe { std::slice::from_raw_parts(initial_partial_paths as *const PartialPath, count) };
1961+
let stitcher = ForwardPartialPathStitcher::from_partial_paths(initial_partial_paths.to_vec());
1962+
Box::into_raw(Box::new(InternalForwardPartialPathStitcher::new(
1963+
stitcher, partials,
1964+
))) as *mut _
1965+
}
1966+
19431967
/// Sets the maximum amount of work that can be performed during each phase of the algorithm. By
19441968
/// bounding our work this way, you can ensure that it's not possible for our CPU-bound algorithm
19451969
/// to starve any worker threads or processes that you might be using. If you don't call this

‎stack-graphs/src/stitching.rs‎

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ impl ForwardPartialPathStitcher {
704704
///
705705
/// [`previous_phase_partial paths`]: #method.previous_phase_partial paths
706706
/// [`process_next_phase`]: #method.process_next_phase
707-
pub fn new<I>(
707+
pub fn from_nodes<I>(
708708
graph: &StackGraph,
709709
partials: &mut PartialPaths,
710710
db: &mut Database,
@@ -743,6 +743,31 @@ impl ForwardPartialPathStitcher {
743743
}
744744
}
745745

746+
/// Creates a new forward partial path stitcher that is "seeded" with a set of initial partial
747+
/// paths.
748+
///
749+
/// Before calling [`process_next_phase`][] for the first time, you must ensure that `db`
750+
/// contains all possible extensions of any of those initial partial paths. You can retrieve a
751+
/// list of those extensions via [`previous_phase_partial paths`][].
752+
///
753+
/// [`previous_phase_partial paths`]: #method.previous_phase_partial paths
754+
/// [`process_next_phase`]: #method.process_next_phase
755+
pub fn from_partial_paths(
756+
initial_partial_paths: Vec<PartialPath>,
757+
) -> ForwardPartialPathStitcher {
758+
let next_iteration = initial_partial_paths.into();
759+
ForwardPartialPathStitcher {
760+
candidate_partial_paths: Vec::new(),
761+
queue: VecDeque::new(),
762+
next_iteration,
763+
cycle_detector: CycleDetector::new(),
764+
// By default, there's no artificial bound on the amount of work done per phase
765+
max_work_per_phase: usize::MAX,
766+
#[cfg(feature = "copious-debugging")]
767+
phase_number: 1,
768+
}
769+
}
770+
746771
/// Returns an iterator of all of the (possibly incomplete) partial paths that were encountered
747772
/// during the most recent phase of the algorithm.
748773
pub fn previous_phase_partial_paths(&self) -> impl Iterator<Item = &PartialPath> + '_ {
@@ -822,8 +847,8 @@ impl ForwardPartialPathStitcher {
822847
copious_debugging!(" is invalid: {:?}", err);
823848
continue;
824849
}
825-
if !new_partial_path.starts_at_reference(graph) {
826-
copious_debugging!(" is invalid: slips off of reference");
850+
if new_partial_path.start_node != partial_path.start_node {
851+
copious_debugging!(" is invalid: slips off of starting node");
827852
continue;
828853
}
829854
if let Err(err) = new_partial_path.resolve(graph, partials) {
@@ -912,7 +937,8 @@ impl ForwardPartialPathStitcher {
912937
I: IntoIterator<Item = Handle<Node>>,
913938
{
914939
let mut result = Vec::new();
915-
let mut stitcher = ForwardPartialPathStitcher::new(graph, partials, db, starting_nodes);
940+
let mut stitcher =
941+
ForwardPartialPathStitcher::from_nodes(graph, partials, db, starting_nodes);
916942
while !stitcher.is_complete() {
917943
let complete_partial_paths = stitcher
918944
.previous_phase_partial_paths()

0 commit comments

Comments
 (0)