Undefined Behavior

Zig has many instances of undefined behavior. If undefined behavior is detected at compile-time, Zig emits a compile error and refuses to continue. Most undefined behavior that cannot be detected at compile-time can be detected at runtime. In these cases, Zig has safety checks. Safety checks can be disabled on a per-block basis with @setRuntimeSafety. The ReleaseFast and ReleaseSmall build modes disable all safety checks (except where overridden by @setRuntimeSafety) in order to facilitate optimizations.

When a safety check fails, Zig crashes with a stack trace, like this:

test.zig

  1. test "safety check"{
  2. unreachable;
  3. }
  1. $ zig test test.zig
  2. 1/1 test "safety check"...reached unreachable code
  3. /deps/zig/docgen_tmp/test.zig:2:5:0x204b9ain test "safety check"(test)
  4. unreachable;
  5. ^
  6. /deps/zig/lib/std/special/test_runner.zig:47:28:0x22ba9ein std.special.main (test)
  7. }else test_fn.func();
  8. ^
  9. /deps/zig/lib/std/start.zig:253:37:0x2055cdin std.start.posixCallMainAndExit (test)
  10. const result = root.main()catch|err|{
  11. ^
  12. /deps/zig/lib/std/start.zig:123:5:0x20530fin std.start._start (test)
  13. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  14. ^
  15. Tests failed.Use the following command to reproduce the failure:
  16. /deps/zig/docgen_tmp/test

Reaching Unreachable Code

At compile-time:

test.zig

  1. comptime {
  2. assert(false);
  3. }
  4. fn assert(ok:bool)void{
  5. if(!ok) unreachable;// assertion failure
  6. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:5:14: error: unable to evaluate constant expression
  3. if(!ok) unreachable;// assertion failure
  4. ^
  5. ./docgen_tmp/test.zig:2:11: note: called from here
  6. assert(false);
  7. ^
  8. ./docgen_tmp/test.zig:1:10: note: called from here
  9. comptime {
  10. ^
  11. ./docgen_tmp/test.zig:2:11: note: referenced here
  12. assert(false);
  13. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. std.debug.assert(false);
  4. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. reached unreachable code
  4. /deps/zig/lib/std/debug.zig:228:14:0x203cbbin std.debug.assert(test)
  5. if(!ok) unreachable;// assertion failure
  6. ^
  7. /deps/zig/docgen_tmp/test.zig:4:21:0x22a7dain main (test)
  8. std.debug.assert(false);
  9. ^
  10. /deps/zig/lib/std/start.zig:243:22:0x2046dfin std.start.posixCallMainAndExit (test)
  11. root.main();
  12. ^
  13. /deps/zig/lib/std/start.zig:123:5:0x2044bfin std.start._start (test)
  14. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  15. ^
  16. (process terminated by signal)

Index out of Bounds

At compile-time:

test.zig

  1. comptime {
  2. const array:[5]u8 ="hello".*;
  3. const garbage = array[5];
  4. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:3:26: error: index 5 outside array of size 5
  3. const garbage = array[5];
  4. ^

At runtime:

test.zig

  1. pub fn main()void{
  2. var x = foo("hello");
  3. }
  4. fn foo(x:[]const u8) u8 {
  5. return x[5];
  6. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. index out of bounds
  4. /deps/zig/docgen_tmp/test.zig:6:13:0x22e969in foo (test)
  5. return x[5];
  6. ^
  7. /deps/zig/docgen_tmp/test.zig:2:16:0x22a7e6in main (test)
  8. var x = foo("hello");
  9. ^
  10. /deps/zig/lib/std/start.zig:243:22:0x2046dfin std.start.posixCallMainAndExit (test)
  11. root.main();
  12. ^
  13. /deps/zig/lib/std/start.zig:123:5:0x2044bfin std.start._start (test)
  14. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  15. ^
  16. (process terminated by signal)

Cast Negative Number to Unsigned Integer

At compile-time:

test.zig

  1. comptime {
  2. const value: i32 =-1;
  3. constunsigned=@intCast(u32, value);
  4. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:3:36: error: cannot cast negative value -1 to unsigned integer type 'u32'
  3. constunsigned=@intCast(u32, value);
  4. ^
  5. ./docgen_tmp/test.zig:3:22: note: referenced here
  6. constunsigned=@intCast(u32, value);
  7. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var value: i32 =-1;
  4. varunsigned=@intCast(u32, value);
  5. std.debug.warn("value: {}\n",.{unsigned});
  6. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. attempt to cast negative value to unsigned integer
  4. /deps/zig/docgen_tmp/test.zig:5:20:0x22a867in main (test)
  5. varunsigned=@intCast(u32, value);
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20472fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20450fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

To obtain the maximum value of an unsigned integer, use std.math.maxInt.

Cast Truncates Data

At compile-time:

test.zig

  1. comptime {
  2. const spartan_count: u16 =300;
  3. constbyte=@intCast(u8, spartan_count);
  4. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:3:31: error: integer value 300 cannot be coerced to type 'u8'
  3. constbyte=@intCast(u8, spartan_count);
  4. ^
  5. ./docgen_tmp/test.zig:3:18: note: referenced here
  6. constbyte=@intCast(u8, spartan_count);
  7. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var spartan_count: u16 =300;
  4. constbyte=@intCast(u8, spartan_count);
  5. std.debug.warn("value: {}\n",.{byte});
  6. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. integer cast truncated bits
  4. /deps/zig/docgen_tmp/test.zig:5:18:0x22a86cin main (test)
  5. constbyte=@intCast(u8, spartan_count);
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20472fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20450fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

To truncate bits, use @truncate.

Integer Overflow

Default Operations

The following operators can cause integer overflow:

  • + (addition)
  • - (subtraction)
  • - (negation)
  • * (multiplication)
  • / (division)
  • @divTrunc (division)
  • @divFloor (division)
  • @divExact (division)

Example with addition at compile-time:

test.zig

  1. comptime {
  2. varbyte: u8 =255;
  3. byte+=1;
  4. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:3:10: error: operation caused overflow
  3. byte+=1;
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. varbyte: u8 =255;
  4. byte+=1;
  5. std.debug.warn("value: {}\n",.{byte});
  6. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. integer overflow
  4. /deps/zig/docgen_tmp/test.zig:5:10:0x22a850in main (test)
  5. byte+=1;
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20472fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20450fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

Standard Library Math Functions

These functions provided by the standard library return possible errors.

  • @import("std").math.add
  • @import("std").math.sub
  • @import("std").math.mul
  • @import("std").math.divTrunc
  • @import("std").math.divFloor
  • @import("std").math.divExact
  • @import("std").math.shl

Example of catching an overflow for addition:

test.zig

  1. const math =@import("std").math;
  2. const warn =@import("std").debug.warn;
  3. pub fn main()!void{
  4. varbyte: u8 =255;
  5. byte=if(math.add(u8,byte,1))|result| result else|err|{
  6. warn("unable to add one: {}\n",.{@errorName(err)});
  7. return err;
  8. };
  9. warn("result: {}\n",.{byte});
  10. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. unable to add one:Overflow
  4. error:Overflow
  5. /deps/zig/lib/std/math.zig:342:5:0x22ee70in std.math.add (test)
  6. returnif(@addWithOverflow(T, a, b,&answer)) error.Overflowelse answer;
  7. ^
  8. /deps/zig/docgen_tmp/test.zig:8:9:0x22ab3bin main (test)
  9. return err;
  10. ^

Builtin Overflow Functions

These builtins return a bool of whether or not overflow occurred, as well as returning the overflowed bits:

Example of @addWithOverflow:

test.zig

  1. const warn =@import("std").debug.warn;
  2. pub fn main()void{
  3. varbyte: u8 =255;
  4. var result: u8 =undefined;
  5. if(@addWithOverflow(u8,byte,10,&result)){
  6. warn("overflowed result: {}\n",.{result});
  7. }else{
  8. warn("result: {}\n",.{result});
  9. }
  10. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. overflowed result:9

Wrapping Operations

These operations have guaranteed wraparound semantics.

  • +% (wraparound addition)
  • -% (wraparound subtraction)
  • -% (wraparound negation)
  • *% (wraparound multiplication)

test.zig

  1. const std =@import("std");
  2. constassert= std.debug.assert;
  3. const minInt = std.math.minInt;
  4. const maxInt = std.math.maxInt;
  5. test "wraparound addition and subtraction"{
  6. const x: i32 = maxInt(i32);
  7. const min_val = x +%1;
  8. assert(min_val == minInt(i32));
  9. const max_val = min_val -%1;
  10. assert(max_val == maxInt(i32));
  11. }
  1. $ zig test test.zig
  2. 1/1 test "wraparound addition and subtraction"...OK
  3. All1 tests passed.

Exact Left Shift Overflow

At compile-time:

test.zig

  1. comptime {
  2. const x =@shlExact(@as(u8,0b01010101),2);
  3. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:2:15: error: operation caused overflow
  3. const x =@shlExact(@as(u8,0b01010101),2);
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var x: u8 =0b01010101;
  4. var y =@shlExact(x,2);
  5. std.debug.warn("value: {}\n",.{y});
  6. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. left shift overflowed bits
  4. /deps/zig/docgen_tmp/test.zig:5:13:0x22a89din main (test)
  5. var y =@shlExact(x,2);
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20475fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20453fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

Exact Right Shift Overflow

At compile-time:

test.zig

  1. comptime {
  2. const x =@shrExact(@as(u8,0b10101010),2);
  3. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:2:15: error: exact shift shifted out1 bits
  3. const x =@shrExact(@as(u8,0b10101010),2);
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var x: u8 =0b10101010;
  4. var y =@shrExact(x,2);
  5. std.debug.warn("value: {}\n",.{y});
  6. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. right shift overflowed bits
  4. /deps/zig/docgen_tmp/test.zig:5:13:0x22a89din main (test)
  5. var y =@shrExact(x,2);
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20475fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20453fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

Division by Zero

At compile-time:

test.zig

  1. comptime {
  2. const a: i32 =1;
  3. const b: i32 =0;
  4. const c = a / b;
  5. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:4:17: error: division by zero
  3. const c = a / b;
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var a: u32 =1;
  4. var b: u32 =0;
  5. var c = a / b;
  6. std.debug.warn("value: {}\n",.{c});
  7. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. division by zero
  4. /deps/zig/docgen_tmp/test.zig:6:15:0x22a859in main (test)
  5. var c = a / b;
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20472fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20450fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

Remainder Division by Zero

At compile-time:

test.zig

  1. comptime {
  2. const a: i32 =10;
  3. const b: i32 =0;
  4. const c = a % b;
  5. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:4:17: error: division by zero
  3. const c = a % b;
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var a: u32 =10;
  4. var b: u32 =0;
  5. var c = a % b;
  6. std.debug.warn("value: {}\n",.{c});
  7. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. remainder division by zero or negative value
  4. /deps/zig/docgen_tmp/test.zig:6:15:0x22a87bin main (test)
  5. var c = a % b;
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20472fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20450fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

Exact Division Remainder

At compile-time:

test.zig

  1. comptime {
  2. const a: u32 =10;
  3. const b: u32 =3;
  4. const c =@divExact(a, b);
  5. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:4:15: error: exact division had a remainder
  3. const c =@divExact(a, b);
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var a: u32 =10;
  4. var b: u32 =3;
  5. var c =@divExact(a, b);
  6. std.debug.warn("value: {}\n",.{c});
  7. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. exact division produced remainder
  4. /deps/zig/docgen_tmp/test.zig:6:13:0x22a89din main (test)
  5. var c =@divExact(a, b);
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20472fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20450fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

Attempt to Unwrap Null

At compile-time:

test.zig

  1. comptime {
  2. const optional_number:?i32 =null;
  3. const number = optional_number.?;
  4. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:3:35: error: unable to unwrap null
  3. const number = optional_number.?;
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var optional_number:?i32 =null;
  4. var number = optional_number.?;
  5. std.debug.warn("value: {}\n",.{number});
  6. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. attempt to usenull value
  4. /deps/zig/docgen_tmp/test.zig:5:33:0x22a86cin main (test)
  5. var number = optional_number.?;
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20474fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20452fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

One way to avoid this crash is to test for null instead of assuming non-null, with the if expression:

test.zig

  1. const warn =@import("std").debug.warn;
  2. pub fn main()void{
  3. const optional_number:?i32 =null;
  4. if(optional_number)|number|{
  5. warn("got number: {}\n",.{number});
  6. }else{
  7. warn("it's null\n",.{});
  8. }
  9. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. it's null

See also:

Attempt to Unwrap Error

At compile-time:

test.zig

  1. comptime {
  2. const number = getNumberOrFail()catch unreachable;
  3. }
  4. fn getNumberOrFail()!i32 {
  5. return error.UnableToReturnNumber;
  6. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:2:38: error: caught unexpected error 'UnableToReturnNumber'
  3. const number = getNumberOrFail()catch unreachable;
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. const number = getNumberOrFail()catch unreachable;
  4. std.debug.warn("value: {}\n",.{number});
  5. }
  6. fn getNumberOrFail()!i32 {
  7. return error.UnableToReturnNumber;
  8. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. attempt to unwrap error:UnableToReturnNumber
  4. /deps/zig/docgen_tmp/test.zig:9:5:0x22ea6cin getNumberOrFail (test)
  5. return error.UnableToReturnNumber;
  6. ^
  7. ???:?:?:0x20698cin???(???)
  8. /deps/zig/docgen_tmp/test.zig:4:38:0x22a8cbin main (test)
  9. const number = getNumberOrFail()catch unreachable;
  10. ^
  11. /deps/zig/lib/std/start.zig:243:22:0x20476fin std.start.posixCallMainAndExit (test)
  12. root.main();
  13. ^
  14. /deps/zig/lib/std/start.zig:123:5:0x20454fin std.start._start (test)
  15. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  16. ^
  17. (process terminated by signal)

One way to avoid this crash is to test for an error instead of assuming a successful result, with the if expression:

test.zig

  1. const warn =@import("std").debug.warn;
  2. pub fn main()void{
  3. const result = getNumberOrFail();
  4. if(result)|number|{
  5. warn("got number: {}\n",.{number});
  6. }else|err|{
  7. warn("got error: {}\n",.{@errorName(err)});
  8. }
  9. }
  10. fn getNumberOrFail()!i32 {
  11. return error.UnableToReturnNumber;
  12. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. got error:UnableToReturnNumber

See also:

Invalid Error Code

At compile-time:

test.zig

  1. comptime {
  2. const err = error.AnError;
  3. const number =@errorToInt(err)+10;
  4. const invalid_err =@intToError(number);
  5. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:4:25: error: integer value 11 represents no error
  3. const invalid_err =@intToError(number);
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. pub fn main()void{
  3. var err = error.AnError;
  4. var number =@errorToInt(err)+500;
  5. var invalid_err =@intToError(number);
  6. std.debug.warn("value: {}\n",.{number});
  7. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. invalid error code
  4. /deps/zig/docgen_tmp/test.zig:6:23:0x22a8b4in main (test)
  5. var invalid_err =@intToError(number);
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20475fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20453fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

Invalid Enum Cast

At compile-time:

test.zig

  1. constFoo=enum{
  2. A,
  3. B,
  4. C,
  5. };
  6. comptime {
  7. const a: u2 =3;
  8. const b =@intToEnum(Foo, a);
  9. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:8:15: error:enum'Foo' has no tag matching integer value 3
  3. const b =@intToEnum(Foo, a);
  4. ^
  5. ./docgen_tmp/test.zig:1:13: note:'Foo' declared here
  6. constFoo=enum{
  7. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. constFoo=enum{
  3. A,
  4. B,
  5. C,
  6. };
  7. pub fn main()void{
  8. var a: u2 =3;
  9. var b =@intToEnum(Foo, a);
  10. std.debug.warn("value: {}\n",.{@tagName(b)});
  11. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. invalid enum value
  4. /deps/zig/docgen_tmp/test.zig:11:13:0x22a8ccin main (test)
  5. var b =@intToEnum(Foo, a);
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x20479fin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x20457fin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)

Invalid Error Set Cast

At compile-time:

test.zig

  1. constSet1= error{
  2. A,
  3. B,
  4. };
  5. constSet2= error{
  6. A,
  7. C,
  8. };
  9. comptime {
  10. _ =@errSetCast(Set2,Set1.B);
  11. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:10:9: error: error.B not a member of error set'Set2'
  3. _ =@errSetCast(Set2,Set1.B);
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. constSet1= error{
  3. A,
  4. B,
  5. };
  6. constSet2= error{
  7. A,
  8. C,
  9. };
  10. pub fn main()void{
  11. foo(Set1.B);
  12. }
  13. fn foo(set1:Set1)void{
  14. const x =@errSetCast(Set2, set1);
  15. std.debug.warn("value: {}\n",.{x});
  16. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. invalid error code
  4. /deps/zig/docgen_tmp/test.zig:15:15:0x22ea4cin foo (test)
  5. const x =@errSetCast(Set2, set1);
  6. ^
  7. /deps/zig/docgen_tmp/test.zig:12:8:0x22a89din main (test)
  8. foo(Set1.B);
  9. ^
  10. /deps/zig/lib/std/start.zig:243:22:0x20479fin std.start.posixCallMainAndExit (test)
  11. root.main();
  12. ^
  13. /deps/zig/lib/std/start.zig:123:5:0x20457fin std.start._start (test)
  14. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  15. ^
  16. (process terminated by signal)

Incorrect Pointer Alignment

At compile-time:

test.zig

  1. comptime {
  2. const ptr =@intToPtr(*align(1) i32,0x1);
  3. const aligned =@alignCast(4, ptr);
  4. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:3:35: error: pointer address 0x1isnot aligned to 4 bytes
  3. const aligned =@alignCast(4, ptr);
  4. ^
  5. ./docgen_tmp/test.zig:3:21: note: referenced here
  6. const aligned =@alignCast(4, ptr);
  7. ^

At runtime:

test.zig

  1. const mem =@import("std").mem;
  2. pub fn main()!void{
  3. var array align(4)=[_]u32{0x11111111,0x11111111};
  4. const bytes = mem.sliceAsBytes(array[0..]);
  5. if(foo(bytes)!=0x11111111)return error.Wrong;
  6. }
  7. fn foo(bytes:[]u8) u32 {
  8. const slice4 = bytes[1..5];
  9. const int_slice = mem.bytesAsSlice(u32,@alignCast(4, slice4));
  10. return int_slice[0];
  11. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. incorrect alignment
  4. /deps/zig/docgen_tmp/test.zig:9:59:0x22edbbin foo (test)
  5. const int_slice = mem.bytesAsSlice(u32,@alignCast(4, slice4));
  6. ^
  7. /deps/zig/docgen_tmp/test.zig:5:12:0x22aa49in main (test)
  8. if(foo(bytes)!=0x11111111)return error.Wrong;
  9. ^
  10. /deps/zig/lib/std/start.zig:253:37:0x2047edin std.start.posixCallMainAndExit (test)
  11. const result = root.main()catch|err|{
  12. ^
  13. /deps/zig/lib/std/start.zig:123:5:0x20452fin std.start._start (test)
  14. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  15. ^
  16. (process terminated by signal)

Wrong Union Field Access

At compile-time:

test.zig

  1. comptime {
  2. var f =Foo{.int=42};
  3. f.float=12.34;
  4. }
  5. constFoo=union{
  6. float: f32,
  7. int: u32,
  8. };
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:3:6: error: accessing union field 'float'while field 'int'isset
  3. f.float=12.34;
  4. ^

At runtime:

test.zig

  1. const std =@import("std");
  2. constFoo=union{
  3. float: f32,
  4. int: u32,
  5. };
  6. pub fn main()void{
  7. var f =Foo{.int=42};
  8. bar(&f);
  9. }
  10. fn bar(f:*Foo)void{
  11. f.float=12.34;
  12. std.debug.warn("value: {}\n",.{f.float});
  13. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. access of inactive union field
  4. /deps/zig/docgen_tmp/test.zig:14:6:0x23c21ain bar (test)
  5. f.float=12.34;
  6. ^
  7. /deps/zig/docgen_tmp/test.zig:10:8:0x23806cin main (test)
  8. bar(&f);
  9. ^
  10. /deps/zig/lib/std/start.zig:243:22:0x211f5fin std.start.posixCallMainAndExit (test)
  11. root.main();
  12. ^
  13. /deps/zig/lib/std/start.zig:123:5:0x211d3fin std.start._start (test)
  14. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  15. ^
  16. (process terminated by signal)

This safety is not available for extern or packed unions.

To change the active field of a union, assign the entire union, like this:

test.zig

  1. const std =@import("std");
  2. constFoo=union{
  3. float: f32,
  4. int: u32,
  5. };
  6. pub fn main()void{
  7. var f =Foo{.int=42};
  8. bar(&f);
  9. }
  10. fn bar(f:*Foo)void{
  11. f.*=Foo{.float=12.34};
  12. std.debug.warn("value: {}\n",.{f.float});
  13. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. value:1.23400001e+01

To change the active field of a union when a meaningful value for the field is not known, use undefined, like this:

test.zig

  1. const std =@import("std");
  2. constFoo=union{
  3. float: f32,
  4. int: u32,
  5. };
  6. pub fn main()void{
  7. var f =Foo{.int=42};
  8. f =Foo{.float=undefined};
  9. bar(&f);
  10. std.debug.warn("value: {}\n",.{f.float});
  11. }
  12. fn bar(f:*Foo)void{
  13. f.float=12.34;
  14. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. value:1.23400001e+01

See also:

Out of Bounds Float to Integer Cast

TODO

Pointer Cast Invalid Null

This happens when casting a pointer with the address 0 to a pointer which may not have the address 0. For example, C Pointers, Optional Pointers, and allowzero pointers allow address zero, but normal Pointers do not.

At compile-time:

test.zig

  1. comptime {
  2. const opt_ptr:?*i32 =null;
  3. const ptr =@ptrCast(*i32, opt_ptr);
  4. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:3:17: error:null pointer casted to type '*i32'
  3. const ptr =@ptrCast(*i32, opt_ptr);
  4. ^

At runtime:

test.zig

  1. pub fn main()void{
  2. var opt_ptr:?*i32 =null;
  3. var ptr =@ptrCast(*i32, opt_ptr);
  4. }
  1. $ zig build-exe test.zig
  2. $ ./test
  3. cast causes pointer to be null
  4. /deps/zig/docgen_tmp/test.zig:3:15:0x22a800in main (test)
  5. var ptr =@ptrCast(*i32, opt_ptr);
  6. ^
  7. /deps/zig/lib/std/start.zig:243:22:0x2046dfin std.start.posixCallMainAndExit (test)
  8. root.main();
  9. ^
  10. /deps/zig/lib/std/start.zig:123:5:0x2044bfin std.start._start (test)
  11. @call(.{.modifier =.never_inline }, posixCallMainAndExit,.{});
  12. ^
  13. (process terminated by signal)