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

90 lines
3.3 KiB
Plaintext

;; The untagged union: one storage, several ways of reading it.
;;
;; Every line here is a fact about *bytes*, which is the whole reason the
;; program exists rather than a checker row. A union's layout is the only
;; thing about it that can be wrong silently: reading the member that was not
;; written is defined behaviour in Flan, so nothing at run time would notice a
;; member placed at the wrong offset or a type sized to the wrong member --
;; the numbers would simply be different ones. So the numbers are written
;; down, and both backends have to produce them.
;;
;; 0x3F800000 is 1.0f and 0x4000000000000000 is 2.0. They are here as decimal
;; literals because that is what a reader who doubts the output has to be able
;; to check by hand against IEEE 754, and a hex literal would only move the
;; question.
(defstruct P [x f32 y f32])
(defunion Bits [i i32 f f32 bs [4 u8]])
(defunion Wide [n i64 d f64 p P bs [8 u8]])
;; A union inside a struct, which is the FFI shape: the C library keeps the
;; tag beside the union and the rule relating them is prose in its manual, so
;; `kind' here is an ordinary field this program reads itself.
(defstruct Slot [kind i32 v Bits])
;; Zeroed and uninit, side by side. A data type refuses uninit because its tag
;; steers a match; this one has no tag to steer anything, so both are legal
;; and the zeroed one is all-bytes-zero.
(defonce zeroed Bits)
(defonce scratch Bits uninit)
(defn as-float [b Bits] f32 (.f b))
(defn of-float [x f32] Bits (Bits {.f x}))
(defn main [] i32
;; Punning, both directions, through the same storage.
(let [b (Bits {.i 1065353216})]
(println (.f b))
(println (.i b))
;; The bytes little-endian: 0x3F800000 is 00 00 80 3F.
(println (at (.bs b) 0))
(println (at (.bs b) 3))
;; A member written through a place, which is the other half of the same
;; claim -- the read above could have been folded from the literal, and a
;; store into the union could not.
(set (.f b) 2.0)
(println (.i b)))
;; The union crosses a call boundary in both directions by value.
(println (as-float (of-float 0.5)))
;; A wider union: the size is the widest member and not the first one. An
;; array of two is where that shows up -- writing element 1 would land
;; inside element 0 if the type were sized to its i64 member alone and the
;; f64 or the [8 u8] were wider, and every element's value would change
;; under the other's write.
(let [w (Wide {.d 2.0})]
(println (.n w))
(println (.x (.p w))))
(let [ws (array 2 Wide)]
(set (.n (at ws 0)) 11)
(set (.n (at ws 1)) 22)
(println (.n (at ws 0)))
(println (.n (at ws 1))))
;; ZII: a union with no member given is all-bytes-zero, and so is a global
;; declared with no value.
(let [empty (Bits {})]
(println (.i empty)))
(println (.i zeroed))
;; And a global is written and read like any other place.
(set (.i scratch) 7)
(println (.i scratch))
;; A union inside a struct, with the tag the program keeps itself.
(let [s (Slot {.kind 1 .v (Bits {.f 1.5})})]
(println (.kind s))
(println (.f (.v s)))
(set (.kind s) 2)
(set (.i (.v s)) 9)
(println (.kind s))
(println (.i (.v s))))
;; Printed by name and not walked: the printer cannot know which member is
;; live, and a member may be a pointer.
(println zeroed)
0)