Skip to content

Commit 3261b21

Browse files
Propagate nested container dirty ids
1 parent d330f2c commit 3261b21

4 files changed

Lines changed: 52 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## [Unreleased] - ReleaseDate
44

5+
- Fix seminaive matching after nested containers rebuild in place by propagating dirty container ids through parent containers.
56
- Desugar `relation`s to `constructor`s to simplify the language and implementation. Relations no longer return unit `()` values.
67
- Refactored API to use [`TermId`] more consistently instead of `Term` where possible, simplifying egglog code.
78
- **Typed primitive surface for seminaive safety (#772).** Custom primitives now pick one of `PurePrim` / `ReadPrim` / `WritePrim` / `FullPrim` based on what the body needs, and register via the matching `add_*_primitive`. Rust enforces capability bounds via the state wrapper passed to the body; the egglog typechecker enforces context bounds. See the `egglog::exec_state` module docs and the `*Prim` trait docs for the full picture. Migration: `rust_rule` callbacks now take `&mut WriteState` (replacing `RustRuleContext`); a new `rust_rule_full` gives action callbacks read access. Higher-order primitives over `unstable-fn` values dispatch via `state.apply_function(&fc, args)`.

core-relations/src/containers/mod.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ pub struct ContainerValues {
8181
/// `dirty_ids` is narrower: it records container ids whose semantics changed
8282
/// while their stored outer id stayed stable. Ordinary table rebuild already
8383
/// handles changed-id cases; these ids need a follow-up parent-row refresh.
84+
/// This includes containers that changed directly and containers whose
85+
/// contained containers changed in place.
8486
///
8587
/// For example, `l(vec-of(w(k(b))))` can rebuild to `l(vec-of(k(b)))` without
8688
/// changing the `Vec` id. The row is now newly matchable, but seminaive will
@@ -174,7 +176,7 @@ impl ContainerValues {
174176
// We may attempt an incremental rebuild.
175177
self.subset_tracker.recent_updates(table_id, table)
176178
});
177-
if parallelize_inter_container_op(self.data.next_id().index()) {
179+
let mut summary = if parallelize_inter_container_op(self.data.next_id().index()) {
178180
self.data
179181
.iter_mut()
180182
.zip(std::iter::repeat_with(|| exec_state.clone()))
@@ -202,6 +204,27 @@ impl ContainerValues {
202204
));
203205
}
204206
summary
207+
};
208+
self.expand_dirty_id_closure(&mut summary);
209+
summary
210+
}
211+
212+
fn expand_dirty_id_closure(&self, summary: &mut ContainerRebuildSummary) {
213+
let mut seen = summary.dirty_ids.clone();
214+
let mut frontier = seen.clone();
215+
216+
while !frontier.is_empty() {
217+
let mut next = IndexSet::default();
218+
for (_, env) in self.data.iter() {
219+
env.extend_containers_containing(&frontier, &mut next);
220+
}
221+
frontier.clear();
222+
for value in next {
223+
if seen.insert(value) {
224+
summary.note_dirty_id(value);
225+
frontier.insert(value);
226+
}
227+
}
205228
}
206229
}
207230

@@ -252,6 +275,7 @@ pub trait DynamicContainerEnv: Any + dyn_clone::DynClone + Send + Sync {
252275
subset: Option<SubsetRef>,
253276
exec_state: &mut ExecutionState,
254277
) -> ContainerRebuildSummary;
278+
fn extend_containers_containing(&self, values: &IndexSet<Value>, out: &mut IndexSet<Value>);
255279
}
256280

257281
// Implements `Clone` for `Box<dyn DynamicContainerEnv>`.
@@ -302,6 +326,14 @@ impl<C: ContainerValue> DynamicContainerEnv for ContainerEnv<C> {
302326
}
303327
self.apply_rebuild_nonincremental(rebuilder, exec_state)
304328
}
329+
330+
fn extend_containers_containing(&self, values: &IndexSet<Value>, out: &mut IndexSet<Value>) {
331+
for value in values {
332+
if let Some(containers) = self.val_index.get(value) {
333+
out.extend(containers.iter().copied());
334+
}
335+
}
336+
}
305337
}
306338

307339
impl<C: ContainerValue> ContainerEnv<C> {

tests/container-fail.egg

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,20 @@
2020
(let $resolved (l (vec-of (k (b)))))
2121
(run-schedule (saturate (run)))
2222
(check (= $resolved (b)))
23+
24+
;; Regression for nested container dirty propagation. The inner Vec rebuilds in
25+
;; place, making the outer Vec newly matchable without changing the outer id.
26+
(sort VVE (Vec VE))
27+
(constructor p (VVE) E)
28+
29+
(rewrite (p (vec-of (vec-of (b)))) (b))
30+
31+
(push)
32+
(let $nested (p (vec-of (vec-of (w (b))))))
33+
(run-schedule (saturate (run)))
34+
(check (= $nested (b)))
35+
(pop)
36+
37+
(let $nested-resolved (p (vec-of (vec-of (b)))))
38+
(run-schedule (saturate (run)))
39+
(check (= $nested-resolved (b)))

tests/snapshots/files__shared_snapshot_container_fail.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ expression: snapshot_content_across_treatments
55
((b 1)
66
(k 1)
77
(l 1)
8+
(p 1)
89
(w 0))

0 commit comments

Comments
 (0)