;; 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. (defvar zeroed Bits) (defvar 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)