VM upvalue support: closures capture variables from enclosing scopes
Compiler (compiler.sx): - Function scopes marked is-function=true; let scopes share parent frame - scope-resolve only creates upvalue captures at function boundaries - Let scope locals use parent's slot numbering (same frame) - OP_CLOSURE emits upvalue descriptors: (is_local, index) per capture VM (sx_vm.ml): - upvalue_cell type: shared mutable reference to captured value - OP_UPVALUE_GET/SET: read/write from closure's upvalue array - OP_CLOSURE: reads upvalue descriptors, creates cells from enclosing frame's locals (is_local=1) or upvalues (is_local=0) - vm_closure carries live env_ref (not snapshot) - vm_call falls back to CEK for Lambda/Component/Island values Verified: (let ((x 10)) (let ((add-x (fn (y) (+ x y)))) (add-x 5))) Compiles to: CONST 10, LOC_SET #0, CLOSURE [UV_GET#0 LOC_GET#0 CPRIM+ RET] with upvalue descriptor: is_local=1 index=0 VM executes → 15 ✓ Auto-compile: 6/117 functions compile (up from 3). Disabled until compiler handles all features — fallback can't reconstruct closure scope for variables like nav-state bound in caller's let*. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -820,18 +820,21 @@ let dispatch env cmd =
|
||||
match result with
|
||||
| Dict d when Hashtbl.mem d "bytecode" ->
|
||||
let code = Sx_vm.code_from_value result in
|
||||
let globals_snapshot = Hashtbl.copy env.bindings in
|
||||
Hashtbl.iter (fun k v ->
|
||||
Hashtbl.replace globals_snapshot k v) lam.l_closure.bindings;
|
||||
(* VM closure with CEK fallback on error *)
|
||||
(* Live env reference — NOT a snapshot. Functions see
|
||||
current bindings, including later-defined functions. *)
|
||||
let live_env = env.bindings in
|
||||
(* Original lambda for CEK fallback *)
|
||||
let orig_lambda = Lambda lam in
|
||||
let fn = NativeFn ("vm:" ^ name, fun args ->
|
||||
try
|
||||
Sx_vm.execute_closure
|
||||
{ Sx_vm.code; name = lam.l_name } args globals_snapshot
|
||||
with _ ->
|
||||
(* Fall back to CEK machine *)
|
||||
Sx_ref.cek_call orig_lambda (List args)) in
|
||||
Sx_vm.call_closure
|
||||
{ Sx_vm.code; upvalues = [||]; name = lam.l_name;
|
||||
env_ref = live_env }
|
||||
args live_env
|
||||
with
|
||||
| _ ->
|
||||
(* Any VM error — fall back to CEK *)
|
||||
Sx_ref.eval_expr (List (orig_lambda :: args)) (Env env)) in
|
||||
Hashtbl.replace env.bindings name fn;
|
||||
incr count
|
||||
| _ -> incr failed
|
||||
|
||||
Reference in New Issue
Block a user