feat: make_variant — construct a minted tagged-union value by index (stdlib)

The metatype system can MINT tagged-union types (`make_enum`) and READ them
(`field_count`/`field_name`/`field_type`/`field_value`); `make_variant` is the
missing WRITE side — build a value of a minted tagged-union `E` whose active
variant is `idx`, carrying `payload`. Needed when the variant is selected at
RUNTIME but the union was synthesized at comptime so its labels can't be spelled
as a literal `.label(payload)` — e.g. the `race` result: an `inline for 0..N (i)`
arm constructs the i-th variant of a synthesized result with the winner's value.

Pure sx in modules/std/meta.sx (NOT a compiler builtin): a minted tagged-union is
laid out `{ i64 tag @0, payload @ size_of(i64) }` (the compiler fixes `tag_type`
to i64 for minted enums), so make_variant zeroes the value then writes the tag and
payload at those offsets — the same offsets the compiler's own enum_init/payload
ops use. The header comment documents the minted-only / i64-tag layout coupling.

Adversarially reviewed (SHIP): the offset-8 payload assumption is exactly the
compiler's enum layout with no alignment hazard (the payload is an alignment-1
`[N x i8]` array immediately after the i64 header); complex payloads (multi-field
struct, string fat pointer, 40-byte struct) round-trip. Locked by
examples/comptime/0650-comptime-make-variant.sx (heterogeneous + complex payloads,
runtime-index selection via the comptime `case` form). Suite green (822/0).
This commit is contained in:
agra
2026-06-26 15:32:47 +03:00
parent 84c2ae4f22
commit 1c26944eda
5 changed files with 100 additions and 0 deletions

View File

@@ -195,3 +195,34 @@ TryResult :: ($T: Type) -> Type {
EnumVariant.{ name = "closed", payload = void },
] }));
}
// ── Constructing a minted tagged-union VALUE by variant index ─────────────────
//
// The read side reflects a type's members (`field_count`/`field_name`/
// `field_type`) and a value's active payload (`field_value`); `make_variant` is
// the missing WRITE side. It builds a value of a MINTED tagged-union `E` (one
// made by `make_enum` / `define(declare, .enum(...))`) whose active variant is
// `idx`, carrying `payload`. Needed when the variant is selected at RUNTIME but
// the union was synthesized at comptime so its labels can't be spelled as a
// literal `.label(payload)` — e.g. the `race` result: an `inline for 0..N (i)`
// arm constructs the i-th variant of a synthesized `RaceResult`.
//
// LAYOUT COUPLING — MINTED ENUMS ONLY: a minted tagged-union is laid out
// `{ i64 tag @0, payload bytes @ size_of(i64) }` (its `tag_type` is always `i64`
// — the compiler's `declare`/`define` path fixes it). `make_variant` writes that
// layout directly. It is NOT valid for a source `enum { … }` whose tag may be
// narrower, nor for a `#packed`-backed union — spell those with a literal
// `.label(…)`. Contract (caller-guaranteed, no runtime check, like any raw
// construction): `0 <= idx < field_count(E)`, and `payload`'s type IS variant
// `idx`'s payload type. `payload` must be non-void (a payload-less variant has
// no value to carry — match it by tag).
make_variant :: ($E: Type, idx: i64, payload: $P) -> E {
out : E = ---;
base : i64 = xx @out;
memset(xx base, 0, size_of(E)); // clear tag padding + unused payload bytes
tag_ptr : *i64 = xx base;
tag_ptr.* = idx; // tag @ offset 0 (i64)
pay_ptr : *P = xx (base + size_of(i64)); // payload @ offset size_of(i64) (after the tag)
pay_ptr.* = payload;
return out;
}