Consider the following code, which has an unreachable call to undefinedFunction
.
void undefinedFunction();
template <bool b = false>
void foo()
{
static_assert(b == false);
if (b)
undefinedFunction();
}
int main()
{
foo();
}
GCC compiles and links this without complaint. With the static_assert
, it's hard to see how the compiler could do anything different, but does the standard have anything to say about this? What if the static_assert
is removed? Is the compiler at all obligated to remove the branch or can it actually emit an unreachable call instruction that will cause the linker to complain?
Normally, calling a function odr-uses it, with two exceptions: if it is pure virtual, or if it is not potentially evaluated ([basic.def.odr]/5). An expression is potentially evaluated unless it is an unevaluated operand or a subexpression thereof ([basic.def.odr]/2). Unevaluated operands occur in typeid
, sizeof
, noexcept
, and decltype
, none of which applies here. Therefore undefinedFunction
is odr-used as long as foo
is instantiated, which it is. There is no exception for "statically unreachable" code.
According to the C++ standard §3.2/p4 One-definition rule [basic.def.odr] (emphasis mine):
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8). An inline function shall be defined in every translation unit in which it is odr-used.
The template function foo
is instantiated undefinedFunction
is odr-used (i.e., a definition of undefinedFunction
is required). It doesn't matter if if
clause is not evaluated. Consequently, the program is ill formed and since no diagnostic is required it may link or it may not.
As others commented, two separate steps contribute to this. The unreachable code may be erased by the optimizer, so the link for undefinedFunction()
is never requested.
The compilation step does not care for symbols that are not defined
(more information about compilation in this community answer).
This is independent of the static_assert
. You can have undefined references in template code that never gets initialized, and the compilation succeeds, as the compiler never considers the code, it never emit requirement for the link.
If the symbol gets through, and is requested in some later step, linkage will fail. This is the same that happens when you compile a library with template classes, and later tries to use a template with argument types for which the class was not explicitly initialized, you will get undefined references to types using a library that compiled fine on it's own.
If you wish to exam if you compiler is actually eliminating dead code, this answer details about profiling dead code in GCC.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With