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

Commit 43fa595

Browse files
author
Hendrik van Antwerpen
committed
Redesign Appendable
The `Appendable` trait was confusing two concerns: (a) abstracting over things that can be added to partial paths, and (b) allowing the use of handles by tracking where handles can be looked up, represented by the `Ctx` type parameter. The new design separates these concerns. The `Appendable` only abstracts over things that can be added to partial paths (currently edges and other partial paths). A new `Index` trait is used to map handles to appendables, and passed any methods that might need to do that. En passent, a bug in the cycle detector was fixed that failed to rename variables, resuling in occasional panics.
1 parent d7d4cac commit 43fa595

File tree

4 files changed

+209
-135
lines changed

4 files changed

+209
-135
lines changed

stack-graphs/src/cycles.rs

Lines changed: 102 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,20 @@
3131
3232
use enumset::EnumSet;
3333
use smallvec::SmallVec;
34+
use std::borrow::Cow;
3435
use std::collections::HashMap;
35-
use std::marker::PhantomData;
3636

3737
use crate::arena::Handle;
3838
use crate::arena::List;
3939
use crate::arena::ListArena;
40-
use crate::graph::Edge;
4140
use crate::graph::Node;
4241
use crate::graph::StackGraph;
4342
use crate::partial::Cyclicity;
4443
use crate::partial::PartialPath;
4544
use crate::partial::PartialPaths;
4645
use crate::paths::PathResolutionError;
46+
use crate::stitching::Appendable;
4747
use crate::stitching::Database;
48-
use crate::stitching::OwnedOrDatabasePath;
4948

5049
/// Helps detect similar paths in the path-finding algorithm.
5150
pub struct SimilarPathDetector<P> {
@@ -130,68 +129,130 @@ where
130129
// ----------------------------------------------------------------------------
131130
// Cycle detector
132131

133-
pub trait Appendable<Ctx> {
134-
fn append_to(
132+
pub trait Index<'a, H, A>
133+
where
134+
A: Appendable + Clone + 'a,
135+
{
136+
fn get(&'a self, handle: &H) -> Cow<'a, A>;
137+
}
138+
139+
impl<'a> Index<'a, Handle<PartialPath>, PartialPath> for Database {
140+
fn get(&'a self, handle: &Handle<PartialPath>) -> Cow<'a, PartialPath> {
141+
Cow::Borrowed(&self[*handle])
142+
}
143+
}
144+
145+
impl<'a, A: Appendable + Clone + 'a> Index<'a, A, A> for () {
146+
fn get(&self, value: &A) -> Cow<'a, A> {
147+
Cow::Owned(value.clone())
148+
}
149+
}
150+
151+
pub struct Appendables<H>(pub(crate) ListArena<PathOrAppendable<H>>);
152+
153+
impl<H> Appendables<H> {
154+
pub fn new() -> Self {
155+
Self(ListArena::new())
156+
}
157+
}
158+
159+
#[derive(Clone)]
160+
pub(crate) enum PathOrAppendable<H> {
161+
Path(PartialPath),
162+
Handle(H),
163+
}
164+
165+
impl<H> PathOrAppendable<H>
166+
where
167+
H: Clone,
168+
{
169+
fn append_to<'a, A, Db>(
135170
&self,
136171
graph: &StackGraph,
137172
partials: &mut PartialPaths,
138-
ctx: &mut Ctx,
173+
db: &'a Db,
139174
path: &mut PartialPath,
140-
) -> Result<(), PathResolutionError>;
141-
fn start_node(&self, ctx: &mut Ctx) -> Handle<Node>;
142-
fn end_node(&self, ctx: &mut Ctx) -> Handle<Node>;
143-
}
175+
) -> Result<(), PathResolutionError>
176+
where
177+
A: Appendable + Clone + 'a,
178+
Db: Index<'a, H, A>,
179+
{
180+
match self {
181+
Self::Path(other) => other.append_to(graph, partials, path),
182+
Self::Handle(h) => db.get(h).append_to(graph, partials, path),
183+
}
184+
}
144185

145-
pub struct AppendingCycleDetector<Ctx, A>
146-
where
147-
A: Appendable<Ctx>,
148-
{
149-
appendages: List<A>,
150-
ctx: PhantomData<Ctx>,
151-
}
186+
fn start_node<'a, A, Db>(&self, db: &'a Db) -> Handle<Node>
187+
where
188+
A: Appendable + Clone + 'a,
189+
Db: Index<'a, H, A>,
190+
{
191+
match self {
192+
Self::Path(path) => path.start_node,
193+
Self::Handle(h) => db.get(h).start_node(),
194+
}
195+
}
152196

153-
impl<Ctx, A: Appendable<Ctx>> Clone for AppendingCycleDetector<Ctx, A> {
154-
fn clone(&self) -> Self {
155-
Self {
156-
appendages: self.appendages.clone(),
157-
ctx: PhantomData::default(),
197+
fn end_node<'a, A, Db>(&self, db: &'a Db) -> Handle<Node>
198+
where
199+
A: Appendable + Clone + 'a,
200+
Db: Index<'a, H, A>,
201+
{
202+
match self {
203+
Self::Path(path) => path.end_node,
204+
Self::Handle(h) => db.get(h).end_node(),
158205
}
159206
}
160207
}
161208

162-
pub type Appendables<A> = ListArena<A>;
209+
#[derive(Clone)]
210+
pub struct AppendingCycleDetector<H> {
211+
appendages: List<PathOrAppendable<H>>,
212+
}
163213

164-
impl<Ctx, A: Appendable<Ctx> + Clone> AppendingCycleDetector<Ctx, A> {
214+
impl<H> AppendingCycleDetector<H> {
165215
pub fn new() -> Self {
166216
Self {
167217
appendages: List::empty(),
168-
ctx: PhantomData::default(),
169218
}
170219
}
171220

172-
pub fn from(appendables: &mut Appendables<A>, appendage: A) -> Self {
221+
pub fn from(appendables: &mut Appendables<H>, path: PartialPath) -> Self {
173222
let mut result = Self::new();
174-
result.appendages.push_front(appendables, appendage);
223+
result
224+
.appendages
225+
.push_front(&mut appendables.0, PathOrAppendable::Path(path));
175226
result
176227
}
177228

178-
pub fn append(&mut self, appendables: &mut Appendables<A>, appendage: A) {
179-
self.appendages.push_front(appendables, appendage);
229+
pub fn append(&mut self, appendables: &mut Appendables<H>, appendage: H) {
230+
self.appendages
231+
.push_front(&mut appendables.0, PathOrAppendable::Handle(appendage));
180232
}
233+
}
181234

235+
impl<H> AppendingCycleDetector<H>
236+
where
237+
H: Clone,
238+
{
182239
/// Tests if the path is cyclic. Returns a vector indicating the kind of cycles that were found.
183240
/// If appending or concatenating all fragments succeeds, this function will never raise and error.
184-
pub fn is_cyclic(
241+
pub fn is_cyclic<'a, A, Db>(
185242
&self,
186243
graph: &StackGraph,
187244
partials: &mut PartialPaths,
188-
ctx: &mut Ctx,
189-
appendables: &mut Appendables<A>,
190-
) -> Result<EnumSet<Cyclicity>, PathResolutionError> {
245+
db: &'a Db,
246+
appendables: &mut Appendables<H>,
247+
) -> Result<EnumSet<Cyclicity>, PathResolutionError>
248+
where
249+
A: Appendable + Clone + 'a,
250+
Db: Index<'a, H, A>,
251+
{
191252
let mut cycles = EnumSet::new();
192253

193-
let end_node = match self.appendages.clone().pop_front(appendables) {
194-
Some(appendage) => appendage.end_node(ctx),
254+
let end_node = match self.appendages.clone().pop_front(&mut appendables.0) {
255+
Some(appendage) => appendage.end_node(db),
195256
None => return Ok(cycles),
196257
};
197258

@@ -201,11 +262,11 @@ impl<Ctx, A: Appendable<Ctx> + Clone> AppendingCycleDetector<Ctx, A> {
201262
// get prefix elements
202263
let mut prefix_appendages = List::empty();
203264
loop {
204-
let appendable = appendages.pop_front(appendables).cloned();
265+
let appendable = appendages.pop_front(&mut appendables.0).cloned();
205266
match appendable {
206267
Some(appendage) => {
207-
let is_cycle = appendage.start_node(ctx) == end_node;
208-
prefix_appendages.push_front(appendables, appendage);
268+
let is_cycle = appendage.start_node(db) == end_node;
269+
prefix_appendages.push_front(&mut appendables.0, appendage);
209270
if is_cycle {
210271
break;
211272
}
@@ -216,62 +277,18 @@ impl<Ctx, A: Appendable<Ctx> + Clone> AppendingCycleDetector<Ctx, A> {
216277

217278
// build prefix path -- prefix starts at end_node, because this is a cycle
218279
let mut prefix_path = PartialPath::from_node(graph, partials, end_node);
219-
while let Some(appendage) = prefix_appendages.pop_front(appendables) {
220-
prefix_path.resolve_to_node(graph, partials, appendage.start_node(ctx))?;
221-
appendage.append_to(graph, partials, ctx, &mut prefix_path)?;
280+
while let Some(appendage) = prefix_appendages.pop_front(&mut appendables.0) {
281+
appendage.append_to(graph, partials, db, &mut prefix_path)?;
222282
}
223283

224284
// build cyclic path
225285
let cyclic_path = maybe_cyclic_path
226286
.unwrap_or_else(|| PartialPath::from_node(graph, partials, end_node));
227-
prefix_path.resolve_to_node(graph, partials, cyclic_path.start_node)?;
228-
prefix_path.ensure_no_overlapping_variables(partials, &cyclic_path);
229-
prefix_path.concatenate(graph, partials, &cyclic_path)?;
287+
cyclic_path.append_to(graph, partials, &mut prefix_path)?;
230288
if let Some(cyclicity) = prefix_path.is_cyclic(graph, partials) {
231289
cycles |= cyclicity;
232290
}
233291
maybe_cyclic_path = Some(prefix_path);
234292
}
235293
}
236294
}
237-
238-
impl Appendable<()> for Edge {
239-
fn append_to(
240-
&self,
241-
graph: &StackGraph,
242-
partials: &mut PartialPaths,
243-
_: &mut (),
244-
path: &mut PartialPath,
245-
) -> Result<(), PathResolutionError> {
246-
path.append(graph, partials, *self)
247-
}
248-
249-
fn start_node(&self, _: &mut ()) -> Handle<Node> {
250-
self.source
251-
}
252-
253-
fn end_node(&self, _: &mut ()) -> Handle<Node> {
254-
self.sink
255-
}
256-
}
257-
258-
impl Appendable<Database> for OwnedOrDatabasePath {
259-
fn append_to(
260-
&self,
261-
graph: &StackGraph,
262-
partials: &mut PartialPaths,
263-
db: &mut Database,
264-
path: &mut PartialPath,
265-
) -> Result<(), PathResolutionError> {
266-
path.ensure_no_overlapping_variables(partials, self.get(db));
267-
path.concatenate(graph, partials, self.get(db))
268-
}
269-
270-
fn start_node(&self, db: &mut Database) -> Handle<Node> {
271-
self.get(db).start_node
272-
}
273-
274-
fn end_node(&self, db: &mut Database) -> Handle<Node> {
275-
self.get(db).end_node
276-
}
277-
}

stack-graphs/src/partial.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,13 +2225,13 @@ impl PartialPath {
22252225
/// as a parameter, instead of building it up ourselves, so that you have control over which
22262226
/// particular collection type to use, and so that you can reuse result collections across
22272227
/// multiple calls.
2228-
fn extend<R: Extend<(PartialPath, AppendingCycleDetector<(), Edge>)>>(
2228+
fn extend<R: Extend<(PartialPath, AppendingCycleDetector<Edge>)>>(
22292229
&self,
22302230
graph: &StackGraph,
22312231
partials: &mut PartialPaths,
22322232
file: Option<Handle<File>>,
22332233
edges: &mut Appendables<Edge>,
2234-
path_cycle_detector: AppendingCycleDetector<(), Edge>,
2234+
path_cycle_detector: AppendingCycleDetector<Edge>,
22352235
result: &mut R,
22362236
) {
22372237
let extensions = graph.outgoing_edges(self.end_node);
@@ -2533,7 +2533,7 @@ impl PartialPaths {
25332533
copious_debugging!(" * visit");
25342534
visit(graph, self, path);
25352535
} else if !path_cycle_detector
2536-
.is_cyclic(graph, self, &mut (), &mut edges)
2536+
.is_cyclic(graph, self, &(), &mut edges)
25372537
.expect("cyclic test failed when finding partial paths")
25382538
.is_empty()
25392539
{
@@ -2592,7 +2592,7 @@ impl PartialPaths {
25922592
visit(graph, self, path.clone());
25932593
}
25942594
if !path_cycle_detector
2595-
.is_cyclic(graph, &mut partials, &mut (), &mut edges)
2595+
.is_cyclic(graph, &mut partials, &(), &mut edges)
25962596
.expect("cyclic test failed when finding complete paths")
25972597
.into_iter()
25982598
.all(|c| c == Cyclicity::StrengthensPrecondition)

0 commit comments

Comments
 (0)