Skip to content

Conversation

thom311
Copy link
Contributor

@thom311 thom311 commented Nov 23, 2022

gcc has a useful warning -Wparentheses, for anti patterns like if (x =1) ..., which suggests to either use "==" or put the assignment in a parentheses.

Inside macros (like c_assert()) we need to make sure to add proper parentheses around the macro arguments, but if we just always add them, we hide the useful warning.

Add _c_boolean_expr_() which with GCC uses and expression statement and __COUNTER__ to evaluate the expression to 0 or 1, without hiding -Wparentheses. And use it from _c_likely_(), _c_unlikely_(), c_assert().

This is also done by glib and NetworkManager.

In file included from ../src/c-stdaux.h:43,
                 from ../src/test-api.c:10:
../src/test-api.c: In function ‘test_api_unix’:
../src/test-api.c:274:26: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
  274 |                 c_assert(i = 1);
      |                          ^
../src/c-stdaux-generic.h:143:37: note: in definition of macro ‘_c_boolean_expr_impl_’
  143 |                                 if (_x)                                         \
      |                                     ^~
../src/c-stdaux-generic.h:166:53: note: in expansion of macro ‘_c_boolean_expr_’
  166 | #  define _c_internal_likely_(_x) (__builtin_expect(_c_boolean_expr_(_x), 1))
      |                                                     ^~~~~~~~~~~~~~~~
../src/c-stdaux-generic.h:164:24: note: in expansion of macro ‘_c_internal_likely_’
  164 | #define _c_likely_(_x) _c_internal_likely_(_x)
      |                        ^~~~~~~~~~~~~~~~~~~
../src/c-stdaux-generic.h:301:17: note: in expansion of macro ‘_c_likely_’
  301 |                 _c_likely_(_x)                                                  \
      |                 ^~~~~~~~~~
../src/test-api.c:274:17: note: in expansion of macro ‘c_assert’
  274 |                 c_assert(i = 1);
      |                 ^~~~~~~~

@thom311
Copy link
Contributor Author

thom311 commented Nov 23, 2022

btw, the reason why _c_boolean_expr_() gives and int and not a bool is that (!x) is also an int. At least that is also what gcc's typeof(!x) gives.

The downside of the patch is that _c_likely_() no long gives a constant expression. For example, char x[_c_likely_(aa) ? 5 : 10]. Is that a problem? I would not be bothered (btw, build with -Wvla to catch such bugs). I guess, that might be solvable with __builtin_constant_p()...

@thom311
Copy link
Contributor Author

thom311 commented Nov 23, 2022

btw, build with -Wvla to catch such bugs

btw, in NetworkManager static-assert is implemented via something like sizeof(char [(expr) ? 1 : -1]), which will silently pass and evaluate at runtime, if expr happens to a not-compile-time-constant. -Wvla avoids that. I think gcc implements _Static_assert() via negative bitfields, which doesn't have that problem. Building with -Wvla still seems a good idea.

@thom311 thom311 force-pushed the th/preserve-wparentheses-warn branch 2 times, most recently from 361f775 to d9e191c Compare November 23, 2022 11:06
@thom311
Copy link
Contributor Author

thom311 commented Nov 23, 2022

 Warning, treated as error:
/__w/c-stdaux/c-stdaux/source/src/c-stdaux-generic.h:132:Unexpected indentation.
Error: Process completed with exit code 2.

documentation check complains about indentation. It's not clear to me how to fix that. Various adjustments didn't make a difference.

Aside the obvious (albeit questionable) benefit of giving the compiler
a hint what to expect, _c_likely_() ensures that a "-Wparentheses" warning
is triggered with suspicious code like `c_assert(x = 1);`.

Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: David Rheinsberg <david.rheinsberg@gmail.com>
Inspired by glib's G_BOOLEAN_EXPR() macro and NetworkManager's
NM_BOOLEAN_EXPR() macor.

Signed-off-by: Thomas Haller <thaller@redhat.com>
(adjust to internal coding-style)
Signed-off-by: David Rheinsberg <david.rheinsberg@gmail.com>
This is useful to not hide "-Wparentheses" warnings in case of wrongly
assigning a value like `if (_c_likely_(x = 1)) {`.

Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: David Rheinsberg <david.rheinsberg@gmail.com>
@dvdhrm
Copy link
Member

dvdhrm commented Nov 23, 2022

I did some slight adjustments to the _c_boolean_expr_() macro: https://github.com/dvdhrm/c-stdaux/commits/rw/wparan

This passes CI and looks ok to me. If you think this is suitable, I will gladly push this!

Btw., you have to be careful when using cpp-conditionals for public symbols. The docbook-style comment will assume any block following it is the symbol to document. That's why I always have the #define c_foobar() c_internal_foobar() as first thing after the docbook-style comment, and then have an #ifdef around the c_internal_foobar() definition.

@thom311
Copy link
Contributor Author

thom311 commented Nov 23, 2022

I did some slight adjustments to the c_boolean_expr() macro: https://github.com/dvdhrm/c-stdaux/commits/rw/wparan

You reordered the commits, so the commit message of the first commit talks about something that only comes later.

Otherwise, looks good to me. Thanks for the cleanup!

@thom311 thom311 force-pushed the th/preserve-wparentheses-warn branch from d9e191c to 789c4fb Compare November 23, 2022 15:41
@dvdhrm dvdhrm merged commit c37722f into c-util:main Nov 25, 2022
@dvdhrm
Copy link
Member

dvdhrm commented Nov 25, 2022

Thanks a lot! Merged!

@thom311
Copy link
Contributor Author

thom311 commented Nov 29, 2022

sigh of course it broke something...


Now we get a compilation error with c_assert(0).

With clang-15.0.4-1.fc37, we get in NetworkManager:

$ make -C build src/n-dhcp4/src/libn_dhcp4_la-n-dhcp4-c-connection.lo V=1
make: Entering directory '/data/src/NetworkManager/build'
/bin/sh ./libtool  --tag=CC   --mode=compile clang -DHAVE_CONFIG_H -I. -I..  -D_GNU_SOURCE   -I../src/c-stdaux/src -I../src/c-list/src -I../src/c-siphash/src   -Wall -Werror -Wall -Wextra -Wdeclaration-after-statement -Wfloat-equal -Wformat-nonliteral -Wformat-security -Wimplicit-function-declaration -Wimplicit-int -Winit-self -Wint-conversion -Wmissing-declarations -Wmissing-include-dirs -Wmissing-prototypes -Wold-style-definition -Wparentheses-equality -Wpointer-arith -Wshadow -Wshift-negative-value -Wstrict-prototypes -Wtypedef-redefinition -Wundef -Wunknown-attributes -Wvla -Wno-duplicate-decl-specifier -Wno-format-y2k -Wno-gnu-variable-sized-type-not-at-end -Wno-missing-field-initializers -Wno-pragmas -Wno-sign-compare -Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -Wno-unused-parameter  -Qunused-arguments -Wunknown-warning-option -Wunknown-attributes -Wtypedef-redefinition -Wno-array-bounds -Wparentheses-equality -Wunused-value -Wcast-function-type -Wno-implicit-fallthrough  -fno-strict-aliasing -std=c11 -Wno-error=declaration-after-statement -Wno-pointer-arith  -fdata-sections -ffunction-sections -Wl,--gc-sections -g -O2 -MT src/n-dhcp4/src/libn_dhcp4_la-n-dhcp4-c-connection.lo -MD -MP -MF src/n-dhcp4/src/.deps/libn_dhcp4_la-n-dhcp4-c-connection.Tpo -c -o src/n-dhcp4/src/libn_dhcp4_la-n-dhcp4-c-connection.lo `test -f 'src/n-dhcp4/src/n-dhcp4-c-connection.c' || echo '../'`src/n-dhcp4/src/n-dhcp4-c-connection.c
libtool: compile:  clang -DHAVE_CONFIG_H -I. -I.. -D_GNU_SOURCE -I../src/c-stdaux/src -I../src/c-list/src -I../src/c-siphash/src -Wall -Werror -Wall -Wextra -Wdeclaration-after-statement -Wfloat-equal -Wformat-nonliteral -Wformat-security -Wimplicit-function-declaration -Wimplicit-int -Winit-self -Wint-conversion -Wmissing-declarations -Wmissing-include-dirs -Wmissing-prototypes -Wold-style-definition -Wparentheses-equality -Wpointer-arith -Wshadow -Wshift-negative-value -Wstrict-prototypes -Wtypedef-redefinition -Wundef -Wunknown-attributes -Wvla -Wno-duplicate-decl-specifier -Wno-format-y2k -Wno-gnu-variable-sized-type-not-at-end -Wno-missing-field-initializers -Wno-pragmas -Wno-sign-compare -Wno-tautological-constant-out-of-range-compare -Wno-unknown-pragmas -Wno-unused-parameter -Qunused-arguments -Wunknown-warning-option -Wunknown-attributes -Wtypedef-redefinition -Wno-array-bounds -Wparentheses-equality -Wunused-value -Wcast-function-type -Wno-implicit-fallthrough -fno-strict-aliasing -std=c11 -Wno-error=declaration-after-statement -Wno-pointer-arith -fdata-sections -ffunction-sections -Wl,--gc-sections -g -O2 -MT src/n-dhcp4/src/libn_dhcp4_la-n-dhcp4-c-connection.lo -MD -MP -MF src/n-dhcp4/src/.deps/libn_dhcp4_la-n-dhcp4-c-connection.Tpo -c ../src/n-dhcp4/src/n-dhcp4-c-connection.c  -fPIC -DPIC -o src/n-dhcp4/src/.libs/libn_dhcp4_la-n-dhcp4-c-connection.o
../src/n-dhcp4/src/n-dhcp4-c-connection.c:369:17: error: variable 'timeout' is used uninitialized whenever '?:' condition is true [-Werror,-Wsometimes-uninitialized]
                c_assert(0);
                ^~~~~~~~~~~
../src/c-stdaux/src/c-stdaux-generic.h:302:17: note: expanded from macro 'c_assert'
                _c_likely_(_x)                                                  \
                ^~~~~~~~~~~~~~
../src/c-stdaux/src/c-stdaux-generic.h:165:24: note: expanded from macro '_c_likely_'
#define _c_likely_(_x) _c_internal_likely_(_x)
                       ^~~~~~~~~~~~~~~~~~~~~~~
../src/c-stdaux/src/c-stdaux-generic.h:167:35: note: expanded from macro '_c_internal_likely_'
#  define _c_internal_likely_(_x) (__builtin_expect(_c_boolean_expr_(_x), 1))
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/n-dhcp4/src/n-dhcp4-c-connection.c:372:21: note: uninitialized use occurs here
        *timeoutp = timeout;
                    ^~~~~~~
../src/n-dhcp4/src/n-dhcp4-c-connection.c:369:17: note: remove the '?:' if its condition is always false
                c_assert(0);
                ^
../src/c-stdaux/src/c-stdaux-generic.h:302:17: note: expanded from macro 'c_assert'
                _c_likely_(_x)                                                  \
                ^
../src/c-stdaux/src/c-stdaux-generic.h:165:24: note: expanded from macro '_c_likely_'
#define _c_likely_(_x) _c_internal_likely_(_x)
                       ^
../src/c-stdaux/src/c-stdaux-generic.h:167:35: note: expanded from macro '_c_internal_likely_'
#  define _c_internal_likely_(_x) (__builtin_expect(_c_boolean_expr_(_x), 1))
                                  ^
../src/n-dhcp4/src/n-dhcp4-c-connection.c:324:25: note: initialize the variable 'timeout' to silence this warning
        uint64_t timeout;
                        ^
                         = 0
../src/n-dhcp4/src/n-dhcp4-c-connection.c:1058:17: error: variable 'r' is used uninitialized whenever '?:' condition is true [-Werror,-Wsometimes-uninitialized]
                c_assert(0);
                ^~~~~~~~~~~
../src/c-stdaux/src/c-stdaux-generic.h:302:17: note: expanded from macro 'c_assert'
                _c_likely_(_x)                                                  \
                ^~~~~~~~~~~~~~
../src/c-stdaux/src/c-stdaux-generic.h:165:24: note: expanded from macro '_c_likely_'
#define _c_likely_(_x) _c_internal_likely_(_x)
                       ^~~~~~~~~~~~~~~~~~~~~~~
../src/c-stdaux/src/c-stdaux-generic.h:167:35: note: expanded from macro '_c_internal_likely_'
#  define _c_internal_likely_(_x) (__builtin_expect(_c_boolean_expr_(_x), 1))
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/n-dhcp4/src/n-dhcp4-c-connection.c:1061:13: note: uninitialized use occurs here
        if (r) {
            ^
../src/n-dhcp4/src/n-dhcp4-c-connection.c:1058:17: note: remove the '?:' if its condition is always false
                c_assert(0);
                ^
../src/c-stdaux/src/c-stdaux-generic.h:302:17: note: expanded from macro 'c_assert'
                _c_likely_(_x)                                                  \
                ^
../src/c-stdaux/src/c-stdaux-generic.h:165:24: note: expanded from macro '_c_likely_'
#define _c_likely_(_x) _c_internal_likely_(_x)
                       ^
../src/c-stdaux/src/c-stdaux-generic.h:167:35: note: expanded from macro '_c_internal_likely_'
#  define _c_internal_likely_(_x) (__builtin_expect(_c_boolean_expr_(_x), 1))
                                  ^
../src/n-dhcp4/src/n-dhcp4-c-connection.c:1009:14: note: initialize the variable 'r' to silence this warning
        int r;
             ^
              = 0
2 errors generated.
make: *** [Makefile:16169: src/n-dhcp4/src/libn_dhcp4_la-n-dhcp4-c-connection.lo] Error 1
make: Leaving directory '/data/src/NetworkManager/build'

here: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/jobs/32667301

Maybe c_assert(0) is not a very nice pattern to begin with, and a c_assert_not_reached() which expands to assert(false); __builtin_unreachable() (on GCC) would be useful (but also again, complicated if you do a GCC specific part). But regardless, maybe c_assert(0) should work and we should handle compile time constants, but that seems getting complicated. Granted, c_assert(0) already has a similar problem when building with -DNDEBUG.

I think the best "solution" is to accept the warning and instead "fix" the unintialized variables to not rely on the compiler to detect c_assert(0) to be unreachable (which anyway breaks down with NDEBUG).

@dvdhrm, WDYT?

@thom311
Copy link
Contributor Author

thom311 commented Nov 29, 2022

#define c_assert(_x) (                                                          \
                (__builtin_constant_p(_x) ? (!!(_x)) : _c_likely_(_x))                                                  \
                        ? assert(true && #_x)                                   \
                        : assert(false && #_x)                                  \
        )

would work, but it requires a GCC check, which seems ugly.

Btw, what is the purpose of the assert(true && ) case?


EDIT: better is

 #define _c_likely_(_x) _c_internal_likely_(_x)
 #if defined(C_COMPILER_GNUC)
-#  define _c_internal_likely_(_x) (__builtin_expect(_c_boolean_expr_(_x), 1))
+#  define _c_internal_likely_(_x) (__builtin_constant_p(_x) ? (!!(_x)) : (__builtin_expect(_c_boolean_expr_(_x), 1)))
 #else
 #  define _c_internal_likely_(_x) (_c_boolean_expr_(_x))
 #endif

which also means that a _c_likely_() would work at places where constant expressions are required, like char buf[_c_likely(true) ? 5 : 6] (of course, using _c_likely_() directly in such context makes no sense, but you might get it from a macro).

thom311 added a commit to NetworkManager/c-stdaux that referenced this pull request Nov 29, 2022
We now use _c_likely_() inside c_assert(), and _c_likely_()
uses _c_boolean_expr_(). The latter is no longer obviously a
compile time constant, and the compiler cannot clearly see
that c_assert(0) is an unreachable code path.

This means for example, if you have a switch statement and
the default case contains "c_assert(0)", then the compiler
will now think that this code path can be taken. If you then
afterwards access a variable that is initialized in all other
switch branches, you get a "-Wsometimes-uninitialized" warning.
This happens with clang-15.0.4-1.fc37 and n-dhcp4. See [1].

Work around that by using __builtin_constant_p() in _c_likely_()/
_c_unlikely_(). As we are already inside a C_COMPILER_GNUC check,
that is easy to do.

This also has the added benefit that now _c_likely_() is a compile
time constant, if the argument is. While that is not directly useful,
you might get into that situation when using a macro that uses
_c_likely_(), like with c_assert().

[1] c-util#11 (comment)

Signed-off-by: Thomas Haller <thaller@redhat.com>
@thom311
Copy link
Contributor Author

thom311 commented Nov 29, 2022

let's continue at #12 :)

thom311 added a commit to NetworkManager/c-stdaux that referenced this pull request Nov 30, 2022
…ly_()/_c_unlikely_()

Since recently we use _c_likely_() and _c_boolean_expr_() inside c_assert().
As _c_boolean_expr_() is merely an statement expression, the compiler
cannot recognize constants.

This is for example a problem with c_assert(0) to indicate unreachable
code. With the statement expression, the compiler no longer sees that
the code always fails an assertion (as long as NDEBUG is undefined).
That has consequences for code that follows the statement, which
we know is unreachable. It may lead to compiler warnings like
"-Wsometimes-uninitialized".

Fix that, by evaluating constant expressions explicitly. With this
change, _c_boolean_expr_() can also be used in places where a constant
is required, like `char buffer[_c_boolean_expr_(true) ? 5 : 10];`. Of
course, this is not a useful thing to do directly, but you might use
a macro that ends up effectively calling _c_boolean_expr_() in a context
where a constant is required.

There is also a unit test that causes a "-Wsometimes-uninitialized"
warning with clang-15.0.4-1.fc37.

[1] c-util#11 (comment)

Signed-off-by: Thomas Haller <thaller@redhat.com>
@dvdhrm
Copy link
Member

dvdhrm commented Nov 30, 2022

Btw, what is the purpose of the assert(true && ) case?

Debatable? ;) The macro should have the same behavior as assert(), both for true and false values, except that we always evaluate the expression. Hence, for traceability we always call into assert(). If your standard-library defines it as a no-op macro, it has no effect, but if it evaluates to a function call, you can trace the behavior in the success-case.

This just seemed like the safer option than shortcutting it ourselves.

For evetrything else: yeah, lets continue in #12.

thom311 added a commit to NetworkManager/c-stdaux that referenced this pull request Nov 30, 2022
…ly_()/_c_unlikely_()

Since recently we use _c_likely_() and _c_boolean_expr_() inside c_assert().
As _c_boolean_expr_() is merely an statement expression, the compiler
cannot recognize constants.

This is for example a problem with c_assert(0) to indicate unreachable
code. With the statement expression, the compiler no longer sees that
the code always fails an assertion (as long as NDEBUG is undefined).
That has consequences for code that follows the statement, which
we know is unreachable. It may lead to compiler warnings like
"-Wsometimes-uninitialized".

Fix that, by evaluating constant expressions explicitly. With this
change, _c_boolean_expr_() can also be used in places where a constant
is required, like `char buffer[_c_boolean_expr_(true) ? 5 : 10];`. Of
course, this is not a useful thing to do directly, but you might use
a macro that ends up effectively calling _c_boolean_expr_() in a context
where a constant is required.

There is also a unit test that causes a "-Wsometimes-uninitialized"
warning with clang-15.0.4-1.fc37.

[1] c-util#11 (comment)

Signed-off-by: Thomas Haller <thaller@redhat.com>
thom311 added a commit to NetworkManager/c-stdaux that referenced this pull request Nov 30, 2022
…ly_()/_c_unlikely_()

Since recently we use _c_likely_() and _c_boolean_expr_() inside c_assert().
As _c_boolean_expr_() is merely an statement expression, the compiler
cannot recognize constants.

This is for example a problem with c_assert(0) to indicate unreachable
code. With the statement expression, the compiler no longer sees that
the code always fails an assertion (as long as NDEBUG is undefined).
That has consequences for code that follows the statement, which
we know is unreachable. It may lead to compiler warnings like
"-Wsometimes-uninitialized".

Fix that, by evaluating constant expressions explicitly. With this
change, _c_boolean_expr_() can also be used in places where a constant
is required, like `char buffer[_c_boolean_expr_(true) ? 5 : 10];`. Of
course, this is not a useful thing to do directly, but you might use
a macro that ends up effectively calling _c_boolean_expr_() in a context
where a constant is required.

There is also a unit test that causes a "-Wsometimes-uninitialized"
warning with clang-15.0.4-1.fc37.

[1] c-util#11 (comment)

Signed-off-by: Thomas Haller <thaller@redhat.com>
thom311 added a commit to NetworkManager/c-stdaux that referenced this pull request Nov 30, 2022
…ly_()/_c_unlikely_()

Since recently we use _c_likely_() and _c_boolean_expr_() inside c_assert().
As _c_boolean_expr_() is merely an statement expression, the compiler
cannot recognize constants.

This is for example a problem with c_assert(0) to indicate unreachable
code. With the statement expression, the compiler no longer sees that
the code always fails an assertion (as long as NDEBUG is undefined).
That has consequences for code that follows the statement, which
we know is unreachable. It may lead to compiler warnings like
"-Wsometimes-uninitialized".

Fix that, by evaluating constant expressions explicitly. With this
change, _c_boolean_expr_() can also be used in places where a constant
is required, like `char buffer[_c_boolean_expr_(true) ? 5 : 10];`. Of
course, this is not a useful thing to do directly, but you might use
a macro that ends up effectively calling _c_boolean_expr_() in a context
where a constant is required.

There is also a unit test that causes a "-Wsometimes-uninitialized"
warning with clang-15.0.4-1.fc37.

[1] c-util#11 (comment)

Signed-off-by: Thomas Haller <thaller@redhat.com>
thom311 added a commit to NetworkManager/c-stdaux that referenced this pull request Nov 30, 2022
…ly_()/_c_unlikely_()

Since recently we use _c_likely_() and _c_boolean_expr_() inside c_assert().
As _c_boolean_expr_() is merely an statement expression, the compiler
cannot recognize constants.

This is for example a problem with c_assert(0) to indicate unreachable
code. With the statement expression, the compiler no longer sees that
the code always fails an assertion (as long as NDEBUG is undefined).
That has consequences for code that follows the statement, which
we know is unreachable. It may lead to compiler warnings like
"-Wsometimes-uninitialized".

Fix that, by evaluating constant expressions explicitly. With this
change, _c_boolean_expr_() can also be used in places where a constant
is required, like `char buffer[_c_boolean_expr_(true) ? 5 : 10];`. Of
course, this is not a useful thing to do directly, but you might use
a macro that ends up effectively calling _c_boolean_expr_() in a context
where a constant is required.

There is also a unit test that causes a "-Wsometimes-uninitialized"
warning with clang-15.0.4-1.fc37.

[1] c-util#11 (comment)

Signed-off-by: Thomas Haller <thaller@redhat.com>
thom311 added a commit to NetworkManager/c-stdaux that referenced this pull request Nov 30, 2022
…ly_()/_c_unlikely_()

Since recently we use _c_likely_() and _c_boolean_expr_() inside c_assert().
As _c_boolean_expr_() is merely an statement expression, the compiler
cannot recognize constants.

This is for example a problem with c_assert(0) to indicate unreachable
code. With the statement expression, the compiler no longer sees that
the code always fails an assertion (as long as NDEBUG is undefined).
That has consequences for code that follows the statement, which
we know is unreachable. It may lead to compiler warnings like
"-Wsometimes-uninitialized".

Fix that, by evaluating constant expressions explicitly. With this
change, _c_boolean_expr_() can also be used in places where a constant
is required, like `char buffer[_c_boolean_expr_(true) ? 5 : 10];`. Of
course, this is not a useful thing to do directly, but you might use
a macro that ends up effectively calling _c_boolean_expr_() in a context
where a constant is required (like c_assert() using _c_likely_() and
_c_boolean_expr_()).

There is also a unit test that would cause a "-Wsometimes-uninitialized"
warning with clang-15.0.4-1.fc37.

Note that __builtin_choose_expr() is only used with GCC greater than 4.
That's because __builtin_constant_p() inside __builtin_choose_expr()
does not properly work with gcc 4.8 ([2]). It's not too bad. First of
all, c-stdaux is a C11 library, while gcc 4 has only partial C11 support
to being with (it still works in large parts though). Also, the entire
point of _c_boolean_expr_() is to preserve the "-Wparentheses" warning
for a macro argument (while being function-like, evaluating arguments once,
use a __COUNTER__ name, and propagating the constness with
__builtin_choose_expr). It's almost the same as "(!!(_x))" were it not
for "-Wparentheses". We can afford to miss that on non-GCC or on GCC
not newer than 4.

[1] c-util#11 (comment)
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=19449

Signed-off-by: Thomas Haller <thaller@redhat.com>
dvdhrm pushed a commit to dvdhrm/c-stdaux that referenced this pull request Dec 15, 2022
…ly_()/_c_unlikely_()

Since recently we use _c_likely_() and _c_boolean_expr_() inside c_assert().
As _c_boolean_expr_() is merely an statement expression, the compiler
cannot recognize constants.

This is for example a problem with c_assert(0) to indicate unreachable
code. With the statement expression, the compiler no longer sees that
the code always fails an assertion (as long as NDEBUG is undefined).
That has consequences for code that follows the statement, which
we know is unreachable. It may lead to compiler warnings like
"-Wsometimes-uninitialized".

Fix that, by evaluating constant expressions explicitly. With this
change, _c_boolean_expr_() can also be used in places where a constant
is required, like `char buffer[_c_boolean_expr_(true) ? 5 : 10];`. Of
course, this is not a useful thing to do directly, but you might use
a macro that ends up effectively calling _c_boolean_expr_() in a context
where a constant is required (like c_assert() using _c_likely_() and
_c_boolean_expr_()).

There is also a unit test that would cause a "-Wsometimes-uninitialized"
warning with clang-15.0.4-1.fc37.

Note that __builtin_choose_expr() is only used with GCC greater than 4.
That's because __builtin_constant_p() inside __builtin_choose_expr()
does not properly work with gcc 4.8 ([2]). It's not too bad. First of
all, c-stdaux is a C11 library, while gcc 4 has only partial C11 support
to being with (it still works in large parts though). Also, the entire
point of _c_boolean_expr_() is to preserve the "-Wparentheses" warning
for a macro argument (while being function-like, evaluating arguments once,
use a __COUNTER__ name, and propagating the constness with
__builtin_choose_expr). It's almost the same as "(!!(_x))" were it not
for "-Wparentheses". We can afford to miss that on non-GCC or on GCC
not newer than 4.

[1] c-util#11 (comment)
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=19449

Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: David Rheinsberg <david.rheinsberg@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants