Conversation
Resolve conflicts with main: - `Cargo.toml`/`Cargo.lock`: take main's dependency set and pin egglog to upstream `9063586`, which already contains egglog#1008, so main's temporary `[patch]` onto the egg-smol fork is no longer needed. Drop the stale "local dev against the forked egglog" comment; the quasiquote work it referred to is not used by this branch. - `src/lib.rs`: keep main's rewritten crate docs and module list, and add a named-arguments bullet under "Language and values". - `CHANGELOG.md`: record named arguments under Unreleased/Added. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`make nits` runs `cargo clippy --tests -- -D warnings` against a crate with `#![warn(missing_docs)]`, so `NamedChange::delete` and `NamedChange::subsume` need doc comments. Also apply `cargo fmt`, which the current rustfmt wants for `named_args.rs` and for the module ordering the merge produced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
saulshanabrook
left a comment
There was a problem hiding this comment.
TLDR: multiple ellipsis shadow names, doesnt work with include, and metadata not linked to push/pop
For push/pop, I believe I added some storage for extensions for named schedules to deal with this kind of thing and attach things correctly as an extension point you can look at
Review from codex:
Reviewed 207518e. I found three correctness issues worth fixing before merging.
-
[P1] Ellipsis variables can collide and silently change query results — named_args.rs:145. The second fresh variable for field
xhas the same name as the first forx1. This program incorrectly fails; replacing each...with_ _passes:(relation R (:x i64 :x1 i64)) (relation Hit ()) (R 1 2) (rule ((R ...) (R ...)) ((Hit))) (run 1) (check (Hit))
Use a fixed fresh-variable hint, as wildcard parsing does, rather than user-provided field names.
-
[P2] Named calls fail after included declarations — named_args.rs:185. If
defs.eggdeclares(relation R (:x i64 :y i64)), then(include "defs.egg")followed by(R :x 1 :y 2)fails withUnbound symbol :x. The caller is parsed before the include executes, so the call macro is registered too late. Included declarations need to be available when subsequent calls expand. -
[P2] Field metadata survives scoped redeclarations — named_args.rs:539.
(push), a named one-argument declaration ofR,(pop), then a positional two-argument declaration causes(R 1 2)to fail with “takes 1 argument.” Parsing retains the old macro, and positional declarations never replace it. Metadata needs to follow declaration scope and redeclaration.
For succinctness, I’d prioritize:
- Drop the unrelated dependency upgrade and stale development comment: the parser APIs used here already exist at the previous revision.
- Keep the macro implementation types private; expose
register_named_args. - Share the duplicated named-field validation between schemas and datatype variants.
- Inline
register_named_call, which is a trivial forwarding helper.
Validation: All six added tests and 47 Rust integration tests pass. The file suite passes 44/45; the remaining math_backoff failure also reproduces on the base commit. Clippy passes. Formatting fails, and the committed lockfile references a different egglog revision than the manifest, preventing --locked builds.
The PR checkout is unchanged; no GitHub comments were posted.
All three correctness issues in the review share one cause: field names were
registered while parsing. egglog parses a whole program before running any of
it, so a parse-time macro sees declarations in source order, not execution
order.
Expanding in a `CommandMacro` fixes them together, because command macros run
after every preceding command has taken effect:
- A call now sees declarations pulled in by an earlier `(include ...)`, which
had not been read yet when the caller was parsed.
- Field names are recorded per `(name, arity)`, and each call resolves against
the arity the e-graph currently reports for that name, so a declaration that
has been popped or redeclared can no longer rewrite calls.
- Each `...` binds fresh variables from a fixed hint instead of the field
names, which could collide with each other ("x" at 1 and "x1" at 0 both
gave "x1"). The pinned egglog also makes `SymbolGen` collision-free, so this
no longer depends on that fix.
No parse-time macro is left: `:name` and `...` already parse as ordinary
variables, and `:name Sort` pairs already parse as a longer sort list, so the
declaration commands no longer need shadowing. That drops the `set`/`delete`/
`subsume` macros (their arguments are rewritten in place instead), makes every
implementation type private, and shares one field-name validator between
schemas and datatype variants. Named arguments are now registered on the
e-graph rather than the parser, since expansion needs the command stage.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Address review: expand named arguments during command macros
`main` gained `unstable-subst` (#60). The only conflict was two doc bullets added at the same place in the crate docs; both are kept. Both sides already pin the same egglog, so the manifest and lockfile merged cleanly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks for the detailed review — all three are fixed, in #72 (already merged into this branch) plus #73 for the fresh conflict with They turned out to share one cause: field names were registered while parsing, and egglog parses a whole program before running any of it, so a parse-time macro only ever sees declarations in source order. Expansion now happens in a
I confirmed 2 and 3 fail on the old implementation and pass now. On the extension storage you pointed at: Succinctness:
One API change to flag: Formatting is fixed too. |
Merge main into named-args
saulshanabrook
left a comment
There was a problem hiding this comment.
Reviewed d3255ef. The original three reproducers are fixed, but I found three regressions:
-
[P1] Cloned e-graphs share mutable field mappings — named_args.rs:79. Clone an empty e-graph, then declare
R(:a i64 :b i64)in one andR(:b i64 :a i64)in the other. In the first graph,(R :a 1 :b 2)now silently inserts(R 2 1). The command registry clones itsArc, sharing this mutable map. Metadata needs to follow the actual declaration and e-graph snapshot;(name, arity)is insufficient. -
[P2] Named calls inside extension commands bypass expansion — named_args.rs:214. The pinned
Command::visit_exprsskipsUserDefinedcommands, including experimentalextractandrun-schedule. This previously working program now fails withUnbound symbol :x:(datatype T (C :x i64)) (extract (C :x 1))
Explicitly traverse those commands’ expression arguments.
-
[P2] Positional tables reject previously valid variables — named_args.rs:325. This succeeds on the previous head but fails now:
(relation R (i64)) (let :value 1) (R :value)
Marker interpretation should remain limited to declarations with named fields; ordinary positional calls should pass through unchanged.
For succinctness, the rewrite removes substantial duplication. One remaining improvement: declare() could modify the command’s schema in place, combining constructor/function handling instead of reconstructing every unchanged field.
Validation: All 10 named-argument tests and 101 Rust integration tests pass. Formatting, Clippy, and locked builds now pass. The findings above have separate reproductions. No repository files or GitHub comments changed.
|
Good catches — all three are fixed in #73, and the first one is the reason I moved the whole thing back into the parser. [P1] cloned e-graphs. You're right that The parser is the right home after all, for exactly the reason you gave: it's part of the e-graph, so it's cloned with one and snapshotted by
[P2] extension commands and [P3] positional variables both disappear with the move rather than needing their own fixes: parse-time macros fire wherever There are now regression tests for all six reported cases, and I checked each one fails on the implementation it was reported against:
The succinctness changes from last round are kept: One thing worth your judgement: expanding
|
|
Correction to my last comment: Only the registration has to move earlier, not the command. The cost is that an included file is read twice, which seems a fair price for leaving include semantics alone. Pushed to #73. Still 175 tests, doctests, clippy and fmt clean. |
No description provided.