datalog: stratifier rejects recursion through aggregation (165/165)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s

Bug: dl-check-stratifiable iterated body literals looking only for
explicit :neg literals, missing aggregate cycles. Now also walks
aggregates via dl-aggregate-dep-edge — q(N) :- count(N, X, q(X))
correctly errors out at saturation time.

3 new tests cover:
- recursion-through-aggregation rejected
- negation + aggregation coexist when in different strata
- min over empty derived relation produces no result
This commit is contained in:
2026-05-08 09:13:10 +00:00
parent a63d67247a
commit b95d8c5a63
5 changed files with 76 additions and 18 deletions

View File

@@ -227,6 +227,39 @@
(list {:L (list)}))
;; Aggregate vs single distinct.
;; Stratification: recursion through aggregation is rejected.
(dl-at-test! "agg recursion rejected"
(dl-at-throws?
(fn ()
(let ((db (dl-make-db)))
(do
(dl-add-rule! db
{:head (list (quote q) (quote N))
:body (list (list (quote count) (quote N) (quote X)
(list (quote q) (quote X))))})
(dl-saturate! db)))))
true)
;; Negation + aggregation in the same body — different strata.
(dl-at-test-set! "neg + agg coexist"
(dl-query
(dl-program
"u(a). u(b). u(c). banned(b).
active(X) :- u(X), not(banned(X)).
cnt(N) :- count(N, X, active(X)).")
(list (quote cnt) (quote N)))
(list {:N 2}))
;; Min over a derived empty relation: no result.
(dl-at-test-set! "min over empty derived"
(dl-query
(dl-program
"s(50). s(60).
score(N) :- s(N), >(N, 100).
low(M) :- min(M, X, score(X)).")
(list (quote low) (quote M)))
(list))
(dl-at-test-set! "distinct counted once"
(dl-query
(dl-program