Devlog: Semantic Unions Become Actual Values (Part I)
In v1.4.6, I made semantic unions actual runtime values instead of just parsed/typechecked shapes.
Before this, Flower could understand:
type Value = int | string
as a type. It could point at it and say, “yes, that is a union,” but that was mostly theoretical.
The goal was to make this work-work for realsies:
type Value = int | string
value: Value = 7
if value is int:
n: int = value as int
end
value = "Ivy"
if value is string:
s: string = value as string
end
That meant Flower needed more than TypeInfo.is_union = true.
It needed a full path from syntax, to type proof, to lowered storage, to runtime extraction. Otherwise unions would just be fancy typechecker thingy-ma-bobbies, which is not very useful when the backend still emits C.
What changed
Instead of treating a union as a vague “one of these types, probably,” Flower now lowers it into a tagged structure.
Conceptually, this:
type Value = int | string
becomes something like:
struct Value {
int tag;
union {
int as_int;
flower_string as_string;
} data;
};
Not the final sacred form forever, but that is the shape of it: a tag saying which variant is active, plus storage for the possible members. Since Flower emits C right now, the lowering is C-shaped. I am not fully sure how I want this to look in ASM yet, but I do know it would be way more technical, and uglier.
This required a few things to play nicely with one another:
-
if value is intcreates a union proof - the guarded branch stores narrowing information
-
value as intis only allowed after proof - assignments pack values into the right tagged member
- explicit casts extract the right member from storage
Otherwise the compiler is just writing checks its own backend cannot cash.
The part that makes it ACTUALLY semantic
For pointer narrowing, if value is T mostly refines how the compiler treats an existing value. For semantic unions, the check also becomes a runtime tag test.
So this:
if value is int:
means:
check the active tag of
value, then inside this branch, treat it as theintvariant.
Then this becomes valid:
n: int = value as int
because the branch already proved the active variant.
Without that proof, value as int should not be casually allowed. Flower is supposed to be explicit, not psychic, and I am not confident enough yet to implement a proper intuitive implicit checker without summoning something cursed.
Assignment and extraction
This:
value: Value = 7
now packs 7 into the int member and sets the tag to the int variant.
Then this:
value = "Ivy"
does the same thing, but for the string member.
So a semantic union is not just some bytes, but rather a tagged runtime object where Flower knows which member is active.
Then explicit casts like:
value as string
can lower into extracting the string member, but only when the typechecker has accepted that the operation is safe in that control-flow path.
Why this checkpoint matters
Flower can now store variant values, prove which variant is active, and require explicit extraction instead of relying on random magic I threw together.
The flower has grown another concerning little organ.
Comments 0
No comments yet. Be the first!
Sign in to join the conversation.