Commit a11da37734

LemonBoy <thatlemon@gmail.com>
2019-11-14 10:20:57
Update discriminant value also for zero-sized unions
Fixes #3681
1 parent d89f39d
Changed files (2)
src
test
stage1
behavior
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;
     }
 
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);
+}