6

When using gcc or clang, it's generally a good idea to enable a number of warnings, and a first batch of warnings is generally provided by -Wall. This batch is pretty large, and includes the specific warning -Wunused-function.

Now, -Wunused-function is useful to detect static functions which are no longer invoked, meaning they are useless, and should therefore preferably be removed from source code. When applying a "zero-warning" policy, it's no longer "preferable", but downright compulsory.

For performance reasons, some functions may be defined directly into header files *.h, so that they can be inlined at compile time (disregarding any kind of LTO magic). Such functions are generally declared and defined as static inline. In the past, such functions would probably have been defined as macros instead, but it's considered better to make them static inline functions instead, whenever applicable (no funny type issue).

OK, so now we have a bunch of functions defined directly into header files, for performance reasons. A unit including such a header file is under no obligation to use all its declared symbols. Therefore, a static inline function defined in a header file may reasonably not be invoked.

For gcc, that's fine. gcc would flag an unused static function, but not an inline static one. For clang though, the outcome is different : static inline functions declared in headers trigger a -Wunused-function warning if a single unit does not invoke them. And it doesn't take a lot of flags to get there : -Wall is enough.

A work-around is to introduce a compiler-specific extension, such as __attribute__((unused)), which explicitly states to the compiler that the function defined in the header may not necessarily be invoked by all its units. OK, but now, the code which used to be clean C99 is including some form of specific compiler extension, adding to the weight of portability and maintenance.

The question therefore is more about the logic of such a choice : why does clang selects to trigger a warning when a static inline function defined in a header is not invoked ? In which case is that a good idea ?

And what does clang proposes to cover the relatively common case of inlined functions defined in header file, without requesting the usage of compiler extension ?

edit : After further investigation, it appears the question is incorrect. The warning is triggered in the editor (VSCode) using clang linter applying a selected list compilation flags (-Wall, etc.). But when the source code is actually compiled with clang and with exactly the same list of flags, the "unused function" warning is not present.

So far, the results visible in the editor used to be exactly the ones found at compilation time. It's the first time I witness a difference.

So the problem seems related to the way the linter uses clang to produce its list of warnings. That's a much more complex and specific question.


Note the comment:

OK, sorry, this is actually different from expectation. It appears the warning is triggered in the editor using clang linter with selected compilation flags (-Wall, etc.). But when the source code is compiled with exactly the same flags, the "unused function" warning is actually not present. So far, the results visible in the editor used to be exactly the ones found at compilation time; it's the first time I witness a difference. So the problem seems related to the way the linter uses clang to produce its list of warnings. It seems to be a more complex question [than I realized].

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Cyan
  • 13,248
  • 8
  • 43
  • 78
  • 4
    The simple fact is: Clang sucks at warnings. They put a lot of effort into a few highly-visible warnings that new programmers hit, then ignore bug reports for years. – o11c Jan 28 '20 at 23:52
  • 1
    It'd be helpful if you added (1) the `clang` version you're using and (2) a minimal example of header file + source file to reproduce the issue. As it's currently written, this post has no real answer. – Marco Bonelli Jan 29 '20 at 00:14
  • 4
    Using Apple clang version 11.0.0 (clang-1100.0.33.17), I get the warning when the `static inline` function is written in a source file but not when it is written in a header. This seems to be the inverse of what you claim to be seeing. Can you provide sample code to prove your point? I'll add a semi-answer with my code if you want (but only after you shown your code). GCC 9.2.0 does not complain about unused `static inline` functions (at least under `-Wall`) in either header or source file. – Jonathan Leffler Jan 29 '20 at 00:46
  • 1
    `static inline` is poor form, that was a compatibility hack that came about because GNU C and ISO C define different semantics for any other use of `inline` besides this . Although I think it is a fairly good argument that the compiler should recognize the idiom and not warn, or at least give the option to recognize the idiom. In ISO C the keyword `static` should not be used – M.M Jan 29 '20 at 00:46
  • @M.M — curious; I'd argue that you should always use `static inline` and never risk using `inline` without `static`. YMMV, clearly. – Jonathan Leffler Jan 29 '20 at 00:48
  • @JonathanLeffler Generally speaking I prefer to follow the ISO standard and the "risk" lies with those who don't want to follow it – M.M Jan 29 '20 at 00:50
  • 3
    @M.M: `static inline` is absolutely not "poor form". External `inline` is almost never what you actually want (neither the old GNU version which is deprecated, nor the C version) when it comes to having reasonable ABI and linkage boundaries. – R.. GitHub STOP HELPING ICE Jan 29 '20 at 01:01
  • See [Is `inline` without `static` or `extern` ever useful in C99?](https://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99) and [What does `extern inline` do?](https://stackoverflow.com/questions/216510/what-does-extern-inline-do/216546#216546), etc. – Jonathan Leffler Jan 29 '20 at 05:22
  • I re-`#define` `inline` (I actually use `$inl` cuz `inline`'s too long a word) as `__attribute((__always_inline__,__unused__)) inline static` and then I don't have this problem. :). – Petr Skocik Jan 29 '20 at 22:30
  • `#ifndef __GNUC__ #define __attribute__((...)) #endif` works for `clang` and `gcc`. It's usually what I use. – S.S. Anne Jan 30 '20 at 12:01

4 Answers4

1

I'm not sure you'll find any "why". I think this is a bug, possibly one that they don't care to fix. As you hint in your question, it does encourage really bad practice (annotation with compiler extensions where no annotation should be needed), and this should not be done; rather, the warning should just be turned off unless/until the bug is fixed.

If you haven't already, you should search their tracker for an existing bug report, and open one if none already exists.

Follow-up: I'm getting reports which I haven't verified that this behavior only happens for functions defined in source files directly, not from included header files. If that's true, it's nowhere near as bad, and probably something you can ignore.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • I read the same report. Unfortunately, the warnings are effectively applied to functions declared and defined in `*.h` header file. – Cyan Jan 29 '20 at 00:26
  • 1
    @Cyan: Can you provide instructions on reproducing the issue with the definition in a header file? – R.. GitHub STOP HELPING ICE Jan 29 '20 at 01:03
  • OK, sorry, this is actually different from expectation. It appears the warning is triggered _in the editor_ using `clang` linter with selected compilation flags (`-Wall`, etc.). But when the source code is compiled with exactly the same flags, the "unused function" warning is actually not present. So far, the results visible in the editor used to be exactly the ones found at compilation time, it's the first time I witness a difference. So the problem seems related to the way the linter uses `clang` to produce its list of warnings. It seems a more complex question. – Cyan Jan 29 '20 at 22:11
  • 2
    @Cyan: I suspect the editor/linter is running the preprocessor as a separate step to get a flat preprocessed file to pass to clang, and this is triggering clang to warn now that the unused function is "in the source file rather than a header". – R.. GitHub STOP HELPING ICE Jan 29 '20 at 22:14
  • I agree @R. , it seems a likely explanation – Cyan Jan 29 '20 at 22:17
1

From C++17 and C23 there's [[maybe_unused]]. It's the compiler-independent way to annotate unused functions (and other objects), and it reliably silences the warning while being clear about the intent. Use as

[[maybe_unused]] int lower_limit_of_calls()
{
   return 1;
}
tobi_s
  • 1,324
  • 13
  • 16
-2

'#ifdef USES_FUNTION_XYZ'

One would have to configure the used inline functions before including the header. Sounds like a hassle and looks clumsy.

horst
  • 1
-3

When using gcc or clang, it's generally a good idea to enable a number of warnings,

When using any C compiler, it's a good idea to ensure that the warning level is turned up, and to pay attention to the resulting warnings. Much breakage, confusion, and wasted effort can be saved that way.

Now, -Wunused-function is useful to detect static functions which are no longer invoked, meaning they are useless, and should therefore preferably be removed from source code. When applying a "zero-warning" policy, it's no longer "preferable", but downright compulsory.

Note well that

  1. Such zero-warning policies, though well-intended, are a crutch. I have little regard for policies that substitute inflexible rules for human judgement.

  2. Such zero-warning policies can be subverted in a variety of ways, with disabling certain warnings being high on the list. Just how useful are they really, then?

  3. Policy is adopted by choice, as a means to an end. Maybe not your choice personally, but someone's. If existing policy is not adequately serving the intended objective, or is interfering with other objectives, then it should be re-evaluated (though that does not necessarily imply that it will be changed).

For performance reasons, some functions may be defined directly into header files *.h, so that they can be inlined at compile time (disregarding any kind of LTO magic).

That's a choice. More often than not, one affording little advantage.

Such functions are generally declared and defined as static inline. In the past, such functions would probably have been defined as macros instead, but it's considered better to make them static inline functions instead, whenever applicable (no funny type issue).

Considered by whom? There are reasons to prefer functions over macros, but there are also reasons to prefer macros in some cases. Not all such reasons are objective.

A unit including such a header file is under no obligation to use all its declared symbols.

Correct.

Therefore, a static inline function defined in a header file may reasonably not be invoked.

Well, that's a matter of what one considers "reasonable". It's one thing to have reasons to want to do things that way, but whether those reasons outweigh those for not doing it that way is a judgement call. I wouldn't do that.

The question therefore is more about the logic of such a choice : why does clang selects to trigger a warning when a static inline function defined in a header is not invoked ? In which case is that a good idea ?

If we accept that it is an intentional choice, one would presume that the Clang developers have a different opinion about how reasonable the practice you're advocating is. You should consider this a quality-of-implementation issue, there being no rules for whether compilers should emit diagnostics in such cases. If they have different ideas about what they should warn about than you do, then maybe a different compiler would be more suitable.

Moreover, it would be of little consequence if you did not also have a zero-warning policy, so multiple choices on your part are going into creating an issue for you.

And what does clang proposes to cover the relatively common case of inlined functions defined in header file, without requesting the usage of compiler extension ?

I doubt that clang or its developers propose any particular course of action here. You seem to be taking the position that they are doing something wrong. They are not. They are doing something that is inconvenient for you, and that therefore you (understandably) dislike. You will surely find others who agree with you. But none of that puts any onus on Clang to have a fix.

With that said, you could try defining the functions in the header as extern inline instead of static inline. You are then obligated to provide one non-inline definition of each somewhere in the whole program, too, but those can otherwise be lexically identical to the inline definitions. I speculate that this may assuage Clang.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157