[PATCH] libstdc++, v2: Implement C++29 P3104R6 - Bit permutations

Jonathan Wakely jwakely@redhat.com
Tue Oct 13 22:15:07 GMT 2026


On Sat, 19 Sep 2026 at 20:44 +0200, Jakub Jelinek wrote:
>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?)

Generally we strengthen the noexcept on such functions, because we
know that our assertions (ie. hardened preconditions) are implemented
as abort, not a contract violations. But it's OK to leave it
noexcept(false) for now.

>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?

OK, thanks

>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