Skip to content

Commit a7479ef

Browse files
committed
Sema: add @errorCast which works for both error sets and error unions
Closes ziglang#17343
1 parent 376242e commit a7479ef

14 files changed

+88
-46
lines changed

doc/langref.html.in

+6-6
Original file line numberDiff line numberDiff line change
@@ -6657,7 +6657,7 @@ test "coercion from homogenous tuple to array" {
66576657
<li>{#link|@alignCast#} - make a pointer have more alignment</li>
66586658
<li>{#link|@enumFromInt#} - obtain an enum value based on its integer tag value</li>
66596659
<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>
66616661
<li>{#link|@floatCast#} - convert a larger float to a smaller float</li>
66626662
<li>{#link|@floatFromInt#} - convert an integer to a float value</li>
66636663
<li>{#link|@intCast#} - convert between integer types</li>
@@ -8410,10 +8410,10 @@ test "main" {
84108410
</p>
84118411
{#header_close#}
84128412

8413-
{#header_open|@errSetCast#}
8414-
<pre>{#syntax#}@errSetCast(value: anytype) anytype{#endsyntax#}</pre>
8413+
{#header_open|@errorCast#}
8414+
<pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre>
84158415
<p>
8416-
Converts an error value from one error set to another error set. The return type is the
8416+
Converts an error set or error union value from one error set to another error set. The return type is the
84178417
inferred result type. Attempting to convert an error which is not in the destination error
84188418
set results in safety-protected {#link|Undefined Behavior#}.
84198419
</p>
@@ -10257,7 +10257,7 @@ const Set2 = error{
1025710257
C,
1025810258
};
1025910259
comptime {
10260-
_ = @as(Set2, @errSetCast(Set1.B));
10260+
_ = @as(Set2, @errorCast(Set1.B));
1026110261
}
1026210262
{#code_end#}
1026310263
<p>At runtime:</p>
@@ -10276,7 +10276,7 @@ pub fn main() void {
1027610276
foo(Set1.B);
1027710277
}
1027810278
fn foo(set1: Set1) void {
10279-
const x: Set2 = @errSetCast(set1);
10279+
const x: Set2 = @errorCast(set1);
1028010280
std.debug.print("value: {}\n", .{x});
1028110281
}
1028210282
{#code_end#}

lib/std/child_process.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ pub const ChildProcess = struct {
446446
// has a value greater than 0
447447
if ((fd[0].revents & std.os.POLL.IN) != 0) {
448448
const err_int = try readIntFd(err_pipe[0]);
449-
return @as(SpawnError, @errSetCast(@errorFromInt(err_int)));
449+
return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
450450
}
451451
} else {
452452
// Write maxInt(ErrInt) to the write end of the err_pipe. This is after
@@ -459,7 +459,7 @@ pub const ChildProcess = struct {
459459
// Here we potentially return the fork child's error from the parent
460460
// pid.
461461
if (err_int != maxInt(ErrInt)) {
462-
return @as(SpawnError, @errSetCast(@errorFromInt(err_int)));
462+
return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
463463
}
464464
}
465465
}

lib/std/os.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -5419,7 +5419,7 @@ pub fn dl_iterate_phdr(
54195419
}
54205420
}.callbackC, @as(?*anyopaque, @ptrFromInt(@intFromPtr(&context))))) {
54215421
0 => return,
5422-
else => |err| return @as(Error, @errSetCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16
5422+
else => |err| return @as(Error, @errorCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16
54235423
}
54245424
}
54255425

lib/std/zig/render.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -1444,7 +1444,7 @@ fn renderBuiltinCall(
14441444
const slice = tree.tokenSlice(builtin_token);
14451445
const rewrite_two_param_cast = params.len == 2 and for ([_][]const u8{
14461446
"@bitCast",
1447-
"@errSetCast",
1447+
"@errorCast",
14481448
"@floatCast",
14491449
"@intCast",
14501450
"@ptrCast",

src/AstGen.zig

+3-3
Original file line numberDiff line numberDiff line change
@@ -8454,11 +8454,11 @@ fn builtinCall(
84548454
});
84558455
return rvalue(gz, ri, result, node);
84568456
},
8457-
.err_set_cast => {
8457+
.error_cast => {
84588458
try emitDbgNode(gz, node);
84598459

8460-
const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{
8461-
.lhs = try ri.rl.resultTypeForCast(gz, node, "@errSetCast"),
8460+
const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{
8461+
.lhs = try ri.rl.resultTypeForCast(gz, node, "@errorCast"),
84628462
.rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
84638463
.node = gz.nodeIndexToRelative(node),
84648464
});

src/AstRlAnnotate.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
945945
.float_cast,
946946
.int_cast,
947947
.truncate,
948-
.err_set_cast,
948+
.error_cast,
949949
.ptr_cast,
950950
.align_cast,
951951
.addrspace_cast,

src/BuiltinFn.zig

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub const Tag = enum {
4343
error_name,
4444
error_return_trace,
4545
int_from_error,
46-
err_set_cast,
46+
error_cast,
4747
@"export",
4848
@"extern",
4949
fence,
@@ -455,9 +455,9 @@ pub const list = list: {
455455
},
456456
},
457457
.{
458-
"@errSetCast",
458+
"@errorCast",
459459
.{
460-
.tag = .err_set_cast,
460+
.tag = .error_cast,
461461
.eval_to_error = .always,
462462
.param_count = 1,
463463
},

src/Sema.zig

+47-21
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,7 @@ fn analyzeBodyInner(
12521252
.wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
12531253
.wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
12541254
.prefetch => try sema.zirPrefetch( block, extended),
1255-
.err_set_cast => try sema.zirErrSetCast( block, extended),
1255+
.error_cast => try sema.zirErrorCast( block, extended),
12561256
.await_nosuspend => try sema.zirAwaitNosuspend( block, extended),
12571257
.select => try sema.zirSelect( block, extended),
12581258
.int_from_error => try sema.zirIntFromError( block, extended),
@@ -21747,17 +21747,31 @@ fn ptrFromIntVal(
2174721747
};
2174821748
}
2174921749

21750-
fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
21750+
fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
2175121751
const mod = sema.mod;
2175221752
const ip = &mod.intern_pool;
2175321753
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
2175421754
const src = LazySrcLoc.nodeOffset(extra.node);
2175521755
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");
2175721757
const operand = try sema.resolveInst(extra.rhs);
21758-
const operand_ty = sema.typeOf(operand);
21759-
try sema.checkErrorSetType(block, src, dest_ty);
21760-
try sema.checkErrorSetType(block, operand_src, operand_ty);
21758+
const base_operand_ty = sema.typeOf(operand);
21759+
const dest_tag = base_dest_ty.zigTypeTag(mod);
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

2176221776
// operand must be defined since it can be an invalid error value
2176321777
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
2180421818
}
2180521819

2180621820
if (maybe_operand_val) |val| {
21807-
if (!dest_ty.isAnyError(mod)) {
21808-
const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
21821+
if (!dest_ty.isAnyError(mod)) check: {
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+
}
2180921830
if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {
2181021831
const msg = msg: {
2181121832
const msg = try sema.errMsg(
@@ -21822,16 +21843,29 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
2182221843
}
2182321844
}
2182421845

21825-
return Air.internedToRef((try mod.getCoerced(val, dest_ty)).toIntern());
21846+
return Air.internedToRef((try mod.getCoerced(val, base_dest_ty)).toIntern());
2182621847
}
2182721848

2182821849
try sema.requireRuntimeBlock(block, src, operand_src);
2182921850
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);
21831-
const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
21832-
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
21851+
if (dest_tag == .ErrorUnion) {
21852+
const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand);
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+
}
2183321867
}
21834-
return block.addBitCast(dest_ty, operand);
21868+
return block.addBitCast(base_dest_ty, operand);
2183521869
}
2183621870

2183721871
fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
@@ -22916,14 +22950,6 @@ fn checkIntOrVectorAllowComptime(
2291622950
}
2291722951
}
2291822952

22919-
fn 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-
2292722953
const SimdBinOp = struct {
2292822954
len: ?usize,
2292922955
/// Coerced to `result_ty`.

src/Zir.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -1997,9 +1997,9 @@ pub const Inst = struct {
19971997
/// Implements `@setCold`.
19981998
/// `operand` is payload index to `UnNode`.
19991999
set_cold,
2000-
/// Implements the `@errSetCast` builtin.
2000+
/// Implements the `@errorCast` builtin.
20012001
/// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
2002-
err_set_cast,
2002+
error_cast,
20032003
/// `operand` is payload index to `UnNode`.
20042004
await_nosuspend,
20052005
/// Implements `@breakpoint`.

src/print_zir.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ const Writer = struct {
594594

595595
.builtin_extern,
596596
.c_define,
597-
.err_set_cast,
597+
.error_cast,
598598
.wasm_memory_grow,
599599
.prefetch,
600600
.c_va_arg,

test/behavior/error.zig

+18-2
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,29 @@ const Set1 = error{ A, B };
228228
const Set2 = error{ A, C };
229229

230230
fn testExplicitErrorSetCast(set1: Set1) !void {
231-
var x = @as(Set2, @errSetCast(set1));
231+
var x = @as(Set2, @errorCast(set1));
232232
try expect(@TypeOf(x) == Set2);
233-
var y = @as(Set1, @errSetCast(x));
233+
var y = @as(Set1, @errorCast(x));
234234
try expect(@TypeOf(y) == Set1);
235235
try expect(y == error.A);
236236
}
237237

238+
test "@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+
238254
test "comptime test error for empty error set" {
239255
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
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const Set1 = error{ A, B };
22
const Set2 = error{ A, C };
33
comptime {
44
var x = Set1.B;
5-
var y: Set2 = @errSetCast(x);
5+
var y: Set2 = @errorCast(x);
66
_ = y;
77
}
88

test/cases/compile_errors/int_to_err_non_global_invalid_number.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const Set2 = error{
88
};
99
comptime {
1010
var x = @intFromError(Set1.B);
11-
var y: Set2 = @errSetCast(@errorFromInt(x));
11+
var y: Set2 = @errorCast(@errorFromInt(x));
1212
_ = y;
1313
}
1414

test/cases/safety/@errSetCast error not present in destination.zig test/cases/safety/@errorCast error not present in destination.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub fn main() !void {
1414
return error.TestFailed;
1515
}
1616
fn foo(set1: Set1) Set2 {
17-
return @errSetCast(set1);
17+
return @errorCast(set1);
1818
}
1919
// run
2020
// backend=llvm

0 commit comments

Comments
 (0)