Undefined Behavior

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

test "safety check" {
    unreachable;
}
``` ```
$ zig test test.zig
1/1 test "safety check"...reached unreachable code
/home/andy/dev/zig/docgen_tmp/test.zig:2:5: 0x205586 in test "safety check" (test)
    unreachable;
    ^
/home/andy/dev/zig/lib/std/special/test_runner.zig:13:25: 0x2283a1 in std.special.main (test)
        if (test_fn.func()) |_| {
                        ^
/home/andy/dev/zig/lib/std/special/start.zig:204:37: 0x227215 in std.special.posixCallMainAndExit (test)
            const result = root.main() catch |err| {
                                    ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22708f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^

Tests failed. Use the following command to reproduce the failure:
/home/andy/dev/zig/docgen_tmp/test

Reaching Unreachable Code

At compile-time:

test.zig

comptime {
    assert(false);
}
fn assert(ok: bool) void {
    if (!ok) unreachable; // assertion failure
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:5:14: error: unable to evaluate constant expression
    if (!ok) unreachable; // assertion failure
             ^
/home/andy/dev/zig/docgen_tmp/test.zig:2:11: note: called from here
    assert(false);
          ^
/home/andy/dev/zig/docgen_tmp/test.zig:1:10: note: called from here
comptime {
         ^
/home/andy/dev/zig/docgen_tmp/test.zig:2:11: note: referenced here
    assert(false);
          ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    std.debug.assert(false);
}
``` ```
$ zig build-exe test.zig
$ ./test
reached unreachable code
/home/andy/dev/zig/lib/std/debug.zig:206:14: 0x20555b in std.debug.assert (test)
    if (!ok) unreachable; // assertion failure
             ^
/home/andy/dev/zig/docgen_tmp/test.zig:4:21: 0x2280fa in main (test)
    std.debug.assert(false);
                    ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Index out of Bounds

At compile-time:

test.zig

comptime {
    const array = "hello";
    const garbage = array[5];
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:3:26: error: index 5 outside array of size 5
    const garbage = array[5];
                         ^

At runtime:

test.zig

pub fn main() void {
    var x = foo("hello");
}

fn foo(x: []const u8) u8 {
    return x[5];
}
``` ```
$ zig build-exe test.zig
$ ./test
index out of bounds
/home/andy/dev/zig/docgen_tmp/test.zig:6:13: 0x2281e9 in foo (test)
    return x[5];
            ^
/home/andy/dev/zig/docgen_tmp/test.zig:2:16: 0x228106 in main (test)
    var x = foo("hello");
               ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Cast Negative Number to Unsigned Integer

At compile-time:

test.zig

comptime {
    const value: i32 = -1;
    const unsigned = @intCast(u32, value);
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:3:36: error: cannot cast negative value -1 to unsigned integer type 'u32'
    const unsigned = @intCast(u32, value);
                                   ^
/home/andy/dev/zig/docgen_tmp/test.zig:3:22: note: referenced here
    const unsigned = @intCast(u32, value);
                     ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var value: i32 = -1;
    var unsigned = @intCast(u32, value);
    std.debug.warn("value: {}\n", unsigned);
}
``` ```
$ zig build-exe test.zig
$ ./test
attempt to cast negative value to unsigned integer
/home/andy/dev/zig/docgen_tmp/test.zig:5:20: 0x228130 in main (test)
    var unsigned = @intCast(u32, value);
                   ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(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

comptime {
    const spartan_count: u16 = 300;
    const byte = @intCast(u8, spartan_count);
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:3:31: error: integer value 300 cannot be implicitly casted to type 'u8'
    const byte = @intCast(u8, spartan_count);
                              ^
/home/andy/dev/zig/docgen_tmp/test.zig:3:18: note: referenced here
    const byte = @intCast(u8, spartan_count);
                 ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var spartan_count: u16 = 300;
    const byte = @intCast(u8, spartan_count);
    std.debug.warn("value: {}\n", byte);
}
``` ```
$ zig build-exe test.zig
$ ./test
integer cast truncated bits
/home/andy/dev/zig/docgen_tmp/test.zig:5:18: 0x228136 in main (test)
    const byte = @intCast(u8, spartan_count);
                 ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(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

comptime {
    var byte: u8 = 255;
    byte += 1;
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:3:10: error: operation caused overflow
    byte += 1;
         ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var byte: u8 = 255;
    byte += 1;
    std.debug.warn("value: {}\n", byte);
}
``` ```
$ zig build-exe test.zig
$ ./test
integer overflow
/home/andy/dev/zig/docgen_tmp/test.zig:5:10: 0x228120 in main (test)
    byte += 1;
         ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(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

const math = @import("std").math;
const warn = @import("std").debug.warn;
pub fn main() !void {
    var byte: u8 = 255;

    byte = if (math.add(u8, byte, 1)) |result| result else |err| {
        warn("unable to add one: {}\n", @errorName(err));
        return err;
    };

    warn("result: {}\n", byte);
}
``` ```
$ zig build-exe test.zig
$ ./test
unable to add one: Overflow
error: Overflow
/home/andy/dev/zig/lib/std/math.zig:332:5: 0x2285b0 in std.math.add (test)
    return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
    ^
/home/andy/dev/zig/docgen_tmp/test.zig:8:9: 0x228305 in main (test)
        return err;
        ^

Builtin Overflow Functions

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

  • @addWithOverflow
  • @subWithOverflow
  • @mulWithOverflow
  • @shlWithOverflow

Example of @addWithOverflow:

test.zig

const warn = @import("std").debug.warn;
pub fn main() void {
    var byte: u8 = 255;

    var result: u8 = undefined;
    if (@addWithOverflow(u8, byte, 10, &result)) {
        warn("overflowed result: {}\n", result);
    } else {
        warn("result: {}\n", result);
    }
}
``` ```
$ zig build-exe test.zig
$ ./test
overflowed result: 9

Wrapping Operations

These operations have guaranteed wraparound semantics.

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

test.zig

const std = @import("std");
const assert = std.debug.assert;
const minInt = std.math.minInt;
const maxInt = std.math.maxInt;

test "wraparound addition and subtraction" {
    const x: i32 = maxInt(i32);
    const min_val = x +% 1;
    assert(min_val == minInt(i32));
    const max_val = min_val -% 1;
    assert(max_val == maxInt(i32));
}
``` ```
$ zig test test.zig
1/1 test "wraparound addition and subtraction"...OK
All tests passed.

Exact Left Shift Overflow

At compile-time:

test.zig

comptime {
    const x = @shlExact(u8(0b01010101), 2);
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:2:15: error: operation caused overflow
    const x = @shlExact(u8(0b01010101), 2);
              ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var x: u8 = 0b01010101;
    var y = @shlExact(x, 2);
    std.debug.warn("value: {}\n", y);
}
``` ```
$ zig build-exe test.zig
$ ./test
left shift overflowed bits
/home/andy/dev/zig/docgen_tmp/test.zig:5:13: 0x228137 in main (test)
    var y = @shlExact(x, 2);
            ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Exact Right Shift Overflow

At compile-time:

test.zig

comptime {
    const x = @shrExact(u8(0b10101010), 2);
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:2:15: error: exact shift shifted out 1 bits
    const x = @shrExact(u8(0b10101010), 2);
              ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var x: u8 = 0b10101010;
    var y = @shrExact(x, 2);
    std.debug.warn("value: {}\n", y);
}
``` ```
$ zig build-exe test.zig
$ ./test
right shift overflowed bits
/home/andy/dev/zig/docgen_tmp/test.zig:5:13: 0x228137 in main (test)
    var y = @shrExact(x, 2);
            ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Division by Zero

At compile-time:

test.zig

comptime {
    const a: i32 = 1;
    const b: i32 = 0;
    const c = a / b;
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:4:17: error: division by zero
    const c = a / b;
                ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var a: u32 = 1;
    var b: u32 = 0;
    var c = a / b;
    std.debug.warn("value: {}\n", c);
}
``` ```
$ zig build-exe test.zig
$ ./test
division by zero
/home/andy/dev/zig/docgen_tmp/test.zig:6:15: 0x228129 in main (test)
    var c = a / b;
              ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Remainder Division by Zero

At compile-time:

test.zig

comptime {
    const a: i32 = 10;
    const b: i32 = 0;
    const c = a % b;
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:4:17: error: division by zero
    const c = a % b;
                ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var a: u32 = 10;
    var b: u32 = 0;
    var c = a % b;
    std.debug.warn("value: {}\n", c);
}
``` ```
$ zig build-exe test.zig
$ ./test
remainder division by zero or negative value
/home/andy/dev/zig/docgen_tmp/test.zig:6:15: 0x228144 in main (test)
    var c = a % b;
              ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Exact Division Remainder

At compile-time:

test.zig

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

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var a: u32 = 10;
    var b: u32 = 3;
    var c = @divExact(a, b);
    std.debug.warn("value: {}\n", c);
}
``` ```
$ zig build-exe test.zig
$ ./test
exact division produced remainder
/home/andy/dev/zig/docgen_tmp/test.zig:6:13: 0x228166 in main (test)
    var c = @divExact(a, b);
            ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Slice Widen Remainder

At compile-time:

test.zig

comptime {
    var bytes = [5]u8{ 1, 2, 3, 4, 5 };
    var slice = @bytesToSlice(u32, bytes);
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:3:17: error: unable to convert [5]u8 to []align(1) const u32: size mismatch
    var slice = @bytesToSlice(u32, bytes);
                ^
/home/andy/dev/zig/docgen_tmp/test.zig:3:31: note: u32 has size 4; remaining bytes: 1
    var slice = @bytesToSlice(u32, bytes);
                              ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var bytes = [5]u8{ 1, 2, 3, 4, 5 };
    var slice = @bytesToSlice(u32, bytes[0..]);
    std.debug.warn("value: {}\n", slice[0]);
}
``` ```
$ zig build-exe test.zig
$ ./test
slice widening size mismatch
/home/andy/dev/zig/docgen_tmp/test.zig:5:17: 0x228166 in main (test)
    var slice = @bytesToSlice(u32, bytes[0..]);
                ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Attempt to Unwrap Null

At compile-time:

test.zig

comptime {
    const optional_number: ?i32 = null;
    const number = optional_number.?;
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:3:35: error: unable to unwrap null
    const number = optional_number.?;
                                  ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var optional_number: ?i32 = null;
    var number = optional_number.?;
    std.debug.warn("value: {}\n", number);
}
``` ```
$ zig build-exe test.zig
$ ./test
attempt to unwrap null
/home/andy/dev/zig/docgen_tmp/test.zig:5:33: 0x22811c in main (test)
    var number = optional_number.?;
                                ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(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

const warn = @import("std").debug.warn;
pub fn main() void {
    const optional_number: ?i32 = null;

    if (optional_number) |number| {
        warn("got number: {}\n", number);
    } else {
        warn("it's null\n");
    }
}
``` ```
$ zig build-exe test.zig
$ ./test
it's null

See also:

  • Optionals

Attempt to Unwrap Error

At compile-time:

test.zig

comptime {
    const number = getNumberOrFail() catch unreachable;
}

fn getNumberOrFail() !i32 {
    return error.UnableToReturnNumber;
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:2:38: error: caught unexpected error 'UnableToReturnNumber'
    const number = getNumberOrFail() catch unreachable;
                                     ^

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    const number = getNumberOrFail() catch unreachable;
    std.debug.warn("value: {}\n", number);
}

fn getNumberOrFail() !i32 {
    return error.UnableToReturnNumber;
}
``` ```
$ zig build-exe test.zig
$ ./test
attempt to unwrap error: UnableToReturnNumber
/home/andy/dev/zig/docgen_tmp/test.zig:9:5: 0x22824a in getNumberOrFail (test)
    return error.UnableToReturnNumber;
    ^
???:?:?: 0x22830c in ??? (???)

/home/andy/dev/zig/docgen_tmp/test.zig:4:38: 0x22815b in main (test)
    const number = getNumberOrFail() catch unreachable;
                                     ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(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

const warn = @import("std").debug.warn;

pub fn main() void {
    const result = getNumberOrFail();

    if (result) |number| {
        warn("got number: {}\n", number);
    } else |err| {
        warn("got error: {}\n", @errorName(err));
    }
}

fn getNumberOrFail() !i32 {
    return error.UnableToReturnNumber;
}
``` ```
$ zig build-exe test.zig
$ ./test
got error: UnableToReturnNumber

See also:

  • Errors

Invalid Error Code

At compile-time:

test.zig

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

At runtime:

test.zig

const std = @import("std");

pub fn main() void {
    var err = error.AnError;
    var number = @errorToInt(err) + 500;
    var invalid_err = @intToError(number);
    std.debug.warn("value: {}\n", number);
}
``` ```
$ zig build-exe test.zig
$ ./test
invalid error code
/home/andy/dev/zig/docgen_tmp/test.zig:6:23: 0x22814c in main (test)
    var invalid_err = @intToError(number);
                      ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Invalid Enum Cast

At compile-time:

test.zig

const Foo = enum {
    A,
    B,
    C,
};
comptime {
    const a: u2 = 3;
    const b = @intToEnum(Foo, a);
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:8:15: error: enum 'Foo' has no tag matching integer value 3
    const b = @intToEnum(Foo, a);
              ^
/home/andy/dev/zig/docgen_tmp/test.zig:1:13: note: 'Foo' declared here
const Foo = enum {
            ^

At runtime:

test.zig

const std = @import("std");

const Foo = enum {
    A,
    B,
    C,
};

pub fn main() void {
    var a: u2 = 3;
    var b = @intToEnum(Foo, a);
    std.debug.warn("value: {}\n", @tagName(b));
}
``` ```
$ zig build-exe test.zig
$ ./test
invalid enum value
/home/andy/dev/zig/docgen_tmp/test.zig:11:13: 0x22812c in main (test)
    var b = @intToEnum(Foo, a);
            ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Invalid Error Set Cast

At compile-time:

test.zig

const Set1 = error{
    A,
    B,
};
const Set2 = error{
    A,
    C,
};
comptime {
    _ = @errSetCast(Set2, Set1.B);
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:10:9: error: error.B not a member of error set 'Set2'
    _ = @errSetCast(Set2, Set1.B);
        ^

At runtime:

test.zig

const std = @import("std");

const Set1 = error{
    A,
    B,
};
const Set2 = error{
    A,
    C,
};
pub fn main() void {
    foo(Set1.B);
}
fn foo(set1: Set1) void {
    const x = @errSetCast(Set2, set1);
    std.debug.warn("value: {}\n", x);
}
``` ```
$ zig build-exe test.zig
$ ./test
invalid error code
/home/andy/dev/zig/docgen_tmp/test.zig:15:15: 0x2281fb in foo (test)
    const x = @errSetCast(Set2, set1);
              ^
/home/andy/dev/zig/docgen_tmp/test.zig:12:8: 0x2280fd in main (test)
    foo(Set1.B);
       ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Incorrect Pointer Alignment

At compile-time:

test.zig

comptime {
    const ptr = @intToPtr(*i32, 0x1);
    const aligned = @alignCast(4, ptr);
}
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes
    const aligned = @alignCast(4, ptr);
                                  ^
/home/andy/dev/zig/docgen_tmp/test.zig:3:21: note: referenced here
    const aligned = @alignCast(4, ptr);
                    ^

At runtime:

test.zig

pub fn main() !void {
    var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
    const bytes = @sliceToBytes(array[0..]);
    if (foo(bytes) != 0x11111111) return error.Wrong;
}
fn foo(bytes: []u8) u32 {
    const slice4 = bytes[1..5];
    const int_slice = @bytesToSlice(u32, @alignCast(4, slice4));
    return int_slice[0];
}
``` ```
$ zig build-exe test.zig
$ ./test
incorrect alignment
/home/andy/dev/zig/docgen_tmp/test.zig:8:56: 0x22850e in foo (test)
    const int_slice = @bytesToSlice(u32, @alignCast(4, slice4));
                                                       ^
/home/andy/dev/zig/docgen_tmp/test.zig:4:12: 0x228297 in main (test)
    if (foo(bytes) != 0x11111111) return error.Wrong;
           ^
/home/andy/dev/zig/lib/std/special/start.zig:204:37: 0x2271f5 in std.special.posixCallMainAndExit (test)
            const result = root.main() catch |err| {
                                    ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)

Wrong Union Field Access

At compile-time:

test.zig

comptime {
    var f = Foo{ .int = 42 };
    f.float = 12.34;
}

const Foo = union {
    float: f32,
    int: u32,
};
``` ```
$ zig test test.zig
/home/andy/dev/zig/docgen_tmp/test.zig:3:6: error: accessing union field 'float' while field 'int' is set
    f.float = 12.34;
     ^

At runtime:

test.zig

const std = @import("std");

const Foo = union {
    float: f32,
    int: u32,
};

pub fn main() void {
    var f = Foo{ .int = 42 };
    bar(&f);
}

fn bar(f: *Foo) void {
    f.float = 12.34;
    std.debug.warn("value: {}\n", f.float);
}
``` ```
$ zig build-exe test.zig
$ ./test
access of inactive union field
/home/andy/dev/zig/docgen_tmp/test.zig:14:6: 0x23021a in bar (test)
    f.float = 12.34;
     ^
/home/andy/dev/zig/docgen_tmp/test.zig:10:8: 0x23010c in main (test)
    bar(&f);
       ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22f18b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22f06f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(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

const std = @import("std");

const Foo = union {
    float: f32,
    int: u32,
};

pub fn main() void {
    var f = Foo{ .int = 42 };
    bar(&f);
}

fn bar(f: *Foo) void {
    f.* = Foo{ .float = 12.34 };
    std.debug.warn("value: {}\n", f.float);
}
``` ```
$ zig build-exe test.zig
$ ./test
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

const std = @import("std");

const Foo = union {
    float: f32,
    int: u32,
};

pub fn main() void {
    var f = Foo{ .int = 42 };
    f = Foo{ .float = undefined };
    bar(&f);
    std.debug.warn("value: {}\n", f.float);
}

fn bar(f: *Foo) void {
    f.float = 12.34;
}
``` ```
$ zig build-exe test.zig
$ ./test
value: 1.23400001e+01

See also:

  • union
  • extern union

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

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

At runtime:

test.zig

pub fn main() void {
    var opt_ptr: ?*i32 = null;
    var ptr = @ptrCast(*i32, opt_ptr);
}
``` ```
$ zig build-exe test.zig
$ ./test
cast causes pointer to be null
/home/andy/dev/zig/docgen_tmp/test.zig:3:15: 0x228120 in main (test)
    var ptr = @ptrCast(*i32, opt_ptr);
              ^
/home/andy/dev/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
            root.main();
                     ^
/home/andy/dev/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
    @noInlineCall(posixCallMainAndExit);
    ^
(process terminated by signal)