The spec's unwritten operators say so by name

error, find-restart and compute-restarts are named in spec-conditions.md and had
no case in parse.ml, so each fell through to Call and came back as unknown name
- the very shape the house rule exists to prevent, and the one that makes a
missing feature look like a typo.

Three message strings from the previous commit had their line continuations
collapsed into runs of spaces. Rewrapped; no change to what they say.
This commit is contained in:
Joseph Ferano 2026-09-11 08:26:46 +07:00
parent ef712e3089
commit ec0d845822
3 changed files with 27 additions and 7 deletions

View File

@ -25,7 +25,9 @@ already has the stack they would walk.
Still open from §3, and each refused by name today: **restarts with
parameters** (argument marshalling plus the runtime arity check), and
`handler-case`, which §"What this does not settle" leaves open as possibly a
macro over `handler-bind` plus a transfer.
macro over `handler-bind` plus a transfer. `error`, `find-restart` and
`compute-restarts` now refuse by name too — they used to fall through to a call
and come back as *unknown name*, which is the house rule's own class of bug.
Read SBCL for what restarts should *mean* and ignore how it moves control: it
transfers with `block`/`return-from`, which §6 rules out.

View File

@ -237,7 +237,9 @@ and form f mk (head : Form.t) (args : Form.t list) : Ast.expr =
when cbody <> [] ->
if ps <> [] then
fail c
"a restart takes no parameters yet — spec-conditions.md §3 has them, and they need argument marshalling and a runtime arity check that this version does not do";
"a restart takes no parameters yet — spec-conditions.md §3 has \
them, and they need argument marshalling and a runtime arity \
check that this version does not do";
{ Ast.rname = n; rbody = List.map expr cbody; rloc = c.Form.loc }
| _ -> fail c "a restart-case clause is (name [] body ...)"
in
@ -252,14 +254,22 @@ and form f mk (head : Form.t) (args : Form.t list) : Ast.expr =
mk (Ast.InvokeRestart n)
| [ _ ] ->
fail f
"invoke-restart takes a quoted restart name, as in (invoke-restart 'use-placeholder)"
"invoke-restart takes a quoted restart name, as in \
(invoke-restart 'use-placeholder)"
| _ ->
fail f
"a restart takes no arguments yet — spec-conditions.md §3 has them, and they need argument marshalling and a runtime arity check that this version does not do")
"a restart takes no arguments yet — spec-conditions.md §3 has them, \
and they need argument marshalling and a runtime arity check that \
this version does not do")
(* Recognised, deliberately unimplemented. Rejected rather than left to fall
through to Call, where they would parse and mean nothing. *)
| Sym ("handler-case"
(* Named in the spec and not written yet, so each says so rather than
falling through to Call and coming back as an unknown name: [error]
is §2's diverging signal, and [find-restart] and [compute-restarts]
are §4's two ways to look at the stack without committing. *)
| "error" | "find-restart" | "compute-restarts"
| "errdefer" | "with-allocator" | "loop" | "recur"
| "defmacro" | "await" as name) ->
fail f "%s is not implemented yet (see the build sequence in plan.org)" name

View File

@ -691,9 +691,17 @@ let () =
rejects_check "invoke-restart inside a defer"
"(defn f [] i32 (defer (invoke-restart 'skip)) 0)"
~needle:"not allowed inside a defer";
(* Still unimplemented, and still says so by name. *)
rejects_check "handler-case is still unimplemented"
"(defn f [] (handler-case 1))" ~needle:"not implemented yet";
(* Still unimplemented, and still says so by name — which is the point: an
operator the spec names and the compiler lacks must not fall through to a
call and come back as an unknown name. *)
List.iter
(fun (name, src) ->
rejects_check (name ^ " is still unimplemented") src
~needle:"not implemented yet")
[ "handler-case", "(defn f [] (handler-case 1))";
"error", "(defn f [] (error 1))";
"find-restart", "(defn f [] (find-restart 'skip))";
"compute-restarts", "(defn f [] (compute-restarts))" ];
(* ── The acceptance program checks end to end ──────────────────── *)
accepts "calc-me.flan type checks"