[libc-commits] [libc] [libc][stdbit] Support compound literals in type-generic macros (PR #228721)
via libc-commits
libc-commits at lists.llvm.org
Sat Oct 3 08:04:47 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Chen Miao (ChenMiaoi)
<details>
<summary>Changes</summary>
Calls such as the following fail because the preprocessor treats the comma in the initializer as a macro argument separator:
``` c
struct PairUI { unsigned int first, second; };
stdc_count_ones((struct PairUI){ 2U, 3U }.first);
```
Use variadic macros so `__VA_ARGS__` preserves all argument tokens, including the separating commas, reconstructing the original expression.
Fixes #<!-- -->81503.
---
Full diff: https://github.com/llvm/llvm-project/pull/228721.diff
3 Files Affected:
- (modified) libc/include/llvm-libc-macros/stdbit-macros.h (+43-42)
- (modified) libc/test/include/stdbit_stub.h (+6-15)
- (modified) libc/test/include/stdbit_test.c (+21)
``````````diff
diff --git a/libc/include/llvm-libc-macros/stdbit-macros.h b/libc/include/llvm-libc-macros/stdbit-macros.h
index c5b2f097783440..d665ebc071b7b3 100644
--- a/libc/include/llvm-libc-macros/stdbit-macros.h
+++ b/libc/include/llvm-libc-macros/stdbit-macros.h
@@ -213,104 +213,105 @@ inline unsigned long long stdc_bit_ceil(unsigned long long x) {
return stdc_bit_ceil_ull(x);
}
#else
-#define stdc_leading_zeros(x) \
- _Generic((x), \
+// Use variadic macros to accept compound literals containing commas.
+#define stdc_leading_zeros(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_leading_zeros_uc, \
unsigned short: stdc_leading_zeros_us, \
unsigned: stdc_leading_zeros_ui, \
unsigned long: stdc_leading_zeros_ul, \
- unsigned long long: stdc_leading_zeros_ull)(x)
-#define stdc_leading_ones(x) \
- _Generic((x), \
+ unsigned long long: stdc_leading_zeros_ull)(__VA_ARGS__)
+#define stdc_leading_ones(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_leading_ones_uc, \
unsigned short: stdc_leading_ones_us, \
unsigned: stdc_leading_ones_ui, \
unsigned long: stdc_leading_ones_ul, \
- unsigned long long: stdc_leading_ones_ull)(x)
-#define stdc_trailing_zeros(x) \
- _Generic((x), \
+ unsigned long long: stdc_leading_ones_ull)(__VA_ARGS__)
+#define stdc_trailing_zeros(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_trailing_zeros_uc, \
unsigned short: stdc_trailing_zeros_us, \
unsigned: stdc_trailing_zeros_ui, \
unsigned long: stdc_trailing_zeros_ul, \
- unsigned long long: stdc_trailing_zeros_ull)(x)
-#define stdc_trailing_ones(x) \
- _Generic((x), \
+ unsigned long long: stdc_trailing_zeros_ull)(__VA_ARGS__)
+#define stdc_trailing_ones(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_trailing_ones_uc, \
unsigned short: stdc_trailing_ones_us, \
unsigned: stdc_trailing_ones_ui, \
unsigned long: stdc_trailing_ones_ul, \
- unsigned long long: stdc_trailing_ones_ull)(x)
-#define stdc_first_leading_zero(x) \
- _Generic((x), \
+ unsigned long long: stdc_trailing_ones_ull)(__VA_ARGS__)
+#define stdc_first_leading_zero(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_first_leading_zero_uc, \
unsigned short: stdc_first_leading_zero_us, \
unsigned: stdc_first_leading_zero_ui, \
unsigned long: stdc_first_leading_zero_ul, \
- unsigned long long: stdc_first_leading_zero_ull)(x)
-#define stdc_first_leading_one(x) \
- _Generic((x), \
+ unsigned long long: stdc_first_leading_zero_ull)(__VA_ARGS__)
+#define stdc_first_leading_one(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_first_leading_one_uc, \
unsigned short: stdc_first_leading_one_us, \
unsigned: stdc_first_leading_one_ui, \
unsigned long: stdc_first_leading_one_ul, \
- unsigned long long: stdc_first_leading_one_ull)(x)
-#define stdc_first_trailing_zero(x) \
- _Generic((x), \
+ unsigned long long: stdc_first_leading_one_ull)(__VA_ARGS__)
+#define stdc_first_trailing_zero(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_first_trailing_zero_uc, \
unsigned short: stdc_first_trailing_zero_us, \
unsigned: stdc_first_trailing_zero_ui, \
unsigned long: stdc_first_trailing_zero_ul, \
- unsigned long long: stdc_first_trailing_zero_ull)(x)
-#define stdc_first_trailing_one(x) \
- _Generic((x), \
+ unsigned long long: stdc_first_trailing_zero_ull)(__VA_ARGS__)
+#define stdc_first_trailing_one(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_first_trailing_one_uc, \
unsigned short: stdc_first_trailing_one_us, \
unsigned: stdc_first_trailing_one_ui, \
unsigned long: stdc_first_trailing_one_ul, \
- unsigned long long: stdc_first_trailing_one_ull)(x)
-#define stdc_count_zeros(x) \
- _Generic((x), \
+ unsigned long long: stdc_first_trailing_one_ull)(__VA_ARGS__)
+#define stdc_count_zeros(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_count_zeros_uc, \
unsigned short: stdc_count_zeros_us, \
unsigned: stdc_count_zeros_ui, \
unsigned long: stdc_count_zeros_ul, \
- unsigned long long: stdc_count_zeros_ull)(x)
-#define stdc_count_ones(x) \
- _Generic((x), \
+ unsigned long long: stdc_count_zeros_ull)(__VA_ARGS__)
+#define stdc_count_ones(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_count_ones_uc, \
unsigned short: stdc_count_ones_us, \
unsigned: stdc_count_ones_ui, \
unsigned long: stdc_count_ones_ul, \
- unsigned long long: stdc_count_ones_ull)(x)
-#define stdc_has_single_bit(x) \
- _Generic((x), \
+ unsigned long long: stdc_count_ones_ull)(__VA_ARGS__)
+#define stdc_has_single_bit(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_has_single_bit_uc, \
unsigned short: stdc_has_single_bit_us, \
unsigned: stdc_has_single_bit_ui, \
unsigned long: stdc_has_single_bit_ul, \
- unsigned long long: stdc_has_single_bit_ull)(x)
-#define stdc_bit_width(x) \
- _Generic((x), \
+ unsigned long long: stdc_has_single_bit_ull)(__VA_ARGS__)
+#define stdc_bit_width(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_bit_width_uc, \
unsigned short: stdc_bit_width_us, \
unsigned: stdc_bit_width_ui, \
unsigned long: stdc_bit_width_ul, \
- unsigned long long: stdc_bit_width_ull)(x)
-#define stdc_bit_floor(x) \
- _Generic((x), \
+ unsigned long long: stdc_bit_width_ull)(__VA_ARGS__)
+#define stdc_bit_floor(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_bit_floor_uc, \
unsigned short: stdc_bit_floor_us, \
unsigned: stdc_bit_floor_ui, \
unsigned long: stdc_bit_floor_ul, \
- unsigned long long: stdc_bit_floor_ull)(x)
-#define stdc_bit_ceil(x) \
- _Generic((x), \
+ unsigned long long: stdc_bit_floor_ull)(__VA_ARGS__)
+#define stdc_bit_ceil(...) \
+ _Generic((__VA_ARGS__), \
unsigned char: stdc_bit_ceil_uc, \
unsigned short: stdc_bit_ceil_us, \
unsigned: stdc_bit_ceil_ui, \
unsigned long: stdc_bit_ceil_ul, \
- unsigned long long: stdc_bit_ceil_ull)(x)
+ unsigned long long: stdc_bit_ceil_ull)(__VA_ARGS__)
#endif // __cplusplus
#endif // __LLVM_LIBC_MACROS_STDBIT_MACROS_H
diff --git a/libc/test/include/stdbit_stub.h b/libc/test/include/stdbit_stub.h
index 4ca26b6d81d825..1174fe55402b3b 100644
--- a/libc/test/include/stdbit_stub.h
+++ b/libc/test/include/stdbit_stub.h
@@ -46,21 +46,12 @@ STDBIT_STUB_FUNCTION(stdc_first_trailing_one, 0x1)
STDBIT_STUB_FUNCTION(stdc_count_zeros, 0x2)
STDBIT_STUB_FUNCTION(stdc_count_ones, 0x3)
-bool stdc_has_single_bit_uc(unsigned char __attribute__((unused)) x) {
- return false;
-}
-bool stdc_has_single_bit_us(unsigned short __attribute__((unused)) x) {
- return false;
-}
-bool stdc_has_single_bit_ui(unsigned __attribute__((unused)) x) {
- return false;
-}
-bool stdc_has_single_bit_ul(unsigned long __attribute__((unused)) x) {
- return false;
-}
-bool stdc_has_single_bit_ull(unsigned long long __attribute__((unused)) x) {
- return false;
-}
+// Distinct accepted values let tests distinguish the bool-returning overloads.
+bool stdc_has_single_bit_uc(unsigned char x) { return x == 0xAU; }
+bool stdc_has_single_bit_us(unsigned short x) { return x == 0xBU; }
+bool stdc_has_single_bit_ui(unsigned x) { return x == 0xCU; }
+bool stdc_has_single_bit_ul(unsigned long x) { return x == 0xDU; }
+bool stdc_has_single_bit_ull(unsigned long long x) { return x == 0xEU; }
STDBIT_STUB_FUNCTION(stdc_bit_width, 0x4)
diff --git a/libc/test/include/stdbit_test.c b/libc/test/include/stdbit_test.c
index 3f444ec70b2290..cadc81ce88b053 100644
--- a/libc/test/include/stdbit_test.c
+++ b/libc/test/include/stdbit_test.c
@@ -25,6 +25,17 @@
#include "include/llvm-libc-macros/stdbit-macros.h"
#include "test/UnitTest/LibcCTest.h"
+// The trailing comma in a compound literal must not be treated as a macro
+// argument separator. Also check that the initializer is evaluated only once.
+#define CHECK_COMPOUND_LITERAL(FUNC_NAME, TYPE, VALUE, EXPECTED) \
+ do { \
+ unsigned evaluations = 0; \
+ EXPECT_TRUE(FUNC_NAME((TYPE){ \
+ (evaluations++, VALUE), \
+ }) == EXPECTED); \
+ EXPECT_TRUE(evaluations == 1); \
+ } while (0)
+
#define CHECK_FUNCTION(FUNC_NAME, VAL) \
do { \
EXPECT_TRUE(FUNC_NAME((unsigned char)0U) == VAL##AU); \
@@ -32,6 +43,11 @@
EXPECT_TRUE(FUNC_NAME(0U) == VAL##CU); \
EXPECT_TRUE(FUNC_NAME(0UL) == VAL##DU); \
EXPECT_TRUE(FUNC_NAME(0ULL) == VAL##EU); \
+ CHECK_COMPOUND_LITERAL(FUNC_NAME, unsigned char, 0, VAL##AU); \
+ CHECK_COMPOUND_LITERAL(FUNC_NAME, unsigned short, 0, VAL##BU); \
+ CHECK_COMPOUND_LITERAL(FUNC_NAME, unsigned int, 0, VAL##CU); \
+ CHECK_COMPOUND_LITERAL(FUNC_NAME, unsigned long, 0, VAL##DU); \
+ CHECK_COMPOUND_LITERAL(FUNC_NAME, unsigned long long, 0, VAL##EU); \
} while (0)
TEST(stdbit) {
@@ -51,6 +67,11 @@ TEST(stdbit) {
EXPECT_FALSE(stdc_has_single_bit(1U));
EXPECT_FALSE(stdc_has_single_bit(1UL));
EXPECT_FALSE(stdc_has_single_bit(1ULL));
+ CHECK_COMPOUND_LITERAL(stdc_has_single_bit, unsigned char, 0xAU, true);
+ CHECK_COMPOUND_LITERAL(stdc_has_single_bit, unsigned short, 0xBU, true);
+ CHECK_COMPOUND_LITERAL(stdc_has_single_bit, unsigned int, 0xCU, true);
+ CHECK_COMPOUND_LITERAL(stdc_has_single_bit, unsigned long, 0xDU, true);
+ CHECK_COMPOUND_LITERAL(stdc_has_single_bit, unsigned long long, 0xEU, true);
CHECK_FUNCTION(stdc_bit_width, 0x4);
CHECK_FUNCTION(stdc_bit_floor, 0x5);
``````````
</details>
https://github.com/llvm/llvm-project/pull/228721
More information about the libc-commits
mailing list