Commit 8b100792eb

g-w1 <jacoblevgw@gmail.com>
2021-01-14 16:14:29
stage2: error set merging with tests
I had to come up with creative tests because we don't have error set type equality yet.
1 parent 4b57fb5
Changed files (2)
src
test
stage2
src/zir_sema.zig
@@ -1196,7 +1196,81 @@ fn zirErrorValue(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorValue) InnerE
 fn zirMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
-    return mod.fail(scope, inst.base.src, "TODO implement merge_error_sets", .{});
+
+    const rhs_ty = try resolveType(mod, scope, inst.positionals.rhs);
+    const lhs_ty = try resolveType(mod, scope, inst.positionals.lhs);
+    if (rhs_ty.zigTypeTag() != .ErrorSet)
+        return mod.fail(scope, inst.positionals.rhs.src, "expected error set type, found {}", .{rhs_ty});
+    if (lhs_ty.zigTypeTag() != .ErrorSet)
+        return mod.fail(scope, inst.positionals.lhs.src, "expected error set type, found {}", .{lhs_ty});
+
+    // anything merged with anyerror is anyerror
+    if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror)
+        return mod.constInst(scope, inst.base.src, .{
+            .ty = Type.initTag(.type),
+            .val = Value.initTag(.anyerror_type),
+        });
+    // The declarations arena will store the hashmap.
+    var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
+    errdefer new_decl_arena.deinit();
+
+    const payload = try new_decl_arena.allocator.create(Value.Payload.ErrorSet);
+    payload.* = .{
+        .base = .{ .tag = .error_set },
+        .data = .{
+            .fields = .{},
+            .decl = undefined, // populated below
+        },
+    };
+    try payload.data.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, switch (rhs_ty.tag()) {
+        .error_set_single => 1,
+        .error_set => rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size,
+        else => unreachable,
+    } + switch (lhs_ty.tag()) {
+        .error_set_single => 1,
+        .error_set => lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size,
+        else => unreachable,
+    }));
+
+    switch (lhs_ty.tag()) {
+        .error_set_single => {
+            const name = lhs_ty.castTag(.error_set_single).?.data;
+            const num = mod.global_error_set.get(name).?;
+            payload.data.fields.putAssumeCapacity(name, num);
+        },
+        .error_set => {
+            var multiple = lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields;
+            var it = multiple.iterator();
+            while (it.next()) |entry| {
+                payload.data.fields.putAssumeCapacity(entry.key, entry.value);
+            }
+        },
+        else => unreachable,
+    }
+
+    switch (rhs_ty.tag()) {
+        .error_set_single => {
+            const name = rhs_ty.castTag(.error_set_single).?.data;
+            const num = mod.global_error_set.get(name).?;
+            payload.data.fields.putAssumeCapacity(name, num);
+        },
+        .error_set => {
+            var multiple = rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields;
+            var it = multiple.iterator();
+            while (it.next()) |name| {
+                const entry = try mod.getErrorValue(name.key);
+                payload.data.fields.putAssumeCapacity(entry.key, entry.value);
+            }
+        },
+        else => unreachable,
+    }
+    const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
+        .ty = Type.initTag(.type),
+        .val = Value.initPayload(&payload.base),
+    });
+    payload.data.decl = new_decl;
+
+    return mod.analyzeDeclVal(scope, inst.base.src, new_decl);
 }
 
 fn zirEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst {
test/stage2/test.zig
@@ -985,7 +985,7 @@ pub fn addCases(ctx: *TestContext) !void {
             "Hello, World!\n",
         );
         try case.files.append(.{
-            .src =
+            .src = 
             \\pub fn print() void {
             \\    asm volatile ("syscall"
             \\        :
@@ -1525,4 +1525,37 @@ pub fn addCases(ctx: *TestContext) !void {
             \\}
         , "");
     }
+    {
+        var case = ctx.exe("merge error sets", linux_x64);
+
+        case.addCompareOutput(
+            \\export fn _start() noreturn {
+            \\    const E = error{ A, B, D } || error { A, B, C };
+            \\    const a = E.A;
+            \\    const b = E.B;
+            \\    const c = E.C;
+            \\    const d = E.D;
+            \\    const E2 = error { X, Y } || @TypeOf(error.Z);
+            \\    const x = E2.X;
+            \\    const y = E2.Y;
+            \\    const z = E2.Z;
+            \\    assert(anyerror || error { Z } == anyerror);
+            \\    exit();
+            \\}
+            \\fn assert(b: bool) void {
+            \\    if (!b) unreachable;
+            \\}
+            \\fn exit() noreturn {
+            \\    asm volatile ("syscall"
+            \\        :
+            \\        : [number] "{rax}" (231),
+            \\          [arg1] "{rdi}" (0)
+            \\        : "rcx", "r11", "memory"
+            \\    );
+            \\    unreachable;
+            \\}
+        ,
+            "",
+        );
+    }
 }