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
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.
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.