-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
introduce reachable
enum tag to std.builtin.BranchHint
; all functions reachable by default
#21511
Comments
Are there regression tests for binary size changes in release builds of Zig? I'd be worried about unintentionally increasing every Zig binary size after this change.
As a data point, here is a terrible hack we do related to this: https://github.com/oven-sh/bun/blob/73e0637cd33b83f6bc3260ccd2e290ced586ce4c/src/napi/napi.zig#L1779-L1933 |
A few I really like this proposal - out of the two options I prefer B. (More errors... and better code!) I can't say I fully understand the inner workings of I am assuming that by the If I fully understand this proposal, then I would want this to happen to my code:
I don't know if what I wrote made much sense, but I also don't know if adding "function reachability" hints to the Whatever option is taken though, I would say that accepting this proposal in any form would be great, and I particularly like the idea of raising |
The main mechanism proposed is to force analysis of functions via As pointed out above, other branch hints such as Another unfortunate asymmetry I wanted to point out is that, while it works for single-instantiation functions, |
I don't think that's really related to any change Zig could make: those functions are all declared and defined in C++, and I thought the dead code elimination that we're preventing had been done by the linker, not Zig. We just chose our Zig code as the place to reference those functions from to stop them from being deleted, as opposed to referencing them from a C or C++ object. |
This proposal is misidentifying the root cause of the problem: Zig has no way of detecting whether and under what conditions a function gets used, due to different build configurations. It's also applying a solution which only works in a subset of cases, and causes surprising behavior in edge cases (you write
Unfortunately, this also means that it would be impossible to specify, say, both |
I agree, reachability is orthogonal to the other options in |
3 problems have the same root cause:
refAllDecls
.unreachable
is unable to be reported at compile time (comptime-known unreachable is a runtime error when it should be a compiler error #18226), because the function might be never called (or the branch never executed).Currently, with the exception of
_start
andmain
, functions in the Zig language may or may not be reachable. This proposal is to addreachable
as astd.builtin.BranchHint
enum tag, allowing functions and branches to be marked as reachable, solving all three problems above.Currently it looks like function-level
@branchHint
is lowered into ZIR like this:However, since the builtin has the rule that it must be the first statement of any block, it could be elevated to function flags, observable without peering into the function body. In the case of a literal
.reachable
value being used, the unused function error could even be caught during ast-check.There is the question of defaults. Here are two choices:
reachable
; other functions default tonone
. This is pretty similar to status quo.reachable
. This will cause an error for non-pub functions that are not referenced (according to Sema!) which can be silenced with@branchHint(.none);
.Either option would unfortunately mean that a lot functions need to be annotated against the default. For example, every function inside
std.os.linux
would need to be marked asnone
so that it does not cause compile errors or bloat when compiling for Windows, and vice versa. Something that might address this could be the ability to set the default branch hint at any scope, for examplecomptime { @setBranchHint(.reachable); }
at file scope. Note that it is already planned to allow overriding safety checking at any scope via this same mechanism.I think a reasonable path forward would be the more aggressive default (B) combined with the ability to override at any scope.
This has implications for incremental compilation and parallel semantic analysis because it allows the compiler to treat every reachable function as a root node in the dependency graph.
Combined with escape analysis, this would provide a way to report use-after-free of stack locals as compile errors (#3180). For example, if a function always returns a pointer to a local variable, and that function's returned pointer is accessed in a reachable branch, a compile error can be emitted.
If this were implemented,
assert
could becomeinline
, making it automatically report compile errors in the case that the asserted value was comptime-known (#425).The text was updated successfully, but these errors were encountered: