| ... | @@ -590,6 +590,7 @@ test "initialization" { | ... | @@ -590,6 +590,7 @@ test "initialization" { |
| 590 | x = 1; | 590 | x = 1; |
| 591 | } | 591 | } |
| 592 | {#code_end#} | 592 | {#code_end#} |
| | 593 | {#header_open|undefined#} |
| 593 | <p>Use <code>undefined</code> to leave variables uninitialized:</p> | 594 | <p>Use <code>undefined</code> to leave variables uninitialized:</p> |
| 594 | {#code_begin|test#} | 595 | {#code_begin|test#} |
| 595 | const assert = @import("std").debug.assert; | 596 | const assert = @import("std").debug.assert; |
| ... | @@ -602,6 +603,7 @@ test "init with undefined" { | ... | @@ -602,6 +603,7 @@ test "init with undefined" { |
| 602 | {#code_end#} | 603 | {#code_end#} |
| 603 | {#header_close#} | 604 | {#header_close#} |
| 604 | {#header_close#} | 605 | {#header_close#} |
| | 606 | {#header_close#} |
| 605 | {#header_open|Integers#} | 607 | {#header_open|Integers#} |
| 606 | {#header_open|Integer Literals#} | 608 | {#header_open|Integer Literals#} |
| 607 | {#code_begin|syntax#} | 609 | {#code_begin|syntax#} |
| ... | @@ -2999,6 +3001,7 @@ test "parse u64" { | ... | @@ -2999,6 +3001,7 @@ test "parse u64" { |
| 2999 | <li>You know with complete certainty it will not return an error, so want to unconditionally unwrap it.</li> | 3001 | <li>You know with complete certainty it will not return an error, so want to unconditionally unwrap it.</li> |
| 3000 | <li>You want to take a different action for each possible error.</li> | 3002 | <li>You want to take a different action for each possible error.</li> |
| 3001 | </ul> | 3003 | </ul> |
| | 3004 | {#header_open|catch#} |
| 3002 | <p>If you want to provide a default value, you can use the <code>catch</code> binary operator:</p> | 3005 | <p>If you want to provide a default value, you can use the <code>catch</code> binary operator:</p> |
| 3003 | {#code_begin|syntax#} | 3006 | {#code_begin|syntax#} |
| 3004 | fn doAThing(str: []u8) void { | 3007 | fn doAThing(str: []u8) void { |
| ... | @@ -3011,6 +3014,8 @@ fn doAThing(str: []u8) void { | ... | @@ -3011,6 +3014,8 @@ fn doAThing(str: []u8) void { |
| 3011 | a default value of 13. The type of the right hand side of the binary <code>catch</code> operator must | 3014 | a default value of 13. The type of the right hand side of the binary <code>catch</code> operator must |
| 3012 | match the unwrapped error union type, or be of type <code>noreturn</code>. | 3015 | match the unwrapped error union type, or be of type <code>noreturn</code>. |
| 3013 | </p> | 3016 | </p> |
| | 3017 | {#header_close#} |
| | 3018 | {#header_open|try#} |
| 3014 | <p>Let's say you wanted to return the error if you got one, otherwise continue with the | 3019 | <p>Let's say you wanted to return the error if you got one, otherwise continue with the |
| 3015 | function logic:</p> | 3020 | function logic:</p> |
| 3016 | {#code_begin|syntax#} | 3021 | {#code_begin|syntax#} |
| ... | @@ -3033,6 +3038,7 @@ fn doAThing(str: []u8) !void { | ... | @@ -3033,6 +3038,7 @@ fn doAThing(str: []u8) !void { |
| 3033 | from the current function with the same error. Otherwise, the expression results in | 3038 | from the current function with the same error. Otherwise, the expression results in |
| 3034 | the unwrapped value. | 3039 | the unwrapped value. |
| 3035 | </p> | 3040 | </p> |
| | 3041 | {#header_close#} |
| 3036 | <p> | 3042 | <p> |
| 3037 | Maybe you know with complete certainty that an expression will never be an error. | 3043 | Maybe you know with complete certainty that an expression will never be an error. |
| 3038 | In this case you can do this: | 3044 | In this case you can do this: |
| ... | @@ -3047,7 +3053,7 @@ fn doAThing(str: []u8) !void { | ... | @@ -3047,7 +3053,7 @@ fn doAThing(str: []u8) !void { |
| 3047 | </p> | 3053 | </p> |
| 3048 | <p> | 3054 | <p> |
| 3049 | Finally, you may want to take a different action for every situation. For that, we combine | 3055 | Finally, you may want to take a different action for every situation. For that, we combine |
| 3050 | the <code>if</code> and <code>switch</code> expression: | 3056 | the {#link|if#} and {#link|switch#} expression: |
| 3051 | </p> | 3057 | </p> |
| 3052 | {#code_begin|syntax#} | 3058 | {#code_begin|syntax#} |
| 3053 | fn doAThing(str: []u8) void { | 3059 | fn doAThing(str: []u8) void { |
| ... | @@ -3062,9 +3068,10 @@ fn doAThing(str: []u8) void { | ... | @@ -3062,9 +3068,10 @@ fn doAThing(str: []u8) void { |
| 3062 | } | 3068 | } |
| 3063 | } | 3069 | } |
| 3064 | {#code_end#} | 3070 | {#code_end#} |
| | 3071 | {#header_open|errdefer#} |
| 3065 | <p> | 3072 | <p> |
| 3066 | The other component to error handling is defer statements. | 3073 | The other component to error handling is defer statements. |
| 3067 | In addition to an unconditional <code>defer</code>, Zig has <code>errdefer</code>, | 3074 | In addition to an unconditional {#link|defer#}, Zig has <code>errdefer</code>, |
| 3068 | which evaluates the deferred expression on block exit path if and only if | 3075 | which evaluates the deferred expression on block exit path if and only if |
| 3069 | the function returned with an error from the block. | 3076 | the function returned with an error from the block. |
| 3070 | </p> | 3077 | </p> |
| ... | @@ -3095,6 +3102,7 @@ fn createFoo(param: i32) !Foo { | ... | @@ -3095,6 +3102,7 @@ fn createFoo(param: i32) !Foo { |
| 3095 | the verbosity and cognitive overhead of trying to make sure every exit path | 3102 | the verbosity and cognitive overhead of trying to make sure every exit path |
| 3096 | is covered. The deallocation code is always directly following the allocation code. | 3103 | is covered. The deallocation code is always directly following the allocation code. |
| 3097 | </p> | 3104 | </p> |
| | 3105 | {#header_close#} |
| 3098 | <p> | 3106 | <p> |
| 3099 | A couple of other tidbits about error handling: | 3107 | A couple of other tidbits about error handling: |
| 3100 | </p> | 3108 | </p> |
| ... | @@ -3223,7 +3231,174 @@ test "inferred error set" { | ... | @@ -3223,7 +3231,174 @@ test "inferred error set" { |
| 3223 | {#header_close#} | 3231 | {#header_close#} |
| 3224 | {#header_close#} | 3232 | {#header_close#} |
| 3225 | {#header_open|Error Return Traces#} | 3233 | {#header_open|Error Return Traces#} |
| 3226 | <p>TODO</p> | 3234 | <p> |
| | 3235 | Error Return Traces show all the points in the code that an error was returned to the calling function. This makes it practical to use {#link|try#} everywhere and then still be able to know what happened if an error ends up bubbling all the way out of your application. |
| | 3236 | </p> |
| | 3237 | {#code_begin|exe_err#} |
| | 3238 | pub fn main() !void { |
| | 3239 | try foo(12); |
| | 3240 | } |
| | 3241 | |
| | 3242 | fn foo(x: i32) !void { |
| | 3243 | if (x >= 5) { |
| | 3244 | try bar(); |
| | 3245 | } else { |
| | 3246 | try bang2(); |
| | 3247 | } |
| | 3248 | } |
| | 3249 | |
| | 3250 | fn bar() !void { |
| | 3251 | if (baz()) { |
| | 3252 | try quux(); |
| | 3253 | } else |err| switch (err) { |
| | 3254 | error.FileNotFound => try hello(), |
| | 3255 | else => try another(), |
| | 3256 | } |
| | 3257 | } |
| | 3258 | |
| | 3259 | fn baz() !void { |
| | 3260 | try bang1(); |
| | 3261 | } |
| | 3262 | |
| | 3263 | fn quux() !void { |
| | 3264 | try bang2(); |
| | 3265 | } |
| | 3266 | |
| | 3267 | fn hello() !void { |
| | 3268 | try bang2(); |
| | 3269 | } |
| | 3270 | |
| | 3271 | fn another() !void { |
| | 3272 | try bang1(); |
| | 3273 | } |
| | 3274 | |
| | 3275 | fn bang1() !void { |
| | 3276 | return error.FileNotFound; |
| | 3277 | } |
| | 3278 | |
| | 3279 | fn bang2() !void { |
| | 3280 | return error.PermissionDenied; |
| | 3281 | } |
| | 3282 | {#code_end#} |
| | 3283 | <p> |
| | 3284 | Look closely at this example. This is no stack trace. |
| | 3285 | </p> |
| | 3286 | <p> |
| | 3287 | You can see that the final error bubbled up was <code>PermissionDenied</code>, |
| | 3288 | but the original error that started this whole thing was <code>FileNotFound</code>. In the <code>bar</code> function, the code handles the original error code, |
| | 3289 | and then returns another one, from the switch statement. Error Return Traces make this clear, whereas a stack trace would look like this: |
| | 3290 | </p> |
| | 3291 | {#code_begin|exe_err#} |
| | 3292 | pub fn main() void { |
| | 3293 | foo(12); |
| | 3294 | } |
| | 3295 | |
| | 3296 | fn foo(x: i32) void { |
| | 3297 | if (x >= 5) { |
| | 3298 | bar(); |
| | 3299 | } else { |
| | 3300 | bang2(); |
| | 3301 | } |
| | 3302 | } |
| | 3303 | |
| | 3304 | fn bar() void { |
| | 3305 | if (baz()) { |
| | 3306 | quux(); |
| | 3307 | } else { |
| | 3308 | hello(); |
| | 3309 | } |
| | 3310 | } |
| | 3311 | |
| | 3312 | fn baz() bool { |
| | 3313 | return bang1(); |
| | 3314 | } |
| | 3315 | |
| | 3316 | fn quux() void { |
| | 3317 | bang2(); |
| | 3318 | } |
| | 3319 | |
| | 3320 | fn hello() void { |
| | 3321 | bang2(); |
| | 3322 | } |
| | 3323 | |
| | 3324 | fn bang1() bool { |
| | 3325 | return false; |
| | 3326 | } |
| | 3327 | |
| | 3328 | fn bang2() void { |
| | 3329 | @panic("PermissionDenied"); |
| | 3330 | } |
| | 3331 | {#code_end#} |
| | 3332 | <p> |
| | 3333 | Here, the stack trace does not explain how the control |
| | 3334 | flow in <code>bar</code> got to the <code>hello()</code> call. |
| | 3335 | One would have to open a debugger or further instrument the application |
| | 3336 | in order to find out. The error return trace, on the other hand, |
| | 3337 | shows exactly how the error bubbled up. |
| | 3338 | </p> |
| | 3339 | <p> |
| | 3340 | This debugging feature makes it easier to iterate quickly on code that |
| | 3341 | robustly handles all error conditions. This means that Zig developers |
| | 3342 | will naturally find themselves writing correct, robust code in order |
| | 3343 | to increase their development pace. |
| | 3344 | </p> |
| | 3345 | <p> |
| | 3346 | Error Return Traces are enabled by default in {#link|Debug#} and {#link|ReleaseSafe#} builds and disabled by default in {#link|ReleaseFast#} and {#link|ReleaseSmall#} builds. |
| | 3347 | </p> |
| | 3348 | <p> |
| | 3349 | There are a few ways to activate this error return tracing feature: |
| | 3350 | </p> |
| | 3351 | <ul> |
| | 3352 | <li>Return an error from main</li> |
| | 3353 | <li>An error makes its way to <code>catch unreachable</code> and you have not overridden the default panic handler</li> |
| | 3354 | <li>Use {#link|errorReturnTrace#} to access the current return trace. You can use <code>std.debug.dumpStackTrace</code> to print it. This function returns comptime-known {#link|null#} when building without error return tracing support.</li> |
| | 3355 | </ul> |
| | 3356 | {#header_open|Implementation Details#} |
| | 3357 | <p> |
| | 3358 | To analyze performance cost, there are two cases: |
| | 3359 | </p> |
| | 3360 | <ul> |
| | 3361 | <li>when no errors are returned</li> |
| | 3362 | <li>when returning errors</li> |
| | 3363 | </ul> |
| | 3364 | <p> |
| | 3365 | For the case when no errors are returned, the cost is a single memory write operation, only in the first non-failable function in the call graph that calls a failable function, i.e. when a function returning <code>void</code> calls a function returning <code>error</code>. |
| | 3366 | This is to initialize this struct in the stack memory: |
| | 3367 | </p> |
| | 3368 | {#code_begin|syntax#} |
| | 3369 | pub const StackTrace = struct { |
| | 3370 | index: usize, |
| | 3371 | instruction_addresses: [N]usize, |
| | 3372 | }; |
| | 3373 | {#code_end#} |
| | 3374 | <p> |
| | 3375 | Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2. |
| | 3376 | </p> |
| | 3377 | <p> |
| | 3378 | A pointer to <code>StackTrace</code> is passed as a secret parameter to every function that can return an error, but it's always the first parameter, so it can likely sit in a register and stay there. |
| | 3379 | </p> |
| | 3380 | <p> |
| | 3381 | That's it for the path when no errors occur. It's practically free in terms of performance. |
| | 3382 | </p> |
| | 3383 | <p> |
| | 3384 | When generating the code for a function that returns an error, just before the <code>return</code> statement (only for the <code>return</code> statements that return errors), Zig generates a call to this function: |
| | 3385 | </p> |
| | 3386 | {#code_begin|syntax#} |
| | 3387 | // marked as "no-inline" in LLVM IR |
| | 3388 | fn __zig_return_error(stack_trace: *StackTrace) void { |
| | 3389 | stack_trace.instruction_addresses[stack_trace.index] = @returnAddress(); |
| | 3390 | stack_trace.index = (stack_trace.index + 1) % N; |
| | 3391 | } |
| | 3392 | {#code_end#} |
| | 3393 | <p> |
| | 3394 | The cost is 2 math operations plus some memory reads and writes. The memory accessed is constrained and should remain cached for the duration of the error return bubbling. |
| | 3395 | </p> |
| | 3396 | <p> |
| | 3397 | As for code size cost, 1 function call before a return statement is no big deal. Even so, |
| | 3398 | I have <a href="https://github.com/ziglang/zig/issues/690">a plan</a> to make the call to |
| | 3399 | <code>__zig_return_error</code> a tail call, which brings the code size cost down to actually zero. What is a return statement in code without error return tracing can become a jump instruction in code with error return tracing. |
| | 3400 | </p> |
| | 3401 | {#header_close#} |
| 3227 | {#header_close#} | 3402 | {#header_close#} |
| 3228 | {#header_close#} | 3403 | {#header_close#} |
| 3229 | {#header_open|Optionals#} | 3404 | {#header_open|Optionals#} |
| ... | @@ -3342,6 +3517,15 @@ test "optional type" { | ... | @@ -3342,6 +3517,15 @@ test "optional type" { |
| 3342 | // Use compile-time reflection to access the child type of the optional: | 3517 | // Use compile-time reflection to access the child type of the optional: |
| 3343 | comptime assert(@typeOf(foo).Child == i32); | 3518 | comptime assert(@typeOf(foo).Child == i32); |
| 3344 | } | 3519 | } |
| | 3520 | {#code_end#} |
| | 3521 | {#header_close#} |
| | 3522 | {#header_open|null#} |
| | 3523 | <p> |
| | 3524 | Just like {#link|undefined#}, <code>null</code> has its own type, and the only way to use it is to |
| | 3525 | cast it to a different type: |
| | 3526 | </p> |
| | 3527 | {#code_begin|syntax#} |
| | 3528 | const optional_value: ?i32 = null; |
| 3345 | {#code_end#} | 3529 | {#code_end#} |
| 3346 | {#header_close#} | 3530 | {#header_close#} |
| 3347 | {#header_close#} | 3531 | {#header_close#} |
| ... | @@ -5426,12 +5610,13 @@ pub const TypeInfo = union(TypeId) { | ... | @@ -5426,12 +5610,13 @@ pub const TypeInfo = union(TypeId) { |
| 5426 | {#header_close#} | 5610 | {#header_close#} |
| 5427 | {#header_open|Build Mode#} | 5611 | {#header_open|Build Mode#} |
| 5428 | <p> | 5612 | <p> |
| 5429 | Zig has three build modes: | 5613 | Zig has four build modes: |
| 5430 | </p> | 5614 | </p> |
| 5431 | <ul> | 5615 | <ul> |
| 5432 | <li>{#link|Debug#} (default)</li> | 5616 | <li>{#link|Debug#} (default)</li> |
| 5433 | <li>{#link|ReleaseFast#}</li> | 5617 | <li>{#link|ReleaseFast#}</li> |
| 5434 | <li>{#link|ReleaseSafe#}</li> | 5618 | <li>{#link|ReleaseSafe#}</li> |
| | 5619 | <li>{#link|ReleaseSmall#}</li> |
| 5435 | </ul> | 5620 | </ul> |
| 5436 | <p> | 5621 | <p> |
| 5437 | To add standard build options to a <code>build.zig</code> file: | 5622 | To add standard build options to a <code>build.zig</code> file: |
| ... | @@ -5448,14 +5633,16 @@ pub fn build(b: &Builder) void { | ... | @@ -5448,14 +5633,16 @@ pub fn build(b: &Builder) void { |
| 5448 | <p> | 5633 | <p> |
| 5449 | This causes these options to be available: | 5634 | This causes these options to be available: |
| 5450 | </p> | 5635 | </p> |
| 5451 | <pre><code class="shell"> -Drelease-safe=(bool) optimizations on and safety on | 5636 | <pre><code class="shell"> -Drelease-safe=[bool] optimizations on and safety on |
| 5452 | -Drelease-fast=(bool) optimizations on and safety off</code></pre> | 5637 | -Drelease-fast=[bool] optimizations on and safety off |
| | 5638 | -Drelease-small=[bool] size optimizations on and safety off</code></pre> |
| 5453 | {#header_open|Debug#} | 5639 | {#header_open|Debug#} |
| 5454 | <pre><code class="shell">$ zig build-exe example.zig</code></pre> | 5640 | <pre><code class="shell">$ zig build-exe example.zig</code></pre> |
| 5455 | <ul> | 5641 | <ul> |
| 5456 | <li>Fast compilation speed</li> | 5642 | <li>Fast compilation speed</li> |
| 5457 | <li>Safety checks enabled</li> | 5643 | <li>Safety checks enabled</li> |
| 5458 | <li>Slow runtime performance</li> | 5644 | <li>Slow runtime performance</li> |
| | 5645 | <li>Large binary size</li> |
| 5459 | </ul> | 5646 | </ul> |
| 5460 | {#header_close#} | 5647 | {#header_close#} |
| 5461 | {#header_open|ReleaseFast#} | 5648 | {#header_open|ReleaseFast#} |
| ... | @@ -5464,6 +5651,7 @@ pub fn build(b: &Builder) void { | ... | @@ -5464,6 +5651,7 @@ pub fn build(b: &Builder) void { |
| 5464 | <li>Fast runtime performance</li> | 5651 | <li>Fast runtime performance</li> |
| 5465 | <li>Safety checks disabled</li> | 5652 | <li>Safety checks disabled</li> |
| 5466 | <li>Slow compilation speed</li> | 5653 | <li>Slow compilation speed</li> |
| | 5654 | <li>Large binary size</li> |
| 5467 | </ul> | 5655 | </ul> |
| 5468 | {#header_close#} | 5656 | {#header_close#} |
| 5469 | {#header_open|ReleaseSafe#} | 5657 | {#header_open|ReleaseSafe#} |
| ... | @@ -5472,9 +5660,19 @@ pub fn build(b: &Builder) void { | ... | @@ -5472,9 +5660,19 @@ pub fn build(b: &Builder) void { |
| 5472 | <li>Medium runtime performance</li> | 5660 | <li>Medium runtime performance</li> |
| 5473 | <li>Safety checks enabled</li> | 5661 | <li>Safety checks enabled</li> |
| 5474 | <li>Slow compilation speed</li> | 5662 | <li>Slow compilation speed</li> |
| | 5663 | <li>Large binary size</li> |
| 5475 | </ul> | 5664 | </ul> |
| 5476 | {#see_also|Compile Variables|Zig Build System|Undefined Behavior#} | | |
| 5477 | {#header_close#} | 5665 | {#header_close#} |
| | 5666 | {#header_open|ReleaseSmall#} |
| | 5667 | <pre><code class="shell">$ zig build-exe example.zig --release-small</code></pre> |
| | 5668 | <ul> |
| | 5669 | <li>Medium runtime performance</li> |
| | 5670 | <li>Safety checks disabled</li> |
| | 5671 | <li>Slow compilation speed</li> |
| | 5672 | <li>Small binary size</li> |
| | 5673 | </ul> |
| | 5674 | {#header_close#} |
| | 5675 | {#see_also|Compile Variables|Zig Build System|Undefined Behavior#} |
| 5478 | {#header_close#} | 5676 | {#header_close#} |
| 5479 | {#header_open|Undefined Behavior#} | 5677 | {#header_open|Undefined Behavior#} |
| 5480 | <p> | 5678 | <p> |
| ... | @@ -5482,7 +5680,7 @@ pub fn build(b: &Builder) void { | ... | @@ -5482,7 +5680,7 @@ pub fn build(b: &Builder) void { |
| 5482 | detected at compile-time, Zig emits an error. Most undefined behavior that | 5680 | detected at compile-time, Zig emits an error. Most undefined behavior that |
| 5483 | cannot be detected at compile-time can be detected at runtime. In these cases, | 5681 | cannot be detected at compile-time can be detected at runtime. In these cases, |
| 5484 | Zig has safety checks. Safety checks can be disabled on a per-block basis | 5682 | Zig has safety checks. Safety checks can be disabled on a per-block basis |
| 5485 | with <code>@setRuntimeSafety</code>. The {#link|ReleaseFast#} | 5683 | with {#link|setRuntimeSafety#}. The {#link|ReleaseFast#} |
| 5486 | build mode disables all safety checks in order to facilitate optimizations. | 5684 | build mode disables all safety checks in order to facilitate optimizations. |
| 5487 | </p> | 5685 | </p> |
| 5488 | <p> | 5686 | <p> |