Commit 19cf987198

Andrew Kelley <andrew@ziglang.org>
2021-04-07 08:19:46
C backend: implement Enum types and values
They are lowered directly as the integer tag type, with no typedef.
1 parent acf9151
Changed files (3)
src/codegen/c.zig
@@ -172,7 +172,10 @@ pub const DeclGen = struct {
         val: Value,
     ) error{ OutOfMemory, AnalysisFail }!void {
         if (val.isUndef()) {
-            return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{});
+            // This should lower to 0xaa bytes in safe modes, and for unsafe modes should
+            // lower to leaving variables uninitialized (that might need to be implemented
+            // outside of this function).
+            return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement renderValue undef", .{});
         }
         switch (t.zigTypeTag()) {
             .Int => {
@@ -288,6 +291,31 @@ pub const DeclGen = struct {
                     try writer.writeAll(", .error = 0 }");
                 }
             },
+            .Enum => {
+                switch (val.tag()) {
+                    .enum_field_index => {
+                        const field_index = val.castTag(.enum_field_index).?.data;
+                        switch (t.tag()) {
+                            .enum_simple => return writer.print("{d}", .{field_index}),
+                            .enum_full, .enum_nonexhaustive => {
+                                const enum_full = t.cast(Type.Payload.EnumFull).?.data;
+                                if (enum_full.values.count() != 0) {
+                                    const tag_val = enum_full.values.entries.items[field_index].key;
+                                    return dg.renderValue(writer, enum_full.tag_ty, tag_val);
+                                } else {
+                                    return writer.print("{d}", .{field_index});
+                                }
+                            },
+                            else => unreachable,
+                        }
+                    },
+                    else => {
+                        var int_tag_ty_buffer: Type.Payload.Bits = undefined;
+                        const int_tag_ty = t.intTagType(&int_tag_ty_buffer);
+                        return dg.renderValue(writer, int_tag_ty, val);
+                    },
+                }
+            },
             else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{
                 @tagName(e),
             }),
@@ -472,6 +500,13 @@ pub const DeclGen = struct {
                 try w.writeAll(name);
                 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
             },
+            .Enum => {
+                // For enums, we simply use the integer tag type.
+                var int_tag_ty_buffer: Type.Payload.Bits = undefined;
+                const int_tag_ty = t.intTagType(&int_tag_ty_buffer);
+
+                try dg.renderType(w, int_tag_ty);
+            },
             .Null, .Undefined => unreachable, // must be const or comptime
             else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{
                 @tagName(e),
src/Sema.zig
@@ -1918,11 +1918,20 @@ fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErr
             switch (enum_tag.ty.tag()) {
                 .enum_full => {
                     const enum_full = enum_tag.ty.castTag(.enum_full).?.data;
-                    const val = enum_full.values.entries.items[field_index].key;
-                    return mod.constInst(arena, src, .{
-                        .ty = int_tag_ty,
-                        .val = val,
-                    });
+                    if (enum_full.values.count() != 0) {
+                        const val = enum_full.values.entries.items[field_index].key;
+                        return mod.constInst(arena, src, .{
+                            .ty = int_tag_ty,
+                            .val = val,
+                        });
+                    } else {
+                        // Field index and integer values are the same.
+                        const val = try Value.Tag.int_u64.create(arena, field_index);
+                        return mod.constInst(arena, src, .{
+                            .ty = int_tag_ty,
+                            .val = val,
+                        });
+                    }
                 },
                 .enum_simple => {
                     // Field index and integer values are the same.
src/type.zig
@@ -691,7 +691,7 @@ pub const Type = extern union {
                     return struct_obj.owner_decl.renderFullyQualifiedName(writer);
                 },
                 .enum_full, .enum_nonexhaustive => {
-                    const enum_full = ty.castTag(.enum_full).?.data;
+                    const enum_full = ty.cast(Payload.EnumFull).?.data;
                     return enum_full.owner_decl.renderFullyQualifiedName(writer);
                 },
                 .enum_simple => {
@@ -1344,7 +1344,7 @@ pub const Type = extern union {
     /// Asserts the type is an enum.
     pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {
         switch (self.tag()) {
-            .enum_full, .enum_nonexhaustive => return self.castTag(.enum_full).?.data.tag_ty,
+            .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty,
             .enum_simple => {
                 const enum_simple = self.castTag(.enum_simple).?.data;
                 const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count());
@@ -1969,7 +1969,7 @@ pub const Type = extern union {
                     return null;
                 }
             },
-            .enum_nonexhaustive => ty = ty.castTag(.enum_full).?.data.tag_ty,
+            .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
 
             .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
             .void => return Value.initTag(.void_value),