I agree that this is somewhat confusing, it’s because of a few subtleties I myself have only gotten a grasp on somewhat recently. It’s a combination of two things:
- Functions only run at comptime if they’re in a comptime scope, or return a comptime only type.
- Even though the return value is evaluated at comptime, it isn’t comptime known in the calling function.
inline for’s parameters aren’t implicitly comptime, because only the iteration condition is required to be comptime.
For the first point, look at the following examples:
pub fn main() void {
const a: u8 = 1;
const b: u8 = 2;
const v = a + b;
_ = comptime v;
}
Compiles just fine. However,
pub fn main() void {
const a: u8 = 1;
const b: u8 = 2;
const v = add(a, b);
_ = comptime v;
}
fn add(a: u8, b: u8) u8 {
return a + b;
}
Gets
An error occurred:
/tmp/playground2832450235/play.zig:5:9: error: unable to resolve comptime value
_ = comptime v;
^~~~~~~~~~
As add doesn’t implicitly run during comptime, v is not comptime known.
pub fn main() void {
const a: u8 = 1;
const b: u8 = 2;
const v = comptime add(a, b);
_ = comptime v;
}
fn add(a: u8, b: u8) u8 {
return a + b;
}
If we explicitly ask to for the scope to be comptime, it will compile again.
As a general rule, functions usually don’t run in comptime implicitly, but there are exceptions:
pub fn main() void {
const a: u8 = 1;
const b: u8 = 2;
const v = add(a, b);
_ = comptime v;
}
fn add(a: u8, b: u8) comptime_int {
return a + b;
}
Bizarrely, the above compiles just fine as well. add returns a comptime only type, so it will implicitly run in comptime.
pub fn main() void {
const a: u8 = 1;
const b: u8 = 2;
const v, _ = @addWithOverflow(a, b);
_ = comptime v;
}
Also works fine. Builtins look like functions, but are really just fancy looking syntax. So this behaves like + and not like fn add.
2. Even though the return value is evaluated at comptime, it isn’t comptime known in the calling function.
pub fn main() void {
const a: u8 = 1;
const b: u8 = 2;
const v = add(a, b);
_ = comptime v;
}
fn add(comptime a: u8, comptime b: u8) u8 {
return comptime a + b;
}
This will give an error still. The arguments to add need to be comptime to avoid a separate error. What happens here is that during comptime, add will run with 1 and 2 as arguments, evaluate to 3, and simplify the function to be essentially:
fn add_1_2() u8 {
return 3;
}
3. inline for’s parameters aren’t implicitly comptime, because only the iteration condition is required to be comptime.
Some expressions are implicitly comptime, such as the default value of container level declarations, type declarations, or functions which return a comptime only type. The parameters for inline for is not such a place.
It requires that the loop condition (essentially, how many times the loop will run) to be comptime known, but not the values themselves.
fn foo(a: [2]u8) u8 {
var sum: u8 = 0;
inline for (a) |v| {
sum += v;
}
return sum;
}
Essentially boils down to:
fn foo(a: [2]u8) u8 {
return a[0] + a[1];
}
Note that
fn foo(a: [2]u8) u8 {
var sum: u8 = 0;
inline for (a) |v| {
_ = comptime v;
sum += v;
}
return sum;
}
will error because of v not being comptime known, despite being the value provided by the inline for.
An error occurred:
/tmp/playground3674012945/play.zig:8:13: error: unable to resolve comptime value
_ = comptime v;
^~~~~~~~~~
I don’t believe your suggestion will work, since the OP code does require the iterated value to be comptime known. Eg,
fn bar() *const [2]u8 {
return comptime &.{1, 2};
}
pub fn main() void {
inline for (bar()) |v| {
_ = comptime v;
}
}
Gets error: unable to resolve comptime value. It would let inline for work for cases where the value doesn’t need to be comptime known, but you could (and likely should) just use a normal for loop in that case.