Fix env-shadowing: rebind host extension points after .sx file load

evaluator.sx defines *custom-special-forms* and register-special-form!
which shadow the host's native bindings when loaded at runtime. The
native bindings route to Sx_ref.custom_special_forms (the dict the CEK
evaluator checks), but the SX-level defines create a separate dict.

Fix: rebind_host_extensions runs after every load command, re-asserting
the native register-special-form! and *custom-special-forms* bindings.

Add regression test: custom form registered before evaluator.sx load
survives and remains callable via CEK dispatch afterward.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 18:29:29 +00:00
parent 2d8741779e
commit 3ae49b69f5
2 changed files with 28 additions and 1 deletions

View File

@@ -711,6 +711,18 @@ let register_jit_hook env =
(* ====================================================================== *)
(* Re-assert host-provided extension points after loading .sx files.
evaluator.sx defines *custom-special-forms* and register-special-form!
which shadow the native bindings from setup_evaluator_bridge. *)
let rebind_host_extensions env =
Hashtbl.replace env.bindings "register-special-form!"
(NativeFn ("register-special-form!", fun args ->
match args with
| [String name; handler] ->
ignore (Sx_ref.register_special_form (String name) handler); Nil
| _ -> raise (Eval_error "register-special-form!: expected (name handler)")));
ignore (env_bind env "*custom-special-forms*" Sx_ref.custom_special_forms)
(* Command dispatch *)
(* ====================================================================== *)
@@ -727,6 +739,9 @@ let rec dispatch env cmd =
ignore (Sx_ref.eval_expr expr (Env env));
incr count
) exprs;
(* Rebind host extension points after .sx load — evaluator.sx
defines *custom-special-forms* which shadows the native dict *)
rebind_host_extensions env;
send_ok_value (Number (float_of_int !count))
with
| Eval_error msg -> send_error msg
@@ -1142,7 +1157,9 @@ let cli_load_files env files =
ignore (Sx_ref.eval_expr expr (Env env))
) exprs
end
) files
) files;
(* Rebind after load in case .sx files shadowed host extension points *)
rebind_host_extensions env
let cli_mode mode =
let env = make_server_env () in