if (!(comptime ArchetypeHeader.arrayable()))
@compileError(std.fmt.comptimePrint("{any}", .{ArchetypeHeader.arrayable()}));
How is the compiler being lazy here and why? My best guess it just lazily assumes it’s false and walks into the compile error without ever actually checking if it should be in the branch in the first place. I would love to know more how the compiler thinks about this.
I very much expected an error about not being able to evaluate the arrayable() in comptime or something like that I can’t remember the exact error, that’s what I usually get.
Unless arrayable is inline, it’s going to be evaluated at runtime, and so the compiler needs to evaluate that branch. At this point the compiler hasn’t actually run ArchetypeHeader.arrayable().
Inside that branch (which it now has to evaluate because it’s runtime-dependent whether or not it takes it) the compiler sees @compileError and std.fmt.comptimePrint. @compileError’s argument has to be compile-time known, and because std.fmt.comptimePrint is inline, it’ll also be evaluated at compile time regardless. Now the compiler calls .arrayable() (which returns true) at compile time in order to pass it to std.fmt.comptimePrint.
By adding comptime inside the if condition, you’re forcing the compiler to run .arrayable() before it evaluates the branch, and preventing it from reaching the @compileError.
the way to think about this is that things that may be evaluated at compile time are, but with the very notable exception of function calls, which is why you need explicit comptime
That used to be true, but Zig is moving closer and closer to the C++ way of comptime. Now the frontend compiler only treats things as comptime-known when you explicitly annotate them as comptime. Otherwise, it assumes it is runtime-known. Granted, once analysis has been done in the optimization phase, it usually notices things are comptime without help, but it’s too late for things like OP’s example, where the frontend will abort before reaching the backend.
if (b) @compileLog(1) is the first statement with a @compileLog; if you reverse the order of the statements, the compile error still happens on the first one.
the only way @compileLog is not analyzed (and thus the program you posted compiles) is if that branch is not reached by analysis. since var b = false makes if (b) runtime-known, the compiler correctly assumes that the predicate is reachable and analyzes it.
@LucasSantos91 unfortunately I wasn’t able to understand what you meant. Could you explain more?
It doesn’t anymore. Unless explicitly marked as comptime, no matter how obvious it is, Zig will assume it is runtime-known. In the example, it will compile the true branch of the if statement, and trigger the compile error.
interesting, when you say “it doesn’t anymore”, what version of Zig are you on? in 0.17.0-dev.956+2dca73595, the following program compiles just fine.
It looks there are some false facts in this thread. LucasSantos91’s code compiles withe last Zig version. And it is not true that the compile error *still* happens on the first one. with the last Zig version.
Hi, I’m really sorry, can you help me out, I don’t understand what you wrote. If I switch the order of your if statements in the program above, here is what I get:
# as you wrote
robbie@robbie ~> zig build-exe test_file.zig
test_file.zig:6:12: error: found compile log statement
if (b) @compileLog(1);
^~~~~~~~~~~~~~
referenced by:
callMain [inlined]: /Users/robbie/bin/lib/std/start.zig:751:64
callMainWithArgs [inlined]: /Users/robbie/bin/lib/std/start.zig:692:20
main: /Users/robbie/bin/lib/std/start.zig:717:28
1 reference(s) hidden; use '-freference-trace=4' to see all references
Compile Log Output:
@as(comptime_int, 1)
@as(comptime_int, 2)
# if the order is switched
robbie@robbie ~> zig build-exe test_file.zig
test_file.zig:6:13: error: found compile log statement
if (!b) @compileLog(2);
^~~~~~~~~~~~~~
referenced by:
callMain [inlined]: /Users/robbie/bin/lib/std/start.zig:751:64
callMainWithArgs [inlined]: /Users/robbie/bin/lib/std/start.zig:692:20
main: /Users/robbie/bin/lib/std/start.zig:717:28
1 reference(s) hidden; use '-freference-trace=4' to see all references
Compile Log Output:
@as(comptime_int, 2)
@as(comptime_int, 1)
I expressly pulled down the master to check this: 0.17.0-dev.1158+1d1193aa7
I don’t think I’ve told any lies in this thread.
Didn’t you say if you reverse the order of the statements, the compile error still happens on the first one?
Your last comment shows the the error is @compileLog(2) when you reverse the order of the statements. @compileLog(1) is not still the first one.
Or maybe I misunderstood the first meaning in your description? Do you think now @compileLog(2) is the first one after the statement order is reversed? But then I totally don’t get what is your point.
I actually prefer it this way. I still always annotate type arguments with comptime, even if it is unnecessary, the same goes for if branches, etc.
I have a vague memory years ago where I had branches I didn’t want evaluated at comptime being evaluated, though I may have just been a newbie and not understanding the nuance at the time.
Sometime I want a runtime keyword, like comptime, runtime expr forces evaluating expr at run time. For example, sometimes, I want all branches of a control flow being semantic analyzed.