I was wondering what happens in case I have some comptime function
fn go(comptime ori: Orientation, comptime dir: Direction) void
{
// call other functions where `ori` and `dir` are not comptime.
// which call on their turn other functions where `ori` and `dir` are not comptime.
}
If that all would be “comptimed out” by the compiler, it would save me some duplicate code (writing a comptime and non-comptime version).
Captain Obvious here. The comptime can only go so far as something is strictly comptime known. It isn’t magical predict-everything fairy dust you can just srpinkle on your code…
Here, you’ve declared a function with two comptime arguments. That doesn’t necessarily make the function itself comptime (that would be comptime fn go..., no?).
For your code, use it and see how far you can get. You might be surprised how far you can go, and equally surprised at how quickly you run into things that must be runtime.
No way to know without writing it and learning how comptime works in detail, and how best it can apply to your use case.
The easiest way to ensure that something is run at comptime is to use the comptime keyword at the callsite. You can then just write the function as normal and it can be called at runtime or comptime:
fn go(ori: Orientation, dir: Direction) void {
// anything in here will be run at comptime
// if the function is called at comptime
}
pub fn main() void {
// run at comptime
comptime go(foo, bar);
// run at runtime
go(foo, bar);
}
(this works as long as the function doesn’t try to do IO)
/home/ryan/Programming/zig/zig/lib/std/Thread.zig:1165:30: error: unable to resolve comptime value
return tls_thread_id orelse {
~~~~~~~~~~~~~~^~~~~~
/home/ryan/Programming/zig/zig/lib/std/Thread.zig:376:29: note: called at comptime from here
return Impl.getCurrentId();
~~~~~~~~~~~~~~~~~^~
/home/ryan/Programming/zig/zig/lib/std/Thread/Mutex/Recursive.zig:50:54: note: called at comptime from here
const current_thread_id = std.Thread.getCurrentId();
~~~~~~~~~~~~~~~~~~~~~~~^~
/home/ryan/Programming/zig/zig/lib/std/Progress.zig:545:22: note: called at comptime from here
stderr_mutex.lock();
~~~~~~~~~~~~~~~~~^~
/home/ryan/Programming/zig/zig/lib/std/debug.zig:204:28: note: called at comptime from here
std.Progress.lockStdErr();
~~~~~~~~~~~~~~~~~~~~~~~^~
/home/ryan/Programming/zig/zig/lib/std/debug.zig:214:15: note: called at comptime from here
lockStdErr();
~~~~~~~~~~^~
comptime_io.zig:4:20: note: called at comptime from here
std.debug.print("{}\n", .{a});
~~~~~~~~~~~~~~~^~~~~~~~~~~~~~
comptime_io.zig:8:16: note: called at comptime from here
comptime go(1);
~~^~~
comptime_io.zig:8:5: note: 'comptime' keyword forces comptime evaluation
comptime go(1);
^~~~~~~~~~~~~~
Yes I know that we can run code at comptime. Maybe I did not make myself clear enough what I was thinking about.
The question is about monomorphization or optimization. When I have a function with 2 comptime booleans the exe will have 4 dedicated specialized functions, each for 1 of the possible 4 situations isn’t it? (If they are used all 4 in the program code).
In my example ori and lane are comptime in the loop.
“How comptime” is the function from board we call? Or maybe better asked. Or how optimized?
Inside there we have a switch on ori and a switch on lane returning a result.
Maybe it calls other functions as well inside there etc.
The function call isn’t comptime, but the values of the parameters are comptime. Which allows the compiler to perform more optimisations.
in your example the switches on ori and lane would be removed and replaced with the code in the correct branch.
other functions would not be called at comptime, and will not have the values of ori or lane known at comptime unless the take them as comptime parameters. As the function body is not comptime, only the specified parameters are.
When compiled in Debug mode, this will generate 4 different versions of foo:
Each one is compiled into a single return statement (i.e. the switch doesn’t make it into the final binary at all).
example.foo__anon_460:
push rbp
mov rbp, rsp
mov eax, 1
pop rbp
ret
example.foo__anon_462:
push rbp
mov rbp, rsp
mov eax, 2
pop rbp
ret
...
When compiled in any release mode, though, the calls actually get inlined and sum just becomes a single return statement with the result of the addition precomputed:
If you put an explicit comptime on the foo calls, then sum becomes a single return statement in Debug mode as well: