Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
4.5 KiB
0072 — global field-access constant initializer silently zero-initializes
RESOLVED. Root cause:
Lowering.globalInitValue's issue-0071.identifierarm closed the bare-identifier hole, but.field_access(and every other non-literal expression shape) still fell through toelse => null, so the global was emitted with no payload and silently zero-initialized (g=0). Fix: theelsenow emits a diagnostic — "global '' must be initialized by a compile-time constant" — instead of returning a null payload, so an unsupported initializer shape can never silently zero. Two arms were added alongside it:.null_literal => .null_val(a*void = nullglobal was previously a no-payload zero-init; this preserves that exact emission —LLVMConstNull), and an explicit.enum_literal => nullcarve-out (the stdlib'sOS : OperatingSystem = .unknown;zero-init is load-bearing for compile-timeinline if OS == .X; documented, not folded into a silent fallthrough). Field-access constant evaluation (materializingK.x→ 9) was intentionally not implemented: a typed struct const likeKis not registered inmodule_const_map, so it would require new plumbing whosemodule_const_mapwrites are read at runtime — out of scope; the diagnostic is the chosen, issue-sanctioned outcome. Regressionexamples/1118-diagnostics-global-non-const-initializer-rejected.sx(exit 1).
Symptom
A top-level global initialized from a field access on a module constant compiles but is zero-initialized.
Observed: g=0
Expected: g should be initialized to 9, or the compiler should reject the
initializer loudly if field-access global constants are not supported yet.
Reproduction
#import "modules/std.sx";
Point :: struct {
x: i32;
y: i32;
}
K : Point : Point.{ x = 9, y = 4 };
g : i32 = K.x;
main :: () -> i32 {
print("g={}\n", g);
return g;
}
Run:
./zig-out/bin/sx run .sx-tmp/review-0071-field-access-const.sx
The repro is standalone; the inline source above is sufficient to recreate the
scratch file under .sx-tmp/.
Investigation prompt
Fix issue 0072: a top-level global initialized from a field access on a module constant must not silently become zero.
Context:
- This surfaced during Codex re-review of commit
ad7200c, the issue-0071 fix. ad7200ccorrectly handles identifier initializers that name module constants (K : A : 42; g : A = K;) and diagnoses identifiers that are not usable constants.- A remaining non-identifier expression shape still falls through silently:
K : Point : Point.{ x = 9, y = 4 }; g : i32 = K.x;emits a null global initializer payload and runs asg=0.
Suspected area:
src/ir/lower.zig,Lowering.globalInitValue.- The
.identifierarm now diagnoses unusable identifier initializers, but the genericelse => nullstill covers.field_accessand other expression forms. constExprValuecurrently handles literals, negative numeric literals, and array literals; it does not evaluate field access or struct-literal module constants.constStructLiteralcan serialize direct struct literals when called with the destination type, but that machinery is not used forK.x.
Likely fix:
- Add a loud diagnostic for unsupported top-level global initializer expression shapes, or implement field-access constant evaluation in the same step.
- If implementing it, resolve the base identifier through
ProgramIndex.module_const_map, materialize the base constant with its recorded type, then extract the named field using the IR struct layout. - Do not replace the current bug with a real-type sentinel such as
.void; an unsupported initializer should either produce a concreteConstantValueor a user-visible diagnostic. - Preserve the issue-0071 behavior:
K : A : 42; g : A = K;must still emitg=42. - Preserve the issue-0070/0068 regressions:
examples/0133-types-forward-alias-global.sxandexamples/1117-diagnostics-value-const-as-type-rejected.sxmust stay green. - Scope-check enum-literal globals separately.
OS : OperatingSystem = .unknowncurrently relies on compile-time constant injection and pre-existing zero-init behavior; decide explicitly whether that stays carved out or gets its own separate issue.
Verification:
- Repro above should either print
g=9and exit9, or fail with a clear "global initializer must be a compile-time constant" diagnostic. It must not printg=0. - Run:
zig build
zig build test
bash tests/run_examples.sh