reject builtin-named user functions at parse time#245
Merged
Conversation
When a user defines a function whose name collides with a builtin (lst, sum, clamp, etc), verify's call-dispatch resolves the builtin first and reports its signature, producing a misleading ILO-T006 arity error against a function the agent thought they had just defined. Repro: lst>n;42 main>n;lst() # ILO-T006: arity mismatch: 'lst' expects 3 args, got 0 The agent has no signal that 'lst' is already taken; debugging the arity error sends them down the wrong path. Extend the existing ILO-P011 reserved-name precedent (parse_stmt already does this for fld=5/cnt=/brk= bindings) into parse_fn_decl: if the function name is a builtin, error with a clear rename hint. One-location fix, runs before any engine dispatch, so tree/VM/Cranelift all surface the same friendly diagnostic. Updates two graph.rs unit tests that had used 'inv' and 'sum' as fixture function names (both are builtins); renamed to 'not' and 'total'.
Covers ten builtin names personas reach for (lst, hd, tl, slc, cat, has, len, str, num, map), across tree/VM/Cranelift, asserting ILO-P011 fires with a clear rename hint. Also pins: - the original repro (lst as fn name + main calling it) and asserts ILO-P011 precedes any downstream ILO-T006 cascade so the agent's first action is the rename - the rename workaround actually compiles on every engine - the lst builtin still works as a 3-arg list-constructor after the parser change examples/builtin-fn-name-rename.ilo gives agents an in-context demonstration of the now-correct pattern and acts as a higher-level regression test the examples_engines harness exercises across every engine.
The new ILO-P011 reserved-name check catches three pre-existing examples that defined user functions over builtin names: - examples/arithmetic.ilo: avg -> mean3 (avg is a list-mean builtin) - examples/builtins.ilo: clamp -> clmp (clamp is a builtin) - examples/guards.ilo: clamp -> clmp SPEC.md's multi-function-files section had the same clamp shape; renamed to clmp and noted the reason inline. ai.txt regenerates from SPEC.md so it picks the change up too. No behaviour change to the examples; the new names work identically and the comments now teach the right pattern (don't reuse builtin names for user functions).
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
danieljohnmorris
added a commit
that referenced
this pull request
May 13, 2026
The frq.ilo example (added by PR #238) had a `chars>n;...` helper function. Introducing the `chars` builtin in this PR triggers the ILO-P011 builtin-shadow check from PR #245 and the existing example stops parsing. Renaming the helper to `cfrq` (char-freq) preserves the example's intent without weakening the new shadow check.
6 tasks
danieljohnmorris
added a commit
that referenced
this pull request
May 16, 2026
A local binding like `flat=cat ls " "` was silently accepted, then any later use of `flat` in operand position resolved to the 1-arg flatten builtin (the verifier checks is_builtin before locals), surfacing as a misleading `ILO-T006 'flat' expects 1 args, got 0`. The agent has no signal that the local binding is being shadowed. Mirrors the PR #245 precedent for builtin-named user functions: intercept at parse time with ILO-P011 and a rename hint (`myflat`, `flatv`) in both parse_decl (top-level) and parse_stmt (in-function) binding sites. Existing `fld=` per-name block keeps its specific fold-builtin message since it runs before the generic check. Cross-engine regression test covers twelve builtin names across tree, VM, and Cranelift, plus the exact pdf-analyst rerun3 repro and a sanity check that the renamed binding compiles and runs. examples/builtin-binding-name-rename.ilo demonstrates the correct shape (`body=cat ls " "`) for the `spl body ". "` pipeline.
danieljohnmorris
added a commit
that referenced
this pull request
May 16, 2026
A local binding like `flat=cat ls " "` was silently accepted, then any later use of `flat` in operand position resolved to the 1-arg flatten builtin (the verifier checks is_builtin before locals), surfacing as a misleading `ILO-T006 'flat' expects 1 args, got 0`. The agent has no signal that the local binding is being shadowed. Mirrors the PR #245 precedent for builtin-named user functions: intercept at parse time with ILO-P011 and a rename hint (`myflat`, `flatv`) in both parse_decl (top-level) and parse_stmt (in-function) binding sites. Existing `fld=` per-name block keeps its specific fold-builtin message since it runs before the generic check. Cross-engine regression test covers twelve builtin names across tree, VM, and Cranelift, plus the exact pdf-analyst rerun3 repro and a sanity check that the renamed binding compiles and runs. examples/builtin-binding-name-rename.ilo demonstrates the correct shape (`body=cat ls " "`) for the `spl body ". "` pipeline.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Defining a user function whose name collides with a builtin (lst, sum, clamp, avg, …) used to fail with a misleading
ILO-T006arity error from the builtin's signature. Personas had no signal their name was already taken and debugged the arity error down the wrong path.This extends the existing
ILO-P011reserved-name precedent (parse_stmt already does this forfld=5/cnt=/brk=bindings) intoparse_fn_decl. One-location fix in the parser, so tree/VM/Cranelift all surface the same friendly diagnostic before any engine dispatch runs.Manifesto framing: replaces a silent shadow + misleading error with an explicit named, fixable error the first time round. Less retry tax, less debugging into the wrong codepath.
Repro
Before:
After:
Renaming to anything non-builtin (e.g.
show) compiles and runs cleanly.What's in the diff
Builtin::is_builtin(&name)right afterexpect_ident()inparse_fn_decl, emitILO-P011with a rename hint. Also updates twograph.rsunit tests that had usedinvandsumas fixture function names (both are builtins); renamed tonotandtotal.tests/regression_builtin_fn_name.rscovering ten builtin names (lst, hd, tl, slc, cat, has, len, str, num, map) across tree/VM/Cranelift, plus the original repro assertingILO-P011precedes any downstreamILO-T006cascade, plus a rename-still-works sanity test, plus alstbuiltin call sanity test. Newexamples/builtin-fn-name-rename.ilogives agents an in-context demo of the correct pattern.examples/arithmetic.iloavg→mean3,examples/builtins.iloandexamples/guards.iloclamp→clmp), and the same shape in SPEC.md's multi-function-files section.ai.txtregenerates from SPEC.md.Test plan
cargo test --release --features craneliftfull suite green (2866 lib + all integration)cargo fmt --checkcleancargo clippy --release --features cranelift --all-targets -- -D warningscleanlst()reproexamples/builtin-fn-name-rename.iloruns to42on every engine viatests/examples_engines.rstests/examples.rsfld=5reserved-name precedent (tests/regression_fld_reserved.rs) still greenFollow-ups
None tracked. The rename hint is intentionally generic (
my{name}/{name}of) to cover any builtin without a hand-curated dictionary; could be made name-specific later if persona feedback shows the generic form isn't sticky enough.