mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Sema: add @errorCast which works for both error sets and error unions
Closes #17343
This commit is contained in:
parent
d8bfbbbf25
commit
63bd2bff12
14 changed files with 90 additions and 46 deletions
|
|
@ -6657,7 +6657,7 @@ test "coercion from homogenous tuple to array" {
|
||||||
<li>{#link|@alignCast#} - make a pointer have more alignment</li>
|
<li>{#link|@alignCast#} - make a pointer have more alignment</li>
|
||||||
<li>{#link|@enumFromInt#} - obtain an enum value based on its integer tag value</li>
|
<li>{#link|@enumFromInt#} - obtain an enum value based on its integer tag value</li>
|
||||||
<li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li>
|
<li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li>
|
||||||
<li>{#link|@errSetCast#} - convert to a smaller error set</li>
|
<li>{#link|@errorCast#} - convert to a smaller error set</li>
|
||||||
<li>{#link|@floatCast#} - convert a larger float to a smaller float</li>
|
<li>{#link|@floatCast#} - convert a larger float to a smaller float</li>
|
||||||
<li>{#link|@floatFromInt#} - convert an integer to a float value</li>
|
<li>{#link|@floatFromInt#} - convert an integer to a float value</li>
|
||||||
<li>{#link|@intCast#} - convert between integer types</li>
|
<li>{#link|@intCast#} - convert between integer types</li>
|
||||||
|
|
@ -8410,10 +8410,10 @@ test "main" {
|
||||||
</p>
|
</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@errSetCast#}
|
{#header_open|@errorCast#}
|
||||||
<pre>{#syntax#}@errSetCast(value: anytype) anytype{#endsyntax#}</pre>
|
<pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
Converts an error value from one error set to another error set. The return type is the
|
Converts an error set or error union value from one error set to another error set. The return type is the
|
||||||
inferred result type. Attempting to convert an error which is not in the destination error
|
inferred result type. Attempting to convert an error which is not in the destination error
|
||||||
set results in safety-protected {#link|Undefined Behavior#}.
|
set results in safety-protected {#link|Undefined Behavior#}.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -10257,7 +10257,7 @@ const Set2 = error{
|
||||||
C,
|
C,
|
||||||
};
|
};
|
||||||
comptime {
|
comptime {
|
||||||
_ = @as(Set2, @errSetCast(Set1.B));
|
_ = @as(Set2, @errorCast(Set1.B));
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
<p>At runtime:</p>
|
<p>At runtime:</p>
|
||||||
|
|
@ -10276,7 +10276,7 @@ pub fn main() void {
|
||||||
foo(Set1.B);
|
foo(Set1.B);
|
||||||
}
|
}
|
||||||
fn foo(set1: Set1) void {
|
fn foo(set1: Set1) void {
|
||||||
const x: Set2 = @errSetCast(set1);
|
const x: Set2 = @errorCast(set1);
|
||||||
std.debug.print("value: {}\n", .{x});
|
std.debug.print("value: {}\n", .{x});
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
|
|
|
||||||
|
|
@ -446,7 +446,7 @@ pub const ChildProcess = struct {
|
||||||
// has a value greater than 0
|
// has a value greater than 0
|
||||||
if ((fd[0].revents & std.os.POLL.IN) != 0) {
|
if ((fd[0].revents & std.os.POLL.IN) != 0) {
|
||||||
const err_int = try readIntFd(err_pipe[0]);
|
const err_int = try readIntFd(err_pipe[0]);
|
||||||
return @as(SpawnError, @errSetCast(@errorFromInt(err_int)));
|
return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Write maxInt(ErrInt) to the write end of the err_pipe. This is after
|
// Write maxInt(ErrInt) to the write end of the err_pipe. This is after
|
||||||
|
|
@ -459,7 +459,7 @@ pub const ChildProcess = struct {
|
||||||
// Here we potentially return the fork child's error from the parent
|
// Here we potentially return the fork child's error from the parent
|
||||||
// pid.
|
// pid.
|
||||||
if (err_int != maxInt(ErrInt)) {
|
if (err_int != maxInt(ErrInt)) {
|
||||||
return @as(SpawnError, @errSetCast(@errorFromInt(err_int)));
|
return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5419,7 +5419,7 @@ pub fn dl_iterate_phdr(
|
||||||
}
|
}
|
||||||
}.callbackC, @as(?*anyopaque, @ptrFromInt(@intFromPtr(&context))))) {
|
}.callbackC, @as(?*anyopaque, @ptrFromInt(@intFromPtr(&context))))) {
|
||||||
0 => return,
|
0 => return,
|
||||||
else => |err| return @as(Error, @errSetCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16
|
else => |err| return @as(Error, @errorCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1444,7 +1444,7 @@ fn renderBuiltinCall(
|
||||||
const slice = tree.tokenSlice(builtin_token);
|
const slice = tree.tokenSlice(builtin_token);
|
||||||
const rewrite_two_param_cast = params.len == 2 and for ([_][]const u8{
|
const rewrite_two_param_cast = params.len == 2 and for ([_][]const u8{
|
||||||
"@bitCast",
|
"@bitCast",
|
||||||
"@errSetCast",
|
"@errorCast",
|
||||||
"@floatCast",
|
"@floatCast",
|
||||||
"@intCast",
|
"@intCast",
|
||||||
"@ptrCast",
|
"@ptrCast",
|
||||||
|
|
@ -1505,6 +1505,8 @@ fn renderBuiltinCall(
|
||||||
try ais.writer().writeAll("@intFromPtr");
|
try ais.writer().writeAll("@intFromPtr");
|
||||||
} else if (mem.eql(u8, slice, "@fabs")) {
|
} else if (mem.eql(u8, slice, "@fabs")) {
|
||||||
try ais.writer().writeAll("@abs");
|
try ais.writer().writeAll("@abs");
|
||||||
|
} else if (mem.eql(u8, slice, "@errSetCast")) {
|
||||||
|
try ais.writer().writeAll("@errorCast");
|
||||||
} else {
|
} else {
|
||||||
try renderToken(ais, tree, builtin_token, .none); // @name
|
try renderToken(ais, tree, builtin_token, .none); // @name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8454,11 +8454,11 @@ fn builtinCall(
|
||||||
});
|
});
|
||||||
return rvalue(gz, ri, result, node);
|
return rvalue(gz, ri, result, node);
|
||||||
},
|
},
|
||||||
.err_set_cast => {
|
.error_cast => {
|
||||||
try emitDbgNode(gz, node);
|
try emitDbgNode(gz, node);
|
||||||
|
|
||||||
const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{
|
const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{
|
||||||
.lhs = try ri.rl.resultTypeForCast(gz, node, "@errSetCast"),
|
.lhs = try ri.rl.resultTypeForCast(gz, node, "@errorCast"),
|
||||||
.rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
|
.rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
|
||||||
.node = gz.nodeIndexToRelative(node),
|
.node = gz.nodeIndexToRelative(node),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -945,7 +945,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
|
||||||
.float_cast,
|
.float_cast,
|
||||||
.int_cast,
|
.int_cast,
|
||||||
.truncate,
|
.truncate,
|
||||||
.err_set_cast,
|
.error_cast,
|
||||||
.ptr_cast,
|
.ptr_cast,
|
||||||
.align_cast,
|
.align_cast,
|
||||||
.addrspace_cast,
|
.addrspace_cast,
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ pub const Tag = enum {
|
||||||
error_name,
|
error_name,
|
||||||
error_return_trace,
|
error_return_trace,
|
||||||
int_from_error,
|
int_from_error,
|
||||||
err_set_cast,
|
error_cast,
|
||||||
@"export",
|
@"export",
|
||||||
@"extern",
|
@"extern",
|
||||||
fence,
|
fence,
|
||||||
|
|
@ -455,9 +455,9 @@ pub const list = list: {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
"@errSetCast",
|
"@errorCast",
|
||||||
.{
|
.{
|
||||||
.tag = .err_set_cast,
|
.tag = .error_cast,
|
||||||
.eval_to_error = .always,
|
.eval_to_error = .always,
|
||||||
.param_count = 1,
|
.param_count = 1,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
68
src/Sema.zig
68
src/Sema.zig
|
|
@ -1252,7 +1252,7 @@ fn analyzeBodyInner(
|
||||||
.wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
|
.wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
|
||||||
.wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
|
.wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
|
||||||
.prefetch => try sema.zirPrefetch( block, extended),
|
.prefetch => try sema.zirPrefetch( block, extended),
|
||||||
.err_set_cast => try sema.zirErrSetCast( block, extended),
|
.error_cast => try sema.zirErrorCast( block, extended),
|
||||||
.await_nosuspend => try sema.zirAwaitNosuspend( block, extended),
|
.await_nosuspend => try sema.zirAwaitNosuspend( block, extended),
|
||||||
.select => try sema.zirSelect( block, extended),
|
.select => try sema.zirSelect( block, extended),
|
||||||
.int_from_error => try sema.zirIntFromError( block, extended),
|
.int_from_error => try sema.zirIntFromError( block, extended),
|
||||||
|
|
@ -21747,17 +21747,31 @@ fn ptrFromIntVal(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
|
fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
|
||||||
const mod = sema.mod;
|
const mod = sema.mod;
|
||||||
const ip = &mod.intern_pool;
|
const ip = &mod.intern_pool;
|
||||||
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
|
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
|
||||||
const src = LazySrcLoc.nodeOffset(extra.node);
|
const src = LazySrcLoc.nodeOffset(extra.node);
|
||||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
|
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
|
||||||
const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@errSetCast");
|
const base_dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast");
|
||||||
const operand = try sema.resolveInst(extra.rhs);
|
const operand = try sema.resolveInst(extra.rhs);
|
||||||
const operand_ty = sema.typeOf(operand);
|
const base_operand_ty = sema.typeOf(operand);
|
||||||
try sema.checkErrorSetType(block, src, dest_ty);
|
const dest_tag = base_dest_ty.zigTypeTag(mod);
|
||||||
try sema.checkErrorSetType(block, operand_src, operand_ty);
|
const operand_tag = base_operand_ty.zigTypeTag(mod);
|
||||||
|
if (dest_tag != operand_tag) {
|
||||||
|
return sema.fail(block, src, "expected source and destination types to match, found '{s}' and '{s}'", .{
|
||||||
|
@tagName(operand_tag), @tagName(dest_tag),
|
||||||
|
});
|
||||||
|
} else if (dest_tag != .ErrorSet and dest_tag != .ErrorUnion) {
|
||||||
|
return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)});
|
||||||
|
}
|
||||||
|
const dest_ty, const operand_ty = if (dest_tag == .ErrorUnion) .{
|
||||||
|
base_dest_ty.errorUnionSet(mod),
|
||||||
|
base_operand_ty.errorUnionSet(mod),
|
||||||
|
} else .{
|
||||||
|
base_dest_ty,
|
||||||
|
base_operand_ty,
|
||||||
|
};
|
||||||
|
|
||||||
// operand must be defined since it can be an invalid error value
|
// operand must be defined since it can be an invalid error value
|
||||||
const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maybe_operand_val) |val| {
|
if (maybe_operand_val) |val| {
|
||||||
if (!dest_ty.isAnyError(mod)) {
|
if (!dest_ty.isAnyError(mod)) check: {
|
||||||
const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name;
|
const operand_val = mod.intern_pool.indexToKey(val.toIntern());
|
||||||
|
var error_name: InternPool.NullTerminatedString = undefined;
|
||||||
|
if (dest_tag == .ErrorUnion) {
|
||||||
|
if (operand_val.error_union.val != .err_name) break :check;
|
||||||
|
error_name = operand_val.error_union.val.err_name;
|
||||||
|
} else {
|
||||||
|
error_name = operand_val.err.name;
|
||||||
|
}
|
||||||
if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {
|
if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {
|
||||||
const msg = msg: {
|
const msg = msg: {
|
||||||
const msg = try sema.errMsg(
|
const msg = try sema.errMsg(
|
||||||
|
|
@ -21822,16 +21843,29 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Air.internedToRef((try mod.getCoerced(val, dest_ty)).toIntern());
|
return Air.internedToRef((try mod.getCoerced(val, base_dest_ty)).toIntern());
|
||||||
}
|
}
|
||||||
|
|
||||||
try sema.requireRuntimeBlock(block, src, operand_src);
|
try sema.requireRuntimeBlock(block, src, operand_src);
|
||||||
if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {
|
if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {
|
||||||
const err_int_inst = try block.addBitCast(Type.err_int, operand);
|
if (dest_tag == .ErrorUnion) {
|
||||||
const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
|
const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand);
|
||||||
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
|
const err_int = try block.addBitCast(Type.err_int, err_code);
|
||||||
|
const zero_u16 = Air.internedToRef(try mod.intern(.{
|
||||||
|
.int = .{ .ty = .u16_type, .storage = .{ .u64 = 0 } },
|
||||||
|
}));
|
||||||
|
|
||||||
|
const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code);
|
||||||
|
const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_u16);
|
||||||
|
const ok = try block.addBinOp(.bit_or, has_value, is_zero);
|
||||||
|
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
|
||||||
|
} else {
|
||||||
|
const err_int_inst = try block.addBitCast(Type.err_int, operand);
|
||||||
|
const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
|
||||||
|
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return block.addBitCast(dest_ty, operand);
|
return block.addBitCast(base_dest_ty, operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
|
fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
|
||||||
|
|
@ -22916,14 +22950,6 @@ fn checkIntOrVectorAllowComptime(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
|
|
||||||
const mod = sema.mod;
|
|
||||||
switch (ty.zigTypeTag(mod)) {
|
|
||||||
.ErrorSet => return,
|
|
||||||
else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SimdBinOp = struct {
|
const SimdBinOp = struct {
|
||||||
len: ?usize,
|
len: ?usize,
|
||||||
/// Coerced to `result_ty`.
|
/// Coerced to `result_ty`.
|
||||||
|
|
|
||||||
|
|
@ -1997,9 +1997,9 @@ pub const Inst = struct {
|
||||||
/// Implements `@setCold`.
|
/// Implements `@setCold`.
|
||||||
/// `operand` is payload index to `UnNode`.
|
/// `operand` is payload index to `UnNode`.
|
||||||
set_cold,
|
set_cold,
|
||||||
/// Implements the `@errSetCast` builtin.
|
/// Implements the `@errorCast` builtin.
|
||||||
/// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
|
/// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
|
||||||
err_set_cast,
|
error_cast,
|
||||||
/// `operand` is payload index to `UnNode`.
|
/// `operand` is payload index to `UnNode`.
|
||||||
await_nosuspend,
|
await_nosuspend,
|
||||||
/// Implements `@breakpoint`.
|
/// Implements `@breakpoint`.
|
||||||
|
|
|
||||||
|
|
@ -594,7 +594,7 @@ const Writer = struct {
|
||||||
|
|
||||||
.builtin_extern,
|
.builtin_extern,
|
||||||
.c_define,
|
.c_define,
|
||||||
.err_set_cast,
|
.error_cast,
|
||||||
.wasm_memory_grow,
|
.wasm_memory_grow,
|
||||||
.prefetch,
|
.prefetch,
|
||||||
.c_va_arg,
|
.c_va_arg,
|
||||||
|
|
|
||||||
|
|
@ -228,13 +228,29 @@ const Set1 = error{ A, B };
|
||||||
const Set2 = error{ A, C };
|
const Set2 = error{ A, C };
|
||||||
|
|
||||||
fn testExplicitErrorSetCast(set1: Set1) !void {
|
fn testExplicitErrorSetCast(set1: Set1) !void {
|
||||||
var x = @as(Set2, @errSetCast(set1));
|
var x = @as(Set2, @errorCast(set1));
|
||||||
try expect(@TypeOf(x) == Set2);
|
try expect(@TypeOf(x) == Set2);
|
||||||
var y = @as(Set1, @errSetCast(x));
|
var y = @as(Set1, @errorCast(x));
|
||||||
try expect(@TypeOf(y) == Set1);
|
try expect(@TypeOf(y) == Set1);
|
||||||
try expect(y == error.A);
|
try expect(y == error.A);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "@errorCast on error unions" {
|
||||||
|
const S = struct {
|
||||||
|
fn doTheTest() !void {
|
||||||
|
const casted: error{Bad}!i32 = @errorCast(retErrUnion());
|
||||||
|
try expect((try casted) == 1234);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retErrUnion() anyerror!i32 {
|
||||||
|
return 1234;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try S.doTheTest();
|
||||||
|
try comptime S.doTheTest();
|
||||||
|
}
|
||||||
|
|
||||||
test "comptime test error for empty error set" {
|
test "comptime test error for empty error set" {
|
||||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ const Set1 = error{ A, B };
|
||||||
const Set2 = error{ A, C };
|
const Set2 = error{ A, C };
|
||||||
comptime {
|
comptime {
|
||||||
var x = Set1.B;
|
var x = Set1.B;
|
||||||
var y: Set2 = @errSetCast(x);
|
var y: Set2 = @errorCast(x);
|
||||||
_ = y;
|
_ = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ const Set2 = error{
|
||||||
};
|
};
|
||||||
comptime {
|
comptime {
|
||||||
var x = @intFromError(Set1.B);
|
var x = @intFromError(Set1.B);
|
||||||
var y: Set2 = @errSetCast(@errorFromInt(x));
|
var y: Set2 = @errorCast(@errorFromInt(x));
|
||||||
_ = y;
|
_ = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ pub fn main() !void {
|
||||||
return error.TestFailed;
|
return error.TestFailed;
|
||||||
}
|
}
|
||||||
fn foo(set1: Set1) Set2 {
|
fn foo(set1: Set1) Set2 {
|
||||||
return @errSetCast(set1);
|
return @errorCast(set1);
|
||||||
}
|
}
|
||||||
// run
|
// run
|
||||||
// backend=llvm
|
// backend=llvm
|
||||||
Loading…
Add table
Reference in a new issue