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

Commit 85552bd

Browse files
author
Hendrik van Antwerpen
committed
Fixes in cycle detection
1 parent da8dbbe commit 85552bd

File tree

7 files changed

+273
-205
lines changed

7 files changed

+273
-205
lines changed

stack-graphs/src/c.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1991,17 +1991,19 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_nodes(
19911991
pub extern "C" fn sg_forward_partial_path_stitcher_from_partial_paths(
19921992
graph: *const sg_stack_graph,
19931993
partials: *mut sg_partial_path_arena,
1994-
_db: *mut sg_partial_path_database,
1994+
db: *mut sg_partial_path_database,
19951995
count: usize,
19961996
initial_partial_paths: *const sg_partial_path,
19971997
) -> *mut sg_forward_partial_path_stitcher {
19981998
let graph = unsafe { &(*graph).inner };
19991999
let partials = unsafe { &mut (*partials).inner };
2000+
let db = unsafe { &mut (*db).inner };
20002001
let initial_partial_paths =
20012002
unsafe { std::slice::from_raw_parts(initial_partial_paths as *const PartialPath, count) };
20022003
let stitcher = ForwardPartialPathStitcher::from_partial_paths(
20032004
graph,
20042005
partials,
2006+
db,
20052007
initial_partial_paths.to_vec(),
20062008
);
20072009
Box::into_raw(Box::new(InternalForwardPartialPathStitcher::new(

stack-graphs/src/cycles.rs

Lines changed: 76 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
//! always use this particular heuristic, however! We reserve the right to change the heuristic at
3030
//! any time.
3131
32-
use std::collections::{HashMap, VecDeque};
32+
use std::collections::HashMap;
33+
use std::collections::VecDeque;
3334

3435
use smallvec::SmallVec;
3536

@@ -135,137 +136,117 @@ where
135136
}
136137

137138
#[derive(Clone)]
138-
pub struct AppendingCycleDetector {
139-
nodes: VecDeque<Handle<Node>>,
139+
pub struct EdgeAppendingCycleDetector {
140+
edges: VecDeque<Edge>,
140141
}
141142

142-
impl AppendingCycleDetector {
143-
pub fn from_node(node: Handle<Node>) -> Self {
143+
impl EdgeAppendingCycleDetector {
144+
pub fn new() -> Self {
144145
Self {
145-
nodes: vec![node].into(),
146+
edges: vec![].into(),
146147
}
147148
}
148149

149-
pub fn appended(
150+
pub fn append_edge(
150151
&mut self,
151152
graph: &StackGraph,
152153
partials: &mut PartialPaths,
153-
appended_node: Handle<Node>,
154-
) -> bool {
155-
// find last occurrence of the appended node
156-
let i = self.nodes.iter().rposition(|n| *n == appended_node);
157-
158-
// add new node
159-
self.nodes.push(appended_node);
160-
161-
// check if the appended node was found
162-
let i = match i {
163-
Some(i) => i,
164-
None => return true,
165-
};
154+
edge: Edge,
155+
) -> Result<(), ()> {
156+
let end_node = edge.sink;
157+
self.edges.push(edge);
158+
159+
let mut cyclic_path = PartialPath::from_node(graph, partials, end_node);
160+
let mut last_idx = self.edges.len();
161+
for idx in (0..last_idx).rev() {
162+
let edge = self.edges[idx];
163+
if edge.source != cyclic_path.end_node {
164+
continue;
165+
}
166166

167-
// construct cyclic path
168-
let mut cyclic_path = PartialPath::from_node(graph, partials, self.nodes[i]);
169-
for node in self.nodes.range(i + 1..) {
170-
if cyclic_path.ends_in_jump(graph) {
171-
// restore jump target
172-
cyclic_path
173-
.scope_stack_precondition
174-
.push_back(partials, *node);
175-
cyclic_path
176-
.scope_stack_postcondition
177-
.push_front(partials, *node);
178-
cyclic_path.resolve(graph, partials).unwrap();
167+
let mut prefix_path = PartialPath::from_node(graph, partials, edge.source);
168+
for idx in idx..last_idx {
169+
let edge = self.edges[idx];
170+
prefix_path
171+
.resolve_to(graph, partials, edge.source)
172+
.unwrap();
173+
prefix_path.append(graph, partials, edge).unwrap();
179174
}
180-
cyclic_path
181-
.append(
182-
graph,
183-
partials,
184-
Edge {
185-
source: cyclic_path.end_node,
186-
sink: *node,
187-
precedence: 0,
188-
},
189-
)
175+
last_idx = idx;
176+
prefix_path.ensure_no_overlapping_variables(partials, &cyclic_path);
177+
prefix_path
178+
.concatenate(graph, partials, &cyclic_path)
190179
.unwrap();
180+
cyclic_path = prefix_path;
181+
182+
if !cyclic_path.is_productive(graph, partials) {
183+
return Err(());
184+
}
191185
}
192186

193-
// process path if it is productive
194-
cyclic_path.is_productive(graph, partials)
187+
Ok(())
195188
}
196189
}
197190

198191
#[derive(Clone)]
199-
pub struct JoiningCycleDetector {
192+
pub struct PartialPathAppendingCycleDetector {
200193
paths: VecDeque<OwnedOrDatabasePath>,
201194
}
202195

203-
impl JoiningCycleDetector {
196+
impl PartialPathAppendingCycleDetector {
204197
pub fn from_partial_path(
205198
_graph: &StackGraph,
206199
_partials: &mut PartialPaths,
207-
path: PartialPath,
208-
) -> Self {
209-
Self {
210-
paths: vec![path.into()].into(),
211-
}
212-
}
213-
214-
pub fn from_partial_path_handle(
215-
_graph: &StackGraph,
216-
_partials: &mut PartialPaths,
217-
_db: &Database,
218-
path: Handle<PartialPath>,
200+
_db: &mut Database,
201+
path: OwnedOrDatabasePath,
219202
) -> Self {
220203
Self {
221-
paths: vec![path.into()].into(),
204+
paths: vec![path].into(),
222205
}
223206
}
224207

225-
pub fn joined(
208+
pub fn append_partial_path(
226209
&mut self,
227210
graph: &StackGraph,
228211
partials: &mut PartialPaths,
229212
db: &Database,
230-
joined_path: OwnedOrDatabasePath,
231-
) -> bool {
232-
// add new fragment
233-
self.paths.push(joined_path.into());
234-
let joined_path = self.paths.back().unwrap().get(db);
235-
236-
// find a fragment starting with our end node
237-
// (includes new fragment, in case that is cyclic)
238-
let end_node = joined_path.end_node;
239-
let i = match self
240-
.paths
241-
.iter()
242-
.rposition(|p| p.get(db).start_node == end_node)
243-
{
244-
Some(i) => i,
245-
None => return true,
246-
};
213+
path: OwnedOrDatabasePath,
214+
) -> Result<(), ()> {
215+
let end_node = path.get(db).end_node;
216+
self.paths.push(path);
217+
218+
let mut cyclic_path = PartialPath::from_node(graph, partials, end_node);
219+
let mut last_idx = self.paths.len();
220+
for idx in (0..last_idx).rev() {
221+
let path = self.paths[idx].get(db);
222+
if path.start_node != cyclic_path.end_node {
223+
continue;
224+
}
247225

248-
// construct the cyclic path
249-
let mut cyclic_path = self.paths[i].get(db).clone();
250-
for path in self.paths.range(i + 1..) {
251-
let path = path.get(db);
252-
if cyclic_path.ends_in_jump(graph) {
253-
// restore jump target
254-
cyclic_path
255-
.scope_stack_precondition
256-
.push_back(partials, path.start_node);
257-
cyclic_path
258-
.scope_stack_postcondition
259-
.push_front(partials, path.start_node);
260-
cyclic_path.resolve(graph, partials).unwrap();
226+
let mut prefix_path = path.clone();
227+
for idx in (idx + 1)..last_idx {
228+
let path = self.paths[idx].get(db);
229+
prefix_path
230+
.resolve_to(graph, partials, path.start_node)
231+
.unwrap();
232+
prefix_path.ensure_no_overlapping_variables(partials, path);
233+
prefix_path.concatenate(graph, partials, path).unwrap();
261234
}
262-
cyclic_path.ensure_no_overlapping_variables(partials, path);
263-
cyclic_path
264-
.append_partial_path(graph, partials, path)
235+
last_idx = idx;
236+
prefix_path
237+
.resolve_to(graph, partials, cyclic_path.start_node)
238+
.unwrap();
239+
prefix_path.ensure_no_overlapping_variables(partials, &cyclic_path);
240+
prefix_path
241+
.concatenate(graph, partials, &cyclic_path)
265242
.unwrap();
243+
cyclic_path = prefix_path;
244+
245+
if !cyclic_path.is_productive(graph, partials) {
246+
return Err(());
247+
}
266248
}
267249

268-
// process path if it is productive
269-
cyclic_path.is_productive(graph, partials)
250+
Ok(())
270251
}
271252
}

0 commit comments

Comments
 (0)