Skip to content

Fix REPL - #113

Open
tln wants to merge 4 commits into
fubark:masterfrom
tln:fix-repl
Open

Fix REPL#113
tln wants to merge 4 commits into
fubark:masterfrom
tln:fix-repl

Conversation

@tln

@tln tln commented Aug 2, 2026

Copy link
Copy Markdown

Running the repl from commit c853fcc did not invoke the cyber-written repl
code. In addition a few other fixes were needed to make the REPL work.

A few basic tests were added for the REPL.

Defining fn main() in a REPL session no longer runs it. Previously it was
treated as an entry point.

Still broken

  • Use-after-free fix (commit bb313b4) is a workaround
  • Variables do not persist between lines
  • Multi-line read() leaks the concatenated input_buffer.
  • A bare expression is rejected by the language, so the REPL's value printing
    path (value_desc) is currently unreachable.
  • REPL code with Type or compiler errors show Zig compiler stack traces

Built and tested with zig 0.15.2; zig build test passes.

tln added 4 commits August 2, 2026 07:38
The REPL starts by evaluating an in-memory chunk named `main`. That uri was
passed to the CLI's file resolver, which called realpath on it and failed, so
the REPL exited immediately with:

    CompileError: Import path does not exist: `main`

An entry chunk has no importing chunk, so it can be in-memory and resolve to
itself. Only entry chunks are treated this way, leaving `use main` from a
script resolving to a file as before.
REPL.new() bound the new REPL to a local and returned it. Returning a local
copies it into the return slot and then runs the block's destructors on the
local, so `VM.@deinit` freed the VM that the returned copy still points at.
The next eval crashed in Compiler.clearReports on a dangling pointer.

This works around the problem rather than fixing it. Any record owning a type
with a destructor hands the caller freed memory when returned this way:

    type Holder:
        vm cy.VM

    fn make() -> Holder:
        h := Holder{ vm = cy.VM() }
        return h

    h := make()
    r := h.vm.eval('print(1)')   -- crash

Returning the literal directly is fine, so it is specifically binding to a
named local and returning it. There is no move tracking to skip destructing a
returned local, so REPL.new now returns the record literal, and the init eval
moved to a separate `init` that runs in the caller's scope.
The compiler is reused across evals, and persist_main re-declares the previous
main's top level syms in the new main chunk. Two things carried over wrongly.

The skip list named `@main_init` and `@main_deinit`, which no longer exist; the
hidden funcs are `@program_init` and `@program_deinit`. So they were carried
over and collided with the ones reserved for the new main, and reporting that
duplicate panicked in Sym.declNode on a `.use_alias`. Every line after the
first failed.

`main` was also treated as the entry point of every line. Each REPL line is
compiled as its own program, so declaring `fn main()` on a line ran it, and it
then stayed the entry point for the rest of the session: later lines re-ran it
and were rejected with "Body statement is not allowed when main function is
declared". A carried over `main` arrives as a use alias, which the unchecked
cast to `.func` panicked on.

`main` is not special in a session that continues across evals; it is an
ordinary function that can be called by name. `main_func` is also reset per
compile, since it was previously only ever assigned.
Each case is a list of input lines and the exact output the REPL prints for
them. The driver runs the same read/eval_print loop as `cy.repl`, minus the
intro banner, which carries a build specific version string.

Covers the regressions fixed in this branch: `main` is an ordinary function
rather than an entry point, so declaring it runs nothing and it stays callable
by name; and a line that fails to compile does not stop the following lines
from evaluating.

Cases feed whole statements per entry rather than driving `read`'s multi-line
buffering, since `input_buffer` currently leaks the concatenated string.
@tln tln mentioned this pull request Aug 2, 2026
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