The is_even() function call is evaluated at runtime. Because of this, the compiler evaluates the code, for both possibilities of the return value of is_even(). You get 2, 4, 6 if you do if (comptime is_even(n)) @compileLog(n);.
Just manually unroll the loop and it will make sense.
pub fn main() void {
const numbers = .{ 1, 2, 3, 4, 5, 6 };
// First loop
if (1 % 2 == 0) {
// Known at compile time to be false,
// so the compiler doesn't even look
// at what's inside here.
// The compiler never reaches this
// `compileLog`, so the 1 doesn't get logged.
@compileLog(1);
}
if (2 % 2 == 0) {
//Known at compile time to be true,
// so the block gets compiled, and the compiler
// sees the `compileLog` and logs 2
@compileLog(2);
//...
// Second loop
if (is_even(1)) {
// Not known at compile time, so the compiler
// must compile what's inside, and it
// reaches the `compileLog`.
// Even if this branch gets removed
// by optimizations, the logging has
// already been done.
@compileLog(1);
}
// Same for all other elements, so all get logged
}
inline for (numbers) |n|
if (n % 2 == 0) @compileLog(n); // 2, 4, 6
This code is being evaluated at comptime. It can detect the dead branches, and they are eliminated. The inner block is never even seen by the compiler. The @compileLog is never even reached by the compiler.
inline for (numbers) |n|
if (is_even(n)) @compileLog(n); // 1, 2, 3, 4, 5, 6
This is not being evaluated at comptime, therefore the compiler goes through the normal processing of the branches. It is not dead code that gets eliminated at the if, so the @compileLog is reached for each occurrence due to the loop being unrolled with inline for.