Commit 015cc41e50

Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
2022-03-12 02:44:52
stage2: zirReify for enums
1 parent 6f98629
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -12470,7 +12470,121 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const ty = try Type.Tag.error_set_merged.create(sema.arena, names);
             return sema.addType(ty);
         },
-        .Enum => return sema.fail(block, src, "TODO: Sema.zirReify for Enum", .{}),
+        .Enum => {
+            const struct_val = union_val.val.castTag(.@"struct").?.data;
+            // TODO use reflection instead of magic numbers here
+            // error_set: type,
+            // layout: ContainerLayout,
+            const layout_val = struct_val[0];
+            // tag_type: type,
+            const tag_type_val = struct_val[1];
+            // fields: []const EnumField,
+            const fields_val = struct_val[2];
+            // decls: []const Declaration,
+            const decls_val = struct_val[3];
+            // is_exhaustive: bool,
+            const is_exhaustive_val = struct_val[4];
+
+            // enum layout is always auto
+            const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout);
+            if (layout != .Auto) {
+                return sema.fail(block, src, "reified enums must have a layout .Auto", .{});
+            }
+
+            // Decls
+            const decls_slice_val = decls_val.castTag(.slice).?.data;
+            const decls_decl = decls_slice_val.ptr.castTag(.decl_ref).?.data;
+            try sema.ensureDeclAnalyzed(decls_decl);
+            if (decls_decl.ty.arrayLen() > 0) {
+                return sema.fail(block, src, "reified enums must have no decls", .{});
+            }
+
+            const mod = sema.mod;
+            const gpa = sema.gpa;
+            var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
+            errdefer new_decl_arena.deinit();
+            const new_decl_arena_allocator = new_decl_arena.allocator();
+
+            // Define our empty enum decl
+            const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull);
+            const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumFull);
+            enum_ty_payload.* = .{
+                .base = .{
+                    .tag = if (!is_exhaustive_val.toBool())
+                        .enum_nonexhaustive
+                    else
+                        .enum_full,
+                },
+                .data = enum_obj,
+            };
+            const enum_ty = Type.initPayload(&enum_ty_payload.base);
+            const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
+            const type_name = try sema.createTypeName(block, .anon);
+            const new_decl = try mod.createAnonymousDeclNamed(block, .{
+                .ty = Type.type,
+                .val = enum_val,
+            }, type_name);
+            new_decl.owns_tv = true;
+            errdefer mod.abortAnonDecl(new_decl);
+
+            enum_obj.* = .{
+                .owner_decl = new_decl,
+                .tag_ty = Type.initTag(.@"null"),
+                .fields = .{},
+                .values = .{},
+                .node_offset = src.node_offset,
+                .namespace = .{
+                    .parent = block.namespace,
+                    .ty = enum_ty,
+                    .file_scope = block.getFileScope(),
+                },
+            };
+
+            // Enum tag type
+            var buffer: Value.ToTypeBuffer = undefined;
+            enum_obj.tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
+
+            // Fields
+            const slice_val = fields_val.castTag(.slice).?.data;
+            const decl = slice_val.ptr.castTag(.decl_ref).?.data;
+            try sema.ensureDeclAnalyzed(decl);
+            const fields_len = decl.ty.arrayLen();
+            if (fields_len > 0) {
+                try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
+                try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{
+                    .ty = enum_obj.tag_ty,
+                });
+
+                const array_vals = decl.val.castTag(.array).?.data;
+                for (array_vals) |elem_val| {
+                    const field_struct_val = elem_val.castTag(.@"struct").?.data;
+                    // TODO use reflection instead of magic numbers here
+                    // name: []const u8
+                    const name_val = field_struct_val[0];
+                    // value: comptime_int
+                    const value_val = field_struct_val[1];
+
+                    const field_name = try name_val.toAllocatedBytes(
+                        Type.initTag(.const_slice_u8),
+                        new_decl_arena_allocator,
+                    );
+
+                    const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name);
+                    if (gop.found_existing) {
+                        // TODO: better source location
+                        return sema.fail(block, src, "duplicate enum tag {s}", .{field_name});
+                    }
+
+                    const copied_tag_val = try value_val.copy(new_decl_arena_allocator);
+                    enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
+                        .ty = enum_obj.tag_ty,
+                    });
+                }
+            }
+
+            try new_decl.finalizeNewArena(&new_decl_arena);
+            return sema.analyzeDeclVal(block, src, new_decl);
+        },
         .Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}),
         .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),
         .BoundFn => @panic("TODO delete BoundFn from the language"),
test/behavior/type.zig
@@ -302,7 +302,11 @@ test "Type.Struct" {
 }
 
 test "Type.Enum" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
 
     const Foo = @Type(.{
         .Enum = .{
@@ -321,7 +325,12 @@ test "Type.Enum" {
     try testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b));
     const Bar = @Type(.{
         .Enum = .{
-            .layout = .Extern,
+            // stage2 only has auto layouts
+            .layout = if (builtin.zig_backend == .stage1)
+                .Extern
+            else
+                .Auto,
+
             .tag_type = u32,
             .fields = &.{
                 .{ .name = "a", .value = 1 },