docs(metatype): declare(name) + self-reference done; update plan/checkpoint
This commit is contained in:
@@ -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).
|
step at a time, per the cadence rule).
|
||||||
|
|
||||||
## Last completed step
|
## Last completed step
|
||||||
**The `declare`/`define` floor — the compiler's only type-construction surface.**
|
**Self-reference — recursive enums via `declare("Name")` + `*Name`.** The
|
||||||
Two comptime `#builtin`s mint types; every constructor (`RecvResult`, `TryResult`,
|
`declare`/`define` floor now supports self-referential types.
|
||||||
the one-shot form) is plain sx built over them. The compiler has ZERO knowledge of
|
|
||||||
any named constructor.
|
|
||||||
|
|
||||||
- `declare() -> Type` mints an empty (undefined) nominal slot; `define(handle,
|
- `declare(name) -> Type` mints an empty (undefined) nominal slot NAMED `name`;
|
||||||
info) -> Type` decodes the `TypeInfo` value (variant names + payload Type-tags),
|
`define(handle, info) -> Type` decodes the `TypeInfo` value (variant names +
|
||||||
names the slot from `EnumInfo.name`, completes it byte-identical to a source
|
payload Type-tags), fills the slot byte-identical to a source enum, and returns
|
||||||
enum, and returns the handle (so the one-shot form chains:
|
the handle (one-shot form chains: `T :: define(declare("T"), info)`). Interp
|
||||||
`T :: define(declare(), info)`). Interp executes both against a `mint` TypeTable
|
executes both against a `mint` TypeTable handle; `defineEnum` +
|
||||||
handle (`setMintTable`); `defineEnum` + `decodeVariantElements` in `interp.zig`.
|
`decodeVariantElements` in `interp.zig`.
|
||||||
- A `::` binding or type-fn body that calls a `Type`-returning fn is
|
- **Self-reference:** `evalComptimeType`'s `preregisterForwardTypes` scans the
|
||||||
**comptime-evaluated** (`evalComptimeType`), running the `declare`/`define`
|
comptime expression (and a called ctor fn's body) for `declare("Name")` calls
|
||||||
builtins to mint the type — no constructor-name pattern-matching. `decl.zig`
|
and, before the body lowers, registers each as an empty forward nominal type AND
|
||||||
trigger = `fnReturnsTypeValue`; type-fn trigger = `returnExprMintsType`
|
binds it as a type alias. The alias is essential — a `Name :: ctor()` decl makes
|
||||||
(return is a `define(…)` call or a bodied `-> Type` helper).
|
`Name` a const_decl author, so a `*Name` self-reference resolves through the
|
||||||
- Nominal identity rides the type-fn instantiation cache (`renameNominalType`
|
forward-ALIAS path (`type_aliases_by_source`), which a bare `findByName`
|
||||||
re-keys the minted type to the mangled name); `RecvResult(i64)` at two sites is
|
registration alone does NOT satisfy (it returns a pending empty-struct stub). The
|
||||||
ONE type.
|
interp's `declare` returns that same slot; `define` fills it.
|
||||||
- The type NAME travels in `EnumInfo.name` — the compiler derives no name from a
|
- A `::` binding or type-fn body calling a `Type`-returning fn is
|
||||||
binding LHS.
|
**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`
|
Examples green: `0614` (one-shot), `0615` (type-fn identity), `0617` (channel
|
||||||
(type-fn identity), `0617` (channel result types). `field_type` reflection still
|
results), **`0618` (recursive `*List`: construct, match through pointer, recursive
|
||||||
green (`0616`). Full suite green (673 examples).
|
traversal)**; `field_type` reflection `0616`. Full suite green (674 examples).
|
||||||
|
|
||||||
## Current state
|
## Current state
|
||||||
- `modules/std/meta.sx`: `EnumVariant` / `EnumInfo{ name, variants }` / `TypeInfo`
|
- `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(), …)`.
|
`RecvResult($T)` / `TryResult($T)` sx constructors over `define(declare(), …)`.
|
||||||
- Compiler primitives only: `declare`/`define` (construction), `field_type`
|
- Compiler primitives only: `declare`/`define` (construction), `field_type`
|
||||||
(reflection). No constructor-name knowledge anywhere in the compiler — every
|
(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.
|
- `type_info` still bails loudly (`call.zig:tryLowerReflectionCall`) — pending.
|
||||||
|
|
||||||
## Decision (kept)
|
## 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.
|
shifts every `.ir` snapshot. On-demand import keeps the prelude clean.
|
||||||
|
|
||||||
## Next step
|
## Next step
|
||||||
**Self-reference (recursive / mutually-recursive enums).** The two-statement form
|
Pick any (independent):
|
||||||
`List :: declare(); define(List, .enum(.{ … payload = *List … }))`: `declare`
|
- **`make_enum(variants: []EnumVariant)`** sx helper over a COMPUTED (non-literal)
|
||||||
binds the handle, `define` completes it, and `*List` resolves because a pointer to
|
variant list — exercises the interpreter decoding a value-arg slice in `define`
|
||||||
an undefined slot is legal. Needs the top-level `define(…)` statement form to run
|
(vs. the literal `.[ … ]` the current examples use).
|
||||||
at comptime in the window before any use of the type's layout (a bare top-level
|
- **`type_info($T) -> TypeInfo`** — reflect a type INTO a value (inverse of
|
||||||
call doesn't parse today — decide the surface). Pairs with a `make_enum(variants:
|
`define`'s decode); widen `TypeInfo` past `` `enum `` + construct a
|
||||||
[]EnumVariant)` sx helper (a computed, non-literal variant list — exercises the
|
`[]EnumVariant` value in the interp. Bails loudly today in
|
||||||
interpreter decoding a value-arg slice).
|
`call.zig:tryLowerReflectionCall`. Its own focused effort.
|
||||||
|
- **Validation + loud diagnostics** — duplicate variant names, by-VALUE
|
||||||
Then: **`type_info($T) -> TypeInfo`** (reflect a type INTO a value — the inverse of
|
self-reference (`payload = List` not `*List` → infinite size), a `declare()`
|
||||||
`define`'s decode; widen `TypeInfo` past `` `enum `` + construct a `[]EnumVariant`
|
never `define()`d (hard error), use-before-define.
|
||||||
value in the interp; its own focused effort) and **validation + loud diagnostics**
|
|
||||||
(dup variants, by-value self-ref, never-defined `declare`, use-before-define).
|
|
||||||
|
|
||||||
## Known issues
|
## Known issues
|
||||||
None.
|
None.
|
||||||
|
|
||||||
## Log
|
## 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
|
- **declare/define floor established.** The comptime type-construction surface is
|
||||||
two primitives (`declare`/`define`); all named constructors are sx. A `::` binding
|
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
|
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.
|
builtins mint the type) — no syntactic constructor recognition in the compiler.
|
||||||
Name travels in `TypeInfo`. Examples 0614 (one-shot) / 0615 (type-fn identity) /
|
Examples 0614 (one-shot) / 0615 (type-fn identity) / 0617 (channel results) on the
|
||||||
0617 (channel results) run on the floor; `field_type` reflection (0616) unchanged.
|
floor; `field_type` reflection (0616) unchanged.
|
||||||
Full suite green (673).
|
|
||||||
- **Stream carved (earlier).** Selected as the first async-first foundation: gates
|
- **Stream carved (earlier).** Selected as the first async-first foundation: gates
|
||||||
channel result types (`RecvResult($T)`) and `race`'s synthesized union, fully
|
channel result types (`RecvResult($T)`) and `race`'s synthesized union, fully
|
||||||
validated, self-contained, testable in isolation (`06xx` comptime).
|
validated, self-contained, testable in isolation (`06xx` comptime).
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
|
|
||||||
Comptime type metaprogramming with the smallest possible compiler surface:
|
Comptime type metaprogramming with the smallest possible compiler surface:
|
||||||
|
|
||||||
- **`declare() -> Type`** — mint a NEW empty (undefined) nominal type, returned as
|
- **`declare(name) -> Type`** — mint a NEW empty (undefined) nominal type NAMED
|
||||||
a first-class `Type` handle.
|
`name`, returned as a first-class `Type` handle. The compiler registers the
|
||||||
|
forward type at compile time, so the body can reference it (`*Name`).
|
||||||
- **`define(handle, info) -> Type`** — fill a declared handle's body from a
|
- **`define(handle, info) -> Type`** — fill a declared handle's body from a
|
||||||
`TypeInfo` *value* (which carries the type's name), and return the handle.
|
`TypeInfo` *value*, and return the handle (so the one-shot form chains).
|
||||||
- **`type_info($T) -> TypeInfo`** — reflect a type INTO data (the inverse of
|
- **`type_info($T) -> TypeInfo`** — reflect a type INTO data (the inverse of
|
||||||
`define`'s decode). *Pending.*
|
`define`'s decode). *Pending.*
|
||||||
- **`field_type($T, i) -> Type`** — the i-th field / variant-payload / element
|
- **`field_type($T, i) -> Type`** — the i-th field / variant-payload / element
|
||||||
@@ -19,17 +20,20 @@ compiler surface. Every higher-level constructor is **plain sx built over
|
|||||||
|
|
||||||
```sx
|
```sx
|
||||||
// one-shot (non-recursive): declare + define chained, define returns the handle
|
// one-shot (non-recursive): declare + define chained, define returns the handle
|
||||||
T :: define(declare(), .enum(.{ name = "T", variants = .[ … ] }));
|
T :: define(declare("T"), .enum(.{ variants = .[ … ] }));
|
||||||
|
|
||||||
// recursive / mutually-recursive: keep the handle, reference it in its own body
|
// recursive: a ctor fn names the forward type via declare, references it as *Name
|
||||||
List :: declare();
|
List :: make_list();
|
||||||
define(List, .enum(.{ name = "List", variants = .[
|
make_list :: () -> Type {
|
||||||
EnumVariant.{ name = "cons", payload = *List },
|
h := declare("List");
|
||||||
EnumVariant.{ name = "nil", payload = void } ] }));
|
return define(h, .enum(.{ variants = .[
|
||||||
|
EnumVariant.{ name = "cons", payload = *List }, // self-reference
|
||||||
|
EnumVariant.{ name = "nil", payload = void } ] }));
|
||||||
|
}
|
||||||
|
|
||||||
// type-fns are ordinary sx (channel result types, etc.)
|
// type-fns are ordinary sx (channel result types, etc.)
|
||||||
RecvResult :: ($T: Type) -> Type {
|
RecvResult :: ($T: Type) -> Type {
|
||||||
return define(declare(), .enum(.{ name = "RecvResult", variants = .[
|
return define(declare("RecvResult"), .enum(.{ variants = .[
|
||||||
EnumVariant.{ name = "value", payload = T },
|
EnumVariant.{ name = "value", payload = T },
|
||||||
EnumVariant.{ name = "closed", payload = void } ] }));
|
EnumVariant.{ name = "closed", payload = void } ] }));
|
||||||
}
|
}
|
||||||
@@ -52,8 +56,13 @@ tagged-union (design [../design/execution-evolution-roadmap.md](../design/execut
|
|||||||
`-> Type` fn call; `instantiateTypeFunction` triggers on a type-fn body that
|
`-> Type` fn call; `instantiateTypeFunction` triggers on a type-fn body that
|
||||||
returns a `define(…)` call (or a bodied `-> Type` helper) — see
|
returns a `define(…)` call (or a bodied `-> Type` helper) — see
|
||||||
`generic.zig:returnExprMintsType`.
|
`generic.zig:returnExprMintsType`.
|
||||||
3. **Name travels in the data.** `define` names the slot from `EnumInfo.name`; the
|
3. **Name on `declare`.** `declare("Name")` carries the name as a compile-time
|
||||||
compiler derives no name from a binding LHS. `type_info` round-trips it.
|
string so `preregisterForwardTypes` (in `evalComptimeType`) can register the
|
||||||
|
forward type — and bind it as a type alias — BEFORE the body lowers. That's
|
||||||
|
what makes a `*Name` self-reference resolve (a `Name :: ctor()` decl makes
|
||||||
|
`Name` a const_decl author, so `*Name` resolves through the forward-ALIAS path;
|
||||||
|
the alias binding, not just the table registration, is what satisfies it). The
|
||||||
|
interp's `declare` returns the same slot by name; `define` fills it in place.
|
||||||
4. **Nominal identity** rides the existing type-fn mangled-name instantiation cache:
|
4. **Nominal identity** rides the existing type-fn mangled-name instantiation cache:
|
||||||
`RecvResult(i64)` at two sites memoizes to ONE `TypeId` (the body runs once;
|
`RecvResult(i64)` at two sites memoizes to ONE `TypeId` (the body runs once;
|
||||||
`renameNominalType` re-keys the minted type to the mangled name).
|
`renameNominalType` re-keys the minted type to the mangled name).
|
||||||
@@ -92,11 +101,14 @@ while red. Examples: `06xx` (comptime), `11xx` (diagnostics).
|
|||||||
- [x] `field_type` reflection (`examples/0616`).
|
- [x] `field_type` reflection (`examples/0616`).
|
||||||
- [x] Examples green on the floor: `0614` (one-shot), `0615` (type-fn identity),
|
- [x] Examples green on the floor: `0614` (one-shot), `0615` (type-fn identity),
|
||||||
`0617` (channel result types).
|
`0617` (channel result types).
|
||||||
- [ ] **Self-reference** — the top-level `List :: declare(); define(List, …)`
|
- [x] **Self-reference** — recursive enums via `declare("Name")` + `*Name` in a
|
||||||
two-statement form with a `*Self` payload (recursive / mutually-recursive
|
constructor fn (`preregisterForwardTypes` registers the forward type + alias
|
||||||
enums). Needs the `define(…)` top-level statement form to run at comptime in
|
before the body lowers). `examples/0618` (recursive `*List`: construct, match
|
||||||
the right window. Pairs with a `make_enum(variants: []EnumVariant)` sx helper
|
through the pointer, recursive traversal). Mutual recursion / by-value-self-ref
|
||||||
(computed variant list).
|
rejection fall out of the same mechanism (F5 adds the loud by-value check).
|
||||||
|
- [ ] **`make_enum(variants: []EnumVariant)`** sx helper over a COMPUTED
|
||||||
|
(non-literal) variant list — exercises the interpreter decoding a value-arg
|
||||||
|
slice in `define`.
|
||||||
- [ ] **`type_info($T) -> TypeInfo`** — reflect a type INTO a value (inverse of
|
- [ ] **`type_info($T) -> TypeInfo`** — reflect a type INTO a value (inverse of
|
||||||
`define`'s decode): widen `TypeInfo` past `` `enum `` (struct/tuple) AND
|
`define`'s decode): widen `TypeInfo` past `` `enum `` (struct/tuple) AND
|
||||||
construct a `[]EnumVariant`-style value in the interpreter. Bails loudly today
|
construct a `[]EnumVariant`-style value in the interpreter. Bails loudly today
|
||||||
|
|||||||
Reference in New Issue
Block a user