2

I tried to use BUILD_BUG_ON_ZERO in a simple user space application and it is failed to compile

#include <stdio.h>

#define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); }))) 
int main()
{
    int i;
    BUILD_BUG_ON_ZERO(i);
    return 0;
}

error: bit-field ‘<anonymous>’ width not an integer constant
 #define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); })))

Can anyone please provide me hints on the error.

md.jamal
  • 4,067
  • 8
  • 45
  • 108
  • [Related](https://stackoverflow.com/questions/9229601/what-is-in-c-code) . `BUILD_BUG_ON_ZERO` needs a compile-time constant as its argument ... – dragosht Feb 28 '20 at 09:37
  • Is 'i' not a compile time constant – md.jamal Feb 28 '20 at 09:39
  • No. it's not. Even `const int i = 5;` isn't :) – P.P Feb 28 '20 at 09:43
  • Can you please explain further, i am unable to get compile-time constant in the above statement – md.jamal Feb 28 '20 at 09:44
  • Anonymous bit-fields isn't C standard as far as I know. – Lundin Feb 28 '20 at 10:16
  • 1
    Just forget about dirty tricks like these and upgrade your code to standard C. You've had `_ Static_assert` in C for 9 years now. That's a very long time. Time to learn how to use it. – Lundin Feb 28 '20 at 10:17
  • @Lundin There are places where ``_Static_assert()`` cannot be used (using only standard C) such as in the macro [``ARRAY_SIZE()``](https://stackoverflow.com/a/57537491/6872717). However, with [GCC extensions](https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html) it can be used, and ``_Static_assert()`` is much cleaner that this dirty trick. But both rely on extensions, though. – alx - recommends codidact Apr 23 '20 at 03:12

3 Answers3

1

The macro BUILD_BUG_ON_ZERO is intended to be used with a constant expression as defined in 6.6 constant expression.

i isn't a constant expression - it's just a local variable. Even const qualified objects are not considered "constant expressions" in C (unlike C++). So you'd have to use:

  • literal values (such as 0) or,
  • a macro (#define value 0) or,
  • use an enum constant enum { value = 0,};)

to get a constant expression

P.P
  • 117,907
  • 20
  • 175
  • 238
1

BUILD_BUG_ON_ZERO is used in the kernel code as a compile-time assertion. Basically it would check if the compiler can evaluate the macro argument to 0 (at compile time) or fail the build otherwise.

/*
 * Force a compilation error if condition is true, but also produce a
 * result (of value 0 and type int), so the expression can be used
 * e.g. in a structure initializer (or where-ever else comma expressions
 * aren't permitted).
 */
#define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); })))

But as the comment points out, it has kind of a dual purpose, as it is also usable as a C expression, like this:

#define HWEIGHT64(w) (BUILD_BUG_ON_ZERO(!__builtin_constant_p(w)) + __const_hweight64(w))

By grepping throughout the kernel code you can find it is commonly used along with other constructs like the builtin __builtin_constant_p or __is_constexpr.

dragosht
  • 3,237
  • 2
  • 23
  • 32
1

Like the compiler says, it needs an integer constant expression. A variable is not a constant expression.

But regardless, that macro is broken beyond repair.

  • If you compile on a standard C compiler with BUILD_BUG_ON_ZERO(0) you get the error "struct has no named members" since anonymous bit-fields aren't standard C.
  • If you compile with gcc -std=gnu11 and BUILD_BUG_ON_ZERO(0) you get a clean compiler log. You do not get a bug on zero.
  • If you pass a non-zero value to the macro with a standard or GNU compiler, you get a compiler error. Thus the intention of the macro BUILD_BUG_ON_ZERO is apparently to create a bug on non-zero.

With standard C you can use _Static_assert and forget about this all, but if you are stuck with C90 and GCC extensions, a working macro can be written as:

#define ACTUAL_BUILD_BUG_ON_ZERO(e) typedef struct { int cant_have[1-2*!(e)]; } static_assert_t;
  • If passed a non-zero value, the ! converts it to zero and 1-2*0 = 1, which is the valid size for an array.
  • If passed a zero value, the ! converts it to 1 and 1-2*1 = -1, which is an invalid size for an array.

It has to be done this way because when compiling as GNU C, it still support zero-size arrays, as a remain from the time before flexible array members were standardized.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Hmm I do remember raging about how stupid this macro was when someone posted it [here](https://stackoverflow.com/questions/9229601/what-is-in-c-code), then I got bandwagon'd by the whole community and deleted my comments. But 8 years later, it seems that I was right...? Either I am making some stupid mistake or this is a huge bug scattered all around the whole Linux kernel. Can someone please verify that I'm sane and this is the exact macro as present in the kernel? Otherwise, Linus, we may have a problem here... – Lundin Feb 28 '20 at 10:48
  • 1
    Ok so it is a known bug since long. http://lkml.iu.edu/hypermail/linux/kernel/0703.1/1546.html. Hotfix: `#ifdef __linux__ MAINTAIN(SAFETY_DISTANCE_METERS, 10) #endif`. – Lundin Feb 28 '20 at 11:52