Possible optimizer miscompilation/safety issue in ReleaseFast

I found that a for loop over a fixed range (0..20) becomes an infinite loop in ReleaseFast if it reads from an undefined buffer. Even though reading undefined is UB, should the loop’s static range boundary not be preserved? Clang/GCC (C) still terminate in similar cases, but Zig seems to prune the loop exit logic entirely.

Code to reproduce:

Code-Snippet

var buf: [20]u8 = undefined;
for (0..buf.len) |i| {
    const c = buf[i];
    if (c == 121) break; // Optimizer seems to delete the exit path
}

(The original example is a bit longer, not much.)

This happens on Zig 0.16.0. Is this expected behavior for undefined or a bug in how UB is leveraged for optimization?

uname -a
Linux cachyos-x8664 7.0.3-1-cachyos #1 SMP PREEMPT Fri, 01 May 2026 19:46:26 +0000 x86_64 GNU/Linux

~
❯ zig version
0.16.0

Generally in ReleaseFast, anything is allowed to happen when your program hits UB, it is not restricted to things that would have been possible with a real value.
The compiler is allowed to assume that undefined behavior doesn’t happen, and remove or alter any code paths that lead to it (and one can imagine cases where an optimization would lead to a jump into arbitrary memory which happens to contain an infinite loop for whatever reason).

Though I am also surprised that it would cause an infinite loop in the given code. Could you post a godbolt link with the full example? I suspect that it has something to do with the rest of the code, as I cannot reproduce the behavior with the given code.

2 Likes

Using undefined values in a conditional is your fault, not ReleaseFast’s.

1 Like

Since c is read from uninitialized memory, it makes sense that the optimizer would be free to assume that the break condition will never be true.

1 Like

Zig’s ReleaseFast is on the aggressive side, relative to compilation profiles you may have used in other languages/compilers. UB means all bets are off.

1 Like