Files
sx/examples/0137-types-global-aggregate-literal-init.sx
agra e93879816d fix(ir): materialize global aggregate struct-literal initializers (issue 0080)
A module-global array of struct literals (`pairs : [2]Pair = .[ .{...}, .{...} ]`)
was emitted as `zeroinitializer`, silently dropping every declared field — reads
returned 0 with no diagnostic. Global struct literals and struct-with-array
already worked; the gap was struct literals used as ARRAY elements.

Root cause: `Lowering.constExprValue` (the const-aggregate serializer for global
initializers) had no `.struct_literal` arm. `constArrayLiteral` serialized each
element through `constExprValue`, so a struct-literal element returned null,
collapsing the whole array initializer to null; `globalInitValue` then emitted no
payload and the LLVM backend zero-initialized the global — the same silent-zero
class as 0071/0072, one level inside an array literal.

Fix: make `constExprValue` type-aware — thread the destination element/field
TypeId so a struct-literal leaf routes through `constStructLiteral` and a nested
array-literal through `constArrayLiteral` with the correct element type.
`constArrayLiteral` derives its element type from the array TypeId;
`constStructLiteral` passes each field's type. A global aggregate initializer that
still does not fully reduce to a compile-time constant is now rejected loudly
(`diagnoseNonConstGlobal`) instead of silently zeroing. `emitConstAggregate`
already recurses over nested aggregates, so `sx run` (JIT) and `sx build` (AOT)
both materialize the declared values.

Regression: examples/0137-types-global-aggregate-literal-init.sx (global
[N]Struct literal, global struct literal, struct-with-array, nested
array-of-struct-with-array; values read back with no prior store, plus a store on
top). Fails on the pre-fix compiler (array-of-struct fields read 0), passes after.

Marks issues 0079 (already resolved) and 0080 RESOLVED.
2026-06-04 04:04:40 +03:00

50 lines
2.4 KiB
Plaintext

// A module-global aggregate (array of struct literals, a struct literal, and
// nested array/struct shapes) materializes its DECLARED field values into the
// global's static initializer, so reading the fields without any prior store
// returns the literal values — not zero.
// Regression (issue 0080): a global `[N]Struct` initialized with struct literals
// was emitted as `zeroinitializer`, silently dropping every field, because the
// constant-aggregate serializer had no struct-literal arm and collapsed the
// whole initializer to null. The fix threads the element/field type so struct
// and nested-array leaves serialize correctly; a genuinely non-constant
// initializer is now rejected loudly instead of silently zeroed.
#import "modules/std.sx";
Pair :: struct { a: s64; b: s64; }
WithArr :: struct { id: s64; xs: [3]s64; }
// global array of struct literals
pairs : [2]Pair = .[ .{ a = 1, b = 2 }, .{ a = 3, b = 4 } ];
// global struct literal
solo : Pair = .{ a = 7, b = 9 };
// global struct containing a fixed array (struct-with-array)
wa : WithArr = .{ id = 5, xs = .[ 11, 22, 33 ] };
// nested: global array of structs each containing an array
nested : [2]WithArr = .[ .{ id = 1, xs = .[ 1, 2, 3 ] }, .{ id = 2, xs = .[ 4, 5, 6 ] } ];
main :: () {
// Read the declared initializer values back with NO prior store.
print("pairs={},{} {},{}\n", pairs[0].a, pairs[0].b, pairs[1].a, pairs[1].b);
print("solo={},{}\n", solo.a, solo.b);
print("wa={} xs={},{},{}\n", wa.id, wa.xs[0], wa.xs[1], wa.xs[2]);
print("nested0={} xs={},{},{}\n", nested[0].id, nested[0].xs[0], nested[0].xs[1], nested[0].xs[2]);
print("nested1={} xs={},{},{}\n", nested[1].id, nested[1].xs[0], nested[1].xs[1], nested[1].xs[2]);
// A store on top of the materialized initializer still works (live storage).
pairs[0].a = 100;
nested[1].xs[2] = 999;
print("after-store={} {}\n", pairs[0].a, nested[1].xs[2]);
if pairs[0].b == 2 and pairs[1].a == 3 and pairs[1].b == 4
and solo.a == 7 and solo.b == 9
and wa.id == 5 and wa.xs[0] == 11 and wa.xs[2] == 33
and nested[0].id == 1 and nested[0].xs[0] == 1 and nested[0].xs[2] == 3
and nested[1].id == 2 and nested[1].xs[0] == 4
and pairs[0].a == 100 and nested[1].xs[2] == 999 {
print("PASS\n");
} else {
print("FAIL: global aggregate literal initializer zeroed\n");
}
}