Skip to content

Commit 660519e

Browse files
fix(interp): eval_decl handles FnExtern (#328 build-failure root cause) (#346)
## Summary Unbreaks main's `dune runtest` gate — root cause of the 2-test failure baseline that's been red since #334 merged. ## Root cause `eval_decl`'s `TopFn fd` arm matched `fd.fd_body` against only `FnBlock` and `FnExpr`, leaving `FnExtern` unmatched. Every prior interp test imported externs via `use effects::{…}` so this case never fired — the bug was latent. The STDLIB-04a (#328) hermetic tests are the **first** to include an inline `extern fn make_ref<T>(x: T) -> Ref<T> / Mut;` declaration in the source they hand to `Interp.eval_program`. That triggers the missing arm and raises: ``` File "lib/interp.ml", line 1010, characters 16-21: Pattern matching failed Raised at Affinescript__Interp.eval_decl ``` Reproduces the `2 failures! in 0.088s. 298 tests run.` line from every PR's `build` log since #334. ## Fix Wrap the body lowering in an outer `match fd.fd_body with`: - `FnExtern` → `Ok env` (externs are runtime-bound via `create_initial_env`'s `VBuiltin` table — `panic` / `error` / `make_ref` / `get` / `set` / etc., not via the AST). - `FnBlock _ | FnExpr _` → existing closure construction. The inner inner-match's `FnExtern` arm is `assert false` with a comment — it's unreachable per the outer guard and the assert makes that explicit for the next reader. ## Verification The two failing tests (`#328 make_ref/set/get round-trip (Int)` and `(String)`) now have a defined evaluation path. The third 04a test (Deno codegen `__cell` shape) already passed — it bypasses interp. ## Test plan - [ ] CI `build` job: `2 failures!` → `298 tests run` clean - [ ] CI `lint`: should also clear (was likely propagating from the same root) - [ ] Hypatia DOC-FORMAT: no `.md` introduced Refs #328. --- _Generated by [Claude Code](https://claude.ai/code/session_01NUHL3MH3yKKQAEhSZn4Thu)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent d4522e4 commit 660519e

1 file changed

Lines changed: 19 additions & 8 deletions

File tree

lib/interp.ml

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,14 +1027,25 @@ let create_initial_env () : env =
10271027
let eval_decl (env : env) (decl : top_level) : env result =
10281028
match decl with
10291029
| TopFn fd ->
1030-
let closure = VClosure {
1031-
cl_params = fd.fd_params;
1032-
cl_body = (match fd.fd_body with
1033-
| FnBlock blk -> ExprBlock blk
1034-
| FnExpr e -> e);
1035-
cl_env = env;
1036-
} in
1037-
Ok (extend_env fd.fd_name.name closure env)
1030+
(match fd.fd_body with
1031+
| FnExtern ->
1032+
(* Externs have no AST body to evaluate. Their runtime binding is
1033+
provided by [create_initial_env]'s builtin table (panic, error,
1034+
make_ref, …). Skip here so an inline `extern fn` declaration in
1035+
a test source (e.g. the STDLIB-04a Mut round-trip tests) doesn't
1036+
blow up the [FnBlock|FnExpr] match below.
1037+
Refs #328 root-cause for the interp pattern-match-failure. *)
1038+
Ok env
1039+
| FnBlock _ | FnExpr _ ->
1040+
let closure = VClosure {
1041+
cl_params = fd.fd_params;
1042+
cl_body = (match fd.fd_body with
1043+
| FnBlock blk -> ExprBlock blk
1044+
| FnExpr e -> e
1045+
| FnExtern -> assert false (* unreachable: outer match guards *));
1046+
cl_env = env;
1047+
} in
1048+
Ok (extend_env fd.fd_name.name closure env))
10381049

10391050
| TopConst tc ->
10401051
let* v = eval env tc.tc_value in

0 commit comments

Comments
 (0)