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.
54 lines
2.0 KiB
Plaintext
54 lines
2.0 KiB
Plaintext
;;;; The second transfer, which the checker can only half refuse.
|
|
;;;;
|
|
;;;; spec-conditions.md §5 says a defer is the cleanup a transfer runs on its
|
|
;;;; way out. Starting a *second* transfer from inside one would leave this
|
|
;;;; frame's defers half run with two targets and no way to choose, so both
|
|
;;;; backends emit a branch into `flan_transfer_fail` for it and the runtime
|
|
;;;; dies there with the frame's location.
|
|
;;;;
|
|
;;;; `check.ml`'s `Ast.InvokeRestart` arm refuses the *lexical* case -- an
|
|
;;;; `invoke-restart` written inside the `defer` form itself -- with the same
|
|
;;;; reasoning. What it cannot see is a defer that *calls* a function that
|
|
;;;; invokes a restart, because the callee is ordinary code and knows nothing
|
|
;;;; about who called it. That is the case below, and it is the only way to
|
|
;;;; reach the branch.
|
|
;;;;
|
|
;;;; The order matters and is what makes this a real second transfer rather
|
|
;;;; than a first one: `outer` establishes both restart-cases, calls `middle`,
|
|
;;;; `deep` signals, `outer`'s handler aims at `esc-one`, and the transfer
|
|
;;;; unwinds `deep` and then `middle`. `middle`'s transfer exit clears the
|
|
;;;; channel and runs its defers -- and the defer calls `second`, which aims a
|
|
;;;; transfer at `esc-two`. Both restart frames are still pushed: `outer`'s
|
|
;;;; pad is what pops them, and `outer` has not been reached yet.
|
|
;;;;
|
|
;;;; Exits 134 with the message on stderr, both ways.
|
|
|
|
(defstruct Blip [n i32])
|
|
|
|
(defonce log i64)
|
|
|
|
(defn deep [n i32] i32
|
|
(signal (Blip {.n n}))
|
|
0)
|
|
|
|
;;; Ordinary code. The checker has nothing to object to here, because from
|
|
;;; where it stands this is a function like any other.
|
|
(defn second [] i64
|
|
(invoke-restart 'esc-two))
|
|
|
|
(defn middle [n i32] i32
|
|
(defer (set log (second)))
|
|
(deep n))
|
|
|
|
(defn outer [n i32] i32
|
|
(restart-case
|
|
(restart-case
|
|
(handler-bind [(Blip [c] (invoke-restart 'esc-one))]
|
|
(middle n))
|
|
(esc-one [] 11))
|
|
(esc-two [] 22)))
|
|
|
|
(defn main [] i32
|
|
(print (outer 1)) (println "")
|
|
0)
|