[PATCH] libstdc++, v2: Implement C++29 P3104R6 - Bit permutations
Jakub Jelinek
jakub@redhat.com
Sat Sep 19 18:44:03 GMT 2026
On Fri, Sep 18, 2026 at 07:46:06PM +0200, Jakub Jelinek wrote:
> The following patch implements the
> C++29 P3104R6 - Bit permutations
> paper.
Full bootstrap/regtest and further review revealed some problems:
1) as I wrote, just __glibcxx_assert( __l > 0 ) is enough even for
constant evaluation
2) the paper doesn't have noexcept on std::bit_repeat (supposedly because
it has precondition and if it is implemented using contracts?)
3) the uglification plugin revealed that the _log2Nd variable name doesn't
satisfy the requirements, renamed to __log2_Nd
4) the std exports plugin revealed I forgot to export them from std module
5) I've changed the bit_repeat<_Tp>(...) calls in bit_reverse fallback
to have std:: qualification; dunno where it is exactly needed and where
it is not, the header e.g. uses std::__countl_zero<_Tp>(...); or is
full qualification only needed for calls where ADL could change anything?
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2026-09-19 Jakub Jelinek <jakub@redhat.com>
P3104R6
* include/bits/version.def: Implement C++29 P3104R6 - Bit permutations.
(bitops): Change value from 202606 to 202607.
* include/bits/version.h: Regenerate.
* include/std/bit (std::bit_repeat, std::bit_reverse,
std::bit_compress, std::bit_expand): New function templates for C++29.
* src/c++23/std.cc.in (std::bit_repeat, std::bit_reverse,
std::bit_compress, std::bit_expand): New exports for C++29.
* testsuite/26_numerics/bit/bit.permute/bit_reverse.cc: New test.
* testsuite/26_numerics/bit/bit.permute/bit_repeat.cc: New test.
* testsuite/26_numerics/bit/bit.permute/bit_compress.cc: New test.
* testsuite/26_numerics/bit/bit.permute/bit_expand.cc: New test.
--- a/libstdc++-v3/include/bits/version.def 2026-09-14 18:15:14.380304280 +0200
+++ b/libstdc++-v3/include/bits/version.def 2026-09-18 12:52:53.354665068 +0200
@@ -890,10 +890,13 @@ ftms = {
ftms = {
name = bitops;
+ // 202606 P3793R2 Better shifting
+ // 202607 P3104R6 Bit permutations
values = {
- v = 202606;
+ v = 202607;
cxxmin = 29;
};
+ // 201907 P0553R4 Bit operations
values = {
v = 201907;
cxxmin = 20;
--- a/libstdc++-v3/include/bits/version.h 2026-09-14 18:16:26.698489949 +0200
+++ b/libstdc++-v3/include/bits/version.h 2026-09-18 12:52:58.583293839 +0200
@@ -990,9 +990,9 @@
#if !defined(__cpp_lib_bitops)
# if (__cplusplus > 202603L)
-# define __glibcxx_bitops 202606L
+# define __glibcxx_bitops 202607L
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_bitops)
-# define __cpp_lib_bitops 202606L
+# define __cpp_lib_bitops 202607L
# endif
# elif (__cplusplus >= 202002L)
# define __glibcxx_bitops 201907L
--- a/libstdc++-v3/include/std/bit 2026-09-11 09:31:04.134777224 +0200
+++ b/libstdc++-v3/include/std/bit 2026-09-18 17:10:09.921978829 +0200
@@ -515,6 +515,123 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return std::__bit_width(__x); }
#endif // defined (__cpp_lib_int_pow2)
+#if __cpp_lib_bitops >= 202607L // >= C++29
+ // [bit.permute], permutation
+ template<__unsigned_integer _Tp>
+ [[nodiscard]] constexpr _Tp
+ bit_repeat(_Tp __x, int __l)
+ {
+ __glibcxx_assert( __l > 0 );
+ constexpr auto _Nd = __gnu_cxx::__int_traits<_Tp>::__digits;
+ if (__l >= _Nd)
+ return __x;
+ __x &= ((_Tp)1 << __l) - 1;
+ do
+ {
+ __x |= __x << __l;
+ __l <<= 1;
+ }
+ while (__l < _Nd);
+ return __x;
+ }
+
+ template<__unsigned_integer _Tp>
+ [[nodiscard]] constexpr _Tp
+ bit_reverse(_Tp __x) noexcept
+ {
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_bitreverseg)
+ return __builtin_bitreverseg(__x);
+#else
+ constexpr auto _Nd = __gnu_cxx::__int_traits<_Tp>::__digits;
+ if constexpr (_Nd % __CHAR_BIT__ == 0 && __CHAR_BIT__ == 8)
+ {
+ __x = std::byteswap(__x);
+ constexpr auto __m1 = std::bit_repeat<_Tp>(15, 8);
+ constexpr auto __m2 = std::bit_repeat<_Tp>(3, 4);
+ constexpr auto __m3 = std::bit_repeat<_Tp>(1, 2);
+ __x = ((__x >> 4) & __m1) | ((__x & __m1) << 4);
+ __x = ((__x >> 2) & __m2) | ((__x & __m2) << 2);
+ __x = ((__x >> 1) & __m3) | ((__x & __m3) << 1);
+ return __x;
+ }
+ else
+ {
+ _Tp __r = 0;
+ for (int __i = 0; __i < _Nd; ++__i)
+ {
+ __r <<= 1;
+ __r |= (__x & 1);
+ __x >>= 1;
+ }
+ return __r;
+ }
+#endif
+ }
+
+ template<__unsigned_integer _Tp>
+ [[nodiscard]] constexpr _Tp
+ bit_compress(_Tp __x, _Tp __m) noexcept
+ {
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_elementwise_pext)
+ return __builtin_elementwise_pext(__x, __m);
+#else
+ constexpr auto _Nd = __gnu_cxx::__int_traits<_Tp>::__digits;
+ // Based on "Compress, or Generalized Extract" in Hacker's Delight
+ __x &= __m;
+ _Tp __mk = ~__m << 1;
+ for (int __i = 1; __i < _Nd; __i <<= 1)
+ {
+ _Tp __mp = __mk;
+ for (int __j = 1; __j < _Nd; __j <<= 1)
+ __mp ^= __mp << __j;
+ _Tp __mv = __mp & __m;
+ __m = (__m ^ __mv) | (__mv >> __i);
+ _Tp __t = __x & __mv;
+ __x = (__x ^ __t) | (__t >> __i);
+ __mk = __mk & ~__mp;
+ }
+ return __x;
+#endif
+ }
+
+ template<__unsigned_integer _Tp>
+ [[nodiscard]] constexpr _Tp
+ bit_expand(_Tp __x, _Tp __m) noexcept
+ {
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_elementwise_pdep)
+ return __builtin_elementwise_pdep(__x, __m);
+#else
+ constexpr auto _Nd = __gnu_cxx::__int_traits<_Tp>::__digits;
+ if constexpr (_Nd == 1)
+ return __x & __m;
+ constexpr auto __log2_Nd
+ = _Nd == 1 ? 1 : (__gnu_cxx::__int_traits<unsigned>::__digits
+ - std::__countl_zero<unsigned>(_Nd - 1));
+ // Based on "Expand, or Generalized Insert" in Hacker's Delight
+ _Tp __m0 = __m;
+ _Tp __mk = ~__m << 1;
+ _Tp __array[__log2_Nd];
+ for (int __i = 0; __i < __log2_Nd; __i++)
+ {
+ _Tp __mp = __mk;
+ for (int __j = 1; __j < _Nd; __j <<= 1)
+ __mp ^= __mp << __j;
+ _Tp __mv = __mp & __m;
+ __array[__i] = __mv;
+ __m = (__m ^ __mv) | (__mv >> (_Tp(1) << __i));
+ __mk = __mk & ~__mp;
+ }
+ for (int __i = __log2_Nd - 1; __i >= 0; __i--)
+ {
+ _Tp __mv = __array[__i];
+ _Tp __t = __x << (_Tp(1) << __i);
+ __x = (__x & ~__mv) | (__t & __mv);
+ }
+ return __x & __m0;
+#endif
+ }
+#endif // __cpp_lib_bitops >= 202607L
+
#ifdef __cpp_lib_endian // C++ >= 20
/// Byte order constants
--- a/libstdc++-v3/src/c++23/std.cc.in 2026-09-11 09:31:04.134777224 +0200
+++ b/libstdc++-v3/src/c++23/std.cc.in 2026-09-19 19:17:15.890363477 +0200
@@ -699,6 +699,12 @@ export namespace std
using std::shl;
using std::shr;
#endif
+#if __cpp_lib_bitops >= 202607L // >= C++29
+ using std::bit_reverse;
+ using std::bit_repeat;
+ using std::bit_compress;
+ using std::bit_expand;
+#endif
}
// 22.9 <bitset>
--- a/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_reverse.cc 2026-09-18 15:05:47.705779245 +0200
+++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_reverse.cc 2026-09-18 15:48:35.514878450 +0200
@@ -0,0 +1,73 @@
+// { dg-do compile { target c++29 } }
+
+#include <bit>
+#include <limits>
+
+template<typename UInt>
+constexpr auto
+test(UInt x)
+-> decltype(std::bit_reverse(x))
+{
+ constexpr unsigned digits = std::numeric_limits<UInt>::digits;
+ static_assert( std::bit_reverse(UInt(0)) == UInt(0) );
+ static_assert( std::bit_reverse(UInt(~UInt(0))) == UInt(~UInt(0)) );
+ static_assert( std::bit_reverse(UInt(1)) == UInt(1) << (digits - 1) );
+ static_assert( std::bit_reverse(UInt(UInt(1) << (digits - 1))) == UInt(1) );
+ if constexpr (std::numeric_limits<UInt>::digits == 8)
+ static_assert( std::bit_reverse<UInt>(0x34u) == 0x2cu );
+ if constexpr (std::numeric_limits<UInt>::digits == 16)
+ static_assert( std::bit_reverse<UInt>(0x1234u) == 0x2c48u );
+ if constexpr (std::numeric_limits<UInt>::digits == 32)
+ static_assert( std::bit_reverse<UInt>(0x01234567u) == 0xe6a2c480u );
+ if constexpr (std::numeric_limits<UInt>::digits == 64)
+ static_assert( std::bit_reverse<UInt>(0x0123456789abcdefull) == 0xf7b3d591e6a2c480ull );
+ if constexpr (std::numeric_limits<UInt>::digits == 128)
+ static_assert( std::bit_reverse<UInt>((UInt(0x0123456789abcdefull) << 64)
+ | 0x2468ace013579bdfull)
+ == ((UInt(0xfbd9eac807351624ull) << 64)
+ | 0xf7b3d591e6a2c480ull) );
+ if (std::bit_reverse(std::bit_reverse(x)) == x)
+ return x;
+ return 0;
+}
+
+static_assert( test( (unsigned char)123 ) );
+static_assert( test( (unsigned short)12345 ) );
+static_assert( test( (unsigned int)1234567890 ) );
+static_assert( test( 123456789UL ) );
+static_assert( test( ~123456789ULL ) );
+
+// std::bit_reverse(T) shall not participate in overload resolution
+// unless T is an unsigned integer type.
+struct X { constexpr bool did_not_match() { return true; } };
+constexpr X test(...) { return X{}; }
+static_assert( test( (bool)0 ).did_not_match() );
+static_assert( test( (char)0 ).did_not_match() );
+static_assert( test( (int)0 ).did_not_match() );
+static_assert( test( (char16_t)0 ).did_not_match() );
+static_assert( test( (float)0 ).did_not_match() );
+static_assert( test( (void*)0 ).did_not_match() );
+static_assert( test( X{} ).did_not_match() );
+enum E : unsigned { e };
+static_assert( test( e ).did_not_match() );
+
+#if !defined(__STRICT_ANSI__) && defined __SIZEOF_INT128__
+static_assert( test( (unsigned __int128)3 ) );
+static_assert( test( (__int128)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_0)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_0)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_0)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_1)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_1)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_1)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_2)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_2)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_2)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_3)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_3)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_3)0 ).did_not_match() );
+#endif
--- a/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_repeat.cc 2026-09-18 15:52:52.105498580 +0200
+++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_repeat.cc 2026-09-18 16:38:38.536710632 +0200
@@ -0,0 +1,125 @@
+// { dg-do compile { target c++29 } }
+
+#include <bit>
+#include <limits>
+
+// Detect whether std::bit_repeat(T(0), N) is a constant expression.
+template<typename T, auto N, typename = void>
+ struct bit_repeat_valid
+ : std::false_type { };
+
+template<typename T, auto N>
+ struct bit_repeat_valid<T, N, std::void_t<char[(std::bit_repeat(T(0), N) == T(0))]>>
+ : std::true_type { };
+
+template<typename UInt>
+constexpr auto
+test(UInt x)
+-> decltype(std::bit_repeat(x, 1))
+{
+ constexpr unsigned digits = std::numeric_limits<UInt>::digits;
+ static_assert( std::bit_repeat(UInt(0), 1) == UInt(0) );
+ static_assert( std::bit_repeat(UInt(0), digits) == UInt(0) );
+ static_assert( std::bit_repeat(UInt(0), digits * 6) == UInt(0) );
+ static_assert( std::bit_repeat(UInt(~UInt(0)), 1) == UInt(~UInt(0)) );
+ static_assert( std::bit_repeat(UInt(~UInt(0)), digits) == UInt(~UInt(0)) );
+ static_assert( std::bit_repeat(UInt(~UInt(0)), digits * 6) == UInt(~UInt(0)) );
+ static_assert( std::bit_repeat(UInt(1), 1) == UInt(~UInt(0)) );
+ static_assert( std::bit_repeat(UInt(~UInt(1)), 1) == UInt(0) );
+ static_assert( std::bit_repeat(std::bit_repeat(UInt(0x0123456789abcdefull), 3), 3)
+ == std::bit_repeat(UInt(0x0123456789abcdefull), 3) );
+ if constexpr (std::numeric_limits<UInt>::digits >= 8)
+ {
+ static_assert( (std::bit_repeat<UInt>(0x34, 3) & UInt(0xff)) == 0x24u );
+ static_assert( (std::bit_repeat<UInt>(0x71, 2) & UInt(0xff)) == 0x55u );
+ static_assert( (std::bit_repeat<UInt>(0xf2, 2) & UInt(0xff)) == 0xaau );
+ static_assert( (std::bit_repeat<UInt>(0xb3, 5) & UInt(0xff)) == 0x73u );
+ static_assert( (std::bit_repeat<UInt>(0xb3, 8) & UInt(0xff)) == 0xb3u );
+ }
+ if constexpr (std::numeric_limits<UInt>::digits >= 16)
+ {
+ static_assert( (std::bit_repeat<UInt>(0x1734, 3) & UInt(0xffff)) == 0x4924u );
+ static_assert( (std::bit_repeat<UInt>(0x2071, 2) & UInt(0xffff)) == 0x5555u );
+ static_assert( (std::bit_repeat<UInt>(0x52f2, 2) & UInt(0xffff)) == 0xaaaau );
+ static_assert( (std::bit_repeat<UInt>(0x07b3, 5) & UInt(0xffff)) == 0xce73u );
+ static_assert( (std::bit_repeat<UInt>(0x21b3, 8) & UInt(0xffff)) == 0xb3b3u );
+ static_assert( (std::bit_repeat<UInt>(0xa2b3u, 16) & UInt(0xffff)) == 0xa2b3u );
+ }
+ if constexpr (std::numeric_limits<UInt>::digits >= 32)
+ {
+ static_assert( (std::bit_repeat<UInt>(0x12345634u, 3) & UInt(0xffffffffu)) == 0x24924924u );
+ static_assert( (std::bit_repeat<UInt>(0x23456771u, 2) & UInt(0xffffffffu)) == 0x55555555u );
+ static_assert( (std::bit_repeat<UInt>(0x345678f2u, 2) & UInt(0xffffffffu)) == 0xaaaaaaaau );
+ static_assert( (std::bit_repeat<UInt>(0x456789b3u, 5) & UInt(0xffffffffu)) == 0xe739ce73u );
+ static_assert( (std::bit_repeat<UInt>(0x56789ab3u, 8) & UInt(0xffffffffu)) == 0xb3b3b3b3u );
+ static_assert( (std::bit_repeat<UInt>(0x6789a2b3u, 16) & UInt(0xffffffffu)) == 0xa2b3a2b3u );
+ static_assert( (std::bit_repeat<UInt>(0x2a4dda0eu, 32) & UInt(0xffffffffu)) == 0x2a4dda0eu );
+ }
+ if constexpr (std::numeric_limits<UInt>::digits >= 64)
+ {
+ static_assert( (std::bit_repeat<UInt>(0x1122334455667734ull, 3) & UInt(0xffffffffffffffffull)) == 0x4924924924924924ull );
+ static_assert( (std::bit_repeat<UInt>(0x2233445566778871ull, 2) & UInt(0xffffffffffffffffull)) == 0x5555555555555555ull );
+ static_assert( (std::bit_repeat<UInt>(0x33445566778899f2ull, 2) & UInt(0xffffffffffffffffull)) == 0xaaaaaaaaaaaaaaaaull );
+ static_assert( (std::bit_repeat<UInt>(0x445566778899aab3ull, 5) & UInt(0xffffffffffffffffull)) == 0x39ce739ce739ce73ull );
+ static_assert( (std::bit_repeat<UInt>(0x5566778899aabbb3ull, 8) & UInt(0xffffffffffffffffull)) == 0xb3b3b3b3b3b3b3b3ull );
+ static_assert( (std::bit_repeat<UInt>(0x66778899aabba2b3ull, 16) & UInt(0xffffffffffffffffull)) == 0xa2b3a2b3a2b3a2b3ull );
+ static_assert( (std::bit_repeat<UInt>(0x778899aa2a4dda0eull, 32) & UInt(0xffffffffffffffffull)) == 0x2a4dda0e2a4dda0eull );
+ }
+ if constexpr (std::numeric_limits<UInt>::digits == 128)
+ {
+ static_assert( std::bit_repeat<UInt>(1, 2)
+ == (UInt(0x5555555555555555ull) << 64) | 0x5555555555555555ull );
+ static_assert( std::bit_repeat<UInt>(2, 2)
+ == (UInt(0xaaaaaaaaaaaaaaaaull) << 64) | 0xaaaaaaaaaaaaaaaaull );
+ }
+ static_assert( bit_repeat_valid<UInt, 1>() );
+ static_assert( bit_repeat_valid<UInt, 17>() );
+ static_assert( bit_repeat_valid<UInt, digits>() );
+ static_assert( bit_repeat_valid<UInt, digits * 8>() );
+ static_assert( !bit_repeat_valid<UInt, 0>() );
+ static_assert( !bit_repeat_valid<UInt, -1>() );
+ static_assert( !bit_repeat_valid<UInt, -5>() );
+
+ return x;
+}
+
+static_assert( test( (unsigned char)123 ) );
+static_assert( test( (unsigned short)12345 ) );
+static_assert( test( (unsigned int)1234567890 ) );
+static_assert( test( 123456789UL ) );
+static_assert( test( ~123456789ULL ) );
+
+// std::bit_repeat(T, N) shall not participate in overload resolution
+// unless T is an unsigned integer type.
+struct X { constexpr bool did_not_match() { return true; } };
+constexpr X test(...) { return X{}; }
+static_assert( test( (bool)0 ).did_not_match() );
+static_assert( test( (char)0 ).did_not_match() );
+static_assert( test( (int)0 ).did_not_match() );
+static_assert( test( (char16_t)0 ).did_not_match() );
+static_assert( test( (float)0 ).did_not_match() );
+static_assert( test( (void*)0 ).did_not_match() );
+static_assert( test( X{} ).did_not_match() );
+enum E : unsigned { e };
+static_assert( test( e ).did_not_match() );
+
+#if !defined(__STRICT_ANSI__) && defined __SIZEOF_INT128__
+static_assert( test( (unsigned __int128)3 ) );
+static_assert( test( (__int128)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_0)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_0)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_0)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_1)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_1)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_1)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_2)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_2)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_2)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_3)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_3)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_3)0 ).did_not_match() );
+#endif
--- a/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_compress.cc 2026-09-18 16:42:30.677700824 +0200
+++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_compress.cc 2026-09-18 17:51:06.971617871 +0200
@@ -0,0 +1,87 @@
+// { dg-do compile { target c++29 } }
+
+#include <bit>
+#include <limits>
+
+template<typename UInt>
+constexpr auto
+test(UInt x)
+-> decltype(std::bit_compress(x, x))
+{
+ constexpr unsigned digits = std::numeric_limits<UInt>::digits;
+ static_assert( std::bit_compress(UInt(0), UInt(0)) == UInt(0) );
+ static_assert( std::bit_compress(UInt(0x123456789abcdefull), UInt(0)) == UInt(0) );
+ static_assert( std::bit_compress(UInt(0x123456789abcdefull), UInt(~UInt(0))) == UInt(0x123456789abcdefull) );
+ static_assert( std::bit_compress(UInt(0), UInt(0x123456789abcdefull)) == UInt(0) );
+ static_assert( std::bit_compress(UInt(~UInt(0)), UInt(~UInt(0))) == UInt(~UInt(0)) );
+ static_assert( std::bit_compress(UInt(~UInt(0)), UInt(0x123456789abcdefull)) == (UInt(1) << std::popcount(UInt(0x123456789abcdefull))) - 1 );
+ static_assert( digits < 16 || digits % 2 != 0 || std::bit_compress(std::bit_repeat(UInt(0x1234), 16), std::bit_repeat(UInt(1), 2)) == std::bit_repeat(UInt(0x46), 8) >> (digits / 2) );
+ static_assert( digits < 16 || digits % 2 != 0 || std::bit_compress(std::bit_repeat(UInt(0x1234), 16), std::bit_repeat(UInt(2), 2)) == std::bit_repeat(UInt(0x14), 8) >> (digits / 2) );
+ static_assert( digits < 8 || std::bit_compress(UInt(0xff), UInt(0xff)) == UInt(0xff) );
+ static_assert( digits < 8 || std::bit_compress(UInt(0xff), UInt(0x0f)) == UInt(0x0f) );
+ static_assert( digits < 8 || std::bit_compress(UInt(0xff), UInt(0xf0)) == UInt(0x0f) );
+ static_assert( digits % 2 != 0 || std::bit_compress(std::bit_repeat(UInt(2), 2), std::bit_repeat(UInt(0xc), 4)) == std::bit_repeat(UInt(0x2), 2) >> (digits / 2) );
+ static_assert( digits % 2 != 0 || std::bit_compress(UInt(~UInt(0)), std::bit_repeat(UInt(2), 2)) == UInt(~UInt(0)) >> (digits / 2) );
+ if constexpr (digits >= 8)
+ {
+ static_assert( std::bit_compress(UInt(0xd4), UInt(0xbf)) == UInt(0x54) );
+ static_assert( std::bit_compress(UInt(0x66), UInt(0x1d)) == UInt(0x02) );
+ }
+ if constexpr (digits >= 16)
+ {
+ static_assert( std::bit_compress(UInt(0xebd4u), UInt(0x93bfu)) == UInt(0x05d4) );
+ static_assert( std::bit_compress(UInt(0xa166u), UInt(0x001du)) == UInt(2) );
+ }
+ if constexpr (digits >= 32)
+ {
+ static_assert( std::bit_compress(UInt(0x9daeebd4u), UInt(0xad8793bfu)) == UInt(0xbf5d4u) );
+ static_assert( std::bit_compress(UInt(0x979ba166u), UInt(0x54ca001du)) == UInt(0x3b2u) );
+ }
+ if constexpr (digits >= 64)
+ {
+ static_assert( std::bit_compress(UInt(0x5bd30f069daeebd4ull), UInt(0x64f9a89bad8793bfull)) == UInt(0x13522bf5d4ull) );
+ static_assert( std::bit_compress(UInt(0xb5672499979ba166ull), UInt(0xd315782354ca001dull)) == UInt(0x2ad0bb2ull) );
+ }
+ return x;
+}
+
+static_assert( test( (unsigned char)123 ) );
+static_assert( test( (unsigned short)12345 ) );
+static_assert( test( (unsigned int)1234567890 ) );
+static_assert( test( 123456789UL ) );
+static_assert( test( ~123456789ULL ) );
+
+// std::bit_compress(T) shall not participate in overload resolution
+// unless T is an unsigned integer type.
+struct X { constexpr bool did_not_match() { return true; } };
+constexpr X test(...) { return X{}; }
+static_assert( test( (bool)0 ).did_not_match() );
+static_assert( test( (char)0 ).did_not_match() );
+static_assert( test( (int)0 ).did_not_match() );
+static_assert( test( (char16_t)0 ).did_not_match() );
+static_assert( test( (float)0 ).did_not_match() );
+static_assert( test( (void*)0 ).did_not_match() );
+static_assert( test( X{} ).did_not_match() );
+enum E : unsigned { e };
+static_assert( test( e ).did_not_match() );
+
+#if !defined(__STRICT_ANSI__) && defined __SIZEOF_INT128__
+static_assert( test( (unsigned __int128)3 ) );
+static_assert( test( (__int128)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_0)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_0)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_0)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_1)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_1)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_1)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_2)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_2)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_2)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_3)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_3)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_3)0 ).did_not_match() );
+#endif
--- a/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_expand.cc 2026-09-18 17:33:27.424621833 +0200
+++ b/libstdc++-v3/testsuite/26_numerics/bit/bit.permute/bit_expand.cc 2026-09-18 18:08:54.689572006 +0200
@@ -0,0 +1,87 @@
+// { dg-do compile { target c++29 } }
+
+#include <bit>
+#include <limits>
+
+template<typename UInt>
+constexpr auto
+test(UInt x)
+-> decltype(std::bit_expand(x, x))
+{
+ constexpr unsigned digits = std::numeric_limits<UInt>::digits;
+ static_assert( std::bit_expand(UInt(0), UInt(0)) == UInt(0) );
+ static_assert( std::bit_expand(UInt(0x123456789abcdefull), UInt(0)) == UInt(0) );
+ static_assert( std::bit_expand(UInt(0x123456789abcdefull), UInt(~UInt(0))) == UInt(0x123456789abcdefull) );
+ static_assert( std::bit_expand(UInt(0), UInt(0x123456789abcdefull)) == UInt(0) );
+ static_assert( std::bit_expand(UInt(~UInt(0)), UInt(~UInt(0))) == UInt(~UInt(0)) );
+ static_assert( std::bit_expand(UInt((UInt(1) << std::popcount(UInt(0x123456789abcdefull))) - 1), UInt(0x123456789abcdefull)) == UInt(0x123456789abcdefull) );
+ static_assert( digits < 16 || std::bit_expand(std::bit_repeat(UInt(0x46), 8), std::bit_repeat(UInt(1), 2)) == std::bit_repeat(UInt(0x1014), 16) );
+ static_assert( digits < 16 || std::bit_expand(std::bit_repeat(UInt(0x14), 8), std::bit_repeat(UInt(2), 2)) == std::bit_repeat(UInt(0x0220), 16) );
+ static_assert( digits < 8 || std::bit_expand(UInt(0xff), UInt(0xff)) == UInt(0xff) );
+ static_assert( digits < 8 || std::bit_expand(UInt(0x0f), UInt(0xff)) == UInt(0x0f) );
+ static_assert( digits < 8 || std::bit_expand(UInt(0x0f), UInt(0xf0)) == UInt(0xf0) );
+ static_assert( std::bit_expand(std::bit_repeat(UInt(2), 2), std::bit_repeat(UInt(0xc), 4)) == std::bit_repeat(UInt(0x8), 4) );
+ static_assert( std::bit_expand(std::bit_repeat(UInt(0x0f), 8), std::bit_repeat(UInt(2), 2)) == std::bit_repeat(UInt(0xaa), 16) );
+ if constexpr (digits >= 8)
+ {
+ static_assert( std::bit_expand(UInt(0xd4), UInt(0xbf)) == UInt(0x94) );
+ static_assert( std::bit_expand(UInt(0x66), UInt(0x1d)) == UInt(0x0c) );
+ }
+ if constexpr (digits >= 16)
+ {
+ static_assert( std::bit_expand(UInt(0xebd4u), UInt(0x93bfu)) == UInt(0x1394) );
+ static_assert( std::bit_expand(UInt(0xa166u), UInt(0x001du)) == UInt(0xc) );
+ }
+ if constexpr (digits >= 32)
+ {
+ static_assert( std::bit_expand(UInt(0x9daeebd4u), UInt(0xad8793bfu)) == UInt(0xa9851394u) );
+ static_assert( std::bit_expand(UInt(0x979ba166u), UInt(0x54ca001du)) == UInt(0x448000cu) );
+ }
+ if constexpr (digits >= 64)
+ {
+ static_assert( std::bit_expand(UInt(0x5bd30f069daeebd4ull), UInt(0x64f9a89bad8793bfull)) == UInt(0x4a1a092a9851394ull) );
+ static_assert( std::bit_expand(UInt(0xb5672499979ba166ull), UInt(0xd315782354ca001dull)) == UInt(0xd01470200448000cull) );
+ }
+ return x;
+}
+
+static_assert( test( (unsigned char)123 ) );
+static_assert( test( (unsigned short)12345 ) );
+static_assert( test( (unsigned int)1234567890 ) );
+static_assert( test( 123456789UL ) );
+static_assert( test( ~123456789ULL ) );
+
+// std::bit_expand(T) shall not participate in overload resolution
+// unless T is an unsigned integer type.
+struct X { constexpr bool did_not_match() { return true; } };
+constexpr X test(...) { return X{}; }
+static_assert( test( (bool)0 ).did_not_match() );
+static_assert( test( (char)0 ).did_not_match() );
+static_assert( test( (int)0 ).did_not_match() );
+static_assert( test( (char16_t)0 ).did_not_match() );
+static_assert( test( (float)0 ).did_not_match() );
+static_assert( test( (void*)0 ).did_not_match() );
+static_assert( test( X{} ).did_not_match() );
+enum E : unsigned { e };
+static_assert( test( e ).did_not_match() );
+
+#if !defined(__STRICT_ANSI__) && defined __SIZEOF_INT128__
+static_assert( test( (unsigned __int128)3 ) );
+static_assert( test( (__int128)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_0)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_0)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_0)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_1)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_1)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_1)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_2)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_2)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_2)0 ).did_not_match() );
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_3)
+static_assert( test( (unsigned __GLIBCXX_TYPE_INT_N_3)1 ) );
+static_assert( test( (__GLIBCXX_TYPE_INT_N_3)0 ).did_not_match() );
+#endif
Jakub
More information about the Libstdc++
mailing list