Skip JIT for lambdas with closure bindings

The closure merging in jit_compile_lambda (copying globals + injecting
closure bindings into vm_env_ref) produces incorrect variable resolution
for inner functions. Symptoms: sx-parse's read-list-loop mishandles
closing parens (siblings become children), parser produces wrong ASTs.

Fix: vm_call skips JIT compilation for lambdas with non-empty closures.
These run on CEK which handles closures correctly. Top-level defines
(empty closure) are still JIT-compiled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 18:45:40 +00:00
parent 6e804bbb5c
commit 2727a2ed8c
2 changed files with 8 additions and 1 deletions

View File

@@ -327,7 +327,13 @@ and vm_call vm f args =
(* Compile failed — CEK *)
push vm (Sx_ref.cek_call f (List args))
| None ->
if l.l_name <> None then begin
if l.l_name <> None
(* Skip JIT for lambdas with closure bindings — the closure
merging into vm_env_ref produces incorrect variable resolution
for inner functions (e.g. parser's read-list-loop). *)
&& Hashtbl.length l.l_closure.bindings = 0
&& l.l_closure.parent = None
then begin
(* Pre-mark before compile attempt to prevent re-entrancy *)
l.l_compiled <- Some jit_failed_sentinel;
match !jit_compile_ref l vm.globals with

View File

@@ -439,6 +439,7 @@ class OcamlBridge:
skipped += 1
_logger.warning("OCaml load skipped %s: %s",
filepath, e)
# SSR overrides: effect is a no-op on the server (prevents
# reactive loops during island SSR — effects are DOM side-effects)
try: