authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-18 18:48:29-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-18 18:48:29-04:00
log626b73e8beeaae1cab23f883f877d89d64bbfa39
tree4ed0ee57e00134f34656e759282b5c0e771726b2
parenta430853a48a5e4dbcd0094c632957e28898159f3

remove error to/from int casting syntax; add `@errorToInt`/`@intToError`

See #1061

10 files changed, 138 insertions(+), 60 deletions(-)

doc/langref.html.in+42-6
......@@ -4658,7 +4658,7 @@ comptime {
46584658 </p>
46594659 <p>
46604660 Attempting to convert a number of bytes with a length that does not evenly divide into a slice of
4661 elements results in {#link|Undefined Behavior#}.
4661 elements results in safety-protected {#link|Undefined Behavior#}.
46624662 </p>
46634663 {#header_close#}
46644664
......@@ -4935,7 +4935,7 @@ test "main" {
49354935 <pre><code class="zig">@errSetCast(comptime T: DestType, value: var) DestType</code></pre>
49364936 <p>
49374937 Converts an error value from one error set to another error set. Attempting to convert an error
4938 which is not in the destination error set results in {#link|Undefined Behavior#}.
4938 which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.
49394939 </p>
49404940 {#header_close#}
49414941
......@@ -4955,6 +4955,7 @@ test "main" {
49554955 error name table will be generated.
49564956 </p>
49574957 {#header_close#}
4958
49584959 {#header_open|@errorReturnTrace#}
49594960 <pre><code class="zig">@errorReturnTrace() ?*builtin.StackTrace</code></pre>
49604961 <p>
......@@ -4964,6 +4965,25 @@ test "main" {
49644965 </p>
49654966 {#header_close#}
49664967
4968 {#header_open|@errorToInt#}
4969 <pre><code class="zig">@errorToInt(err: var) @IntType(false, @sizeOf(error) * 8)</code></pre>
4970 <p>
4971 Supports the following types:
4972 </p>
4973 <ul>
4974 <li>error unions</li>
4975 <li><code>E!void</code></li>
4976 </ul>
4977 <p>
4978 Converts an error to the integer representation of an error.
4979 </p>
4980 <p>
4981 It is generally recommended to avoid this
4982 cast, as the integer representation of an error is not stable across source code changes.
4983 </p>
4984 {#see_also|@intToError#}
4985 {#header_close#}
4986
49674987 {#header_open|@export#}
49684988 <pre><code class="zig">@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8</code></pre>
49694989 <p>
......@@ -5071,8 +5091,24 @@ fn add(a: i32, b: i32) i32 { return a + b; }
50715091 <p>
50725092 Converts an integer to another integer while keeping the same numerical value.
50735093 Attempting to convert a number which is out of range of the destination type results in
5074 {#link|Undefined Behavior#}.
5094 safety-protected {#link|Undefined Behavior#}.
5095 </p>
5096 {#header_close#}
5097
5098 {#header_open|@intToError#}
5099 <pre><code class="zig">@intToError(value: @IntType(false, @sizeOf(error) * 8)) error</code></pre>
5100 <p>
5101 Converts from the integer representation of an error into the global error set type.
5102 </p>
5103 <p>
5104 It is generally recommended to avoid this
5105 cast, as the integer representation of an error is not stable across source code changes.
5106 </p>
5107 <p>
5108 Attempting to convert an integer that does not correspond to any error results in
5109 safety-protected {#link|Undefined Behavior#}.
50755110 </p>
5111 {#see_also|@errorToInt#}
50765112 {#header_close#}
50775113
50785114 {#header_open|@intToFloat#}
......@@ -6123,8 +6159,8 @@ fn getNumberOrFail() !i32 {
61236159 {#code_begin|test_err|integer value 11 represents no error#}
61246160comptime {
61256161 const err = error.AnError;
6126 const number = u32(err) + 10;
6127 const invalid_err = error(number);
6162 const number = @errorToInt(err) + 10;
6163 const invalid_err = @intToError(number);
61286164}
61296165 {#code_end#}
61306166 <p>At runtime crashes with the message <code>invalid error code</code> and a stack trace.</p>
......@@ -6831,7 +6867,7 @@ hljs.registerLanguage("zig", function(t) {
68316867 a = t.IR + "\\s*\\(",
68326868 c = {
68336869 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",
6834 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",
6870 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",
68356871 literal: "true false null undefined"
68366872 },
68376873 n = [e, t.CLCM, t.CBCM, s, r];
src/all_types.hpp+2
......@@ -1376,6 +1376,8 @@ enum BuiltinFnId {
13761376 BuiltinFnIdIntToFloat,
13771377 BuiltinFnIdFloatToInt,
13781378 BuiltinFnIdBoolToInt,
1379 BuiltinFnIdErrToInt,
1380 BuiltinFnIdIntToErr,
13791381 BuiltinFnIdIntType,
13801382 BuiltinFnIdSetCold,
13811383 BuiltinFnIdSetRuntimeSafety,
src/codegen.cpp+2
......@@ -6323,6 +6323,8 @@ static void define_builtin_fns(CodeGen *g) {
63236323 create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2);
63246324 create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2);
63256325 create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1);
6326 create_builtin_fn(g, BuiltinFnIdErrToInt, "errorToInt", 1);
6327 create_builtin_fn(g, BuiltinFnIdIntToErr, "intToError", 1);
63266328 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
63276329 create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
63286330 create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int
src/ir.cpp+57-17
......@@ -4167,6 +4167,26 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
41674167 IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value);
41684168 return ir_lval_wrap(irb, scope, result, lval);
41694169 }
4170 case BuiltinFnIdErrToInt:
4171 {
4172 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4173 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4174 if (arg0_value == irb->codegen->invalid_instruction)
4175 return arg0_value;
4176
4177 IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value);
4178 return ir_lval_wrap(irb, scope, result, lval);
4179 }
4180 case BuiltinFnIdIntToErr:
4181 {
4182 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4183 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4184 if (arg0_value == irb->codegen->invalid_instruction)
4185 return arg0_value;
4186
4187 IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value);
4188 return ir_lval_wrap(irb, scope, result, lval);
4189 }
41704190 case BuiltinFnIdBoolToInt:
41714191 {
41724192 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
......@@ -10465,21 +10485,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1046510485 return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
1046610486 }
1046710487
10468 // explicit cast from T!void to integer type which can fit it
10469 bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
10470 !type_has_bits(actual_type->data.error_union.payload_type);
10471 bool actual_type_is_err_set = actual_type->id == TypeTableEntryIdErrorSet;
10472 if ((actual_type_is_void_err || actual_type_is_err_set) && wanted_type->id == TypeTableEntryIdInt) {
10473 return ir_analyze_err_to_int(ira, source_instr, value, wanted_type);
10474 }
10475
10476 // explicit cast from integer to error set
10477 if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdInt &&
10478 !actual_type->data.integral.is_signed)
10479 {
10480 return ir_analyze_int_to_err(ira, source_instr, value, wanted_type);
10481 }
10482
1048310488 // explicit cast from integer to enum type with no payload
1048410489 if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum) {
1048510490 return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type);
......@@ -17785,6 +17790,39 @@ static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrIns
1778517790 return dest_type;
1778617791}
1778717792
17793static TypeTableEntry *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) {
17794 IrInstruction *target = instruction->target->other;
17795 if (type_is_invalid(target->value.type))
17796 return ira->codegen->builtin_types.entry_invalid;
17797
17798 IrInstruction *casted_target;
17799 if (target->value.type->id == TypeTableEntryIdErrorSet) {
17800 casted_target = target;
17801 } else {
17802 casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set);
17803 if (type_is_invalid(casted_target->value.type))
17804 return ira->codegen->builtin_types.entry_invalid;
17805 }
17806
17807 IrInstruction *result = ir_analyze_err_to_int(ira, &instruction->base, casted_target, ira->codegen->err_tag_type);
17808 ir_link_new_instruction(result, &instruction->base);
17809 return result->value.type;
17810}
17811
17812static TypeTableEntry *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstructionIntToErr *instruction) {
17813 IrInstruction *target = instruction->target->other;
17814 if (type_is_invalid(target->value.type))
17815 return ira->codegen->builtin_types.entry_invalid;
17816
17817 IrInstruction *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type);
17818 if (type_is_invalid(casted_target->value.type))
17819 return ira->codegen->builtin_types.entry_invalid;
17820
17821 IrInstruction *result = ir_analyze_int_to_err(ira, &instruction->base, casted_target, ira->codegen->builtin_types.entry_global_error_set);
17822 ir_link_new_instruction(result, &instruction->base);
17823 return result->value.type;
17824}
17825
1778817826static TypeTableEntry *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) {
1778917827 IrInstruction *target = instruction->target->other;
1779017828 if (type_is_invalid(target->value.type))
......@@ -20229,8 +20267,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
2022920267 case IrInstructionIdInvalid:
2023020268 case IrInstructionIdWidenOrShorten:
2023120269 case IrInstructionIdIntToEnum:
20232 case IrInstructionIdIntToErr:
20233 case IrInstructionIdErrToInt:
2023420270 case IrInstructionIdStructInit:
2023520271 case IrInstructionIdUnionInit:
2023620272 case IrInstructionIdStructFieldPtr:
......@@ -20491,6 +20527,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
2049120527 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
2049220528 case IrInstructionIdSqrt:
2049320529 return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
20530 case IrInstructionIdIntToErr:
20531 return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction);
20532 case IrInstructionIdErrToInt:
20533 return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction);
2049420534 }
2049520535 zig_unreachable();
2049620536}
std/os/child_process.zig+2-2
......@@ -318,7 +318,7 @@ pub const ChildProcess = struct {
318318 // Here we potentially return the fork child's error
319319 // from the parent pid.
320320 if (err_int != @maxValue(ErrInt)) {
321 return SpawnError(err_int);
321 return @errSetCast(SpawnError, @intToError(err_int));
322322 }
323323
324324 return statusToTerm(status);
......@@ -756,7 +756,7 @@ fn destroyPipe(pipe: *const [2]i32) void {
756756// Child of fork calls this to report an error to the fork parent.
757757// Then the child exits.
758758fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
759 _ = writeIntFd(fd, ErrInt(err));
759 _ = writeIntFd(fd, ErrInt(@errorToInt(err)));
760760 posix.exit(1);
761761}
762762
test/cases/cast.zig+2-2
......@@ -140,8 +140,8 @@ test "explicit cast from integer to error type" {
140140 comptime testCastIntToErr(error.ItBroke);
141141}
142142fn testCastIntToErr(err: error) void {
143 const x = usize(err);
144 const y = error(x);
143 const x = @errorToInt(err);
144 const y = @intToError(x);
145145 assert(error.ItBroke == y);
146146}
147147
test/cases/error.zig+5-5
......@@ -31,8 +31,8 @@ test "@errorName" {
3131}
3232
3333test "error values" {
34 const a = i32(error.err1);
35 const b = i32(error.err2);
34 const a = @errorToInt(error.err1);
35 const b = @errorToInt(error.err2);
3636 assert(a != b);
3737}
3838
......@@ -147,14 +147,14 @@ test "syntax: optional operator in front of error union operator" {
147147}
148148
149149test "comptime err to int of error set with only 1 possible value" {
150 testErrToIntWithOnePossibleValue(error.A, u32(error.A));
151 comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A));
150 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
151 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
152152}
153153fn testErrToIntWithOnePossibleValue(
154154 x: error{A},
155155 comptime value: u32,
156156) void {
157 if (u32(x) != value) {
157 if (@errorToInt(x) != value) {
158158 @compileError("bad");
159159 }
160160}
test/cases/type_info.zig+1-1
......@@ -130,7 +130,7 @@ fn testErrorSet() void {
130130 assert(TypeId(error_set_info) == TypeId.ErrorSet);
131131 assert(error_set_info.ErrorSet.errors.len == 3);
132132 assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First"));
133 assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third));
133 assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third));
134134
135135 const error_union_info = @typeInfo(TestErrorSet!usize);
136136 assert(TypeId(error_union_info) == TypeId.ErrorUnion);
test/compile_errors.zig+18-20
......@@ -467,25 +467,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
467467
468468 cases.add(
469469 "int to err global invalid number",
470 \\const Set1 = error{A, B};
470 \\const Set1 = error{
471 \\ A,
472 \\ B,
473 \\};
471474 \\comptime {
472 \\ var x: usize = 3;
473 \\ var y = error(x);
475 \\ var x: u16 = 3;
476 \\ var y = @intToError(x);
474477 \\}
475478 ,
476 ".tmp_source.zig:4:18: error: integer value 3 represents no error",
479 ".tmp_source.zig:7:13: error: integer value 3 represents no error",
477480 );
478481
479482 cases.add(
480483 "int to err non global invalid number",
481 \\const Set1 = error{A, B};
482 \\const Set2 = error{A, C};
484 \\const Set1 = error{
485 \\ A,
486 \\ B,
487 \\};
488 \\const Set2 = error{
489 \\ A,
490 \\ C,
491 \\};
483492 \\comptime {
484 \\ var x = usize(Set1.B);
485 \\ var y = Set2(x);
493 \\ var x = @errorToInt(Set1.B);
494 \\ var y = @errSetCast(Set2, @intToError(x));
486495 \\}
487496 ,
488 ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'",
497 ".tmp_source.zig:11:13: error: error.B not a member of error set 'Set2'",
489498 );
490499
491500 cases.add(
......@@ -2612,17 +2621,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
26122621 ".tmp_source.zig:2:21: error: expected pointer, found 'usize'",
26132622 );
26142623
2615 cases.add(
2616 "too many error values to cast to small integer",
2617 \\const Error = error { A, B, C, D, E, F, G, H };
2618 \\fn foo(e: Error) u2 {
2619 \\ return u2(e);
2620 \\}
2621 \\export fn entry() usize { return @sizeOf(@typeOf(foo)); }
2622 ,
2623 ".tmp_source.zig:3:14: error: too many error values to fit in 'u2'",
2624 );
2625
26262624 cases.add(
26272625 "asm at compile time",
26282626 \\comptime {
test/runtime_safety.zig+7-7
......@@ -175,7 +175,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
175175 \\ if (x.len == 0) return error.Whatever;
176176 \\}
177177 \\fn widenSlice(slice: []align(1) const u8) []align(1) const i32 {
178 \\ return ([]align(1) const i32)(slice);
178 \\ return @bytesToSlice(i32, slice);
179179 \\}
180180 );
181181
......@@ -227,12 +227,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
227227 \\pub fn main() void {
228228 \\ _ = bar(9999);
229229 \\}
230 \\fn bar(x: u32) error {
231 \\ return error(x);
230 \\fn bar(x: u16) error {
231 \\ return @intToError(x);
232232 \\}
233233 );
234234
235 cases.addRuntimeSafety("cast integer to non-global error set and no match",
235 cases.addRuntimeSafety("@errSetCast error not present in destination",
236236 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
237237 \\ @import("std").os.exit(126);
238238 \\}
......@@ -242,7 +242,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
242242 \\ _ = foo(Set1.B);
243243 \\}
244244 \\fn foo(set1: Set1) Set2 {
245 \\ return Set2(set1);
245 \\ return @errSetCast(Set2, set1);
246246 \\}
247247 );
248248
......@@ -252,12 +252,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
252252 \\}
253253 \\pub fn main() !void {
254254 \\ var array align(4) = []u32{0x11111111, 0x11111111};
255 \\ const bytes = ([]u8)(array[0..]);
255 \\ const bytes = @sliceToBytes(array[0..]);
256256 \\ if (foo(bytes) != 0x11111111) return error.Wrong;
257257 \\}
258258 \\fn foo(bytes: []u8) u32 {
259259 \\ const slice4 = bytes[1..5];
260 \\ const int_slice = ([]u32)(@alignCast(4, slice4));
260 \\ const int_slice = @bytesToSlice(u32, @alignCast(4, slice4));
261261 \\ return int_slice[0];
262262 \\}
263263 );