stage2: error set merging with tests

I had to come up with creative tests because we don't have error set type equality yet.
This commit is contained in:
g-w1 2021-01-14 10:14:29 -05:00 committed by Andrew Kelley
parent 4b57fb5f23
commit 8b100792eb
2 changed files with 109 additions and 2 deletions

View File

@ -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 {

View File

@ -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;
\\}
,
"",
);
}
}