Triage pass: every issue file in `issues/` was re-verified against HEAD. Three (0041, 0042, 0043) reproduce no longer — they were silently fixed by adjacent work since the issue was filed. 0047 landed in the previous commit. All four header sections now lead with **FIXED** + a one-line locator so the next reader doesn't re-investigate. After this, `issues/` is the actual open-issue list: | Issue | Status | |---|---| | 0041 | FIXED (silently, by alias/parser work) | | 0042 | FIXED (silently, type_alias_map lookup landed) | | 0043 | FIXED (silently, lazy-lower foreign-class dispatch) | | 0044 | FIXED | | 0045 | FIXED | | 0046 | FIXED | | 0047 | FIXED (commit0119c9c) | | 0048 | FIXED (commit0ede097) | | 0049 | FIXED (commitb5301c4) | | 0050 | FIXED (commit5316bf7) | No open issues remain. The files stay in tree as a record; new issues take the next free number (0051).
4.9 KiB
issue-0042 — Const-decl type aliases (MyInt :: s32;) silently return .s64 from size_of / align_of
FIXED. MyInt :: s32; size_of(MyInt) now returns 4
correctly. The resolveTypeArg .identifier branch consults
type_alias_map before falling through. The fix landed
alongside the broader alias-resolution work tracked in
CHECKPOINT.md (Session 63's type_bridge alias-resolution
extension); no specific commit isolates this issue.
Below preserved as a record of the original problem.
Symptom
A type alias declared via Foo :: SomeType; is registered in the
lowering's type_alias_map but is never consulted when the alias
name is later used as a type argument to size_of / align_of. The
fallback returns .s64 (8 bytes) — which coincidentally produces a
correct result for any alias whose underlying type is 8 bytes
(*T, f64, function pointers, s64, u64), silently masking the
bug for years.
Observed:
size_of(s32) = 4 ← direct, correct
size_of(MyInt) = 8 ← via alias, WRONG (expected 4)
Where MyInt :: s32;.
Reproduction
#import "modules/std.sx";
MyInt :: s32;
main :: () -> s32 {
print("direct: {}\n", size_of(s32)); // 4
print("alias: {}\n", size_of(MyInt)); // 8 — should be 4
0;
}
./zig-out/bin/sx run against unmodified master prints:
direct: 4
alias: 8
Why this surfaces now
issue-0041 work extends the const-decl alias path to register
pointer, optional, array, slice, many-pointer, and function-type
aliases (Ptr :: *u8;, Maybe :: ?u8;, Arr :: [3]u8;,
Cb :: (s32) -> s32;). Every one of those aliases ends up in
type_alias_map, then size_of(<alias>) falls through the same
.identifier branch that ignores the map — returning .s64 (8).
For pointer and function-type aliases this is coincidentally right
(8 bytes). For optional, array, etc. it produces silently-wrong
sizes (size_of(Maybe) = 8 instead of 2;
size_of(Arr) = 8 instead of 3).
The issue-0041 work cannot land without this being fixed — the test snapshots would pin in the wrong values and the new feature would ship subtly broken.
Investigation prompt
The bug lives in
src/ir/lower.zig, inresolveTypeArg(line ~7132). The.identifierbranch looks like:.identifier => |id| { if (self.type_bindings) |tb| { if (tb.get(id.name)) |ty| return ty; } const name_id = self.module.types.internString(id.name); return self.module.types.findByName(name_id) orelse .s64; },It checks
type_bindings(generic-monomorphization) andfindByName(registered named types), but never consultsself.type_alias_map— which is where the const-decl alias registration inlower.zig:425puts entries. The neighbouring.type_exprbranch (line ~7143) DOES checktype_alias_map:.type_expr => |te| { if (self.type_alias_map.get(te.name)) |alias_ty| return alias_ty; return type_bridge.resolveAstType(node, &self.module.types); },Why two branches: an
.identifierAST node is what parsePrimary emits for non-keyword names;.type_expris what it emits for built-in primitive names recognised byType.fromName(s32,u8, etc.) and for thef32/f64/Typekeywords. User-defined alias names likeMyIntandPtrflow through.identifier.Likely fix: mirror the
type_alias_map.getlookup in the.identifierbranch — try alias map first (or before/after findByName, whichever is the established precedence elsewhere)..identifier => |id| { if (self.type_bindings) |tb| { if (tb.get(id.name)) |ty| return ty; } if (self.type_alias_map.get(id.name)) |alias_ty| return alias_ty; const name_id = self.module.types.internString(id.name); return self.module.types.findByName(name_id) orelse .s64; },Verification:
- Add the repro above as
examples/issue-0042.sx.bash tests/run_examples.sh --updateto capture expected output (alias: 4, notalias: 8).- Make sure existing snapshots that test type aliases (search
examples/for::patterns followed bysize_of) don't change in unexpected ways.Possible adjacency: the issue may extend to
align_of(likely same call path) and to type-alias chains (A :: s32; B :: A;— does B resolve through A's alias entry?). Worth pinning down with a test once the primary fix lands.
Plan-level impact
Blocks issue-0041 (compound-type-as-expression). Once 0042 is fixed, 0041 work can resume from the testing phase (the parser and lowering edits for 0041 are already in place; only the alias lookup is broken).
Suggested fix order
- Land 0042's
.identifieralias-map lookup. - Resume 0041 from the test step — re-run
examples/issue-0041.sxand verifysize_of(Maybe) = 2,size_of(Arr) = 3, etc. - Regenerate snapshots and proceed with the 0041 finishing steps (50-smoke, rename, etc.).