flan/test/programs/allocators.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

69 lines
3.1 KiB
Plaintext

;;;; Allocators — spec-memory.md, "Allocators". No container here: this is the
;;;; tier on its own, so that a failure in it is not read as a Vec bug.
;;;;
;;;; What is asserted: the capability set is readable at run time and differs
;;;; per allocator; with-allocator rebinds for its dynamic extent and restores
;;;; afterwards, including out of a call and out of a transfer; free-all is
;;;; retain-capacity and bumps the epoch anyway; and nothing is released at
;;;; scope exit, which is the point the spec is most emphatic about.
;; A zeroed Allocator. A global rather than a local because the arena has to
;; outlive the frame that makes it, and because a handler cannot see a local
;; (check.ml's `captured` says so by name).
(defonce frame Allocator)
;;; The context is a dynamic variable, so a function called from inside a
;;; with-allocator body sees the rebinding without anything being passed.
(defn who-am-i [] bool
(can-free? context/allocator))
(defn main [] i32
;; The heap allocator frees one block; an arena does not. That is Odin's
;; answer too — its arena returns Mode_Not_Implemented for .Free — and it is
;; the capability spec-memory.md calls load-bearing.
(set frame (arena-new 1024))
(println (can-free? (heap-allocator))) ; true
(println (can-free? frame)) ; false
(println (can-free-all? (heap-allocator))) ; false
(println (can-free-all? frame)) ; true
;; The default context is the heap allocator, and context/temp is its own
;; arena — the per-frame tier, distinct from it.
(println (can-free? context/allocator)) ; true
(println (can-free? context/temp)) ; false
;; with-allocator rebinds for the dynamic extent, so a call made from inside
;; the body sees the arena, and the binding is gone after the body.
(println (with-allocator frame (who-am-i))) ; false
(println (who-am-i)) ; true
;; ... and it is an expression: the body's last value is the form's value.
(println (with-allocator frame 41)) ; 41
;; free-all is retain-capacity: the pages stay, the epoch moves. Both halves
;; matter — the first is what makes a per-frame reset free, and the second is
;; what a container's dev trap reads.
(println (alloc-epoch frame)) ; 0
(free-all frame)
(println (alloc-epoch frame)) ; 1
(free-all frame)
(println (alloc-epoch frame)) ; 2
;; Nothing is released at scope exit — not at the end of a let, not at the
;; end of a with-allocator body. The epoch is the observable proof: leaving
;; the body did not release the region it named.
(let [before (alloc-epoch frame)]
(with-allocator frame (println (alloc-epoch frame))) ; 2
(println (= before (alloc-epoch frame)))) ; true
;; And out of a transfer. The restart-case's clause runs after the body has
;; left through the pad, so the context allocator here is the one the
;; with-allocator displaced, not the arena.
(println
(restart-case
(with-allocator frame (invoke-restart 'resync))
(resync [] (can-free? context/allocator)))) ; true
(arena-destroy frame)
0)