# 0080 - global array of struct literals silently zero-initializes

> **RESOLVED.**
> **Root cause:** `Lowering.constExprValue` (`src/ir/lower.zig`) — the constant-
> aggregate serializer for global initializers — handled primitive and nested-
> array leaves but had **no `.struct_literal` arm**. A module-global `[N]Struct`
> initialized with struct literals reached `constArrayLiteral` → `constExprValue`
> per element; each struct-literal element returned `null`, collapsing the whole
> array initializer to `null`. `globalInitValue` then emitted no payload, so the
> LLVM backend zero-initialized the global (`@pairs = ... zeroinitializer`),
> silently dropping every declared field — the same silent-zero class as
> 0071/0072, one level inside an array literal. (A global *struct* literal and a
> *struct-with-array* already worked, because `constStructLiteral` existed and was
> reached directly; the gap was specifically struct literals *as array elements*.)
> **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 falling through to a zeroed
> global. The downstream `emitConstAggregate` already recurses over nested
> aggregates, so const/AOT (`sx build`) and JIT (`sx run`) 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.

## Symptom

A module-global fixed array whose elements are struct literals is emitted as
zero-initialized storage instead of preserving the literal fields.

Observed: reading `pairs[0].b` and `pairs[1].a` prints `0`.
Expected: the global should contain the declared struct literal values
(`2` and `3`), or the compiler should reject the initializer loudly if this
constant shape is unsupported.

## Reproduction

```sx
#import "modules/std.sx";

Pair :: struct {
    a: i64;
    b: i64;
}

pairs : [2]Pair = .[ .{ a = 1, b = 2 }, .{ a = 3, b = 4 } ];

main :: () -> i32 {
    print("pairs[0]={},{}\n", pairs[0].a, pairs[0].b);
    print("pairs[1]={},{}\n", pairs[1].a, pairs[1].b);
    if pairs[0].a == 1 and pairs[0].b == 2 and pairs[1].a == 3 and pairs[1].b == 4 {
        print("PASS\n");
        return 0;
    }
    print("FAIL: global array struct literal initializer zeroed\n");
    return 1;
}
```

On the current compiler this prints:

```text
pairs[0]=0,0
pairs[1]=0,0
FAIL: global array struct literal initializer zeroed
```

`sx ir <file>` shows the global as:

```llvm
@pairs = internal global [2 x { i64, i64 }] zeroinitializer
```

## Investigation prompt

Fix issue 0080: a module-global array initialized with struct literal elements
silently becomes `zeroinitializer`.

Suspected area:
- `src/ir/lower.zig`, `Lowering.globalInitValue`.
- `src/ir/lower.zig`, `Lowering.constArrayLiteral`.
- `src/ir/lower.zig`, `Lowering.constExprValue`.
- `src/ir/lower.zig`, `Lowering.constStructLiteral`.

Likely root cause: `globalInitValue` handles a top-level `.array_literal` by
calling `constArrayLiteral`, and `constArrayLiteral` serializes each element via
`constExprValue`. `constExprValue` handles primitive literals and nested arrays,
but not `.struct_literal`, so an array whose element is a struct literal returns
`null`. That null initializer payload is later emitted as zero-initialized
storage, recreating the silent zero pattern from issues 0071/0072 one level
inside an otherwise-supported array literal.

Likely fix:
- Thread the expected element `TypeId` into `constArrayLiteral`, or otherwise
  make `constExprValue` type-aware for struct literals.
- Serialize each struct element through `constStructLiteral` with the array's
  element type.
- If any element shape is still unsupported, emit a diagnostic naming the global
  instead of returning `null` and allowing zero-initialization.

Verification:
- Run the repro above and expect:

```text
pairs[0]=1,2
pairs[1]=3,4
PASS
```

- Add a pinned regression in the `01xx` types block.
- Run:

```sh
zig build
zig build test
bash tests/run_examples.sh
```
