gcc warns about unused static functions, but not static inline: is there a practical distinction?
Asked Answered
W

2

18

My version (5.4) of gcc warns about unused static functions, even in a header file when -Wall is used. It doesn't complain if the same functions are defined static inline or simply inline.

For example, the following function in a file unused.h:

static void foo() {}

... when included in a test.cpp file as follows:

#include "unused.h"

Generates the following compiler diagnostic when compiler with -Wall:

In file included from test.cpp:11:0:
unused.h: At global scope:
unused.h:9:13: warning: ‘void foo()’ defined but not used [-Wunused-function]
 static void foo() {}
             ^

It is common practice, as far as I know, to include headers with many utility functions, only a handful of which might be used in any given source file. This behavior means that I get warnings for any functions I don't use which are declared only static.

As a practical matter I can simply change these to static inline to get rid of the warning (or turn off the specific warning entirely, but I do find it useful from time to time), but it seems that large utility functions which won't benefit from inlining1 are more logically declared static2.

As far as I know unused static functions (just like static inline) are simply dropped by gcc when the translation unit is compiled, so they pose no binary size or link-time overhead at all.

Am I missing something here? Is there a good reason that unused static functions are more problematic than static inline?


1 Yes, I'm aware it's a hint only but gcc actually takes the hint in many cases.

2 Or perhaps better, only declared in the header file and defined somewhere else in a .cpp file - but that inhibits header-only use which is sometimes convenient.

Wednesday answered 28/11, 2017 at 23:38 Comment(19)
Do you have an example header we can use to reproduce this problem and test possible solutions?Bluenose
@Bluenose - updated the question.Wednesday
Is there any reason why the implementation has to be in the header file? Could you not have a separate stub .cpp file with the necessary implementations? The optimizer can decide what to do with them then. It's not clear why this is flagged as static. Is it used elsewhere in the header file some how and important enough that it stay local to the file?Bluenose
Why do you declare functions in a header to be static to begin with? Why not just inline? I mean, something that's in a header is not really local to some translation unit to begin with.Zeke
@Bluenose - because it's convenient to have it in a header file, especially when nothing else in the header file needs corresponding implementation in another file. You can simply include the file in your project and forget about it. Once you have an implementation file you need to move that file into somewhere your build system picks it up, or build a library and link against it, etc.Wednesday
@Wednesday It's just a different sort of annoying. I find people use inline as the silver bullet to fix their problems when really the best solution is to back up and evaluate why they're defining code inline in the first place. This tends to bloat your executable with duplicated copies of the same function if they are truly inlined.Bluenose
@BaummitAugen - because (a) inline also serves as a hint that the function might be good for inlining, so I prefer static for functions that I don't want to hint for inlining (e.g., large or non-performance sensitive functions) and (b) in header files shared with C inline alone doesn't work there (sometimes an out-of-line function is required, so it's not header-only, etc).Wednesday
a) does not apply, compilers rightfully don't care about that "hint" usually (and even when they do, are you sure you got that right better than the compiler?). b) only applies if you are mixing in some language you did not tag. Is your question about compatibility between C and C++?Zeke
@BaummitAugen - a common misunderstanding (essentially a second-order myth). Compilers can still use it as a hint and gcc in its newest version certainly does. Here are two identical functions (changed one constant so they aren't folded together) and gcc only inlines the one one marked inline in an identical calling context). About (b) I'm sorry, I can't stand the flames anymore from tagging a question both C and C++. Because I have a valid reason to use static functions that might involve interop with some other language doesn't mean I have to tag it.Wednesday
I'd suggest just disabling the "unused function" warning if you have no interest in seeing itEconomist
@Economist - I do want it, in general. As mentioned above it is usually nice when it applies to a function in a .cpp file and in some other cases. For now I'm just adding inline to make the functions static inline or I can use __attribute((unused))__ or other gcc magic like #pragma GCC diagnostic ignored "-Wunused-function" to make it go away on a case-by-case basis. The question is more about why gcc does this and if in particular I'm doing "something wrong" with my static-but-not-inline functions.Wednesday
"a common misunderstanding (essentially a second-order myth)" Not really. You did not enable -finline-functions, neither explicitly nor implicitly through -O3, so you are practically operating in "trust me, I know better" mode. You are, in the end, stopping gcc from taking the hint as a hint and mostly force it to treat it as a command instead (for functions that are not "small"). Disinterest about optimization flags does not justify by hand in-code optimization.Zeke
"I can't stand the flames anymore from tagging a question both C and C++." If the question is about code that's to be used as both C and C++ for proper reasons, I'll gladly jump to your side in that flame war, universally damming the double-tag is a wrong reflex originating from misguided beginner questions.Zeke
@BaummitAugen - what's your point? gcc does tons of inlining even at -O2 because various inlining options are active there (without inlining it would generate terrible code for all the "zero cost abstractions" that are prevalent in C++). You can try for yourself: make the function smaller and it will inline it, even without inline and at -O2. gcc uses the presence of an inline specifier on a function in its inline cost heuristic. Is that hard to believe? I know it's vogue to say "Duh, compiler ignores inline!!!!" but it's no more true than the claim it is countering.Wednesday
"You can try for yourself: make the function smaller and it will inline it, even without inline and at -O2" Yes, because the docs say so. "gcc uses the presence of an inline specifier on a function in its inline cost heuristic" Only if you tell it to. "Is that hard to believe?" Nope, but it's apparently easy to believe -O2 and -O3 are some magic black-box commands that just "do the right thing". So my point is: Tell the compiler what you want it to do, and inlining functions is one of the things you shouldn't do by hand.Zeke
@BaummitAugen - the answer as always is "it depends". The compiler has some advantages when it compiles to code generation decisions and so I do I. When it comes to compiling a small function I'll generate better code than the compiler 95% of the time (ignoring ties) - but the compiler's time is nearly free and mine isn't, so I'm happy to let the compiler generate 99% of my code for me. For the remaining 1% I totally make use of hints like inline, hot, cold, likely, unlikely, pure, etc. We are getting more hints these days, not less.Wednesday
More to the point, you made a claim that the compiler simply ignores inline and I showed at the mainstream, most commonly used optimization level on the most recent version of gcc it plainly does not. That's all I have to do. Showing that that inline might not make a difference in some other scenario, with some other compilation flags is totally irrelevant. For my use case, it makes a difference, I showed you actual code, end of story.Wednesday
You are right that gcc in particular does use the hint under some commonly used setting. I would, however, still disagree to the claim that inline is a useful compiler hint; even for your simple example the not exactly rarely used clang inlines the function anyways. I don't dispute the fact that you can out-perform the compiler from time to time, and granted, it sometimes certainly does matter a lot, but in those cases I think you should be a lot more explicit than a mere addition or omission of the inline keyword. Unless you control both the exact compiler version and flags, but that's rare.Zeke
Also, note that commonly used flags are not necessarily correctly used flags, if that inlining flag matters, you'll have to ensure each supported platform uses the proper flags anyways.Zeke
E
14

The warning is because an unused static function might indicate a logic error: why would you have written such a function if it was never called?

However, it is a common idiom to have static inline functions in a header file. Those functions might only be used by some translation units that include the header. It would be annoying if the compiler gave a warning for a translation unit that didn't happen to use one of the functions.

If you deliberately have an unused static non-inline function, you probably will want to either disable the warning entirely, or use a compiler-specific feature to suppress the warning for that function.


Someone asked, "why would you use static inline anyway?". Well, in new C++ you mostly wouldn't use it. However, in C it is a reasonable thing to do. (This is because static inline means the same thing in ISO C and GNU C; however inline without static behaves differently in ISO C than GNU C, so defaulting to static inline just avoids all those issues with no down-side).

People might use static inline in headers that are to be included from both .c and .cpp files; or perhaps they just carry over that habit from C to C++. In the latter case, IMHO it would be annoying for the compiler to warn about something that, although unnecessary, is not a mistake or a problem either.

Economist answered 28/11, 2017 at 23:46 Comment(9)
Well I have "written" (or something else wrote) such a function for the same reason that most functions in header files you are never called: you include a header file because you need to call at least one function - but not all of them! It makes sense to warn if you actually wrote a function in a .cpp file that you didn't call, but not if you included a header file with various functions.Wednesday
Why would you define them as static, rather than just inline?Digitoxin
@Wednesday The preprocessor combines all the relevant source files into a translation unit before the compiler runs; there's no separation of ".cpp file" and "header" at the point the warnings are being generated.Economist
@NeilButterworth - because (a) inline also serves as a hint that the function might be good for inlining, so I prefer static for functions that I don't want to hint for inlining (e.g., large or non-performance sensitive functions) and (b) in header files shared with C inline alone doesn't work there (sometimes an out-of-line function is required, so it's not header-only, etc). Don't get me wrong, the solution is static inline but I'm curious if somehow only-static functions are some kind of anti-pattern.Wednesday
@Economist - at a conceptual "compiler 101" level sure, where cpp runs as a separate process and leaves no trace, but compilers certainly do know where the source came from (how else to generate debug info, for example) and clang for example warns only for unused functions in the actual base .cpp file, not in header files (btw it warns for static inline functions too, if they are unused and appear in the .cpp file).Wednesday
@Neil Butterworth: In my case in order to "see" these inline functions in profiler, I had to be able to disable inlining. In order to do that I used macro INLINE_ instead of an explicit keyword inline. Normally I'd have #define INLINE_ inline, but for profiling builds I'd just define INLINE_ as an empty macro. Without static in header files the compilation would break down. There might be better ways to do this, but that was one way.Melany
@Wednesday Fair enough. I expanded my answer to cover Neil Butterworth's pointEconomist
@Economist - you mention that in new C++ you wouldn't use static inline - is the more idiomatic thing just inline then, or anonymous namespace + inline or what? I guess there are still some practical differences between inline and static inline if you take the address of the function (all pointers to the same inline function must compare the same, across translation units, but the opposite is true of static, I think).Wednesday
@Wednesday the main purpose of inline is to allow code in headers; there's nothing to gain by using static there in C++. If you were using it in a .cpp file for the optimization feature then yeah, you could use static I guessEconomist
H
0

For such functions you need to set attribute __attribute__((unused)).

Homestead answered 7/1, 2020 at 0:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.