Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can statically unreachable calls cause undefined reference errors?

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?

like image 820
Always Confused Avatar asked Apr 08 '16 22:04

Always Confused


3 Answers

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.

like image 89
Brian Bi Avatar answered Oct 08 '22 21:10

Brian Bi


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.

like image 20
101010 Avatar answered Oct 08 '22 20:10

101010


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.

like image 1
Kahler Avatar answered Oct 08 '22 21:10

Kahler