Skip to content

Commit

Permalink
remove error to/from int casting syntax; add @errorToInt/@intToError
Browse files Browse the repository at this point in the history
See #1061
  • Loading branch information
andrewrk committed Jun 18, 2018
1 parent a430853 commit 626b73e
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 60 deletions.
48 changes: 42 additions & 6 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -4658,7 +4658,7 @@ comptime {
</p>
<p>
Attempting to convert a number of bytes with a length that does not evenly divide into a slice of
elements results in {#link|Undefined Behavior#}.
elements results in safety-protected {#link|Undefined Behavior#}.
</p>
{#header_close#}

Expand Down Expand Up @@ -4935,7 +4935,7 @@ test "main" {
<pre><code class="zig">@errSetCast(comptime T: DestType, value: var) DestType</code></pre>
<p>
Converts an error value from one error set to another error set. Attempting to convert an error
which is not in the destination error set results in {#link|Undefined Behavior#}.
which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.
</p>
{#header_close#}

Expand All @@ -4955,6 +4955,7 @@ test "main" {
error name table will be generated.
</p>
{#header_close#}

{#header_open|@errorReturnTrace#}
<pre><code class="zig">@errorReturnTrace() ?*builtin.StackTrace</code></pre>
<p>
Expand All @@ -4964,6 +4965,25 @@ test "main" {
</p>
{#header_close#}

{#header_open|@errorToInt#}
<pre><code class="zig">@errorToInt(err: var) @IntType(false, @sizeOf(error) * 8)</code></pre>
<p>
Supports the following types:
</p>
<ul>
<li>error unions</li>
<li><code>E!void</code></li>
</ul>
<p>
Converts an error to the integer representation of an error.
</p>
<p>
It is generally recommended to avoid this
cast, as the integer representation of an error is not stable across source code changes.
</p>
{#see_also|@intToError#}
{#header_close#}

{#header_open|@export#}
<pre><code class="zig">@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8</code></pre>
<p>
Expand Down Expand Up @@ -5071,8 +5091,24 @@ fn add(a: i32, b: i32) i32 { return a + b; }
<p>
Converts an integer to another integer while keeping the same numerical value.
Attempting to convert a number which is out of range of the destination type results in
{#link|Undefined Behavior#}.
safety-protected {#link|Undefined Behavior#}.
</p>
{#header_close#}

{#header_open|@intToError#}
<pre><code class="zig">@intToError(value: @IntType(false, @sizeOf(error) * 8)) error</code></pre>
<p>
Converts from the integer representation of an error into the global error set type.
</p>
<p>
It is generally recommended to avoid this
cast, as the integer representation of an error is not stable across source code changes.
</p>
<p>
Attempting to convert an integer that does not correspond to any error results in
safety-protected {#link|Undefined Behavior#}.
</p>
{#see_also|@errorToInt#}
{#header_close#}

{#header_open|@intToFloat#}
Expand Down Expand Up @@ -6123,8 +6159,8 @@ fn getNumberOrFail() !i32 {
{#code_begin|test_err|integer value 11 represents no error#}
comptime {
const err = error.AnError;
const number = u32(err) + 10;
const invalid_err = error(number);
const number = @errorToInt(err) + 10;
const invalid_err = @intToError(number);
}
{#code_end#}
<p>At runtime crashes with the message <code>invalid error code</code> and a stack trace.</p>
Expand Down Expand Up @@ -6831,7 +6867,7 @@ hljs.registerLanguage("zig", function(t) {
a = t.IR + "\\s*\\(",
c = {
keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse",
built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall",
built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError",
literal: "true false null undefined"
},
n = [e, t.CLCM, t.CBCM, s, r];
Expand Down
2 changes: 2 additions & 0 deletions src/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1376,6 +1376,8 @@ enum BuiltinFnId {
BuiltinFnIdIntToFloat,
BuiltinFnIdFloatToInt,
BuiltinFnIdBoolToInt,
BuiltinFnIdErrToInt,
BuiltinFnIdIntToErr,
BuiltinFnIdIntType,
BuiltinFnIdSetCold,
BuiltinFnIdSetRuntimeSafety,
Expand Down
2 changes: 2 additions & 0 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6323,6 +6323,8 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2);
create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2);
create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1);
create_builtin_fn(g, BuiltinFnIdErrToInt, "errorToInt", 1);
create_builtin_fn(g, BuiltinFnIdIntToErr, "intToError", 1);
create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int
Expand Down
74 changes: 57 additions & 17 deletions src/ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4167,6 +4167,26 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdErrToInt:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;

IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdIntToErr:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;

IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval);
}
case BuiltinFnIdBoolToInt:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
Expand Down Expand Up @@ -10465,21 +10485,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
}

// explicit cast from T!void to integer type which can fit it
bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
!type_has_bits(actual_type->data.error_union.payload_type);
bool actual_type_is_err_set = actual_type->id == TypeTableEntryIdErrorSet;
if ((actual_type_is_void_err || actual_type_is_err_set) && wanted_type->id == TypeTableEntryIdInt) {
return ir_analyze_err_to_int(ira, source_instr, value, wanted_type);
}

// explicit cast from integer to error set
if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdInt &&
!actual_type->data.integral.is_signed)
{
return ir_analyze_int_to_err(ira, source_instr, value, wanted_type);
}

// explicit cast from integer to enum type with no payload
if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum) {
return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type);
Expand Down Expand Up @@ -17785,6 +17790,39 @@ static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrIns
return dest_type;
}

static TypeTableEntry *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) {
IrInstruction *target = instruction->target->other;
if (type_is_invalid(target->value.type))
return ira->codegen->builtin_types.entry_invalid;

IrInstruction *casted_target;
if (target->value.type->id == TypeTableEntryIdErrorSet) {
casted_target = target;
} else {
casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set);
if (type_is_invalid(casted_target->value.type))
return ira->codegen->builtin_types.entry_invalid;
}

IrInstruction *result = ir_analyze_err_to_int(ira, &instruction->base, casted_target, ira->codegen->err_tag_type);
ir_link_new_instruction(result, &instruction->base);
return result->value.type;
}

static TypeTableEntry *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstructionIntToErr *instruction) {
IrInstruction *target = instruction->target->other;
if (type_is_invalid(target->value.type))
return ira->codegen->builtin_types.entry_invalid;

IrInstruction *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type);
if (type_is_invalid(casted_target->value.type))
return ira->codegen->builtin_types.entry_invalid;

IrInstruction *result = ir_analyze_int_to_err(ira, &instruction->base, casted_target, ira->codegen->builtin_types.entry_global_error_set);
ir_link_new_instruction(result, &instruction->base);
return result->value.type;
}

static TypeTableEntry *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) {
IrInstruction *target = instruction->target->other;
if (type_is_invalid(target->value.type))
Expand Down Expand Up @@ -20229,8 +20267,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
case IrInstructionIdInvalid:
case IrInstructionIdWidenOrShorten:
case IrInstructionIdIntToEnum:
case IrInstructionIdIntToErr:
case IrInstructionIdErrToInt:
case IrInstructionIdStructInit:
case IrInstructionIdUnionInit:
case IrInstructionIdStructFieldPtr:
Expand Down Expand Up @@ -20491,6 +20527,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
case IrInstructionIdSqrt:
return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
case IrInstructionIdIntToErr:
return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction);
case IrInstructionIdErrToInt:
return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction);
}
zig_unreachable();
}
Expand Down
4 changes: 2 additions & 2 deletions std/os/child_process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ pub const ChildProcess = struct {
// Here we potentially return the fork child's error
// from the parent pid.
if (err_int != @maxValue(ErrInt)) {
return SpawnError(err_int);
return @errSetCast(SpawnError, @intToError(err_int));
}

return statusToTerm(status);
Expand Down Expand Up @@ -756,7 +756,7 @@ fn destroyPipe(pipe: *const [2]i32) void {
// Child of fork calls this to report an error to the fork parent.
// Then the child exits.
fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
_ = writeIntFd(fd, ErrInt(err));
_ = writeIntFd(fd, ErrInt(@errorToInt(err)));
posix.exit(1);
}

Expand Down
4 changes: 2 additions & 2 deletions test/cases/cast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ test "explicit cast from integer to error type" {
comptime testCastIntToErr(error.ItBroke);
}
fn testCastIntToErr(err: error) void {
const x = usize(err);
const y = error(x);
const x = @errorToInt(err);
const y = @intToError(x);
assert(error.ItBroke == y);
}

Expand Down
10 changes: 5 additions & 5 deletions test/cases/error.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ test "@errorName" {
}

test "error values" {
const a = i32(error.err1);
const b = i32(error.err2);
const a = @errorToInt(error.err1);
const b = @errorToInt(error.err2);
assert(a != b);
}

Expand Down Expand Up @@ -147,14 +147,14 @@ test "syntax: optional operator in front of error union operator" {
}

test "comptime err to int of error set with only 1 possible value" {
testErrToIntWithOnePossibleValue(error.A, u32(error.A));
comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A));
testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
}
fn testErrToIntWithOnePossibleValue(
x: error{A},
comptime value: u32,
) void {
if (u32(x) != value) {
if (@errorToInt(x) != value) {
@compileError("bad");
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/cases/type_info.zig
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ fn testErrorSet() void {
assert(TypeId(error_set_info) == TypeId.ErrorSet);
assert(error_set_info.ErrorSet.errors.len == 3);
assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First"));
assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third));
assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third));

const error_union_info = @typeInfo(TestErrorSet!usize);
assert(TypeId(error_union_info) == TypeId.ErrorUnion);
Expand Down
38 changes: 18 additions & 20 deletions test/compile_errors.zig
Original file line number Diff line number Diff line change
Expand Up @@ -467,25 +467,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {

cases.add(
"int to err global invalid number",
\\const Set1 = error{A, B};
\\const Set1 = error{
\\ A,
\\ B,
\\};
\\comptime {
\\ var x: usize = 3;
\\ var y = error(x);
\\ var x: u16 = 3;
\\ var y = @intToError(x);
\\}
,
".tmp_source.zig:4:18: error: integer value 3 represents no error",
".tmp_source.zig:7:13: error: integer value 3 represents no error",
);

cases.add(
"int to err non global invalid number",
\\const Set1 = error{A, B};
\\const Set2 = error{A, C};
\\const Set1 = error{
\\ A,
\\ B,
\\};
\\const Set2 = error{
\\ A,
\\ C,
\\};
\\comptime {
\\ var x = usize(Set1.B);
\\ var y = Set2(x);
\\ var x = @errorToInt(Set1.B);
\\ var y = @errSetCast(Set2, @intToError(x));
\\}
,
".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'",
".tmp_source.zig:11:13: error: error.B not a member of error set 'Set2'",
);

cases.add(
Expand Down Expand Up @@ -2612,17 +2621,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
".tmp_source.zig:2:21: error: expected pointer, found 'usize'",
);

cases.add(
"too many error values to cast to small integer",
\\const Error = error { A, B, C, D, E, F, G, H };
\\fn foo(e: Error) u2 {
\\ return u2(e);
\\}
\\export fn entry() usize { return @sizeOf(@typeOf(foo)); }
,
".tmp_source.zig:3:14: error: too many error values to fit in 'u2'",
);

cases.add(
"asm at compile time",
\\comptime {
Expand Down
Loading

0 comments on commit 626b73e

Please sign in to comment.