Commit 51b1083d66

Veikka Tuominen <git@vexu.eu>
2022-11-04 17:38:42
stage2: fix onePossibleValue of empty unions and enums
Closes #13402
1 parent 42db468
Changed files (3)
src/Sema.zig
@@ -10343,6 +10343,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         }
         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand);
         if (special.is_inline) child_block.inline_case_capture = operand;
+        if (empty_enum) {
+            return Air.Inst.Ref.void_value;
+        }
         return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
     }
 
@@ -30479,23 +30482,23 @@ pub fn typeHasOnePossibleValue(
             if (enum_obj.tag_ty.hasRuntimeBits()) {
                 return null;
             }
-            if (enum_obj.fields.count() == 1) {
-                if (enum_obj.values.count() == 0) {
+            switch (enum_obj.fields.count()) {
+                0 => return Value.initTag(.unreachable_value),
+                1 => if (enum_obj.values.count() == 0) {
                     return Value.zero; // auto-numbered
                 } else {
                     return enum_obj.values.keys()[0];
-                }
-            } else {
-                return null;
+                },
+                else => return null,
             }
         },
         .enum_simple => {
             const resolved_ty = try sema.resolveTypeFields(block, src, ty);
             const enum_simple = resolved_ty.castTag(.enum_simple).?.data;
-            if (enum_simple.fields.count() == 1) {
-                return Value.zero;
-            } else {
-                return null;
+            switch (enum_simple.fields.count()) {
+                0 => return Value.initTag(.unreachable_value),
+                1 => return Value.zero,
+                else => return null,
             }
         },
         .enum_nonexhaustive => {
@@ -30512,7 +30515,7 @@ pub fn typeHasOnePossibleValue(
             const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
                 return null;
             const fields = union_obj.fields.values();
-            if (fields.len == 0) return Value.initTag(.empty_struct_value);
+            if (fields.len == 0) return Value.initTag(.unreachable_value);
             const only_field = fields[0];
             if (only_field.ty.eql(resolved_ty, sema.mod)) {
                 const msg = try Module.ErrorMsg.create(
src/type.zig
@@ -5015,22 +5015,22 @@ pub const Type = extern union {
                 if (enum_full.tag_ty.hasRuntimeBits()) {
                     return null;
                 }
-                if (enum_full.fields.count() == 1) {
-                    if (enum_full.values.count() == 0) {
-                        return Value.zero;
+                switch (enum_full.fields.count()) {
+                    0 => return Value.initTag(.unreachable_value),
+                    1 => if (enum_full.values.count() == 0) {
+                        return Value.zero; // auto-numbered
                     } else {
                         return enum_full.values.keys()[0];
-                    }
-                } else {
-                    return null;
+                    },
+                    else => return null,
                 }
             },
             .enum_simple => {
                 const enum_simple = ty.castTag(.enum_simple).?.data;
-                if (enum_simple.fields.count() == 1) {
-                    return Value.zero;
-                } else {
-                    return null;
+                switch (enum_simple.fields.count()) {
+                    0 => return Value.initTag(.unreachable_value),
+                    1 => return Value.zero,
+                    else => return null,
                 }
             },
             .enum_nonexhaustive => {
@@ -5044,6 +5044,7 @@ pub const Type = extern union {
             .@"union", .union_safety_tagged, .union_tagged => {
                 const union_obj = ty.cast(Payload.Union).?.data;
                 const tag_val = union_obj.tag_ty.onePossibleValue() orelse return null;
+                if (union_obj.fields.count() == 0) return Value.initTag(.unreachable_value);
                 const only_field = union_obj.fields.values()[0];
                 const val_val = only_field.ty.onePossibleValue() orelse return null;
                 _ = tag_val;
test/behavior/empty_union.zig
@@ -48,3 +48,21 @@ test "empty extern union" {
     try expect(@sizeOf(U) == 0);
     try expect(@alignOf(U) == 1);
 }
+
+test "empty union passed as argument" {
+    const U = union(enum) {
+        fn f(u: @This()) void {
+            switch (u) {}
+        }
+    };
+    U.f(@as(U, undefined));
+}
+
+test "empty enum passed as argument" {
+    const E = enum {
+        fn f(e: @This()) void {
+            switch (e) {}
+        }
+    };
+    E.f(@as(E, undefined));
+}