From a823e59376ff8394b8919d0f5e1a35ee788390b2 Mon Sep 17 00:00:00 2001 From: giles Date: Mon, 23 Mar 2026 19:24:14 +0000 Subject: [PATCH] Fix root cause: skip JIT for closure lambdas in BOTH hook and vm_call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The closure check was only in vm_call (sx_vm.ml) but inner functions like read-list-loop were also compiled through the JIT hook in sx_server.ml. The hook compiled them with closure merging, producing incorrect bytecode (read-list-loop mishandled closing parens). Added the same closure check to the JIT hook: skip lambdas with non-empty closures. Now sx-parse works correctly: (a (b) (c)) → 3 siblings, not (a (b (c))) Pre-compiled count increased from 17 to 33 — more top-level functions compiled (inner ones correctly skipped to CEK). Co-Authored-By: Claude Opus 4.6 (1M context) --- hosts/ocaml/bin/sx_server.ml | 4 ++++ shared/sx/ocaml_bridge.py | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index 4a5be83..4cfae75 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -913,6 +913,10 @@ let register_jit_hook env = | Some _ -> None (* compile failed — CEK handles *) | None -> if !_jit_compiling then None + else if Hashtbl.length l.l_closure.bindings > 0 + || l.l_closure.parent <> None then + (* Skip JIT for lambdas with closure bindings *) + None else begin let fn_name = match l.l_name with Some n -> n | None -> "?" in _jit_compiling := true; diff --git a/shared/sx/ocaml_bridge.py b/shared/sx/ocaml_bridge.py index 05dc206..184eb3e 100644 --- a/shared/sx/ocaml_bridge.py +++ b/shared/sx/ocaml_bridge.py @@ -440,16 +440,6 @@ class OcamlBridge: _logger.warning("OCaml load skipped %s: %s", filepath, e) - # JIT workaround: sx-parse has a complex letrec (20 mutually - # recursive functions) that the JIT compiles incorrectly — - # closing parens cause siblings to become children. - # CEK-interpreted sx-parse works correctly. - try: - await self._send('(vm-reset-fn "sx-parse")') - await self._read_until_ok(ctx=None) - except OcamlBridgeError: - pass - # SSR overrides: effect is a no-op on the server (prevents # reactive loops during island SSR — effects are DOM side-effects) try: