Enum design notes (maintainers)
Why the user-facing behavior in reference/enums-options.md is sound. Users don't need this page.
Payloads
Each variant's payload is arena-allocated and the enum value itself is a small tagged descriptor. Copying an enum deep-copies the whole payload; == compares structurally. Option/Result stay inside the model — no null pointer, no exceptions.
Why recursive enums stay finite
A recursive enum (Expr = Num | Add(Expr, Expr)) is the hardest shape. Because the payload lives in the arena rather than inline, the descriptor is finite — no infinitely-large type — and copying the tree runs the recursive copy at every level. Generic enums monomorphize the same way generic structs do, including recursive payloads that name the enum itself. The layout and the copy behavior are covered by the memory model.
Lowering
- A value
if/matchlowers to the declare-then-assign-in-each-arm statement form: same exhaustiveness, same single-type rule, no new semantics. or_returnbinds tighter than any arithmetic and applies wherever the unwrapped value is wanted. The short-circuitedErrpayload is copied into the caller's scope so it outlives the return; theOptionform returnsNonethe same way.or_else x: hbinds the error toxand returns the one-expressionh(to end of line or closing bracket) from the enclosing function.
Inline fixed-size enums
A user enum whose payloads are all scalar is stored as a packed tag plus a union of the variants, rather than as a pointer to an arena cell. It is still a value: assignment copies it, == compares the tag and active payload, and match reads the same fields. Recursive, generic, fieldless, package-mangled and heap-payload enums remain pointer cells. Both compilers use this lowering; TYCHOC_NO_INLINE_ENUM=1 keeps the old representation for benchmarking.