Skip to content

Make entry points optional - #35

Merged
wtholliday merged 3 commits into
mainfrom
optional-entry-points
Aug 16, 2026
Merged

Make entry points optional#35
wtholliday merged 3 commits into
mainfrom
optional-entry-points

Conversation

@wtholliday

@wtholliday wtholliday commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Previously a requested entry point that the source didn't define was a hard compile error. Now the backends skip undefined entry points, and the client decides whether a missing one is a problem.

Compiler / backends

  • DeclTable::find_entry_point() is the single Option-returning lookup. The monomorph pass, Cranelift JIT, VM codegen, stack codegen, LLVM JIT, and LLVM AOT all skip entry points that aren't defined — only the ones found are compiled and show up in program.entry_points (or the JIT's name→ptr map).
  • program.entry is now the first entry point that was actually found, instead of hard-requiring entry_points[0].
  • Running still needs one: run_vm/run_stack/jit()/LLVM compile_and_run error with entry point function 'x' not found, and the VM/stack/asm run helpers early-return on an entry-less program rather than indexing an empty function table.

Client-facing API

  • Compiler::found_entry_points() / missing_entry_points().
  • FFI entry-point slots are Option, so indices stay aligned with the names passed to lyte_compiler_new when one is missing (before, the list would silently shift). New lyte_program_has_entry_point(program, index); lyte_entry_point_call returns false for an undefined one; Swift's Program.entryPoint(named:/at:) returns nil.
  • The CLI is a client that requires them: it reports every missing entry point and exits 1 before compiling, AOT, or running (no_main.lyte still prints entry point function 'main' not found). --check no longer needs an entry point at all.

Side effects

  • --entry foo previously compiled and ran main regardless under the Cranelift JIT (Compiler::jit() hardcoded "main"); it now runs the first defined requested entry point. New golden test tests/cases/custom_entry.lyte covers this across all four backends.
  • The Swift wrapper change won't link until a new CLyte.xcframework release is published, since Package.swift pulls the 0.34 binary artifact.

Testing

cargo test --workspace passes (354 lib + 4 golden-backend + 21 LSP tests). cargo check is clean with --features llvm, --no-default-features, and --all-targets.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KU75jHWHD6nzWZb5tau87e

Fixes #34

wtholliday and others added 3 commits August 15, 2026 15:28
Previously a requested entry point that the source didn't define was a
hard compile error. Now the backends skip undefined entry points and the
client decides whether a missing one matters.

- DeclTable::find_entry_point() is the single Option-returning lookup;
  monomorph, Cranelift, VM, stack, LLVM JIT and AOT all skip misses.
- program.entry is the first entry point actually found; the VM/stack/asm
  run helpers early-return on an entry-less program.
- Compiler gains found_entry_points()/missing_entry_points(); running
  still errors when nothing was found.
- FFI entry-point slots are Option so indices stay aligned with the names
  passed to lyte_compiler_new when one is missing. New
  lyte_program_has_entry_point(); Swift entryPoint(named:/at:) returns nil
  for an undefined one.
- The CLI is a client that requires them: it reports every missing entry
  point and exits 1 before compiling, AOT, or running. --check no longer
  needs an entry point.

Also fixes --entry under the Cranelift JIT, which compiled and ran "main"
regardless of the requested entry points.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KU75jHWHD6nzWZb5tau87e
find_entry_point only looked at find(name).first(), so a non-function decl
sharing the entry point's name (decls with equal names are ordered by source
position) shadowed the function: `var main: i32` above `main { ... }` reported
'main' as missing, and specialize() reported a bogus "Multiple overloads
found". Scan for the first Decl::Func instead, via a new
entry_point_overloads() iterator that monomorph_pass also uses for its
overload count.

The VM run guards tested functions.is_empty() as a proxy for "no entry point
was resolved", but `entry` defaults to 0, so a program with functions and an
unresolved entry would run function 0. Add has_entry() to VMProgram and
StackProgram checking that `entry` indexes a real function, and use it at all
four run sites. VM::run also re-zeroes globals and clears cancelled/trap
before the early return, per its documented contract.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KU75jHWHD6nzWZb5tau87e
collect_entries silently skipped undefined entry points, so a library caller
of llvm_aot::compile_aot that misspelled an entry name got a successfully
written .o + .h missing that symbol, surfacing only as an undefined-symbol
error in the host's link. The CLI pre-checks with require_entry_points, but
the public API shouldn't depend on that. AOT's entry point list is the
object's export list, so an undefined one is now an error, reported before
any file is written.

found_entry_points/missing_entry_points were documented "only meaningful
after check()" with nothing enforcing it: called earlier (easy for an FFI
embedder, since lyte_compiler_compile is the only thing that runs check()),
self.decls is empty and every entry point looks missing. Answer from the AST
instead, which check() copies into self.decls wholesale, so the result is the
same after check() and correct before it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KU75jHWHD6nzWZb5tau87e
@wtholliday
wtholliday merged commit 97688ad into main Aug 16, 2026
8 checks passed
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.

Script dies with no init{}

1 participant