Commit 7deadf4301

Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
2022-03-02 02:09:03
stage2: reify error sets
1 parent 6594301
Changed files (3)
src
test
behavior
src/Sema.zig
@@ -10397,7 +10397,13 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         vals,
                     ),
                 );
-                break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
+
+                const new_decl_val = try Value.Tag.decl_ref.create(sema.arena, new_decl);
+                const slice_val = try Value.Tag.slice.create(sema.arena, .{
+                    .ptr = new_decl_val,
+                    .len = try Value.Tag.int_u64.create(sema.arena, vals.len),
+                });
+                break :v try Value.Tag.opt_payload.create(sema.arena, slice_val);
             } else Value.@"null";
 
             // Construct TypeInfo{ .ErrorSet = errors_val }
@@ -12317,7 +12323,31 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             });
             return sema.addType(ty);
         },
-        .ErrorSet => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorSet", .{}),
+        .ErrorSet => {
+            const payload_val = union_val.val.optionalValue() orelse
+                return sema.addType(Type.initTag(.anyerror));
+            const slice_val = payload_val.castTag(.slice).?.data;
+            const decl = slice_val.ptr.castTag(.decl_ref).?.data;
+            try sema.ensureDeclAnalyzed(decl);
+            const array_val = decl.val.castTag(.array).?.data;
+
+            var names: Module.ErrorSet.NameMap = .{};
+            try names.ensureUnusedCapacity(sema.arena, array_val.len);
+            for (array_val) |elem_val| {
+                const struct_val = elem_val.castTag(.@"struct").?.data;
+                // TODO use reflection instead of magic numbers here
+                // error_set: type,
+                const name_val = struct_val[0];
+
+                names.putAssumeCapacityNoClobber(
+                    try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena),
+                    {},
+                );
+            }
+
+            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", .{}),
         .Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}),
         .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),
src/value.zig
@@ -2506,6 +2506,15 @@ pub const Value = extern union {
         };
     }
 
+    /// Value of the optional, null if optional has no payload.
+    pub fn optionalValue(val: Value) ?Value {
+        if (val.isNull()) return null;
+
+        // Valid for optional representation to be the direct value
+        // and not use opt_payload.
+        return if (val.castTag(.opt_payload)) |p| p.data else val;
+    }
+
     /// Valid for all types. Asserts the value is not undefined.
     pub fn isFloat(self: Value) bool {
         return switch (self.tag()) {
test/behavior/type.zig
@@ -240,12 +240,19 @@ fn add(a: i32, b: i32) i32 {
 }
 
 test "Type.ErrorSet" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+    try testing.expect(@Type(TypeInfo{ .ErrorSet = null }) == anyerror);
 
     // error sets don't compare equal so just check if they compile
     _ = @Type(@typeInfo(error{}));
     _ = @Type(@typeInfo(error{A}));
     _ = @Type(@typeInfo(error{ A, B, C }));
+    _ = @Type(TypeInfo{
+        .ErrorSet = &[_]TypeInfo.Error{
+            .{ .name = "A" },
+            .{ .name = "B" },
+            .{ .name = "C" },
+        },
+    });
 }
 
 test "Type.Struct" {