datalog: anonymous-renamer avoids user _anon<N> collision
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s

The renamer for anonymous `_` variables started at counter 0 and
produced `_anon1, _anon2, ...` unconditionally. A user writing the
same naming convention would see their variables shadowed:

  (dl-eval "p(a, b). p(c, d). q(_anon1) :- p(_anon1, _)."
           "?- q(X).")
  => ()    ; should be ({:X a} {:X c})

The `_` got renamed to `_anon1` too, collapsing the two positions
of `p` to a single var (forcing args to be equal — which neither
tuple satisfies).

Fix: scan each rule (and query goal) for the highest `_anon<N>`
already present and start the renamer past it. New helpers
`dl-max-anon-num` / `dl-max-anon-num-list` / `dl-try-parse-int`
walk the rule tree; `dl-make-anon-renamer` now takes a `start`
argument; `dl-rename-anon-rule` and the query-time renamer in
`dl-query` both compute the start from the input.

1 regression test; conformance 275/275.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 09:34:41 +00:00
parent 82dfa20e82
commit ce98d97728
6 changed files with 105 additions and 14 deletions

View File

@@ -15,7 +15,7 @@ for rose-ash data (e.g. federation graph, content relationships).
## Status (rolling)
`bash lib/datalog/conformance.sh`**274/274 across 11 suites**
`bash lib/datalog/conformance.sh`**275/275 across 11 suites**
(tokenize, parse, unify, eval, builtins, semi_naive, negation, aggregates,
api, magic, demo). Source is ~3100 LOC, tests ~2900 LOC, public API
documented in `lib/datalog/datalog.sx`.
@@ -320,6 +320,17 @@ large graphs.
_Newest first._
- 2026-05-11 — Anonymous-variable renamer collided with user-written
`_anon<N>` symbols. The renamer started counter at 0 and produced
`_anon1, _anon2, ...` unconditionally; if the user wrote
`q(_anon1) :- p(_anon1, _).` the `_` got renamed to `_anon1` too,
collapsing the two positions of `p` to a single var and returning
the empty result instead of `{a, c}`. Fix: scan each rule (and
query) for the max `_anon<N>` and start the renamer past it. The
renamer constructor now takes a `start` arg; new helpers
`dl-max-anon-num` / `dl-max-anon-num-list` walk the rule tree.
1 regression test; 275/275.
- 2026-05-11 — `dl-magic-query` could silently diverge from
`dl-query` when an aggregate's inner-goal relation was IDB. The
rewriter passes aggregate body lits through unchanged (no magic