authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-08-06 15:00:58+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-08-06 20:47:03+01:00
log3de8bbd3d4e262df11a582fb52401b8077b5f352
tree56473442feb4a41b2d32601c637d835d98a5c4d9
parent44ea11d71f6a713081e3dec11a08ec322dfeb787

Sema: fix initializing comptime-known constant with OPV union field

Resolves: #24716

2 files changed, 13 insertions(+), 5 deletions(-)

src/Sema.zig+5-5
...@@ -3932,11 +3932,12 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,...@@ -3932,11 +3932,12 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
3932 // Whilst constructing our mapping, we will also initialize optional and error union payloads when3932 // Whilst constructing our mapping, we will also initialize optional and error union payloads when
3933 // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters.3933 // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters.
3934 var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len);3934 var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len);
3935
3935 for (stores) |store_inst_idx| {3936 for (stores) |store_inst_idx| {
3936 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));3937 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
3937 const ptr_to_map = switch (store_inst.tag) {3938 const ptr_to_map = switch (store_inst.tag) {
3938 .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to.3939 .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to.
3939 .set_union_tag => continue, // Ignore for now; handled after we map pointers3940 .set_union_tag => store_inst.data.bin_op.lhs.toIndex().?, // Map the union pointer.
3940 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.3941 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.
3941 else => unreachable,3942 else => unreachable,
3942 };3943 };
...@@ -4053,13 +4054,12 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,...@@ -4053,13 +4054,12 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
4053 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);4054 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
4054 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| {4055 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| {
4055 // As this is a union field, we must store to the pointer now to set the tag.4056 // As this is a union field, we must store to the pointer now to set the tag.
4056 // If the payload is OPV, there will not be a payload store, so we store that value.4057 // The payload value will be stored later, so undef is a sufficent payload for now.
4057 // Otherwise, there will be a payload store to process later, so undef will suffice.
4058 const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]);4058 const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]);
4059 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);4059 const payload_val = try pt.undefValue(payload_ty);
4060 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx);4060 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx);
4061 const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);4061 const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);
4062 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), store_val, maybe_union_ty);4062 try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty);
4063 }4063 }
4064 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();4064 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
4065 },4065 },
test/behavior/union.zig+8
...@@ -2311,3 +2311,11 @@ test "set mutable union by switching on same union" {...@@ -2311,3 +2311,11 @@ test "set mutable union by switching on same union" {
2311 try expect(val == .bar);2311 try expect(val == .bar);
2312 try expect(val.bar == 2);2312 try expect(val.bar == 2);
2313}2313}
2314
2315test "initialize empty field of union inside comptime-known struct constant" {
2316 const Inner = union { none: void, some: u8 };
2317 const Wrapper = struct { inner: Inner };
2318
2319 const val: Wrapper = .{ .inner = .{ .none = {} } };
2320 comptime assert(val.inner.none == {});
2321}