lang: fix struct-field null/undef over-store (issue 0154)

Assigning null/--- to a struct field picked up a leaked enclosing
target_type (the function's return type, set for the whole body), so
constNull/constUndef built a whole-struct-typed value. The oversized
store overran the field's slot and clobbered the saved frame pointer,
so the function returned to 0x0. Surfaced building a by-value-returned
struct whose array field precedes a pointer field (Scheduler.init()).

Fix: add null_literal/undef_literal to the needs_target switch in
lowerAssignment so the field's own type is used. Regression:
examples/types/0193-types-sret-array-before-pointer.sx.
This commit is contained in:
agra
2026-06-21 18:43:33 +03:00
parent bdf83db4c8
commit b1e06f21e3
6 changed files with 95 additions and 1 deletions

View File

@@ -629,7 +629,14 @@ pub fn lowerAssignment(self: *Lowering, asgn: *const ast.Assignment) void {
// unchanged into method-call arg slots (`resolveCallParamTypes` can't
// override target_type per-arg).
const needs_target = switch (asgn.value.data) {
.enum_literal, .struct_literal, .tuple_literal, .if_expr, .match_expr, .block, .unary_op, .binary_op => true,
// `null` / `---` (undef) carry NO type of their own — they take the
// store slot's type from `target_type`. Without setting it to the
// field type here, a leaked enclosing `target_type` (e.g. the
// function's return type while lowering its body, decl.zig:2691)
// reaches `constNull`/`constUndef` and builds a WHOLE-STRUCT-typed
// null, emitting an oversized store that overruns the field's slot
// and corrupts neighboring stack (issue 0154).
.enum_literal, .struct_literal, .tuple_literal, .if_expr, .match_expr, .block, .unary_op, .binary_op, .null_literal, .undef_literal => true,
.call => |vc| vc.callee.data == .enum_literal,
else => false,
};