Why Git Has a Variable Named false_but_the_compiler_does_not_know_it
A small C trick that keeps Clang from flagging valid code as unreachable
Today, in weird C code tricks, I want to show you a small example from Git’s source code.
Recently, I was poking around the Git source when a directory name caught my eye, it was named “compiler-tricks”. I thought, “This is a promising name, let’s see what’s inside of it”.
Inside this directory, there was a file called not-constant.c, and the whole file contained just this:
#include <git-compat-util.h>
int false_but_the_compiler_does_not_know_it_ = 0;The variable name made me curious. Why would Git need a global variable whose name says that the value is false, but the compiler does not know it?
To understand this, let’s first look at where this variable is used.
It is used inside a macro called NOT_CONSTANT in git-compat-util.h:
/*
* Prevent an overly clever compiler from optimizing an expression
* out, triggering a false positive when building with the
* -Wunreachable-code option. false_but_the_compiler_does_not_know_it_
* is defined in a compilation unit separate from where the macro is
* used, initialized to 0, and never modified.
*/
#define NOT_CONSTANT(expr) ((expr) || false_but_the_compiler_does_not_know_it_)
extern int false_but_the_compiler_does_not_know_it_;The macro takes an expression and performs a logical OR with this global variable:
(expr) || false_but_the_compiler_does_not_know_it_Now, the global variable is initialized to 0, and Git never modifies it (I checked the entire Git codebase). So, logically, this expression is equivalent to:
(expr) || 0which is simply:
exprSo, why write the code this way?
The variable false_but_the_compiler_does_not_know_it_ is not declared as const. It also has external linkage and is defined in a different translation unit from the code that uses it. As a result, while compiling a file that uses NOT_CONSTANT, the compiler cannot prove that the value of this variable will always remain 0.
In practice, it is always 0. But from the compiler’s point of view, someone else could modify it.
This is the whole trick. Git creates a value that is false at runtime, but not obviously false to the compiler. At this point, you might be wondering: what is the point of this trickery? To answer that, we need to look at where this macro is actually used.
Tripo AI Raises Nearly $200M to Advance AI 3D and World Models (Sponsored)
Tripo AI builds AI 3D foundation models for high-demand 3D workflows across interior design, e-commerce, gaming, film, VR/AR, digital twins, robotics, and interactive entertainment.
Used by more than 20 million users worldwide, Tripo AI helps creators, developers, and studios turn ideas into high-quality 3D assets faster, from product visualization and home design to game-ready assets and simulation workflows.
Following nearly $200 million in Series A+ and A++ financing, Tripo AI is accelerating its research roadmap, product development, and global creator ecosystem. Its new research initiative, Project Eden, explores how AI 3D can move beyond single-asset generation toward persistent, editable, reusable, multi-agent interactive worlds.
With Tripo AI, you can explore:
AI-generated 3D assets and production-ready meshes
Native 8K AI textures
Intelligent part segmentation for editable 3D workflows
Project Eden, Tripo AI’s world model research initiative
Where Git Uses This Trick
So, the question is, where does Git use this macro? One of the interesting uses appears in refs/files-backend.c. Git has code that tries to create a symbolic ref using a symlink. If that succeeds, it continues to the next update. If it fails, it falls back to creating a regular symbolic ref.
The code looks like this:
/*
* By using the `NOT_CONSTANT()` trick, we can avoid
* errors by `clang`'s `-Wunreachable` logic that would
* report that the `continue` statement is not reachable
* when `NO_SYMLINK_HEAD` is `#define`d.
*/
if (NOT_CONSTANT(!create_ref_symlink(lock, update->new_target)))
continue;
To understand why this is needed, we need to look at how create_ref_symlink is defined.
Depending on the build configuration, it can either be a real function:
static int create_ref_symlink(struct ref_lock *lock, const char *target)
{
/* ... */
}Or, it can be compiled away into a constant expression:
#if defined(NO_SYMLINK_HEAD) || defined(WITH_BREAKING_CHANGES)
#define create_ref_symlink(a, b) (-1)
#endif
So, when NO_SYMLINK_HEAD is defined, this expression:
!create_ref_symlink(lock, update->new_target)effectively becomes:
!(-1)In C, -1 is treated as true. Therefore, !(-1) is false. So, the compiler sees something like this:
if (0)
continue;
This causes the Compiler (clang in this case) to raise a warning that the continue statement is unreachable.
But the problem is that this is only true for one build configuration. On other platforms, where NO_SYMLINK_HEAD is not defined, create_ref_symlink is a real function and the continue statement is reachable.
Git wants to keep the same source-level control flow across these configurations. So it wraps the condition in NOT_CONSTANT, so that the compiler sees something like this:
if (!(-1) || false_but_the_compiler_does_not_know_it_)
continue;
At runtime, this still evaluates to false because false_but_the_compiler_does_not_know_it_ is 0.
But at compile time, the compiler cannot prove that the condition is always false. As a result, it doesn’t emit the unreachable-code warning.
Why Git Added This Macro
While that explained what this code does and where it is used, I was also interested in looking at the commit history behind it to udnerstand the original motivation.
The NOT_CONSTANT macro was added in commit 82e79c63642c by Junio C Hamano (the maintainer of Git) in March 2025. The motivation was Git’s experiment with Clang’s -Wunreachable-code warning.
This warning can be useful because it can catch genuinely unreachable code. But it can also produce false positives when the same source code is compiled across different platforms and build configurations.
The original case was not the symbolic ref code. It was in run-command.c, around sigfillset().
POSIX says that sigfillset() can fail, so Git wants to check its return value. But on some platforms, the system headers make it obvious to the compiler that sigfillset() always succeeds. In such builds, Clang sees the error handling branch and concludes that it is unreachable.
One option would be to disable -Wunreachable-code, but that would also disable the useful warnings. Git’s approach was to keep the warning enabled and add a small escape hatch for cases where the warning is known to be a false positive.
The later refs change used the same escape hatch. In commit 3860985105a, Johannes Schindelin added NOT_CONSTANT around the create_ref_symlink condition because Clang was complaining in NO_SYMLINK_HEAD builds.
So, this macro is a warning-management trick. It tells the compiler: this expression may look constant in this build, but please do not treat it as a compile-time constant.
Could This Be Done Differently?
But this problem is not unique to Git, many projects face similar problems and solve them in different ways. The following are a few alternate solutions.
The most direct option is to directly wrap the call to create_ref_symlink using conditional preprocessor directives so that it compiles only on platforms where NO_SYMLINK_HEAD is not defined.
#ifndef NO_SYMLINK_HEAD
if (!create_ref_symlink(lock, update->new_target))
continue;
#endif
This would remove the unreachable code in NO_SYMLINK_HEAD builds. But it also spreads build-configuration logic into the middle of the control flow.
Another option is to disable the warning locally:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
if (!create_ref_symlink(lock, update->new_target))
continue;
#pragma clang diagnostic pop
But this is compiler-specific and noisy. It also does not explain the real issue to anyone reading the code.
You could also use volatile:
static volatile int not_constant_zero;
if ((expr) || not_constant_zero)
...
This would also prevent the compiler from treating the value as a normal constant. But volatile has a stronger meaning. It tells the compiler that every read of the variable must really happen because the value may change outside the compiler’s normal model. That is useful for things like memory-mapped I/O, but it is too strong for this case.
The approach used by Git is much more precise. It uses an ordinary external variable defined in a separate translation unit. This is enough to hide the value from the compiler during normal compilation, without introducing volatile semantics.
You could also use a function:
int false_but_the_compiler_does_not_know_it(void);
if ((expr) || false_but_the_compiler_does_not_know_it())
...
But then you may introduce an extra function call in the generated code unless the compiler or linker can optimize it away.
So, the global variable trick is not the only way to do this. But it gives Git a small, reusable annotation for a very specific situation that they can apply anywhere this problem surfaces.
If you are enjoying this article, you may also like my virtual memory article/ebook. It explains virtual memory from first principles and builds up to the details programmers usually hear about but rarely understand deeply: page tables, page faults, TLB shootdowns, NUMA placement, and more.
Does the Linker Remove This Branch?
This brings us to another interesting question.
While this trick fools the compiler into believing that the branch may evaluate to true at runtime, in reality, we know that on NO_SYMLINK_HEAD builds, the branch is always false. So, the question is will link-time optimization (LTO) eliminate the branch from the final binary?
To check this, I wrote a small reproducer with three files, similar to how Git structures this code.
First, let’s see the file that uses the variable:
extern int false_but_the_compiler_does_not_know_it_;
extern int slow_path(void);
int branch_test(void)
{
if (0 || false_but_the_compiler_does_not_know_it_)
return slow_path();
return 0;
}
Then, the file that defines the variable:
int false_but_the_compiler_does_not_know_it_ = 0;And finally, a small main:
int branch_test(void);
int slow_path(void)
{
return 42;
}
int main(void)
{
return branch_test();
}To make this visible, I used a small reproducer where the branch calls slow_path(). This makes the branch easy to spot in the generated assembly if it survives optimization. I compiled it with Clang 18 using normal optimization and warning flags enabled:
clang-18 -O2 -Wall -Wextra -Wunreachable-code -c branch.c -o branch.o
clang-18 -O2 -Wall -Wextra -Wunreachable-code -c main.c -o main.o
clang-18 -O2 -Wall -Wextra -Wunreachable-code -c not_constant.c -o not_constant.o
clang-18 -O2 -Wall -Wextra -Wunreachable-code \
branch.o main.o not_constant.o -o normal
Then I used objdump to inspect the final executable. objdump -d disassembles the binary, which lets us see the actual machine instructions that survived compilation and linking:
objdump -d normalThe final binary still contains the load and the branch. The following is a cleaned-up version of the output produced by objdump to focus on the interesting bits.
lea false_but_the_compiler_does_not_know_it_(%rip), %rax
cmpl $0x0, (%rax)
jne slow_path
xor %eax, %eax
retLet’s break this down instruction by instruction:
lea false_but_the_compiler_does_not_know_it_(%rip), %raxThis computes the address of the global variable and stores it in the rax register.
cmpl $0x0, (%rax)This compares the value stored at that address with zero.
jne slow_pathIf the value is not zero, execution jumps to slow_path.
xor %eax, %eax
retOtherwise, the function returns 0.
So, we can see that with normal compilation and linking, the branch is still present in the final binary. The ordinary linker does not reason about the value of this global variable and doesn’t remove the branch.
PS: If you are interested in learning x86 assembly, I have a series in progress that teaches it. Check it out at the link below:
But then I tried the same experiment with link-time optimization:
clang-18 -O2 -Wall -Wextra -Wunreachable-code -flto \
branch.c main.c not_constant.c -o lto
objdump -d lto
With LTO enabled, the branch disappears. The final main simply becomes:
<main>:
xor %eax, %eax
retSo what changed?
Normally, the compiler optimizes each translation unit separately. When it compiles branch.c, it only sees this declaration:
extern int false_but_the_compiler_does_not_know_it_;It does not see the definition in not_constant.c, so it cannot know that the value is always 0.
With link-time optimization, the compiler keeps an intermediate representation of the code and performs optimization after seeing all the translation units together. At that point, it can see both the use of the variable and its definition. It can also see that nothing modifies it. As a result, it can prove that the condition is always false and safely remove the branch.
That gives Git the best of both worlds. The trick prevents Clang from producing an unreachable-code warning during normal compilation. Later, if LTO is enabled, the compiler can still remove the useless branch from the final binary. In effect, this is similar to the conditional-compilation solution we discussed earlier, but without muddying the code with #ifndef directives all over the place.




I hope Claude never writes such beautiful code.
PS: Great article as always, Abhinav. Got to learn something new.