[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