Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warn for function argument that is NOT a compile time constant

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 an int.
  • 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 an int and thefun 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.

like image 654
zwol Avatar asked Aug 26 '19 15:08

zwol


2 Answers

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.

like image 91
KamilCuk Avatar answered Oct 28 '22 16:10

KamilCuk


There is no way to do what you describe without GNU extensions.

This portable approach gives a hard error (because _Static_assert requires a constant expression):

#define thefun(a, b) \
({ \
   _Static_assert(b == 0, \
       "'thefun' called with second argument not NULL"); \
   real_thefun(a, b); \
})

However, there is one fortified-style approach that works on both GCC and Clang:

extern void thefun_called_with_nonnull_arg (void)
    __attribute__((__deprecated__(
        "'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((unsigned short)(unsigned long)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);
}

Tested with GCC 8.3.0 and Clang 8.0.0.

See GCC bug report #91554 for more information about the need for the casts.

like image 44
S.S. Anne Avatar answered Oct 28 '22 14:10

S.S. Anne