fix(ir): materialize global initialized from module const (issue 0071)
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.
This commit is contained in:
117
issues/0071-global-initializer-module-const-silent-zero.md
Normal file
117
issues/0071-global-initializer-module-const-silent-zero.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# 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 to `else => null`, so the
|
||||
> global was emitted with no payload and silently zero-initialized.
|
||||
> Fix: extracted the initializer serialization into `Lowering.globalInitValue`
|
||||
> and added an `.identifier` arm that materializes the global's static value from
|
||||
> `ProgramIndex.module_const_map` (typed module consts are registered in the same
|
||||
> pass-2 just before, via `registerTypedModuleConst`). 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:
|
||||
|
||||
```text
|
||||
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
|
||||
|
||||
```sx
|
||||
#import "modules/std.sx";
|
||||
|
||||
A :: B;
|
||||
B :: s32;
|
||||
|
||||
K : A : 42;
|
||||
g : A = K;
|
||||
|
||||
main :: () -> s32 {
|
||||
print("g={}\n", g);
|
||||
return g;
|
||||
}
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
./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.
|
||||
- `932cdfa` correctly 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;` resolves `A` correctly, registers `K` in
|
||||
`ProgramIndex.module_const_map`, but `g` is emitted as zero.
|
||||
|
||||
Suspected area:
|
||||
- `src/ir/lower.zig`, `Lowering.registerTopLevelGlobal`.
|
||||
- Its `init_val` switch serializes literal / array / struct-literal initializers,
|
||||
but an identifier initializer falls through to `else => null`, and the global
|
||||
is emitted with no initializer payload. That silently becomes zero-initialized.
|
||||
- Related facts: `ProgramIndex.module_const_map` already records typed module
|
||||
constants via `registerTypedModuleConst`, 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 `ConstantValue` with
|
||||
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;` and
|
||||
`K : 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 `01xx` types block:
|
||||
|
||||
```sx
|
||||
#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.sx`
|
||||
- `examples/0132-types-forward-type-alias.sx`
|
||||
- `examples/0116-types-type-alias-size-align.sx`
|
||||
- `examples/0201-generics-generic-struct.sx`
|
||||
- `examples/1117-diagnostics-value-const-as-type-rejected.sx`
|
||||
- Run:
|
||||
|
||||
```sh
|
||||
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.
|
||||
Reference in New Issue
Block a user