flan/spike/x86/p12-handler-value.flan
Joseph Ferano a4c6b996ff def re-runs its initialiser, and defvar is renamed defonce
The trio the author decided on 2026-09-20 is now all built: def is CL's
defparameter — its initialiser runs on every daemon re-run, unguarded, so
an edited initialiser repaints the same storage on C-c C-c plus re-run —
defonce (Clojure's name for CL's defvar, per the author) initialises once
behind the .init~once. flag, and defconst stays the image.

One parse arm reads both forms; the difference is Ast.reinit, carried to
Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and
Check.check_global lifts every def initialiser — zero and literal
included — into global/<n>, so the host's startup reaches it through the
function cell and a re-evaluated def swaps it (Session's def_inits;
Emit.redefinition declares the cell for a non-sibling target). The old
defvar spelling is refused with the rename and both compiling spellings,
and every program, test, doc and editor list is swept — except sand.flan,
the author's live WIP, whose seven defvar lines are flagged in FIX.org
and keep its three dependent tests red on this branch.
2026-09-21 07:12:04 +07:00

96 lines
4.0 KiB
Plaintext

;;;; A handler-bind in value position, and the shapes around it.
;;;;
;;;; This is here because for a long time nothing in the corpus asked a
;;;; [handler-bind] what its value was. [check.ml] typed the form [unit] and
;;;; dropped the expectation without checking it, so a caller that asked got an
;;;; answer anyway -- and the two backends had picked different ones. [emit.ml]
;;;; answered a literal zero; the hand-written backend answered whatever the
;;;; body's last form had left in the destination slot. The program below
;;;; printed 0 through LLVM and 2 through --x86, and neither number was the
;;;; restart's.
;;;;
;;;; The four programs that did write a [handler-bind] in value position all
;;;; wrote it as a [restart-case] body -- restarts.flan, cleanup.flan,
;;;; p6-transfer.flan, p10-defer-transfer.flan -- where §3 makes the types
;;;; agree but a transfer always leaves before the fall-through is reached. So
;;;; the value was never read and the hole stayed open. Here it is read:
;;;; [answered] returns through the handler-bind, and its value has to be the
;;;; clause's.
;;;;
;;;; The rest are the neighbouring shapes, because a divergence is rarely
;;;; alone: a clause parameter, two nested restart-cases, a defer between the
;;;; signal and the restart-case, and three types wider than the i32 the
;;;; condition corpus is written in.
(defstruct Oops [id i32])
(defonce trace i64)
;;; The shape that had no answer. The handler-bind is this function's last
;;; form, so what it yields is what the function returns, two frames above the
;;; restart-case the transfer lands in.
(defn risky [] i64
(restart-case (do (signal (Oops {.id 1})) 7)
(use-zero [] 42)))
(defn answered [] i64
(handler-bind [(Oops [c] (invoke-restart 'use-zero))]
(risky)))
;;; The fall-through half of the same shape: nothing handles the signal, so the
;;; body's own value is the one that comes back out through both forms.
(defn unanswered [] i64
(handler-bind [(Oops [c] (set trace (+ trace 100)))]
(risky)))
;;; §3's parameter, crossing into an i64 clause.
(defn supplied [] i64
(restart-case (do (signal (Oops {.id 2})) 7)
(use-value [v i64] (* v 3))))
;;; §4: the inner frame wins, and the arithmetic written around it still runs.
(defn nested [] i64
(restart-case
(+ (restart-case (do (signal (Oops {.id 3})) 7)
(use-zero [] 10))
1000)
(use-zero [] 20)))
;;; §5: a defer between the signal and the restart-case runs on the way out,
;;; before the clause body starts.
(defn mid [] i64
(defer (set trace (+ trace 1)))
(signal (Oops {.id 4}))
7)
(defn deferred [] i64
(restart-case (mid) (use-zero [] 9)))
;;; Two widths the condition corpus does not otherwise carry across a transfer:
;;; a float, which travels in the other register file, and a string, which is a
;;; pointer and a length rather than one machine word.
(defn floating [] f64
(restart-case (do (signal (Oops {.id 5})) 1.5)
(use-value [v f64] (* v 2.0))))
(defn spelled [] string
(restart-case (do (signal (Oops {.id 6})) "fell-through")
(use-value [v string] v)))
(defn main [] i32
(handler-bind [(Oops [c] (invoke-restart 'use-zero))]
(println (answered))) ; 42
(println (unanswered)) ; 7
(println trace) ; 100
(handler-bind [(Oops [c] (invoke-restart 'use-value (i64 5)))]
(println (supplied))) ; 15
(handler-bind [(Oops [c] (invoke-restart 'use-zero))]
(println (nested))) ; 1010
(handler-bind [(Oops [c] (invoke-restart 'use-zero))]
(println (deferred))) ; 9
(println trace) ; 101 — the defer ran
(handler-bind [(Oops [c] (invoke-restart 'use-value 2.5))]
(println (floating))) ; 5
(handler-bind [(Oops [c] (invoke-restart 'use-value "supplied"))]
(println (spelled))) ; supplied
0)