Issue
Suppose I am maintaining a library function that takes two arguments, both pointers. The second argument exists only for backward compatibility; callers should always pass a NULL. I would like to put something into my header file that makes the compiler issue warnings if the second argument is not a compile-time constant NULL. I thought I would be able to do this using GCC's __builtin_constant_p
and __attribute__((warning))
extensions:
extern void thefun_called_with_nonnull_arg (void)
__attribute__((__warning__(
"'thefun' called with second argument not NULL")));
extern int real_thefun (void *, void *);
static inline int
thefun (void *a, void *b)
{
if (!__builtin_constant_p(b) || b != 0)
thefun_called_with_nonnull_arg();
return real_thefun(a, b);
}
int warning_expected (void *a, void *b)
{
return thefun(a, b);
}
int warning_not_expected (void *a)
{
return thefun(a, 0);
}
But this doesn't work with any version of GCC I have tested. I get warnings for both calls to thefun
. (Compiler Explorer demo.)
Can anyone suggest an alternative construct that will produce a warning for warning_expected
, and not for warning_not_expected
?
Notes:
- Curiously, the above does work if
b
is anint
. - The above uses GCC-specific extensions, however a solution that works on a broader variety of compilers would be welcome. (In particular, clang does not implement
attribute((warning))
and I haven't had any luck finding an alternative.) - A solution that still works when optimization is turned off would be preferable to one that doesn't. (The above does not work with optimization turned off, even if
b
is anint
andthefun
is marked always-inline.) - A solution that doesn't involve defining
thefun
as a macro would be preferable to one that does. - The header has to work when included from C programs and from C++ programs. A modest amount of ifdeffage is acceptable.
- It must be a warning, not a hard error, unless
-Werror
or equivalent is active.
EDIT: Based on Kamil Cuk's discovery that the unwanted warning can be suppressed by casting the pointer to an integer of a different size, I have determined that this is an oversight in the implementation of __builtin_constant_p
and filed GCC bug report #91554. I'd still be interested in answers that provide ways to do this with clang, icc, or any other compiler that's commonly used together with GNU libc.
Solution
I finally managed to get it to work:
if (!__builtin_constant_p((int)(uintptr_t)b) || b != 0) {
With this you get only one warning.
It seems that gcc
can't do __builtin_constant_p
on a pointer type. The __builtin_constant_p(b)
always returns 0, so the warn function is always linked. Casting b
to int
strangely works. Although it looses precision in the pointer value, we don't care about it, cause we only check if it's a constant.
Answered By - KamilCuk