authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-10-02 15:44:50+03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-03 00:45:48-07:00
log0bdbd3e235daee64caf1f99e5b9d7b62f4c5c484
tree374a407a0db101d2d17fc358e4b0d30dbc8b1c9d
parentc9c3ee704c45efe0e19bd02c08f51c7513268e76

Sema: fix issues in `@errorCast` with error unions


3 files changed, 46 insertions(+), 9 deletions(-)

src/Sema.zig+14-7
...@@ -21771,10 +21771,10 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData...@@ -21771,10 +21771,10 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
21771 // operand must be defined since it can be an invalid error value21771 // operand must be defined since it can be an invalid error value
21772 const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);21772 const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);
2177321773
21774 if (disjoint: {21774 const disjoint = disjoint: {
21775 // Try avoiding resolving inferred error sets if we can21775 // Try avoiding resolving inferred error sets if we can
21776 if (!dest_ty.isAnyError(mod) and dest_ty.errorSetNames(mod).len == 0) break :disjoint true;21776 if (!dest_ty.isAnyError(mod) and dest_ty.errorSetIsEmpty(mod)) break :disjoint true;
21777 if (!operand_ty.isAnyError(mod) and operand_ty.errorSetNames(mod).len == 0) break :disjoint true;21777 if (!operand_ty.isAnyError(mod) and operand_ty.errorSetIsEmpty(mod)) break :disjoint true;
21778 if (dest_ty.isAnyError(mod)) break :disjoint false;21778 if (dest_ty.isAnyError(mod)) break :disjoint false;
21779 if (operand_ty.isAnyError(mod)) break :disjoint false;21779 if (operand_ty.isAnyError(mod)) break :disjoint false;
21780 for (dest_ty.errorSetNames(mod)) |dest_err_name| {21780 for (dest_ty.errorSetNames(mod)) |dest_err_name| {
...@@ -21796,7 +21796,8 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData...@@ -21796,7 +21796,8 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
21796 }21796 }
2179721797
21798 break :disjoint true;21798 break :disjoint true;
21799 }) {21799 };
21800 if (disjoint and dest_tag != .ErrorUnion) {
21800 const msg = msg: {21801 const msg = msg: {
21801 const msg = try sema.errMsg(21802 const msg = try sema.errMsg(
21802 block,21803 block,
...@@ -21850,10 +21851,16 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData...@@ -21850,10 +21851,16 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
21850 .int = .{ .ty = .u16_type, .storage = .{ .u64 = 0 } },21851 .int = .{ .ty = .u16_type, .storage = .{ .u64 = 0 } },
21851 }));21852 }));
2185221853
21853 const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code);
21854 const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_u16);21854 const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_u16);
21855 const ok = try block.addBinOp(.bit_or, has_value, is_zero);21855 if (disjoint) {
21856 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);21856 // Error must be zero.
21857 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code);
21858 } else {
21859 // Error must be in destination set or zero.
21860 const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code);
21861 const ok = try block.addBinOp(.bit_or, has_value, is_zero);
21862 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
21863 }
21857 } else {21864 } else {
21858 const err_int_inst = try block.addBitCast(Type.err_int, operand);21865 const err_int_inst = try block.addBitCast(Type.err_int, operand);
21859 const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);21866 const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
test/behavior/error.zig+12-2
...@@ -238,13 +238,23 @@ fn testExplicitErrorSetCast(set1: Set1) !void {...@@ -238,13 +238,23 @@ fn testExplicitErrorSetCast(set1: Set1) !void {
238test "@errorCast on error unions" {238test "@errorCast on error unions" {
239 const S = struct {239 const S = struct {
240 fn doTheTest() !void {240 fn doTheTest() !void {
241 const casted: error{Bad}!i32 = @errorCast(retErrUnion());241 {
242 try expect((try casted) == 1234);242 const casted: error{Bad}!i32 = @errorCast(retErrUnion());
243 try expect((try casted) == 1234);
244 }
245 {
246 const casted: error{Bad}!i32 = @errorCast(retInferredErrUnion());
247 try expect((try casted) == 5678);
248 }
243 }249 }
244250
245 fn retErrUnion() anyerror!i32 {251 fn retErrUnion() anyerror!i32 {
246 return 1234;252 return 1234;
247 }253 }
254
255 fn retInferredErrUnion() !i32 {
256 return 5678;
257 }
248 };258 };
249259
250 try S.doTheTest();260 try S.doTheTest();
test/cases/safety/@errorCast error union casted to disjoint set.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "invalid error code")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10pub fn main() !void {
11 const bar: error{Foo}!i32 = @errorCast(foo());
12 _ = &bar;
13 return error.TestFailed;
14}
15fn foo() anyerror!i32 {
16 return error.Bar;
17}
18// run
19// backend=llvm
20// target=native