Description
I propose a @stack_size(function)
builtin which returns the number of bytes that function
- which must be a compile time constant function - will ever possibly use for the deepest call invocation.
This function causes it to become a compile error if the function named - or any function called therein, invokes direct or indirect recursion on a non-compile time constant parameter. Similarly, if any function causes a runtime stack allocation to occur, such as This feature has been removed from zigvar foo: [x]u8 = undefined;
, where x
is a parameter to the function invoked with a non compile time constant.
This builtin function could be used when creating a new thread, to determine the stack size. This stack would never have to grow or shrink, and it would be the maximum number of bytes that could possibly be used, likely very small.
It would also force the programmer to not use recursion on user input, a limitation that some categories of software require, and violating this would be caught by the compiler, since it makes returning a correct number from @stack_size()
impossible.
Users may even want to introduce this limitation on the main thread, and they could do this by adding a top level assert such as comptime { assert(@stack_size(main) <= 16 * 1024 * 1024); }
or some such.
If they do this, then we can modify the binary's stack size to be only as large as necessary, which is probably a minuscule improvement in memory footprint and startup time, but it's a strong guarantee of robustness at least in this one regard.
Another thing that would cause this function to cause a compile error is calls to an external library. External library functions could be annotated with a maximum stack size attribute, or we can have another threading abstraction that does not use @stack_size()
for when we don't know the upper bound of how big a stack will grow.
Tangential idea: when Zig outputs a shared library, in the .h file it generates, it can annotate functions with additional information in the comments, such as maximum stack size, and Zig can look for these annotations when importing .h files.
This function also introduces some weird interdependencies. For example, I could do:
fn a() {
var bytes: [@stack_size(b)] = undefined;
}
fn b() {
a();
}
Zig would have to detect this dependency loop and report a compile error.
Implementation of this would be a bit tricky since currently it is LLVM that determines the stack size of everything, and optimizations can increase and decrease the stack size of various functions. However, I think it is possible to have a sound upper bound.
@thejoshwolfe thoughts?
Metadata
Metadata
Assignees
Labels
Type
Projects
Status