From a11da377347aea1085d3b43726040993952122c9 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 14 Nov 2019 10:20:57 +0100 Subject: [PATCH] Update discriminant value also for zero-sized unions Fixes #3681 --- src/codegen.cpp | 33 ++++++++++++++++++++++++--------- test/stage1/behavior/union.zig | 19 +++++++++++++++---- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index a97f5cbd55..a288f397fd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4351,17 +4351,32 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab TypeUnionField *field = instruction->field; if (!type_has_bits(field->type_entry)) { - if (union_type->data.unionation.gen_tag_index == SIZE_MAX) { + ZigType *tag_type = union_type->data.unionation.tag_type; + if (!instruction->initializing || !type_has_bits(tag_type)) return nullptr; + + // The field has no bits but we still have to change the discriminant + // value here + LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); + + LLVMTypeRef tag_type_ref = get_llvm_type(g, tag_type); + LLVMValueRef tag_field_ptr = nullptr; + if (union_type->data.unionation.gen_field_count == 0) { + assert(union_type->data.unionation.gen_tag_index == SIZE_MAX); + // The whole union is collapsed into the discriminant + tag_field_ptr = LLVMBuildBitCast(g->builder, union_ptr, + LLVMPointerType(tag_type_ref, 0), ""); + } else { + assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); + tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_tag_index, ""); } - if (instruction->initializing) { - LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, - union_type->data.unionation.gen_tag_index, ""); - LLVMValueRef tag_value = bigint_to_llvm_const(get_llvm_type(g, union_type->data.unionation.tag_type), - &field->enum_field->value); - gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - } + + LLVMValueRef tag_value = bigint_to_llvm_const(tag_type_ref, + &field->enum_field->value); + assert(tag_field_ptr != nullptr); + gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); + return nullptr; } diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index caad1d474f..d7481b21c7 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -110,7 +110,7 @@ fn doTest() void { } fn bar(value: Payload) i32 { - expect(@as(Letter,value) == Letter.A); + expect(@as(Letter, value) == Letter.A); return switch (value) { Payload.A => |x| return x - 1244, Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21, @@ -208,7 +208,7 @@ test "cast union to tag type of union" { } fn testCastUnionToTagType(x: TheUnion) void { - expect(@as(TheTag,x) == TheTag.B); + expect(@as(TheTag, x) == TheTag.B); } test "cast tag type of union to union" { @@ -558,16 +558,27 @@ test "anonymous union literal syntax" { }; fn doTheTest() void { - var i: Number = .{.int = 42}; + var i: Number = .{ .int = 42 }; var f = makeNumber(); expect(i.int == 42); expect(f.float == 12.34); } fn makeNumber() Number { - return .{.float = 12.34}; + return .{ .float = 12.34 }; } }; S.doTheTest(); comptime S.doTheTest(); } + +test "update the tag value for zero-sized unions" { + const S = union(enum) { + U0: void, + U1: void, + }; + var x = S{ .U0 = {} }; + expect(x == .U0); + x = S{ .U1 = {} }; + expect(x == .U1); +}