Verbatim relocation of the 5-method closure cluster (lowerLambda,
bare-fn trampoline, closure-to-bare-fn adapter, capture collection, env
sizing) into src/ir/lower/closure.zig. 5 aliases on Lowering keep all
call sites unchanged. Method pub-flip: typeAlignBytes.
Resolves the B7.1 flag: CaptureInfo relocates from lower/call.zig to
lower/closure.zig (its domain home, next to collectCaptures); the
Lowering type alias is repointed so external references are unchanged.
Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
Verbatim relocation of the 30-method expression cluster (struct/array/
tuple/enum/tagged-enum literals, init blocks, field access on values and
types, optional chains, numeric limits, indexing, slicing, deref, force
unwrap, null coalesce) into src/ir/lower/expr.zig — one contiguous
1,372-line cut. 30 aliases on Lowering keep all call sites unchanged.
Nested StructConstInfo stays on Lowering (field type of
struct_const_map), flipped pub and reached via an alias const, alongside
headNameOfCallee.
Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
Verbatim relocation of the 18-method call cluster (lowerCall moved
whole, context diagnostics, foreign-call helper, builtin/function
resolution, generic + runtime-dispatch calls, reflection calls + guards,
default-arg expansion, call param typing) into src/ir/lower/call.zig.
18 fn aliases keep all call sites unchanged.
CaptureInfo (closure-domain type that sat inside the run) travelled and
is re-exposed via a Lowering type alias; candidate to relocate to
lower/closure.zig in B8.3.
Method pub-flips: callResolver, createBareFnTrampoline,
ensureGenericInstanceMethodLowered, fixupMethodReceiver,
getStructTypeName, isStaticTypeArg, lowerPackFnCall, packSpreadRefs,
packVariadicCallArgs, refCapturePointee, resolveParamTypeInSource,
typeSizeBytes, headNameOfCallee.
Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
Verbatim relocation of the 23-method defined-class cluster (IMP/property
emission: class/alloc/static/dealloc IMPs, property getters/setters +
ARC runtime decls, defined-state field access, property/method chain
lookup, string-constant globals) plus the single-home
ObjcDefinedStateField type into src/ir/lower/objc_class.zig. 23 aliases
on Lowering keep all call sites (incl. expr_typer.zig facade and
lower/stmt.zig) unchanged. Zero pub-flips — all callees were already
public from earlier steps.
Gate: zig build OK; zig build test 426/426; run_examples 541/0; all 37
.ir snapshots byte-identical, zero expected/ churn.
Verbatim relocation of the 19-method coercion cluster (lowerXX, user
conversions, protocol erasure, default-value construction, zero values,
coerceToType implicit/explicit ladder, C-variadic promotion, call-arg
coercion) plus the nested single-home CoerceMode enum into
src/ir/lower/coerce.zig. 19 aliases on Lowering keep all call sites
unchanged.
Method pub-flip: prependCtxIfNeeded. ParamImplEntry stays a Lowering
nested type (field type of param_impl_map) and is reached via an alias
const.
Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
Verbatim relocation of the 13-method protocol cluster (protocol decl
registration, param-protocol instantiation, thunk creation, vtable
globals, protocol-value construction, dispatch emission, impl lookup)
into src/ir/lower/protocol.zig. 13 fn aliases on Lowering keep all call
sites unchanged.
Two pub nested types travelled with the run (ProjectionPosition,
PackProjection) and are re-exposed via Lowering type aliases; they are
pack-domain types and may relocate to lower/pack.zig in B7.2.
Method pub-flips: allocViaContext, callForeign, genericInstanceMethod,
monomorphizeFunction.
Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
Verbatim relocation of the 23-method nominal-type cluster (struct/enum/
union/error-set registration, anon-type qualification, nominal-id
stamping, shadow-slot reservation, named-type interning, generic struct
templates + alias registration) plus the nested ShadowTypeDecl union
into src/ir/lower/nominal.zig. 23 aliases on Lowering keep all call
sites unchanged.
Method pub-flip: instantiateGenericStruct. nominal.zig reaches
VisibleStructAuthor and structDeclOfRaw (both relocated to decl.zig in
B4.1) via Lowering-namespace alias consts.
Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
Verbatim relocation of the 53-method error cluster (error typing,
raise/failable, try/catch/or, inferred-set convergence, trace runtime
hooks) out of the Lowering struct into src/ir/lower/error.zig as free
functions taking *Lowering. Each gets a pub-const alias on Lowering, so
every call site compiles unchanged (decl-alias method resolution).
Pub-flips (callees now referenced cross-file): lowerExpr, coerceToType,
freshBlock, freshBlockWithParams, emitErrorCleanup,
currentBlockHasTerminator, lowerBlock, lowerBlockValue.
Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
headTypeGate and bareVisibleStructDecl were using the same
moduleTypeAuthor + flatTypeAuthorCount pattern that selectNominalLeaf
used before R2. Migrated both to a single collectVisibleAuthors call
with inline type-specific resolution, matching the R2 pattern.
Deleted now-unused helpers: moduleTypeAuthor, FlatTypeAuthor,
moduleTypeAuthorTid, FlatTypeAuthorCount, flatTypeAuthorCount.
Net: -76 lines.
541/541 regression tests pass. 426/426 unit tests pass.
Replaces the 3 separate author-collection calls inside selectNominalLeaf
(moduleTypeAuthor + ownConstDeclIsPendingAlias + flatTypeAuthorCount +
forwardAliasOrUndeclared) with a single collectVisibleAuthors call plus
inline type-specific resolution. The flat walk now handles:
- own named type: resolved or forward (slot not yet interned)
- own const_decl: resolved alias or pending (own wins over flat)
- flat named types: ambiguous / resolved / forward
- flat const_decl pending aliases: pending (for forward aliases in imports)
Deletes 3 now-unused helpers: forwardAliasOrUndeclared, constAuthor,
ownConstDeclIsPendingAlias. Net: -17 lines.
541/541 regression tests pass. Issue 0107 repro still outputs 300.
When a module declares `A :: B; B :: u64;` and both a flat import and a
namespaced import export `B :: u8`, the flat import's B was discovered by
flatTypeAuthorCount before the own B :: u64 was processed — binding A to
u8 and silently truncating values.
Fix: ownConstDeclIsPendingAlias guard added to selectNominalLeaf between
the own-alias check and the flat-import walk. If the querying module has
an own const_decl for the name that is not yet in type_aliases_by_source,
return .pending so the forward-alias fixpoint resolves it correctly.
Regression: examples/0830-modules-flat-ns-same-name-forward-alias.sx
(x : A = 300 prints 300, not 44). 541/541 tests pass.
Removes the S2.x pre-pass and its 10 NodeRefTable maps — 1934 net lines
deleted. The Resolver gains two lazy functions: resolveBare(name, from,
domain) and resolveQualified(target, name), each returning ResolvedAuthors
(verdict + author set). verdictOver and authoredAsDomainAnywhere move from
ResolvePass to Resolver as private methods. All domain-predicate helpers
(eligibleKind, structDeclOf, fnDeclOf, etc.) are promoted to pub.
Test file trimmed from 1352 to 396 lines; old pre-pass population tests
replaced by focused resolveBare / resolveQualified verdict tests.
540/540 regression tests pass. Zero behavior change.
Move the author-SELECTION SEMANTICS (the verdicts) into the resolver. Every
`.authors` ResolvedRef now carries the verdict the resolver COMPUTES above the
collector — own-wins / single-flat-visible / ≥2-ambiguous / not-visible /
type-vs-value domain-filtered — evaluated over the DOMAIN-ELIGIBLE subset of the
collected author set (`eligibleKind`). This folds the per-kind selection the old
lower-side selectors carried (selectNominalLeaf / flatTypeAuthorCount /
selectModuleConst / selectPlainCallableAuthor / selectGenericStructHead /
headTypeGate / headFnLeak) into ONE uniform computation, closing the
protocol / error-set / foreign per-kind surfaces (E6c/d/e) as resolver behavior.
Template/pack grammar stays carried as `.template` / `.pack` refs — NO
`sig_registration_mode`.
ADDITIVE / PARALLEL / UNCONSUMED: lowering still reads the old selectors, so the
verdict changes no generated byte. No file outside resolver.zig reads
ResolvedRef, so byte-identity is structural. ResolvedRef.authors is wrapped into
{ set, verdict } (the RAW set is preserved; the verdict filters).
Resolver unit tests prove the verdicts on real Phase A facts: the five bare-type
outcomes incl. the type-vs-value filter, and the resolver-target classes the old
selectors get WRONG — 0811 error-set / 0821 protocol-head / 0829 generic-struct
all → ambiguous (two flat authors, none own) and → own-wins (own author present).
The resolver-target corpus stays xfail in run_examples (unconsumed until S3.9);
verdicts asserted via the harness, not by flipping goldens.
Gate: zig build && zig build test (430) && tests/run_examples.sh (540 byte-identical),
all exit 0; tests/resolver-target 18 xfail unchanged.
On the S2.1a owning traversal, populate the last three ResolvedProgram side
tables, closing planspec S2.1's full-population acceptance (all ten domains):
- foreign_class_refs: a bare reference whose collected author is a
foreign_class_decl is routed here (its own domain) instead of the bare
type/value/callable table.
- struct_const_refs: a Type.CONST field access whose base resolves to a
struct author carrying that const member (mirrors lowering's struct_const_map).
- ufcs_refs: a ufcs_alias decl (alias -> target author) plus its UFCS-rewrite
call sites (alias(args), incl. pipe-desugared), via a global traversal-ordered
alias map mirroring lowering's flat ufcs_alias_map.
Still PARALLEL / UNCONSUMED / RAW: lowering reads the OLD selectors, no consumer
cut over, ResolvedRef stays raw. Byte-identical vs baseline (540 examples).
Population proof extended: a new resolver.test fixture exercises all ten domains
at once and asserts each side table non-empty + node-keyed for the three new ones.
On the S2.1a exhaustive traversal, populate four more ResolvedProgram side
tables, still RAW / PARALLEL / UNCONSUMED:
- namespace-qualified references: an `alias.member` field_access whose base
alias is a NamespaceEdges[ambient_source] target resolves via
collectNamespaceAuthors into namespace_refs, keyed by the access node.
- the three HEAD domains at parameterized_type_expr heads, binned by the
resolved author's decl kind: a struct with type params -> generic_struct_heads,
a fn/const-wrapped fn with type params -> type_fn_heads, a protocol ->
protocol_heads. RAW: the whole author set is recorded with no winner picked;
a name authored as >1 head kind lands a distinct entry in every matching table.
Lowering still reads the old selectors and resolved_program has no consumer, so
generated output is byte-identical. ResolvedRef stays RAW (selection is S2.2);
generics stay symbolic. S2.1c (foreign-class / struct-const / UFCS) owns the
remaining three tables.
Extends the population proof: a resolver unit test asserting all four tables are
non-empty + node-keyed with the expected RAW authors.
Gate (all exit 0): zig build; zig build test (All 427 mod + exe + LSP sweep 574);
tests/run_examples.sh (540 passed, byte-identical); tests/resolver-target
(18 xfail, 0 leaked); m3te ios-sim via the main sx binary.
Turn src/ir/resolver.zig from a raw author-collection facade into the OWNING
resolution pass: one exhaustive recursive AST walk (exhaustive switch over
ast.Node.Data with NO else arm, so a new node kind is a compile error here
rather than a silently unvisited subtree) populating a ResolvedProgram.
- ResolvedProgram: all 10 node-keyed side tables declared as
AutoHashMap(*const ast.Node, ResolvedRef) + symbolic TemplateParamId/
PackParamId registries. ResolvedRef is the S2.1 RAW form — collected author
identity (AuthorSet, own ∪ flat), NO verdict (own-wins/ambiguity is S2.2).
- Populate the 3 bare-name domains (type / value-const / callable heads) via
collectVisibleAuthors(.user_bare_flat); record $T / ..$Ts / $pack[i] as
SYMBOLIC template/pack refs, never TypeIds. The 7 head/qualified/foreign
domains stay declared-but-empty (S2.1b/c own them).
- Slot via Compilation.resolveProgram() after the program_index facts are
wired and before lowerRoot; ResolvedProgram owned on Compilation, borrowed
*ResolvedProgram lent to ProgramIndex (lowerToIR signature unchanged).
- Population proof unit test over real Phase A facts: the 3 tables are
non-empty, keyed by node identity, and carry symbolic template/pack refs.
ADDITIVE / PARALLEL / UNCONSUMED: lowering still reads the OLD selectors, so
single-author output is byte-identical. Gate green: zig build; zig build test
(425/425, LSP smoke 574 files no crash); run_examples (540 passed, 0 failed,
byte-identical incl. FFI 12xx-14xx + 1615 ios-sim); resolver-target (18 xfail
unchanged).
Delete module_fns as a separate function-author fact source. Its authors
already live in the module_decls raw facts, so lowerRetainedSameNameAuthors now
reads function authors straight out of module_decls (filtered to *FnDecl via
fnDeclOfRaw) — the same path → name → RawDeclRef store, fn-filtered. Remove
imports.ModuleFns / FnIndex / indexModuleFns / buildModuleFns / fnDeclOf, the
Compilation.module_fns field + its build + wiring, and ProgramIndex.module_fns.
Remove VisibilityMode.legacy_direct_any (the quarantined own-scope-plus-full-
import_graph mode): no production caller passed it, so the collectVisibleAuthors
and isVisible switch arms that handled it are dead and go too, collapsing
VisEdgeSet to the single flat-import walk. No semantic fallback is introduced;
import_graph stays the transitive-visibility source for findVisibleImpls.
Additive: the old maps stay active and lowering still consumes them — no
lowering consumer is cut over to the DeclTable (that is S3), and no resolution
behavior changes. Tests that drove the removed symbols are rerouted through
module_decls / the flat-edge walk.
Gate over the baseline-green corpus: zig build, zig build test (424/424),
bash tests/run_examples.sh (540 passed) — all exit 0; single-author output
byte-identical; multi-author 0722–0740 stdout/exit unchanged.
Build a DeclTable in parallel with the import facts: every RawDeclRef
(source / imported / namespaced / C-imported) gets a stable DeclId carrying
source path, display name, AST node identity, span, and DeclKind. Namespace
targets record their members' DeclIds (NamespaceTarget.member_ids). A generic
struct's template is keyed by DeclId in a parallel struct_template_by_decl
store, written alongside the live name-keyed struct_template_map.
A Debug-only round-trip cross-check (RawDeclRef -> DeclId -> AST node ptr)
asserts the table identifies the same node across the corpus, run from
buildDeclTable and pinned by a unit test.
Additive (S0.1 class: mirror): the old maps stay active and lowering still
consumes them; nothing reads the DeclTable / struct_template_by_decl for
selection yet (the S4 cutover does). Generated IR + output bytes are unchanged
by construction.
Gate over the baseline-green corpus: zig build, zig build test (424/424),
bash tests/run_examples.sh (540 passed) — all exit 0; single-author output
byte-identical (37 .ir snapshots unchanged).
attempt-2 review fixes (docs-only; contract mechanics confirmed sound):
- README + S0.2 grep-clean: 'S0 HEAD == base' / 'S0 == base' were inaccurate
(HEAD carries the docs/examples/tests diff). Reword to: production/compiler
behavior is base-equivalent — zero src/ changes, single-author output
byte-identical to base by construction — HEAD is a distinct commit, not base.
- S0.3 ledger: drop the stale '116-class corpus' FFI wording for the grounded
live count (96 entry trees / 95 active markers), matching the S0.1 count note.
No partition / manifest / examples / harness change. Gate green:
zig build + zig build test (LSP sweep 574, no crash) + run_examples (540/0);
m3te ios-sim build via main binary exit 0.
S0 of the ratified Fork C plan (zero-legacy name-resolution redesign, S0→S6).
Pure setup/documentation: NO production code change, NO behavior change.
Single-author output byte-identical to wt-stdlib-base by construction.
Deliverables under docs/fork-c/ (docs/, not current/, because current/ is
gitignored and the contract must be committed):
S0.1 — byte-baseline + commit-discipline: the committed examples/expected/*
snapshots are the single-author byte-identity reference; the zero-diff repro is
`zig build && zig build test && bash tests/run_examples.sh`. Resolver-target set
explicitly excluded + listed. Commit-classification rule: mirror | consumer-cutover | deletion.
S0.2 — E6b disposition + two-corpus partition: transitional E6b src NOT merged
(grep-clean: no resolveRegistrationSigTypeInSource / sig_registration_mode /
e6br_gate.test.zig on baseline). Harvested 0811–0829 trees + goldens (never the
src), empirically partitioned by running each through the base compiler vs the
E6b target:
- baseline-green (mirror-equivalence): 0795–0798 (merged) + 0823, 0828 — given
examples/expected/ markers, locked into the S0 baseline.
- resolver-target (known-wrong old behavior): 0811–0822, 0824–0827, 0829 + the
re-filed E6BR-5 nested-pattern regression — a listed xfail harness under
tests/resolver-target/ (manifest + TARGET goldens, NO active marker), flips
active+green at S3.9. 0811/0829 noted as old-selector-wrong on the E6b-unmerged
base; E6BR-5 subsumed by the whole-AST resolver, NOT an E6b attempt-6.
S0.3 — A–E6 reuse/delete ledger: every load-bearing A–E6 artifact mapped REUSED
(Fork C home) or DELETED/TRANSITIONAL (S3/S6 phase); E6c/d/e dropped, F/H/I/K
absorbed/superseded.
Gate over the baseline-green corpus: zig build + zig build test (LSP corpus sweep
574 files, no crash) + bash tests/run_examples.sh (540 passed, 0 failed) all exit 0.
attempt-1's per-decl enum/union register path panicked on any valid
self- or mutually-referential top-level enum/union: a `*Name` field in
the body is resolved through the stateless `type_resolver.resolveNamed`,
which has no kind context and forward-stubs an as-yet-unregistered name
as a STRUCT. `internNamedTypeDecl` then `findByName`-adopted that struct
stub and called `updatePreservingKey`, whose kind-stability assert tripped
on struct -> enum/union (types.zig:446). The corpus had no recursive
enum/union, so the gate missed it.
Fix: when the slot `findByName` returns is a wrong-kind forward struct
placeholder (empty-fields struct) for an enum/union/tagged_union
registration, re-key it in place (`replaceKeyedInfo`) under the same
TypeId instead of `updatePreservingKey`. This mirrors how a self-ref
struct adopts its own (same-kind) forward stub; the new helper
`adoptsForwardStructStub` gates the re-key precisely to that case, so a
struct adopting a struct stub and every non-recursive enum/union stay on
the byte-identical `updatePreservingKey`/fresh-intern path.
Regression 0799 (single-author): self-ref union linked cells
(`next: *Node`), self-ref enum/tagged-union (`branch: *Tree`), and a
mutual-ref pair (A holds *B, B holds *A); builds and walks each recursive
link. Fail-before: panic at registerUnionDecl on eed2f99. Pass-after:
exit 0, "union=7 enum=42 mutual=99".
Gate: zig build && zig build test && run_examples.sh all exit 0
(538 passed, 0 failed; 0795-0798 + 0752-0794 + FFI byte-identical);
m3te ios-sim build via the main binary exit 0.
Give top-level ENUM and UNION decls per-decl nominal identity so two
same-name flat enums/unions intern DISTINCT nominal TypeIds instead of
collapsing to one global last-wins entry. Establishes the reusable
non-struct register path the later E6 kind-steps (E6b error-set, E6c
protocol, E6d foreign-class) extend.
Registration side (was: stateless `type_bridge.resolveInlineEnum/Union`
`findByName` last-wins short-circuit, no Lowering access):
- Split the type_bridge inline builders into a body-BUILDER
(`buildEnumInfo` / `buildUnionInfo`) + the existing thin interner
wrappers (field-type positions keep the legacy single-slot path).
- Add `Lowering.registerEnumDecl` / `registerUnionDecl` mirroring
`registerStructDecl`: build the TypeInfo, intern via
`internNamedTypeDecl(decl_key, name_id, info, nominal_id)` under the
per-decl nominal identity (reserved slot id, else `shadowNominalId`).
- Reroute all six enum/union registration dispatch sites (scanDecls
const-wrapped + top-level, lowerDecls/comptime, block-local, local
const) to the new path.
Shared infra generalized ONCE:
- Pass-0b genuine-shadow pre-pass now reserves struct/enum/union shadow
slots of the MATCHING kind, grouped by (kind, name), via a kind-generic
`topLevelTypeDecl` / `reserveShadowSlot`. A forward/self/mutual ref to a
shadow name binds to the reserved nominal TypeId.
- `namedRefTid` consults `type_decl_tids` for `.enum_decl`/`.union_decl`
before the global `findByName`.
No new per-kind resolution path: selectNominalLeaf / headTypeGate /
flatTypeAuthorCount already gate every kind. Single-author /
phantom-double-spelling names keep nominal_id 0 (byte-identical corpus).
Regressions 0795-0798 (enum + union: ambiguity over every bare-type form,
and own-wins with distinct nominal TypeIds), fail-before/pass-after:
0795/0797 exit 0 -> exit 1 with the loud "type is ambiguous" diagnostic;
0796 silently printed `own=.east` -> correct `own=.north`; 0798 hard
`field 'm' not found` error -> correct `own=5 dep=9`.
Gate: zig build && zig build test (423/423) && run_examples.sh (537/537)
all exit 0; m3te ios-sim build via the main binary exit 0.