Skip to content

cli: restore documented auto-run for main and inline programs#307

Merged
danieljohnmorris merged 5 commits into
mainfrom
fix/autorun-main-default
May 16, 2026
Merged

cli: restore documented auto-run for main and inline programs#307
danieljohnmorris merged 5 commits into
mainfrom
fix/autorun-main-default

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Two soundness-class regressions in the CLI default-engine dispatch:

  1. ilo file.ilo with main-as-default broken. A multi-fn file containing main was erroring with defines multiple functions, please specify one instead of auto-running main. SKILL.md ([CLI invocation]) has documented this contract since 0.x.

  2. ilo 'f>n;42' silently AST-dumping. Inline programs with a runnable entry were dumping AST JSON to stdout with exit 0 instead of running. For piped consumers this is the worst kind of bug: valid-looking JSON output that is not the program result. SKILL.md: "Inline programs (ilo 'code') and single-function files run their entry function with the remaining CLI args; no explicit function name needed."

Plus a related papercut several persona reports flagged: synthetic __lit_N inline-lambda decls were leaking into the "available functions" listing.

The CLI default branch handled the single-fn-inline / multi-fn / main cases inconsistently with the documented contract, treating inline as "always AST-dump" and treating multi-fn files as "always error". This PR aligns the runtime with the docs and tightens the dispatcher to hide compiler-internal names.

Repro

Before:

$ cat > /tmp/automain.ilo <<EOF
> helper a:n>n;+a 1
> main>n;helper 41
> EOF
$ ilo /tmp/automain.ilo
error: /tmp/automain.ilo defines multiple functions, please specify one.
available functions:
  helper
  main
$ ilo 'f>n;42'
{
  "declarations": [
    { "Function": { "name": "f", ... } }
  ]
}
$ echo $?
0

After:

$ ilo /tmp/automain.ilo
42
$ ilo 'f>n;42'
42

What's in the diff

Five discrete commits:

  1. cli: hide synthetic __lit_N decls from func-name discovery — filter Decl::Function whose name starts with __ at the source. Synthetic names from inline-lambda lifting (parser/mod.rs ~line 2863) no longer leak into the auto-run heuristic, the multi-fn listing, or CLI positional-arg matching. __ is reserved for the compiler so the filter is safe and forward-compatible if more synthetic prefixes land.
  2. cli: prefer main when a multi-fn file omits the func arg — restore the documented main-as-default convention for multi-fn files. Multi-fn files without main keep the friendly listing.
  3. cli: auto-run inline programs with a runnable entryilo '<single fn>' and ilo '<multi-fn with main>' now auto-run. Zero-fn (declaration-only / comment-only) and multi-fn-without-main inline snippets still AST-dump, preserving the inspection escape hatch from PR main: skip verify errors in inline AST-dump mode #178. --ast remains the explicit form for any case where AST output is wanted regardless of shape.
  4. test: pin auto-run contract across file and inline paths — extends tests/regression_cli_default.rs with 7 new cross-cutting tests, adds examples/autorun-main.ilo so the engine harness exercises the main-as-default path across tree / VM / Cranelift, and so future agents have an in-context demonstration.
  5. test: migrate pre-existing inline-snippet tests to --ast — several tests in eval_inline.rs, regression_friendly_errors.rs, regression_p001_cascade.rs, and main.rs's embedded test module used inline single-fn snippets with required params as parse-acceptance smoke tests, relying on the implicit AST-dump path. Switched to explicit --ast so the parse-acceptance signal stays decoupled from runtime semantics.

Test plan

  • cargo test --release --features cranelift — 5296 passed, 0 failed across 134 suites
  • cargo fmt --check
  • cargo clippy --release --features cranelift --all-targets -- -D warnings
  • New regressions: multi_fn_file_with_main_auto_runs_main, multi_fn_file_explicit_func_arg_overrides_main, inline_single_fn_auto_runs, inline_multi_fn_with_main_auto_runs_main, inline_multi_fn_without_main_still_dumps_ast, inline_zero_fn_still_dumps_ast, synthetic_lambda_decls_hidden_from_multi_fn_listing
  • examples/autorun-main.ilo runs across every engine via tests/examples_engines.rs
  • Manual repro verified fixed for both reported shapes
  • Existing legacy contracts still pinned: single-fn auto-run, multi-fn-without-main listing, --ast always wins, comment-only inline AST-dumps

Follow-ups

None. SKILL.md was already correct; the bug was runtime drift from the documented contract.

Inline-lambda lifting emits synthetic top-level decls named __lit_N.
These are an implementation detail of HOF dispatch and were leaking
into the auto-run / multi-fn-listing logic, surfacing in the friendly
'available functions' error and confusing several persona reports.

Filter them out at the source: any Decl::Function whose name starts
with __ is excluded from the func-name set the CLI uses to pick a
default entry, list candidates, or match a positional arg.
SKILL.md has long documented that a multi-fn file with a function
called `main` auto-runs main, matching the convention every persona
reaches for first. The CLI default branch was instead erroring out
with 'defines multiple functions, please specify one' on every
multi-fn file, even when main was the obvious entry point.

Fix the auto-run heuristic to pick main when it exists. Multi-fn
files without a main keep the friendly listing as before.
`ilo 'f>n;42'` was silently dumping the AST to stdout with exit 0
instead of running f. For piped consumers this is soundness-class:
valid-looking JSON output that is not the program result. SKILL.md
has documented inline auto-run since 0.x.

Auto-run when an inline snippet has a runnable entry: main if
present, else the only user-defined fn. Zero-fn snippets (type and
alias declarations only) and multi-fn snippets without a main keep
the legacy AST-dump fallback, so the `ilo '<snippet>'` inspection
shortcut from PR #178 still works for non-runnable shapes. The
explicit --ast flag continues to AST-dump unconditionally.
Cover the documented behaviour from SKILL.md end-to-end:
- multi-fn file with main auto-runs main
- multi-fn file with explicit func arg overrides main
- inline single fn auto-runs (was AST-dumping, soundness fix)
- inline multi-fn with main auto-runs main
- inline multi-fn without main still AST-dumps (PR #178 legacy)
- inline zero-fn (comment-only) still AST-dumps
- synthetic __lit_N decls hidden from the multi-fn listing

Also adds examples/autorun-main.ilo so the engine harness exercises
the main-as-default path across every backend (tree, VM, Cranelift)
and so future agents have an in-context demonstration of the
canonical shape.
Several tests used `ilo '<single-fn snippet>'` with no args as a
parse-acceptance smoke test, relying on the implicit AST-dump path
that triggered for inline single-fn input. Now that inline auto-runs,
those snippets would fail arity at runtime instead of dumping the AST.

Switch the affected tests to the explicit `--ast` form, which keeps
the parse/verify-acceptance signal cleanly decoupled from runtime
semantics. Files touched: tests/eval_inline.rs (6 tests),
tests/regression_friendly_errors.rs (run_ok helper),
tests/regression_p001_cascade.rs (run_ok helper), src/main.rs (3
dispatch unit tests in the embedded module).

No behaviour change beyond the inline auto-run fix in the earlier
commit, just test-shape catch-up.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris merged commit 4ed61b7 into main May 16, 2026
5 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/autorun-main-default branch May 16, 2026 15:54
danieljohnmorris added a commit that referenced this pull request May 16, 2026
three P0 fixes since 0.11.3, all surfaced by rerun4 personas: srt-cranelift TLS desync silent miscompile (#306), CLI auto-run for main and inline programs restored (#307), OP_LISTAPPEND O(n^2) memory regression in in-process Cranelift JIT (#308).
danieljohnmorris added a commit that referenced this pull request May 16, 2026
twelve fixes since 0.11.3, surfaced by rerun4 personas plus standing asks: srt-Cranelift TLS desync (#306), CLI auto-run restoration (#307), OP_LISTAPPEND O(n^2) JIT memory regression (#308), precedence-pair hint false-positive on parens (#309), prefix ?? accepts call expression (#310), += pure-shape docs (#311), bare-mutation silent no-op verifier warning ILO-T033 (#312), asin/acos/atan inverse trig builtins (#313), flat cross-engine (#314), cond{~v} discard hint multi-stmt false-positive (#315), rsrt fn xs key-fn overloads (#316), xs.(expr) paren-after-dot diagnostic hint (#317).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant