From 8b100792ebaa4fa17dd75fadc7a8bdb47328dfad Mon Sep 17 00:00:00 2001 From: g-w1 Date: Thu, 14 Jan 2021 10:14:29 -0500 Subject: [PATCH] stage2: error set merging with tests I had to come up with creative tests because we don't have error set type equality yet. --- src/zir_sema.zig | 76 +++++++++++++++++++++++++++++++++++++++++++- test/stage2/test.zig | 35 +++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/src/zir_sema.zig b/src/zir_sema.zig index cfc1ea3280..acbe21466b 100644 --- a/src/zir_sema.zig +++ b/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 { diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 9bd5655d22..d475f5dff0 100644 --- a/test/stage2/test.zig +++ b/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; + \\} + , + "", + ); + } }