;;;; Computed global initialisers — plan.org, Data model. ;;;; ;;;; A global whose value is not something a linker can write into the image. ;;;; The initialiser is lifted into a function of its own and called at startup, ;;;; from main, after the runtime is up and before a line of the program's own ;;;; code — Odin's __$startup_runtime shape rather than a constructor. Both ;;;; backends do it the same way, which is what the x86 survey is comparing. ;;;; ;;;; What each half of this file is asserting: ;;;; ;;;; - an arena allocated at startup and used from main, which is the form ;;;; the author kept writing: (defvar frame Allocator (arena-new N)). ;;;; - the order. `derived` is written above the global it reads, so the ;;;; declaration order is the wrong one and the sort is what makes it 30. ;;;; - a dependency that runs through a call rather than through the text of ;;;; the initialiser: `via-fn` names no global at all, and the function it ;;;; calls reads one. ;;;; - control flow in an initialiser. Every one of these needs a frame, and ;;;; before the lift there was none — a `let` in an initialiser indexed a ;;;; slot array of length zero and took the compiler down with it. ;;;; - a container loaded by its own initialiser, and a data type case ;;;; written into a global. Both were refused while there was nowhere for ;;;; an initialiser to run. (defdata Shape [Nothing (Circle [r i32]) (Square [side i32])]) ;; Written above `base`, and it reads it. (defvar derived i64 (* base 10)) (defvar base i64 (+ 1 2)) (defn twice-base [] i64 (* base 2)) ;; Names no global; the function it calls does. (defvar via-fn i64 (+ (twice-base) 1)) ;; The author's arena, and the Vec it is meant to hold. (defvar frame Allocator (arena-new 262144)) ;; Control flow, each shape in its own global. (defn maybe [] (Option i32) (Some 3)) (defvar matched i32 (match (maybe) (Some x) x None 0)) (defvar branched i64 (if (> base 2) (let [k (+ base 1)] (* k 2)) 0)) (defvar counted i64 (let [t (i64 0)] (while (< t 4) (set t (+ t 1))) t)) ;; A data type case: a store at startup, where a constant would have needed a ;; byte-level encoder for the payload blob. (defvar shape Shape (Shape.Circle {.r 7})) ;; And a container whose real value only exists behind an allocator. (defvar names (Vec i64) (vec-new i64)) (defn main [] i32 (println derived) (println base) (println via-fn) (println matched) (println branched) (println counted) (match shape (Circle r) (println r) (Square s) (println s) Nothing (println -1)) ;; The Vec was made with the default allocator at startup and is still the ;; program's when main runs. (push names 11) (push names 22) (println (len (as-slice names))) (println (at names 1)) (free names) ;; And the arena, used the way sand.flan means to use it. (with-allocator frame (let [v (vec-new i64)] (push v 7) (println (at v 0)))) (free-all frame) 0)