fix: union struct-literal init (issue 0158)
A plain union initialized with a struct literal (b : Overlay = .{ f = 3.14 })
silently miscompiled — it fell through the generic struct-literal path
(getStructFields returns empty for a union), building a malformed structInit
whose overlapping zero-fill clobbered the named member, so it read back 0.0
(and a type-pun read segfaulted).
lowerStructLiteral now detects a plain-union target and dispatches to a new
lowerUnionLiteral, which writes each named member into a union-sized slot via
the same lvalue resolver the u.member = v assignment path uses, then loads the
union value back. Validity: the named members must share one arm — a single
direct member, or several promoted members of the same anonymous-struct variant.
Overlapping members, members from different arms, and positional union literals
are rejected with a diagnostic (no silent last-wins); an empty .{} yields an
undefined union (matching the --- form).
specs.md updated. Regressions: examples/types/0194 (valid forms) +
examples/diagnostics/1191 (overlap rejection).
This commit is contained in:
@@ -86,6 +86,15 @@ pub fn lowerStructLiteral(self: *Lowering, sl: *const ast.StructLiteral, span: a
|
||||
self.resolveTypeWithBindings(te)
|
||||
else self.target_type orelse .unresolved;
|
||||
|
||||
// Plain (untagged) union target: build by writing each named member into a
|
||||
// union-sized slot. `getStructFields` returns empty for a union, so the
|
||||
// generic struct path below would emit a malformed `structInit` whose
|
||||
// overlapping zero-fill clobbers the named member (issue 0158). Tagged
|
||||
// unions were already handled above.
|
||||
if (!ty.isBuiltin() and self.module.types.get(ty) == .@"union") {
|
||||
return self.lowerUnionLiteral(sl, ty, span);
|
||||
}
|
||||
|
||||
// Get struct field types for coercion and ordering
|
||||
const struct_fields = self.getStructFields(ty);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user