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:
@@ -1271,16 +1271,7 @@ pub const Lowering = struct {
|
||||
// name is the optional override or the sx name itself.
|
||||
const sym_name = vd.foreign_name orelse vd.name;
|
||||
const name_id = self.module.types.internString(sym_name);
|
||||
const init_val: ?inst_mod.ConstantValue = if (vd.is_foreign) null else if (vd.value) |v| switch (v.data) {
|
||||
.undef_literal => .zeroinit,
|
||||
.int_literal => |il| .{ .int = il.value },
|
||||
.bool_literal => |bl| .{ .boolean = bl.value },
|
||||
.float_literal => |fl| .{ .float = fl.value },
|
||||
.string_literal => |sl| .{ .string = self.module.types.internString(sl.raw) },
|
||||
.array_literal => |al| self.constArrayLiteral(al.elements),
|
||||
.struct_literal => |sl| self.constStructLiteral(&sl, var_ty),
|
||||
else => null,
|
||||
} else null;
|
||||
const init_val = self.globalInitValue(vd, var_ty);
|
||||
const gid = self.module.addGlobal(.{
|
||||
.name = name_id,
|
||||
.ty = var_ty,
|
||||
@@ -1291,6 +1282,44 @@ pub const Lowering = struct {
|
||||
self.program_index.global_names.put(vd.name, .{ .id = gid, .ty = var_ty }) catch {};
|
||||
}
|
||||
|
||||
/// Serialize a top-level global's initializer into a static `ConstantValue`.
|
||||
/// Foreign globals (extern symbol) and value-less declarations carry no
|
||||
/// payload — they default to zero/extern at link, which is correct. An
|
||||
/// identifier initializer that names a module constant is materialized from
|
||||
/// the recorded constant (`K : A : 42; g : A = K;` → 42, issue 0071); a
|
||||
/// global initialized from an identifier that resolves to no usable constant
|
||||
/// is rejected with a diagnostic rather than silently zero-initialized — a
|
||||
/// global has no run site for a dynamic initializer.
|
||||
fn globalInitValue(self: *Lowering, vd: *const ast.VarDecl, var_ty: TypeId) ?inst_mod.ConstantValue {
|
||||
if (vd.is_foreign) return null;
|
||||
const v = vd.value orelse return null;
|
||||
return switch (v.data) {
|
||||
.undef_literal => .zeroinit,
|
||||
.int_literal => |il| .{ .int = il.value },
|
||||
.bool_literal => |bl| .{ .boolean = bl.value },
|
||||
.float_literal => |fl| .{ .float = fl.value },
|
||||
.string_literal => |sl| .{ .string = self.module.types.internString(sl.raw) },
|
||||
.array_literal => |al| self.constArrayLiteral(al.elements),
|
||||
.struct_literal => |sl| self.constStructLiteral(&sl, var_ty),
|
||||
.identifier => |id| blk: {
|
||||
// A global initialized from a module constant copies the
|
||||
// constant's recorded value (typed module consts land in
|
||||
// `module_const_map` via `registerTypedModuleConst`, run in the
|
||||
// same pass-2 before this).
|
||||
if (self.program_index.module_const_map.get(id.name)) |ci| {
|
||||
if (self.constExprValue(ci.value)) |cv| break :blk cv;
|
||||
}
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, v.span, "global '{s}' must be initialized by a compile-time constant; '{s}' is not a usable constant here", .{ vd.name, id.name });
|
||||
break :blk null;
|
||||
},
|
||||
// Other initializer shapes (enum-literal shorthand, etc.) keep their
|
||||
// established static-lowering behavior; this pass only closes the
|
||||
// identifier/module-const hole (issue 0071).
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Resolve identifier-RHS type aliases whose target is declared LATER in the
|
||||
/// file. The forward scan above only registers an alias (`A :: B`) when `B`
|
||||
/// is already in `type_alias_map` / the `TypeTable`; a forward target isn't
|
||||
|
||||
Reference in New Issue
Block a user