Commit b0a55e1b3b

Veikka Tuominen <git@vexu.eu>
2022-08-17 12:21:07
Sema: make noreturn error union behave correctly
1 parent db0f372
Changed files (3)
src
test
behavior
cases
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,
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);
+}
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