Files
sx/current/CHECKPOINT-REIFY.md
agra 18a4f9dd54 green(reify): type-fn over reify memoizes by mangled name (identity)
REIFY Phase 1.1 (Phase 1 complete). instantiateTypeFunction detects a
type-fn body that returns reify(...) (findReturnReifyCall) and routes it
to reifyType under the instantiation's name — mangled for inline use,
the alias name for `Foo :: Box(i64)` — with the type-arg bindings active
so reify payloads (`payload = T`) resolve against the instantiation args.
Placed before the general case, whose resolveTypeWithBindings would
route the reify call to the inline-position loud bail.

Registering under the mangled name lets the top-of-instantiation cache
return the SAME TypeId on a second instantiation, so Box(i64) resolved
at two independent sites is ONE type (Contract 1). examples/0615 green
(build()->consume() cross-site + `b : Box(i64) = .none`). Suite green
(671 examples, 447 unit).
2026-06-16 18:54:11 +03:00

115 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CHECKPOINT-REIFY — comptime `type_info` / `reify` (async-first foundation, step 3)
Companion to [PLAN-REIFY.md](PLAN-REIFY.md). Update after every step (one step at a
time, per the cadence rule).
## Last completed step
**Phase 1 (type-fn → reify identity) — COMPLETE.** A type-fn body that returns
`reify(...)` now mints the enum under the instantiation's name:
`instantiateTypeFunction` (`lower/generic.zig`) detects a reify-returning body
(`findReturnReifyCall`) and routes it to `reifyType(<mangled-or-alias name>,
reify_call)` with the type-arg bindings active (so `payload = T` resolves to the
bound arg) — placed BEFORE the general case, which would otherwise route the
reify call to the inline-position loud bail. Registering under the mangled name
lets the fn's top-of-instantiation cache return the SAME `TypeId` on the second
instantiation → `Box(i64)` at two independent sites is ONE type (**Contract 1**).
`examples/0615` green (`build()``consume()` cross-site, plus `b : Box(i64) =
.none`); full suite green (671 examples, 447 unit). Cadence: 1.0 xfail (empty
marker, RED) → 1.1 green (this commit).
### (prior) Phase 0.2 (green) — Phase 0 COMPLETE. Implemented `reify(.enum(...))`:
`Lowering.reifyType` (in `lower/nominal.zig`) reads the flat-enum `TypeInfo`
literal off the AST, synthesizes an `ast.EnumDecl`, and feeds it through the
SAME `type_bridge.buildEnumInfo` path source enums use → the minted type is
byte-identical to a hand-written `enum { value: i64; closed; }` and flows
through enum codegen unmodified (**Contract 2 confirmed** — a source enum
exhibits the exact same construct/match behavior, verified by probe). Wired at
the `E :: reify(...)` const-decl hook in `lower/decl.zig`. `examples/0614`
green (`value 3` / `closed`, exit 0); snapshots captured; full suite green
(670 examples, 447 unit). Unsupported reify shapes bail loudly via
`reifyBail` (never a silent default).
Also landed (user-directed, separate commit `feat(parser):`): reserved keyword
as a member name after `.` (`.enum`, `case .enum:`, `x.enum`) — so the reify
example reads `reify(.enum(...))` without a backtick. readme updated.
### Two earlier Phase-0 commits
- **0.1 (xfail).** Added `examples/0614` + empty `.exit` marker → RED.
- **0.0 (lock).** Added the comptime type-metaprogramming surface as the
on-demand module `library/modules/std/meta.sx` (NOT the prelude — see decision
below): `EnumVariant`/`EnumInfo`/`TypeInfo` data types + bodyless `#builtin`
decls `reify` / `type_info` / `field_type`. Each builtin bails LOUDLY when
reached unimplemented (no silent default). Unit test `src/parser.test.zig`
(registered in `src/root.zig`) locks that the decls parse. `zig build test`
green (447/447 unit, 669/669 examples).
## Current state
- `modules/std/meta.sx` declares the surface; the variant uses the backtick raw
escape `` `enum `` (reads as the keyword, not a mangled `enum_`).
- **Loud bails wired (unimplemented → diagnostic, never a silent type):**
- `reify(...)` in a `::` type-alias position → `decl.zig` (the `.call`
const-decl branch) emits "reify is not yet implemented (REIFY Phase 0.2)"
and poisons the alias to `.unresolved`. This is also where Phase 0.2 will
hook the real construction.
- `reify` / `field_type` in any other type position →
`generic.zig:resolveTypeCallWithBindings` (defense-in-depth).
- `type_info(...)` in expression position →
`call.zig:tryLowerReflectionCall`.
- No interpreter-side construction yet — `reify` mints nothing.
## Decision (0.0)
**Meta lives in `modules/std/meta.sx`, not the prelude (`core.sx`).** Declaring
the data types in the always-loaded prelude interns them into every module's
type table and shifts every `.ir` snapshot (broke 37 examples in a trial). An
on-demand import keeps the prelude clean; reify users `#import
"modules/std/meta.sx"`. (User-directed.)
## Next step
**Phase 2 (`type_info` + `field_type`).** Reflect a struct/tuple → read variant /
field names + **types** (`field_type($T, i) -> Type`, `type_info($T) -> TypeInfo`).
xfail → green by implementing both over the type table (reuse the
`field_count`/`field_name` reflection path; both currently bail loudly —
`type_info` in `call.zig:tryLowerReflectionCall`, `field_type` in
`generic.zig:resolveTypeCallWithBindings`). NOTE: `reifyType` still reads a LITERAL
`TypeInfo` off the AST (works for the inline-literal and type-fn-over-literal cases
Phases 01 use); a `type_info`-derived (computed, non-literal) `TypeInfo` fed back
into `reify` would need the reader generalized (or interp evaluation) — call that out
when Phase 2 enables round-tripping.
SELF-REFERENCE = Phase 4, API DECIDED (user-directed): explicit **`declare()` →
`define(h, info)`** (the declaration-vs-definition split; NOT a `reify_rec((self)=>…)`
closure). `declare()` returns a forward nominal `Type` handle (named from the `::`
LHS) usable freely in any later `TypeInfo` — `*List`, `[]List`, and across types for
MUTUAL recursion the one-`self` closure couldn't express; `define(handle, info)` fills
the body. `reify(info)` stays as the one-shot sugar. Maps onto existing machinery:
declare = empty nominal slot (`reserveShadowEnumSlot`-style), define = `buildEnumInfo`
+ `updatePreservingKey`, struct-stub→tagged_union re-key already handled
(`internNamedTypeDecl`'s `adoptsForwardStructStub`). Loud invariants: never-defined
declare = hard error; by-value self-inclusion rejected (infinite size). Full write-up
in PLAN-REIFY Phase 4.
## Known issues
None yet.
## Log
- **1.1 (green) — Phase 1 done.** Type-fn body `return reify(...)` routes through
`reifyType` under the instantiation name (`findReturnReifyCall` +
mangled-name registration); `Box(i64)` at two sites = one type (Contract 1);
`0615` green.
- **1.0 (xfail).** `0615` + empty `.exit` marker → RED (reify bailed in type-fn body).
- **0.2 (green) — Phase 0 done.** `reifyType` mints a flat enum from a literal
`TypeInfo` via the shared `buildEnumInfo` path; `0614` green; Contract 2 confirmed
(reify'd enum == source enum, same construct/match). Payload-less variant idiom in
the example is `c : E = .closed;` (enum-literal target), same as a source enum.
- **parser (user-directed).** Keyword as member name after `.` — see `feat(parser)`.
- **0.1 (xfail).** `0614` + empty `.exit` marker → RED.
- **0.0 (lock).** Meta surface in `modules/std/meta.sx` (data types + 3 bodyless
`#builtin` decls), loud bails at all three reach points, `src/parser.test.zig`
parse-lock. Two user-directed refinements folded in: variant uses `` `enum ``
raw escape; surface moved out of the prelude into its own module to avoid
type-table / `.ir`-snapshot churn. `zig build test` green.
- **Stream carved.** Selected as the first async-first foundation: `reify` gates both
channel result types (`RecvResult($T)`) and `race`'s synthesized union, is fully
validated (3 reviewers), and is a self-contained compiler/type-system feature
testable in isolation (`06xx` comptime). Generic-enum syntax dropped in its favor.