# 0137 — `sx run` on a program with no `main` segfaults (JIT entry lookup unguarded) ## Symptom `sx run ` on a program that defines no `main` function **crashes** (SIGSEGV/abort, "Segmentation fault at address 0x60") instead of emitting a clean diagnostic like `error: no 'main' function found`. - **Observed:** process crash, exit 134 (abort) / 139 (SIGSEGV); no diagnostic. - **Expected:** a normal compile-style error ("no `main` entry point") and a clean non-zero exit, the same way any other missing-entry condition reports. Independent of inline assembly — surfaced while writing an ASM-stream probe that omitted `main`, but reproduces with an ordinary, asm-free program (see below). ## Reproduction A file with only an (uncalled) function and no `main`: ```sx foo :: (n: u64) -> u64 { return n + 1; } ``` ```sh sx run that.sx # => "Segmentation fault at address 0x60", exit 134 # expected: "error: no 'main' function found" (or similar), clean non-zero exit ``` ## Root cause (suspected) `src/target.zig` JIT-run path, ~lines 256–273. After the ORC lookup: ```zig var main_addr: c.LLVMOrcExecutorAddress = 0; err = c.LLVMOrcLLJITLookup(jit, &main_addr, "main"); if (err != null) { /* prints "JIT lookup error" and returns error.CompileError */ } // no guard for main_addr == 0 here: const main_fn: *const fn () callconv(.c) i32 = @ptrFromInt(main_addr); const result = main_fn(); // <- calls a null/garbage pointer when no main ``` When the module has no `main` symbol, the lookup leaves `main_addr` at `0` (or ORC returns a degenerate success), so `@ptrFromInt(main_addr)` + `main_fn()` calls into null → the crash. There is no `main_addr == 0` check. ## Investigation prompt (paste into a fresh session) > `sx run` on a program with no `main` segfaults instead of diagnosing. The JIT > run path in `src/target.zig` (~lines 256–273) looks up `"main"` via > `LLVMOrcLLJITLookup`, then unconditionally casts `main_addr` to a function > pointer and calls it. When the program defines no `main`, `main_addr` is `0` > (or the lookup degenerately "succeeds"), so the call dereferences null and > crashes. > > Fix: after the lookup's `err` check, add `if (main_addr == 0) { … }` that emits > a clean user-facing error ("no `main` function found" / "program has no entry > point") and returns `error.CompileError` (matching the existing > `JIT lookup error` style), BEFORE the `@ptrFromInt` + call. Consider whether a > pre-JIT check (the module/program already knows whether a `main` decl exists — > e.g. emit_llvm.zig:631 already null-checks `LLVMGetNamedFunction(.., "main")`) > is the better choke point so the diagnostic carries a source span rather than a > bare message. Either is acceptable; the hard requirement is *no crash*. > > Verification: `printf 'foo :: (n: u64) -> u64 { return n + 1; }\n' > /tmp/x.sx > && sx run /tmp/x.sx` — expect a clean error message + non-zero exit, NOT a > segfault. Add a pinned repro under `issues/` (or an `examples/11xx-diagnostics-*` > once the message is settled) asserting the diagnostic on stderr + the exit code.