registerTopLevelGlobal's init_val switch serialized only literal / array- literal / struct-literal initializers. An identifier initializer (`K : A : 42; g : A = K;`) fell through to `else => null`, so the global was emitted with no payload and silently zero-initialized (printed g=0). Extract the initializer serialization into globalInitValue and add an .identifier arm that materializes the global's static value from ProgramIndex.module_const_map (typed module consts are registered in the same scanDecls pass-2 just before, via registerTypedModuleConst). An identifier that names no usable constant now emits a diagnostic instead of silently zeroing — a global has no run site for a dynamic initializer. Other initializer shapes (enum-literal shorthand, etc.) keep their established static-lowering behavior; enum-literal globals' zero-init is load-bearing for `inline if OS == ...` in the stdlib, so it stays out of scope here. This pass only closes the identifier/module-const hole. Regression: examples/0134-types-global-init-from-module-const.sx (g=42, exit 42). Gate: zig build, zig build test, run_examples.sh -> 355/0.
3.9 KiB
0071 — global initialized from module const silently zero-initializes
RESOLVED. Root cause:
Lowering.registerTopLevelGlobal's init_val switch serialized only literal / array-literal / struct-literal initializers; an identifier initializer (g : A = K;) fell through toelse => null, so the global was emitted with no payload and silently zero-initialized. Fix: extracted the initializer serialization intoLowering.globalInitValueand added an.identifierarm that materializes the global's static value fromProgramIndex.module_const_map(typed module consts are registered in the same pass-2 just before, viaregisterTypedModuleConst). An identifier that names no usable constant now emits a diagnostic instead of silently zeroing. Other initializer shapes (enum-literal shorthand, etc.) keep their established static-lowering behavior — this pass only closes the identifier/module-const hole. Regression:examples/0134-types-global-init-from-module-const.sx(g=42/ exit 42).
Symptom
A top-level global initialized from a module constant compiles but is zero-initialized instead of receiving the constant's value.
Observed:
g=0
Expected: g should be initialized to 42, or the compiler should reject the
initializer loudly if identifier/module-const global initializers are not
supported.
Reproduction
#import "modules/std.sx";
A :: B;
B :: s32;
K : A : 42;
g : A = K;
main :: () -> s32 {
print("g={}\n", g);
return g;
}
Run:
./zig-out/bin/sx run .sx-tmp/probe-0070-global-init-from-const.sx
The repro is standalone; the inline source above is sufficient to recreate the
scratch file under .sx-tmp/.
Investigation prompt
Fix issue 0071: a top-level global initialized from a module constant must not silently become zero.
Context:
- This surfaced during Codex re-review of
932cdfa, the issue-0070 fix. 932cdfacorrectly defers top-level global and typed-module-const annotation resolution until after the forward-alias fixpoint.- The remaining bug is in the global initializer path, not the annotation path:
K : A : 42; g : A = K;resolvesAcorrectly, registersKinProgramIndex.module_const_map, butgis emitted as zero.
Suspected area:
src/ir/lower.zig,Lowering.registerTopLevelGlobal.- Its
init_valswitch serializes literal / array / struct-literal initializers, but an identifier initializer falls through toelse => null, and the global is emitted with no initializer payload. That silently becomes zero-initialized. - Related facts:
ProgramIndex.module_const_mapalready records typed module constants viaregisterTypedModuleConst, now in pass 2 after alias convergence.
Likely fix:
- Add explicit handling for identifier initializers that name a module constant,
converting the recorded constant value into the global's
ConstantValuewith the global's declared type. - If some initializer shape cannot be represented as a global constant yet, emit
a diagnostic instead of returning
null/ zero-initializing. - Do not regress issue 0070:
A :: B; B :: s32; g : A = 7;andK : A : 35;must still resolve through the converged alias map. - Preserve literal, array literal, struct literal, and foreign-global behavior.
Verification:
- Add a focused regression, likely in the
01xxtypes block:
#import "modules/std.sx";
A :: B;
B :: s32;
K : A : 42;
g : A = K;
main :: () -> s32 { print("g={}\n", g); return g; }
- Keep these green:
examples/0133-types-forward-alias-global.sxexamples/0132-types-forward-type-alias.sxexamples/0116-types-type-alias-size-align.sxexamples/0201-generics-generic-struct.sxexamples/1117-diagnostics-value-const-as-type-rejected.sx
- Run:
zig build
zig build test
bash tests/run_examples.sh
Expected result: the new regression prints g=42 and exits 42; unsupported
global initializer shapes no longer silently zero-initialize; the full suite
passes.