Sema: make noreturn error union behave correctly

This commit is contained in:
Veikka Tuominen 2022-08-17 13:21:07 +03:00
parent db0f372da8
commit b0a55e1b3b
3 changed files with 88 additions and 1 deletions

View File

@ -6789,6 +6789,15 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
error_set.fmt(sema.mod),
});
}
if (payload.zigTypeTag() == .Opaque) {
return sema.fail(block, rhs_src, "error union with payload of opaque type '{}' not allowed", .{
payload.fmt(sema.mod),
});
} else if (payload.zigTypeTag() == .ErrorSet) {
return sema.fail(block, rhs_src, "error union with payload of error set type '{}' not allowed", .{
payload.fmt(sema.mod),
});
}
const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod);
return sema.addType(err_union_ty);
}
@ -25763,6 +25772,11 @@ fn analyzeIsNonErrComptimeOnly(
if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
assert(ot == .ErrorUnion);
const payload_ty = operand_ty.errorUnionPayload();
if (payload_ty.zigTypeTag() == .NoReturn) {
return Air.Inst.Ref.bool_false;
}
if (Air.refToIndex(operand)) |operand_inst| {
switch (sema.air_instructions.items(.tag)[operand_inst]) {
.wrap_errunion_payload => return Air.Inst.Ref.bool_true,

View File

@ -725,7 +725,7 @@ test "simple else prong allowed even when all errors handled" {
try expect(value == 255);
}
test {
test "pointer to error union payload" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
@ -736,3 +736,63 @@ test {
const payload_ptr = &(err_union catch unreachable);
try expect(payload_ptr.* == 15);
}
const NoReturn = struct {
var a: u32 = undefined;
fn someData() bool {
a -= 1;
return a == 0;
}
fn loop() !noreturn {
while (true) {
if (someData())
return error.GenericFailure;
}
}
fn testTry() anyerror {
try loop();
}
fn testCatch() anyerror {
loop() catch return error.OtherFailure;
@compileError("bad");
}
};
test "error union of noreturn used with if" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
NoReturn.a = 64;
if (NoReturn.loop()) {
@compileError("bad");
} else |err| {
try expect(err == error.GenericFailure);
}
}
test "error union of noreturn used with try" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
NoReturn.a = 64;
const err = NoReturn.testTry();
try expect(err == error.GenericFailure);
}
test "error union of noreturn used with catch" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
NoReturn.a = 64;
const err = NoReturn.testCatch();
try expect(err == error.OtherFailure);
}

View File

@ -0,0 +1,13 @@
comptime {
_ = anyerror!anyopaque;
}
comptime {
_ = anyerror!anyerror;
}
// error
// backend=stage2
// target=native
//
// :2:18: error: error union with payload of opaque type 'anyopaque' not allowed
// :5:18: error: error union with payload of error set type 'anyerror' not allowed