Skip to content

[cli] exit() in embedded AILANG escapes as a raw panic into the host (internal/embed) #691

Description

@sunholo-voight-kampff

Found by the iteration-192 evaluator while reviewing #607 (PR #690), and confirmed first-party.

The gap

exit() raises a *eval.EvalExitCode sentinel panic (internal/effects/io.go:145). Two consumers recover it:

internal/embed — the sanctioned dashboard → compiler bridge — does not. Engine.Call (embed.go:237) and Engine.CallPreserveFloats (embed.go:314) call runtime.CallEntrypoint directly.

Measured:

grep -c 'recover()' internal/embed/embed.go   -> 0
grep -c 'recover()' cmd/ailang/run_helpers.go -> 2   # known-positive control, same instrument
test -f internal/embed/embed.go                -> YES # scope control

So an embedded module that calls exit() panics the host process with a raw Go stack — the same defect class as #607, one layer down. For a long-lived host (the dashboard/observatory), that is a crash rather than a bad exit code.

Why it was not fixed in #690

#607 is specifically about ailang run --batch, and the call-site census there was correctly scoped to executeModuleEntrypoint (exactly two sites, both now recovered). runtime.CallEntrypoint is the broader primitive, and widening the fix into the embed layer needs its own decision: an embedding host has no os.Exit semantics to map onto, so the right contract is probably to surface exit(N) as a typed error to the caller rather than to recover-and-ignore it.

Suggested acceptance

  • Decide the contract: typed error (e.g. ErrProgramExit{Code}) returned from Call/CallPreserveFloats, not a panic.
  • A test that embeds a module calling exit(1) and asserts the host survives with that typed error.
  • Per the mutation discipline: neutering the new recover must red that test.

Related: #607, PR #690.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions