authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-01 12:00:17-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-01 12:00:17-07:00
log8e1421f19ef3daea9b8a414bd5783189bc292676
treecf642506120e82fed3de9c4a32a2232492d49b94
parentd8bfbbbf25984cfdc4d50a92569523a0a151d9e6
parent0b1ba6eb521a15b38b08f9923dd84329487d5755
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17346 from Vexu/errSetCast

Sema: implement `@errSetCast` for error unions

16 files changed, 110 insertions(+), 66 deletions(-)

doc/langref.html.in+6-6
...@@ -6657,7 +6657,7 @@ test "coercion from homogenous tuple to array" {...@@ -6657,7 +6657,7 @@ test "coercion from homogenous tuple to array" {
6657 <li>{#link|@alignCast#} - make a pointer have more alignment</li>6657 <li>{#link|@alignCast#} - make a pointer have more alignment</li>
6658 <li>{#link|@enumFromInt#} - obtain an enum value based on its integer tag value</li>6658 <li>{#link|@enumFromInt#} - obtain an enum value based on its integer tag value</li>
6659 <li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li>6659 <li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li>
6660 <li>{#link|@errSetCast#} - convert to a smaller error set</li>6660 <li>{#link|@errorCast#} - convert to a smaller error set</li>
6661 <li>{#link|@floatCast#} - convert a larger float to a smaller float</li>6661 <li>{#link|@floatCast#} - convert a larger float to a smaller float</li>
6662 <li>{#link|@floatFromInt#} - convert an integer to a float value</li>6662 <li>{#link|@floatFromInt#} - convert an integer to a float value</li>
6663 <li>{#link|@intCast#} - convert between integer types</li>6663 <li>{#link|@intCast#} - convert between integer types</li>
...@@ -8410,10 +8410,10 @@ test "main" {...@@ -8410,10 +8410,10 @@ test "main" {
8410 </p>8410 </p>
8411 {#header_close#}8411 {#header_close#}
84128412
8413 {#header_open|@errSetCast#}8413 {#header_open|@errorCast#}
8414 <pre>{#syntax#}@errSetCast(value: anytype) anytype{#endsyntax#}</pre>8414 <pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre>
8415 <p>8415 <p>
8416 Converts an error value from one error set to another error set. The return type is the8416 Converts an error set or error union value from one error set to another error set. The return type is the
8417 inferred result type. Attempting to convert an error which is not in the destination error8417 inferred result type. Attempting to convert an error which is not in the destination error
8418 set results in safety-protected {#link|Undefined Behavior#}.8418 set results in safety-protected {#link|Undefined Behavior#}.
8419 </p>8419 </p>
...@@ -10257,7 +10257,7 @@ const Set2 = error{...@@ -10257,7 +10257,7 @@ const Set2 = error{
10257 C,10257 C,
10258};10258};
10259comptime {10259comptime {
10260 _ = @as(Set2, @errSetCast(Set1.B));10260 _ = @as(Set2, @errorCast(Set1.B));
10261}10261}
10262 {#code_end#}10262 {#code_end#}
10263 <p>At runtime:</p>10263 <p>At runtime:</p>
...@@ -10276,7 +10276,7 @@ pub fn main() void {...@@ -10276,7 +10276,7 @@ pub fn main() void {
10276 foo(Set1.B);10276 foo(Set1.B);
10277}10277}
10278fn foo(set1: Set1) void {10278fn foo(set1: Set1) void {
10279 const x: Set2 = @errSetCast(set1);10279 const x: Set2 = @errorCast(set1);
10280 std.debug.print("value: {}\n", .{x});10280 std.debug.print("value: {}\n", .{x});
10281}10281}
10282 {#code_end#}10282 {#code_end#}
lib/std/child_process.zig+2-2
...@@ -446,7 +446,7 @@ pub const ChildProcess = struct {...@@ -446,7 +446,7 @@ pub const ChildProcess = struct {
446 // has a value greater than 0446 // has a value greater than 0
447 if ((fd[0].revents & std.os.POLL.IN) != 0) {447 if ((fd[0].revents & std.os.POLL.IN) != 0) {
448 const err_int = try readIntFd(err_pipe[0]);448 const err_int = try readIntFd(err_pipe[0]);
449 return @as(SpawnError, @errSetCast(@errorFromInt(err_int)));449 return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
450 }450 }
451 } else {451 } else {
452 // Write maxInt(ErrInt) to the write end of the err_pipe. This is after452 // Write maxInt(ErrInt) to the write end of the err_pipe. This is after
...@@ -459,7 +459,7 @@ pub const ChildProcess = struct {...@@ -459,7 +459,7 @@ pub const ChildProcess = struct {
459 // Here we potentially return the fork child's error from the parent459 // Here we potentially return the fork child's error from the parent
460 // pid.460 // pid.
461 if (err_int != maxInt(ErrInt)) {461 if (err_int != maxInt(ErrInt)) {
462 return @as(SpawnError, @errSetCast(@errorFromInt(err_int)));462 return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
463 }463 }
464 }464 }
465 }465 }
lib/std/os.zig+1-1
...@@ -5419,7 +5419,7 @@ pub fn dl_iterate_phdr(...@@ -5419,7 +5419,7 @@ pub fn dl_iterate_phdr(
5419 }5419 }
5420 }.callbackC, @as(?*anyopaque, @ptrFromInt(@intFromPtr(&context))))) {5420 }.callbackC, @as(?*anyopaque, @ptrFromInt(@intFromPtr(&context))))) {
5421 0 => return,5421 0 => return,
5422 else => |err| return @as(Error, @errSetCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u165422 else => |err| return @as(Error, @errorCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16
5423 }5423 }
5424 }5424 }
54255425
lib/std/zig/render.zig+3-1
...@@ -1444,7 +1444,7 @@ fn renderBuiltinCall(...@@ -1444,7 +1444,7 @@ fn renderBuiltinCall(
1444 const slice = tree.tokenSlice(builtin_token);1444 const slice = tree.tokenSlice(builtin_token);
1445 const rewrite_two_param_cast = params.len == 2 and for ([_][]const u8{1445 const rewrite_two_param_cast = params.len == 2 and for ([_][]const u8{
1446 "@bitCast",1446 "@bitCast",
1447 "@errSetCast",1447 "@errorCast",
1448 "@floatCast",1448 "@floatCast",
1449 "@intCast",1449 "@intCast",
1450 "@ptrCast",1450 "@ptrCast",
...@@ -1505,6 +1505,8 @@ fn renderBuiltinCall(...@@ -1505,6 +1505,8 @@ fn renderBuiltinCall(
1505 try ais.writer().writeAll("@intFromPtr");1505 try ais.writer().writeAll("@intFromPtr");
1506 } else if (mem.eql(u8, slice, "@fabs")) {1506 } else if (mem.eql(u8, slice, "@fabs")) {
1507 try ais.writer().writeAll("@abs");1507 try ais.writer().writeAll("@abs");
1508 } else if (mem.eql(u8, slice, "@errSetCast")) {
1509 try ais.writer().writeAll("@errorCast");
1508 } else {1510 } else {
1509 try renderToken(ais, tree, builtin_token, .none); // @name1511 try renderToken(ais, tree, builtin_token, .none); // @name
1510 }1512 }
src/AstGen.zig+3-3
...@@ -8454,11 +8454,11 @@ fn builtinCall(...@@ -8454,11 +8454,11 @@ fn builtinCall(
8454 });8454 });
8455 return rvalue(gz, ri, result, node);8455 return rvalue(gz, ri, result, node);
8456 },8456 },
8457 .err_set_cast => {8457 .error_cast => {
8458 try emitDbgNode(gz, node);8458 try emitDbgNode(gz, node);
84598459
8460 const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{8460 const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{
8461 .lhs = try ri.rl.resultTypeForCast(gz, node, "@errSetCast"),8461 .lhs = try ri.rl.resultTypeForCast(gz, node, "@errorCast"),
8462 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),8462 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8463 .node = gz.nodeIndexToRelative(node),8463 .node = gz.nodeIndexToRelative(node),
8464 });8464 });
src/AstRlAnnotate.zig+1-1
...@@ -945,7 +945,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -945,7 +945,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
945 .float_cast,945 .float_cast,
946 .int_cast,946 .int_cast,
947 .truncate,947 .truncate,
948 .err_set_cast,948 .error_cast,
949 .ptr_cast,949 .ptr_cast,
950 .align_cast,950 .align_cast,
951 .addrspace_cast,951 .addrspace_cast,
src/BuiltinFn.zig+3-3
...@@ -43,7 +43,7 @@ pub const Tag = enum {...@@ -43,7 +43,7 @@ pub const Tag = enum {
43 error_name,43 error_name,
44 error_return_trace,44 error_return_trace,
45 int_from_error,45 int_from_error,
46 err_set_cast,46 error_cast,
47 @"export",47 @"export",
48 @"extern",48 @"extern",
49 fence,49 fence,
...@@ -455,9 +455,9 @@ pub const list = list: {...@@ -455,9 +455,9 @@ pub const list = list: {
455 },455 },
456 },456 },
457 .{457 .{
458 "@errSetCast",458 "@errorCast",
459 .{459 .{
460 .tag = .err_set_cast,460 .tag = .error_cast,
461 .eval_to_error = .always,461 .eval_to_error = .always,
462 .param_count = 1,462 .param_count = 1,
463 },463 },
src/Sema.zig+47-21
...@@ -1252,7 +1252,7 @@ fn analyzeBodyInner(...@@ -1252,7 +1252,7 @@ fn analyzeBodyInner(
1252 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended),1252 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
1253 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),1253 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
1254 .prefetch => try sema.zirPrefetch( block, extended),1254 .prefetch => try sema.zirPrefetch( block, extended),
1255 .err_set_cast => try sema.zirErrSetCast( block, extended),1255 .error_cast => try sema.zirErrorCast( block, extended),
1256 .await_nosuspend => try sema.zirAwaitNosuspend( block, extended),1256 .await_nosuspend => try sema.zirAwaitNosuspend( block, extended),
1257 .select => try sema.zirSelect( block, extended),1257 .select => try sema.zirSelect( block, extended),
1258 .int_from_error => try sema.zirIntFromError( block, extended),1258 .int_from_error => try sema.zirIntFromError( block, extended),
...@@ -21747,17 +21747,31 @@ fn ptrFromIntVal(...@@ -21747,17 +21747,31 @@ fn ptrFromIntVal(
21747 };21747 };
21748}21748}
2174921749
21750fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {21750fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
21751 const mod = sema.mod;21751 const mod = sema.mod;
21752 const ip = &mod.intern_pool;21752 const ip = &mod.intern_pool;
21753 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;21753 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
21754 const src = LazySrcLoc.nodeOffset(extra.node);21754 const src = LazySrcLoc.nodeOffset(extra.node);
21755 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };21755 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
21756 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@errSetCast");21756 const base_dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast");
21757 const operand = try sema.resolveInst(extra.rhs);21757 const operand = try sema.resolveInst(extra.rhs);
21758 const operand_ty = sema.typeOf(operand);21758 const base_operand_ty = sema.typeOf(operand);
21759 try sema.checkErrorSetType(block, src, dest_ty);21759 const dest_tag = base_dest_ty.zigTypeTag(mod);
21760 try sema.checkErrorSetType(block, operand_src, operand_ty);21760 const operand_tag = base_operand_ty.zigTypeTag(mod);
21761 if (dest_tag != operand_tag) {
21762 return sema.fail(block, src, "expected source and destination types to match, found '{s}' and '{s}'", .{
21763 @tagName(operand_tag), @tagName(dest_tag),
21764 });
21765 } else if (dest_tag != .ErrorSet and dest_tag != .ErrorUnion) {
21766 return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)});
21767 }
21768 const dest_ty, const operand_ty = if (dest_tag == .ErrorUnion) .{
21769 base_dest_ty.errorUnionSet(mod),
21770 base_operand_ty.errorUnionSet(mod),
21771 } else .{
21772 base_dest_ty,
21773 base_operand_ty,
21774 };
2176121775
21762 // operand must be defined since it can be an invalid error value21776 // operand must be defined since it can be an invalid error value
21763 const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);21777 const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);
...@@ -21804,8 +21818,15 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -21804,8 +21818,15 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
21804 }21818 }
2180521819
21806 if (maybe_operand_val) |val| {21820 if (maybe_operand_val) |val| {
21807 if (!dest_ty.isAnyError(mod)) {21821 if (!dest_ty.isAnyError(mod)) check: {
21808 const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;21822 const operand_val = mod.intern_pool.indexToKey(val.toIntern());
21823 var error_name: InternPool.NullTerminatedString = undefined;
21824 if (dest_tag == .ErrorUnion) {
21825 if (operand_val.error_union.val != .err_name) break :check;
21826 error_name = operand_val.error_union.val.err_name;
21827 } else {
21828 error_name = operand_val.err.name;
21829 }
21809 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {21830 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {
21810 const msg = msg: {21831 const msg = msg: {
21811 const msg = try sema.errMsg(21832 const msg = try sema.errMsg(
...@@ -21822,16 +21843,29 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -21822,16 +21843,29 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
21822 }21843 }
21823 }21844 }
2182421845
21825 return Air.internedToRef((try mod.getCoerced(val, dest_ty)).toIntern());21846 return Air.internedToRef((try mod.getCoerced(val, base_dest_ty)).toIntern());
21826 }21847 }
2182721848
21828 try sema.requireRuntimeBlock(block, src, operand_src);21849 try sema.requireRuntimeBlock(block, src, operand_src);
21829 if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {21850 if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {
21830 const err_int_inst = try block.addBitCast(Type.err_int, operand);21851 if (dest_tag == .ErrorUnion) {
21831 const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);21852 const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand);
21832 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);21853 const err_int = try block.addBitCast(Type.err_int, err_code);
21854 const zero_u16 = Air.internedToRef(try mod.intern(.{
21855 .int = .{ .ty = .u16_type, .storage = .{ .u64 = 0 } },
21856 }));
21857
21858 const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code);
21859 const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_u16);
21860 const ok = try block.addBinOp(.bit_or, has_value, is_zero);
21861 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
21862 } else {
21863 const err_int_inst = try block.addBitCast(Type.err_int, operand);
21864 const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
21865 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
21866 }
21833 }21867 }
21834 return block.addBitCast(dest_ty, operand);21868 return block.addBitCast(base_dest_ty, operand);
21835}21869}
2183621870
21837fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {21871fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
...@@ -22916,14 +22950,6 @@ fn checkIntOrVectorAllowComptime(...@@ -22916,14 +22950,6 @@ fn checkIntOrVectorAllowComptime(
22916 }22950 }
22917}22951}
2291822952
22919fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
22920 const mod = sema.mod;
22921 switch (ty.zigTypeTag(mod)) {
22922 .ErrorSet => return,
22923 else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}),
22924 }
22925}
22926
22927const SimdBinOp = struct {22953const SimdBinOp = struct {
22928 len: ?usize,22954 len: ?usize,
22929 /// Coerced to `result_ty`.22955 /// Coerced to `result_ty`.
src/Zir.zig+2-2
...@@ -1997,9 +1997,9 @@ pub const Inst = struct {...@@ -1997,9 +1997,9 @@ pub const Inst = struct {
1997 /// Implements `@setCold`.1997 /// Implements `@setCold`.
1998 /// `operand` is payload index to `UnNode`.1998 /// `operand` is payload index to `UnNode`.
1999 set_cold,1999 set_cold,
2000 /// Implements the `@errSetCast` builtin.2000 /// Implements the `@errorCast` builtin.
2001 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.2001 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
2002 err_set_cast,2002 error_cast,
2003 /// `operand` is payload index to `UnNode`.2003 /// `operand` is payload index to `UnNode`.
2004 await_nosuspend,2004 await_nosuspend,
2005 /// Implements `@breakpoint`.2005 /// Implements `@breakpoint`.
src/print_zir.zig+1-1
...@@ -594,7 +594,7 @@ const Writer = struct {...@@ -594,7 +594,7 @@ const Writer = struct {
594594
595 .builtin_extern,595 .builtin_extern,
596 .c_define,596 .c_define,
597 .err_set_cast,597 .error_cast,
598 .wasm_memory_grow,598 .wasm_memory_grow,
599 .prefetch,599 .prefetch,
600 .c_va_arg,600 .c_va_arg,
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior/error.zig+18-2
...@@ -228,13 +228,29 @@ const Set1 = error{ A, B };...@@ -228,13 +228,29 @@ const Set1 = error{ A, B };
228const Set2 = error{ A, C };228const Set2 = error{ A, C };
229229
230fn testExplicitErrorSetCast(set1: Set1) !void {230fn testExplicitErrorSetCast(set1: Set1) !void {
231 var x = @as(Set2, @errSetCast(set1));231 var x = @as(Set2, @errorCast(set1));
232 try expect(@TypeOf(x) == Set2);232 try expect(@TypeOf(x) == Set2);
233 var y = @as(Set1, @errSetCast(x));233 var y = @as(Set1, @errorCast(x));
234 try expect(@TypeOf(y) == Set1);234 try expect(@TypeOf(y) == Set1);
235 try expect(y == error.A);235 try expect(y == error.A);
236}236}
237237
238test "@errorCast on error unions" {
239 const S = struct {
240 fn doTheTest() !void {
241 const casted: error{Bad}!i32 = @errorCast(retErrUnion());
242 try expect((try casted) == 1234);
243 }
244
245 fn retErrUnion() anyerror!i32 {
246 return 1234;
247 }
248 };
249
250 try S.doTheTest();
251 try comptime S.doTheTest();
252}
253
238test "comptime test error for empty error set" {254test "comptime test error for empty error set" {
239 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO255 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
240256
test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig+1-1
...@@ -2,7 +2,7 @@ const Set1 = error{ A, B };...@@ -2,7 +2,7 @@ const Set1 = error{ A, B };
2const Set2 = error{ A, C };2const Set2 = error{ A, C };
3comptime {3comptime {
4 var x = Set1.B;4 var x = Set1.B;
5 var y: Set2 = @errSetCast(x);5 var y: Set2 = @errorCast(x);
6 _ = y;6 _ = y;
7}7}
88
test/cases/compile_errors/int_to_err_non_global_invalid_number.zig+1-1
...@@ -8,7 +8,7 @@ const Set2 = error{...@@ -8,7 +8,7 @@ const Set2 = error{
8};8};
9comptime {9comptime {
10 var x = @intFromError(Set1.B);10 var x = @intFromError(Set1.B);
11 var y: Set2 = @errSetCast(@errorFromInt(x));11 var y: Set2 = @errorCast(@errorFromInt(x));
12 _ = y;12 _ = y;
13}13}
1414
test/cases/safety/@errSetCast error not present in destination.zig deleted-21
...@@ -1,21 +0,0 @@
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}
10const Set1 = error{ A, B };
11const Set2 = error{ A, C };
12pub fn main() !void {
13 foo(Set1.B) catch {};
14 return error.TestFailed;
15}
16fn foo(set1: Set1) Set2 {
17 return @errSetCast(set1);
18}
19// run
20// backend=llvm
21// target=native
test/cases/safety/@errorCast error not present in destination.zig created+21
...@@ -0,0 +1,21 @@
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}
10const Set1 = error{ A, B };
11const Set2 = error{ A, C };
12pub fn main() !void {
13 foo(Set1.B) catch {};
14 return error.TestFailed;
15}
16fn foo(set1: Set1) Set2 {
17 return @errorCast(set1);
18}
19// run
20// backend=llvm
21// target=native