From b0a55e1b3be3a274546f9c18016e9609d546bdb0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Aug 2022 13:21:07 +0300 Subject: [PATCH] Sema: make noreturn error union behave correctly --- src/Sema.zig | 14 +++++ test/behavior/error.zig | 62 ++++++++++++++++++- .../invalid_error_union_payload_type.zig | 13 ++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/invalid_error_union_payload_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index ceeb8af23c..772abf5a84 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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, diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 306dad5d9e..84b18a2738 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -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); +} diff --git a/test/cases/compile_errors/invalid_error_union_payload_type.zig b/test/cases/compile_errors/invalid_error_union_payload_type.zig new file mode 100644 index 0000000000..f8646d9450 --- /dev/null +++ b/test/cases/compile_errors/invalid_error_union_payload_type.zig @@ -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