scanDecls' `.identifier` alias branch registered `A :: B` into ProgramIndex.type_alias_map only when `B` was already known (in type_alias_map or the TypeTable). A forward target declared later (`MyChain :: MyInt; MyInt :: s32;`) was never present during the single forward scan, so the alias name went unregistered and the A2.4 unknown-type pass — which treats type_alias_map keys as declared types — flagged its uses as `unknown type 'MyChain'`. Add a fixpoint post-pass `resolveForwardIdentifierAliases` at the end of scanDecls that re-resolves identifier-RHS aliases until no progress, after every top-level name has been seen. A value const is never an `.identifier` node, and an alias whose target is a value const still misses both lookups, so issue 0068's value-const rejection is preserved. Regression: examples/0132-types-forward-type-alias.sx (forward alias + forward chain). Gate: zig build, zig build test, run_examples.sh -> 353/0.
4.4 KiB
0069 — forward identifier type alias is falsely rejected by unknown-type pass
RESOLVED. Root cause:
Lowering.scanDecls'.identifieralias branch only registeredA :: BintoProgramIndex.type_alias_mapwhenBwas already known (intype_alias_mapor theTypeTable). A forward target declared later (MyChain :: MyInt; MyInt :: s32;) was never present during the single forward scan, so the alias name went unregistered and the A2.4 unknown-type pass — which treatstype_alias_mapkeys as declared types — flagged its uses. Fix: added a fixpoint post-passresolveForwardIdentifierAliasesat the end ofscanDeclsthat re-resolves identifier-RHS aliases until no progress, after every top-level name has been seen. A value const is never an.identifiernode and an alias whose target is a value const still misses both lookups, so issue 0068's value-const rejection is preserved. Regression:examples/0132-types-forward-type-alias.sx.
Symptom
A forward-referenced identifier type alias is rejected as an unknown type, even though the same alias chain works when ordered after its target.
Observed: MyChain is diagnosed as an unknown type.
Expected: MyChain :: MyInt; MyInt :: s32; should resolve MyChain to s32
when used in a type annotation, matching the existing ordered-chain behavior
(MyInt :: s32; MyChain :: MyInt;).
Reproduction
MyChain :: MyInt;
MyInt :: s32;
main :: () -> s32 {
v: MyChain = 7;
return v;
}
Run:
./zig-out/bin/sx run .sx-tmp/probe-0068-forward-alias.sx
Observed output:
error: unknown type 'MyChain'
--> .sx-tmp/probe-0068-forward-alias.sx:7:8
|
7 | v: MyChain = 7;
| ^^^^^^^
The repro is standalone; the inline source above is sufficient to recreate the
scratch file under .sx-tmp/.
Investigation prompt
Fix issue 0069: a forward-referenced identifier type alias must not be falsely rejected by the A2.4 unknown-type diagnostic pass.
Context:
- This surfaced while re-reviewing
8770145, the issue-0068 fix. That fix correctly stopped arbitrary value consts (NotAType :: 123) from satisfying the unknown-type check. - Ordered identifier aliases still work:
MyInt :: s32; MyChain :: MyInt;. .calltype aliases still work:Vec3 :: Vec(3, f32);andFoo :: Complex(u32);.- The failing shape is specifically a forward identifier alias:
MyChain :: MyInt; MyInt :: s32;.
Suspected area:
src/ir/lower.zig,Lowering.scanDecls, especially the.identifieralias branch forconst_declvalues. It only insertscd.nameintoProgramIndex.type_alias_mapif the RHS is already intype_alias_mapor already registered in theTypeTable. A forward target is not present yet, so the alias name is never recorded.src/ir/semantic_diagnostics.zig,UnknownTypeChecker.collectDeclaredTypeNames/reportIfUnknownType. After issue 0068,.identifieraliases are intentionally excluded fromconstValueIntroducesTypeand are supposed to be covered by canonical facts (ProgramIndex.type_alias_map/TypeTable). Because the forward alias never reaches those facts, the checker flags the alias as unknown.
Likely fix:
- Do not reintroduce the issue-0068 bug by adding all
.identifierconst names to the declared-type set. - Instead, make identifier aliases converge through canonical alias facts even
when the RHS is declared later. A small two-pass alias registration/resolution
in
scanDecls, or an explicit pending-alias graph that resolves after all top-level declarations are scanned, would keepProgramIndex.type_alias_mapauthoritative without accepting value constants. - Preserve value const rejection:
NotAType :: 123; v: NotATypemust continue to emitunknown type 'NotAType'. - Preserve ordered/chained aliases and type-returning call aliases:
examples/0116-types-type-alias-size-align.sx,examples/0201-generics-generic-struct.sx, andexamples/1117-diagnostics-value-const-as-type-rejected.sx.
Verification:
- Add a focused regression for the repro above, likely in the
01xxtypes block because the desired behavior is successful alias resolution. - Run the new regression and the existing alias/diagnostic guards:
zig build
zig build test
bash tests/run_examples.sh
Expected result: the forward alias program compiles/runs (returning 7), the
issue-0068 value-const case still fails with a diagnostic, and the full suite
passes.