docs(metatype): declare(name) + self-reference done; update plan/checkpoint

This commit is contained in:
agra
2026-06-16 22:08:50 +03:00
parent 2a9ffd25a8
commit a7dde2efd1
2 changed files with 72 additions and 57 deletions

View File

@@ -4,31 +4,32 @@ Companion to [PLAN-METATYPE.md](PLAN-METATYPE.md). Update after every step (one
step at a time, per the cadence rule).
## Last completed step
**The `declare`/`define` floor — the compiler's only type-construction surface.**
Two comptime `#builtin`s mint types; every constructor (`RecvResult`, `TryResult`,
the one-shot form) is plain sx built over them. The compiler has ZERO knowledge of
any named constructor.
**Self-reference — recursive enums via `declare("Name")` + `*Name`.** The
`declare`/`define` floor now supports self-referential types.
- `declare() -> Type` mints an empty (undefined) nominal slot; `define(handle,
info) -> Type` decodes the `TypeInfo` value (variant names + payload Type-tags),
names the slot from `EnumInfo.name`, completes it byte-identical to a source
enum, and returns the handle (so the one-shot form chains:
`T :: define(declare(), info)`). Interp executes both against a `mint` TypeTable
handle (`setMintTable`); `defineEnum` + `decodeVariantElements` in `interp.zig`.
- A `::` binding or type-fn body that calls a `Type`-returning fn is
**comptime-evaluated** (`evalComptimeType`), running the `declare`/`define`
builtins to mint the type — no constructor-name pattern-matching. `decl.zig`
trigger = `fnReturnsTypeValue`; type-fn trigger = `returnExprMintsType`
(return is a `define(…)` call or a bodied `-> Type` helper).
- Nominal identity rides the type-fn instantiation cache (`renameNominalType`
re-keys the minted type to the mangled name); `RecvResult(i64)` at two sites is
ONE type.
- The type NAME travels in `EnumInfo.name` — the compiler derives no name from a
binding LHS.
- `declare(name) -> Type` mints an empty (undefined) nominal slot NAMED `name`;
`define(handle, info) -> Type` decodes the `TypeInfo` value (variant names +
payload Type-tags), fills the slot byte-identical to a source enum, and returns
the handle (one-shot form chains: `T :: define(declare("T"), info)`). Interp
executes both against a `mint` TypeTable handle; `defineEnum` +
`decodeVariantElements` in `interp.zig`.
- **Self-reference:** `evalComptimeType`'s `preregisterForwardTypes` scans the
comptime expression (and a called ctor fn's body) for `declare("Name")` calls
and, before the body lowers, registers each as an empty forward nominal type AND
binds it as a type alias. The alias is essential — a `Name :: ctor()` decl makes
`Name` a const_decl author, so a `*Name` self-reference resolves through the
forward-ALIAS path (`type_aliases_by_source`), which a bare `findByName`
registration alone does NOT satisfy (it returns a pending empty-struct stub). The
interp's `declare` returns that same slot; `define` fills it.
- A `::` binding or type-fn body calling a `Type`-returning fn is
**comptime-evaluated** (`evalComptimeType`) — no constructor-name knowledge.
`decl.zig` trigger = `fnReturnsTypeValue`; type-fn trigger = `returnExprMintsType`.
- Nominal identity rides the type-fn instantiation cache (`renameNominalType`).
- The type NAME is on `declare(name)` (compile-time string), not `EnumInfo`.
Examples green on the floor: `0614` (one-shot `define(declare(), …)`), `0615`
(type-fn identity), `0617` (channel result types). `field_type` reflection still
green (`0616`). Full suite green (673 examples).
Examples green: `0614` (one-shot), `0615` (type-fn identity), `0617` (channel
results), **`0618` (recursive `*List`: construct, match through pointer, recursive
traversal)**; `field_type` reflection `0616`. Full suite green (674 examples).
## Current state
- `modules/std/meta.sx`: `EnumVariant` / `EnumInfo{ name, variants }` / `TypeInfo`
@@ -36,7 +37,8 @@ green (`0616`). Full suite green (673 examples).
`RecvResult($T)` / `TryResult($T)` sx constructors over `define(declare(), …)`.
- Compiler primitives only: `declare`/`define` (construction), `field_type`
(reflection). No constructor-name knowledge anywhere in the compiler — every
named constructor is sx.
named constructor is sx. `declare(name)` carries the type name (compile-time
string) for forward-type registration.
- `type_info` still bails loudly (`call.zig:tryLowerReflectionCall`) — pending.
## Decision (kept)
@@ -45,31 +47,32 @@ in the always-loaded prelude interns them into every module's type table and
shifts every `.ir` snapshot. On-demand import keeps the prelude clean.
## Next step
**Self-reference (recursive / mutually-recursive enums).** The two-statement form
`List :: declare(); define(List, .enum(.{ … payload = *List … }))`: `declare`
binds the handle, `define` completes it, and `*List` resolves because a pointer to
an undefined slot is legal. Needs the top-level `define(…)` statement form to run
at comptime in the window before any use of the type's layout (a bare top-level
call doesn't parse today — decide the surface). Pairs with a `make_enum(variants:
[]EnumVariant)` sx helper (a computed, non-literal variant list — exercises the
interpreter decoding a value-arg slice).
Then: **`type_info($T) -> TypeInfo`** (reflect a type INTO a value — the inverse of
`define`'s decode; widen `TypeInfo` past `` `enum `` + construct a `[]EnumVariant`
value in the interp; its own focused effort) and **validation + loud diagnostics**
(dup variants, by-value self-ref, never-defined `declare`, use-before-define).
Pick any (independent):
- **`make_enum(variants: []EnumVariant)`** sx helper over a COMPUTED (non-literal)
variant list — exercises the interpreter decoding a value-arg slice in `define`
(vs. the literal `.[ … ]` the current examples use).
- **`type_info($T) -> TypeInfo`** — reflect a type INTO a value (inverse of
`define`'s decode); widen `TypeInfo` past `` `enum `` + construct a
`[]EnumVariant` value in the interp. Bails loudly today in
`call.zig:tryLowerReflectionCall`. Its own focused effort.
- **Validation + loud diagnostics** — duplicate variant names, by-VALUE
self-reference (`payload = List` not `*List` → infinite size), a `declare()`
never `define()`d (hard error), use-before-define.
## Known issues
None.
## Log
- **Self-reference done.** `declare(name)` + `preregisterForwardTypes` (forward
type + alias before body lowers) → `*Name` resolves; recursive `*List` enum
constructs, matches through the pointer, and traverses recursively. `0618` locks
it. `declare` gained its `name` arg; `EnumInfo.name` dropped. Suite green (674).
- **declare/define floor established.** The comptime type-construction surface is
two primitives (`declare`/`define`); all named constructors are sx. A `::` binding
or type-fn body that calls a `Type`-returning fn is comptime-evaluated (the
builtins mint the type) — no syntactic constructor recognition in the compiler.
Name travels in `TypeInfo`. Examples 0614 (one-shot) / 0615 (type-fn identity) /
0617 (channel results) run on the floor; `field_type` reflection (0616) unchanged.
Full suite green (673).
Examples 0614 (one-shot) / 0615 (type-fn identity) / 0617 (channel results) on the
floor; `field_type` reflection (0616) unchanged.
- **Stream carved (earlier).** Selected as the first async-first foundation: gates
channel result types (`RecvResult($T)`) and `race`'s synthesized union, fully
validated, self-contained, testable in isolation (`06xx` comptime).