[libcxx-commits] [libcxx] [libc++] Implement `<bit>` parts of P3793R2 Better shifting (PR #219389)

Jan Schultke via libcxx-commits libcxx-commits at lists.llvm.org
Mon Sep 14 04:18:32 PDT 2026


https://github.com/eisenwave updated https://github.com/llvm/llvm-project/pull/219389

>From 7db002381bf3cb16efebabba0d1aec725297c1d3 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 08:57:58 +0200
Subject: [PATCH 01/13] [libc++] Implement <bit> parts of P3793R2 Better
 shifting

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/ReleaseNotes/24.rst               |   1 +
 libcxx/docs/Status/Cxx29Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__bit/shift.h                  |  60 +++++
 libcxx/include/bit                            |  10 +
 libcxx/include/version                        |   5 +-
 libcxx/modules/std/bit.inc                    |   4 +
 .../libcxx/utilities/bit/nodiscard.verify.cpp |   6 +
 .../bit.version.compile.pass.cpp              |   4 +-
 .../version.version.compile.pass.cpp          |   4 +-
 .../numerics/bit/bitops.shift/shl.pass.cpp    | 209 +++++++++++++++++
 .../numerics/bit/bitops.shift/shr.pass.cpp    | 217 ++++++++++++++++++
 .../generate_feature_test_macro_components.py |   2 +-
 14 files changed, 520 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/include/__bit/shift.h
 create mode 100644 libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
 create mode 100644 libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index cabd30847b2af..ea9157141cef6 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -558,6 +558,8 @@ Status
     ---------------------------------------------------------- -----------------
     **C++29**
     ----------------------------------------------------------------------------
+    ``__cpp_lib_bitops``                                       ``202606L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_map_lookup``                                   *unimplemented*
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_mdspan_copy``                                  *unimplemented*
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 9cf08b5732970..ff774e636efc5 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -47,6 +47,7 @@ Implemented Papers
 - P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 - P1901R2: Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers (`Github <https://llvm.org/PR105372>`__)
 - P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
+- P3793R2: Better shifting (`Github <https://llvm.org/PR219389>`__) (no ``std::simd`` overloads)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx29Papers.csv b/libcxx/docs/Status/Cxx29Papers.csv
index 3c48fe89be33d..30f0588b49e60 100644
--- a/libcxx/docs/Status/Cxx29Papers.csv
+++ b/libcxx/docs/Status/Cxx29Papers.csv
@@ -11,7 +11,7 @@
 "`P3154R3 <https://wg21.link/P3154R3>`__","Deprecating signed character types in iostreams","2026-06 (Brno)","","","`#204398 <https://github.com/llvm/llvm-project/issues/204398>`__",""
 "`P3428R4 <https://wg21.link/P3428R4>`__","Hazard Pointer Batches","2026-06 (Brno)","","","`#204399 <https://github.com/llvm/llvm-project/issues/204399>`__",""
 "`P3248R5 <https://wg21.link/P3248R5>`__","Require ``[u]intptr_t``","2026-06 (Brno)","","","`#204400 <https://github.com/llvm/llvm-project/issues/204400>`__",""
-"`P3793R2 <https://wg21.link/P3793R2>`__","Better shifting","2026-06 (Brno)","","","`#204401 <https://github.com/llvm/llvm-project/issues/204401>`__",""
+"`P3793R2 <https://wg21.link/P3793R2>`__","Better shifting","2026-06 (Brno)","|Partial|","24","`#204401 <https://github.com/llvm/llvm-project/issues/204401>`__","No `std::simd` overloads provided yet."
 "`P3242R4 <https://wg21.link/P3242R4>`__","Copy and fill for ``mdspan``","2026-06 (Brno)","","","`#204402 <https://github.com/llvm/llvm-project/issues/204402>`__",""
 "`P3692R4 <https://wg21.link/P3692R4>`__","How to Avoid OOTA Without Really Trying","2026-06 (Brno)","","","`#204403 <https://github.com/llvm/llvm-project/issues/204403>`__",""
 "`P3104R6 <https://wg21.link/P3104R6>`__","Bit permutations","2026-06 (Brno)","","","`#204404 <https://github.com/llvm/llvm-project/issues/204404>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f0caccdc94194..af3ac91e97c9a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -244,6 +244,7 @@ set(files
   __bit/invert_if.h
   __bit/popcount.h
   __bit/rotate.h
+  __bit/shift.h
   __bit_reference
   __charconv/chars_format.h
   __charconv/from_chars_floating_point.h
diff --git a/libcxx/include/__bit/shift.h b/libcxx/include/__bit/shift.h
new file mode 100644
index 0000000000000..4011b7ad83757
--- /dev/null
+++ b/libcxx/include/__bit/shift.h
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___BIT_SHIFT_H
+#define _LIBCPP___BIT_SHIFT_H
+
+#include <__config>
+#include <__type_traits/integer_traits.h>
+#include <__type_traits/make_unsigned.h>
+#include <limits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 29
+
+template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp shl(_Tp __t, _Shift __cnt) noexcept {
+  constexpr auto __n = static_cast<_Shift>(numeric_limits<make_unsigned_t<_Tp>>::digits);
+  if constexpr (is_signed_v<_Shift>) {
+    if (__cnt < 0) {
+      if (__cnt <= -__n)
+        return static_cast<_Tp>(__t < 0 ? -1 : 0);
+      return __t >> -__cnt;
+    }
+  }
+  if (__cnt >= __n)
+    return static_cast<_Tp>(0);
+  return __t << __cnt;
+}
+
+template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp shr(_Tp __t, _Shift __cnt) noexcept {
+  constexpr auto __n = static_cast<_Shift>(numeric_limits<make_unsigned_t<_Tp>>::digits);
+  if constexpr (is_signed_v<_Shift>) {
+    if (__cnt < 0) {
+      if (__cnt <= -__n)
+        return static_cast<_Tp>(0);
+      return __t << -__cnt;
+    }
+    if (__cnt >= __n)
+      return static_cast<_Tp>(__t < 0 ? -1 : 0);
+  } else if (__cnt >= __n)
+    return static_cast<_Tp>(0);
+  return __t >> __cnt;
+}
+
+#endif // _LIBCPP_STD_VER >= 29
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___BIT_SHIFT_H
diff --git a/libcxx/include/bit b/libcxx/include/bit
index 40fcda27856bb..be3dfdc451b3d 100644
--- a/libcxx/include/bit
+++ b/libcxx/include/bit
@@ -32,6 +32,12 @@ namespace std {
   template <class T>
     constexpr int bit_width(T x) noexcept;       // C++20
 
+  // [bit.shift], shifting
+  template<class T, class S>
+    constexpr T shl(T x, S s) noexcept; // C++29
+  template<class T, class S>
+    constexpr T shr(T x, S s) noexcept; // C++29
+
   // [bit.rotate], rotating
   template<class T>
     constexpr T rotl(T x, int s) noexcept; // C++20
@@ -84,6 +90,10 @@ namespace std {
 #    include <__bit/byteswap.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 29
+#    include <__bit/shift.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/version b/libcxx/include/version
index 589ce789ffa7c..421ceb20b270e 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -47,7 +47,8 @@ __cpp_lib_bind_back                                     202202L <functional>
 __cpp_lib_bind_front                                    202306L <functional>
                                                         201907L // C++20
 __cpp_lib_bit_cast                                      201806L <bit>
-__cpp_lib_bitops                                        201907L <bit>
+__cpp_lib_bitops                                        202606L <bit>
+                                                        201907L // C++20
 __cpp_lib_bitset                                        202306L <bitset>
 __cpp_lib_bool_constant                                 201505L <type_traits>
 __cpp_lib_bounded_array_traits                          201902L <type_traits>
@@ -661,6 +662,8 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #endif
 
 #if _LIBCPP_STD_VER >= 29
+# undef  __cpp_lib_bitops
+# define __cpp_lib_bitops                               202606L
 // # define __cpp_lib_map_lookup                           202606L
 // # define __cpp_lib_mdspan_copy                          202606L
 // # define __cpp_lib_pointer_tag_pair                     202606L
diff --git a/libcxx/modules/std/bit.inc b/libcxx/modules/std/bit.inc
index 027a6c17ad6a6..e0e845d8249c5 100644
--- a/libcxx/modules/std/bit.inc
+++ b/libcxx/modules/std/bit.inc
@@ -22,6 +22,10 @@ export namespace std {
   using std::bit_width;
   using std::has_single_bit;
 
+  // [bit.shift], shifting
+  using std::shl;
+  using std::shr;
+
   // [bit.rotate], rotating
   using std::rotl;
   using std::rotr;
diff --git a/libcxx/test/libcxx/utilities/bit/nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/bit/nodiscard.verify.cpp
index 4d3c9e41b783e..6915728843717 100644
--- a/libcxx/test/libcxx/utilities/bit/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/utilities/bit/nodiscard.verify.cpp
@@ -31,6 +31,12 @@ void func() {
   std::has_single_bit(0u); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::popcount(0u); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
+#if TEST_STD_VER >= 29
+  // [bit.shift]
+  std::shl(1, 1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::shr(1, 1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   // [bit.rotate]
   std::rotl(0u, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::rotr(0u, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/bit.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/bit.version.compile.pass.cpp
index 39df8f7ed649e..28a7debf546fe 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/bit.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/bit.version.compile.pass.cpp
@@ -204,8 +204,8 @@
 #  ifndef __cpp_lib_bitops
 #    error "__cpp_lib_bitops should be defined in c++29"
 #  endif
-#  if __cpp_lib_bitops != 201907L
-#    error "__cpp_lib_bitops should have the value 201907L in c++29"
+#  if __cpp_lib_bitops != 202606L
+#    error "__cpp_lib_bitops should have the value 202606L in c++29"
 #  endif
 
 #  ifndef __cpp_lib_byteswap
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 769b79c7924d2..480b8692cfe5e 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -8829,8 +8829,8 @@
 #  ifndef __cpp_lib_bitops
 #    error "__cpp_lib_bitops should be defined in c++29"
 #  endif
-#  if __cpp_lib_bitops != 201907L
-#    error "__cpp_lib_bitops should have the value 201907L in c++29"
+#  if __cpp_lib_bitops != 202606L
+#    error "__cpp_lib_bitops should have the value 202606L in c++29"
 #  endif
 
 #  ifndef __cpp_lib_bitset
diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
new file mode 100644
index 0000000000000..0ffdcacb8d704
--- /dev/null
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
@@ -0,0 +1,209 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++29
+
+// template <class T, class S>
+//   constexpr T shl(T x, S s) noexcept;
+
+// Constraints: Each of T and S is a signed or unsigned integer type.
+
+#include <bit>
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class T>
+constexpr bool test() {
+  using U             = std::make_unsigned_t<T>;
+  constexpr int width = std::numeric_limits<U>::digits;
+  constexpr U highbit_u = U(1) << (width - 1);
+
+  if constexpr (std::is_signed_v<T>) {
+    ASSERT_SAME_TYPE(decltype(std::shl(T(), 0)), T);
+    ASSERT_NOEXCEPT(std::shl(T(), 0));
+
+    assert(std::shl(T(1), 0) == T(1));
+    assert(std::shl(T(1), 1) == T(2));
+    assert(std::shl(T(1), 2) == T(4));
+    assert(std::shl(T(1), 3) == T(8));
+    assert(std::shl(T(1), 4) == T(16));
+    assert(std::shl(T(1), 5) == T(32));
+    assert(std::shl(T(1), 6) == T(64));
+    assert(std::shl(T(1), 7) == static_cast<T>(U(128)));
+
+    // Overlong shifts return 0
+    assert(std::shl(T(1), width) == T(0));
+    assert(std::shl(T(1), width + 1) == T(0));
+    assert(std::shl(T(1), width + 100) == T(0));
+    assert(std::shl(T(1), std::numeric_limits<int>::max()) == T(0));
+
+    assert(std::shl(T(-1), width) == T(0));
+    assert(std::shl(T(-1), width + 5) == T(0));
+
+    // Negative shift amounts shift right arithmetically (bidirectional)
+    T highbit = static_cast<T>(highbit_u);
+    assert(std::shl(highbit, -1) == (highbit >> 1));
+    assert(std::shl(highbit, -2) == (highbit >> 2));
+    assert(std::shl(highbit, -3) == (highbit >> 3));
+    assert(std::shl(highbit, -4) == (highbit >> 4));
+    assert(std::shl(highbit, -5) == (highbit >> 5));
+    assert(std::shl(highbit, -6) == (highbit >> 6));
+    assert(std::shl(highbit, -7) == (highbit >> 7));
+    assert(std::shl(highbit, -(width - 1)) == (highbit >> (width - 1)));
+
+    // Negative overlong shifts: sign-extended for negative values, 0 otherwise
+    assert(std::shl(T(-1), -width) == T(-1));
+    assert(std::shl(T(-1), -(width + 1)) == T(-1));
+    assert(std::shl(T(-1), std::numeric_limits<int>::min()) == T(-1));
+    assert(std::shl(T(42), -width) == T(0));
+    assert(std::shl(T(42), -(width + 1)) == T(0));
+
+    if constexpr (width == 128) {
+      assert(std::shl(highbit, -64) == (highbit >> 64));
+      assert(std::shl(highbit, -127) == (highbit >> 127));
+      assert(std::shl(highbit, -128) == T(-1));
+      assert(std::shl(highbit, -200) == T(-1));
+    } else if constexpr (width == 256) {
+      assert(std::shl(highbit, -64) == (highbit >> 64));
+      assert(std::shl(highbit, -127) == (highbit >> 127));
+      assert(std::shl(highbit, -128) == (highbit >> 128));
+      assert(std::shl(highbit, -200) == (highbit >> 200));
+      assert(std::shl(highbit, -255) == T(-1));
+      assert(std::shl(highbit, -256) == T(-1));
+    }
+  } else {
+    ASSERT_SAME_TYPE(decltype(std::shl(T(), 0)), T);
+    ASSERT_NOEXCEPT(std::shl(T(), 0));
+
+    assert(std::shl(T(1), 0) == T(1));
+    assert(std::shl(T(1), 1) == T(2));
+    assert(std::shl(T(1), 2) == T(4));
+    assert(std::shl(T(1), 3) == T(8));
+    assert(std::shl(T(1), 4) == T(16));
+    assert(std::shl(T(1), 5) == T(32));
+    assert(std::shl(T(1), 6) == T(64));
+    assert(std::shl(T(1), 7) == T(128));
+
+    // Overlong shifts return 0
+    assert(std::shl(T(1), width) == T(0));
+    assert(std::shl(T(1), width + 1) == T(0));
+    assert(std::shl(T(1), width + 100) == T(0));
+    assert(std::shl(T(1), std::numeric_limits<int>::max()) == T(0));
+
+    assert(std::shl(T(~T(0)), width) == T(0));
+    assert(std::shl(T(~T(0)), width + 5) == T(0));
+
+    // Negative shift amounts shift right (bidirectional)
+    T highbit = highbit_u;
+    assert(std::shl(highbit, -1) == (highbit_u >> 1));
+    assert(std::shl(highbit, -2) == (highbit_u >> 2));
+    assert(std::shl(highbit, -3) == (highbit_u >> 3));
+    assert(std::shl(highbit, -4) == (highbit_u >> 4));
+    assert(std::shl(highbit, -5) == (highbit_u >> 5));
+    assert(std::shl(highbit, -6) == (highbit_u >> 6));
+    assert(std::shl(highbit, -7) == (highbit_u >> 7));
+    assert(std::shl(highbit, -(width - 1)) == T(1));
+
+    // Negative overlong shifts: 0 for unsigned
+    assert(std::shl(T(~T(0)), -width) == T(0));
+    assert(std::shl(T(~T(0)), -(width + 1)) == T(0));
+    assert(std::shl(T(1), std::numeric_limits<int>::min()) == T(0));
+
+    if constexpr (width == 128) {
+      assert(std::shl(T(1), 64) == T(1) << 64);
+      assert(std::shl(T(1), 127) == T(1) << 127);
+      assert(std::shl(T(1), 128) == T(0));
+      assert(std::shl(T(1), 200) == T(0));
+      assert(std::shl(T(1) << 127, -1) == T(1) << 126);
+      assert(std::shl(T(1) << 127, -127) == T(1));
+      assert(std::shl(T(1) << 127, -128) == T(0));
+      assert(std::shl(T(1) << 127, -200) == T(0));
+    } else if constexpr (width == 256) {
+      assert(std::shl(T(1), 64) == T(1) << 64);
+      assert(std::shl(T(1), 127) == T(1) << 127);
+      assert(std::shl(T(1), 128) == T(1) << 128);
+      assert(std::shl(T(1), 200) == T(1) << 200);
+      assert(std::shl(T(1) << 127, -1) == T(1) << 126);
+      assert(std::shl(T(1) << 127, -127) == T(1));
+      assert(std::shl(T(1) << 127, -128) == T(0));
+      assert(std::shl(T(1) << 127, -200) == T(0));
+      assert(std::shl(T(1), 256) == T(0));
+      assert(std::shl(T(1), 300) == T(0));
+    }
+  }
+
+  return true;
+}
+
+struct A {};
+enum E1 : unsigned char { rEd };
+enum class E2 : unsigned char { red };
+
+template <class T>
+concept can_shl_value = requires(T value) { std::shl(value, 1); };
+
+template <class T>
+concept can_shl_shift = requires(T shift) { std::shl(1, shift); };
+
+int main(int, char**) {
+  static_assert(!can_shl_value<bool>);
+  static_assert(!can_shl_value<char>);
+  static_assert(!can_shl_value<wchar_t>);
+  static_assert(!can_shl_value<char8_t>);
+  static_assert(!can_shl_value<char16_t>);
+  static_assert(!can_shl_value<char32_t>);
+  static_assert(!can_shl_value<A>);
+  static_assert(!can_shl_value<A*>);
+  static_assert(!can_shl_value<E1>);
+  static_assert(!can_shl_value<E2>);
+
+  static_assert(!can_shl_shift<bool>);
+  static_assert(!can_shl_shift<char>);
+  static_assert(!can_shl_shift<wchar_t>);
+  static_assert(!can_shl_shift<char8_t>);
+  static_assert(!can_shl_shift<char16_t>);
+  static_assert(!can_shl_shift<char32_t>);
+  static_assert(!can_shl_shift<A>);
+  static_assert(!can_shl_shift<A*>);
+  static_assert(!can_shl_shift<E1>);
+  static_assert(!can_shl_shift<E2>);
+
+  constexpr auto test_type = []<class T> {
+    static_assert(test<T>());
+    test<T>();
+  };
+
+  types::for_each(types::unsigned_integer_types{}, test_type);
+  types::for_each(types::signed_integer_types{}, test_type);
+
+#if TEST_HAS_BITINT
+  using bitint_types = types::type_list<
+      signed _BitInt(32),
+      unsigned _BitInt(32),
+      signed _BitInt(64),
+      unsigned _BitInt(64)
+#  if __BITINT_MAXWIDTH__ >= 128
+          ,
+      signed _BitInt(128),
+      unsigned _BitInt(128)
+#  endif
+#  if __BITINT_MAXWIDTH__ >= 256
+          ,
+      signed _BitInt(256),
+      unsigned _BitInt(256)
+#  endif
+      >;
+  types::for_each(bitint_types{}, test_type);
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
new file mode 100644
index 0000000000000..b3501284bfd4d
--- /dev/null
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
@@ -0,0 +1,217 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++29
+
+// template <class T, class S>
+//   constexpr T shr(T x, S s) noexcept;
+
+// Constraints: Each of T and S is a signed or unsigned integer type.
+
+#include <bit>
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class T>
+constexpr bool test() {
+  using U               = std::make_unsigned_t<T>;
+  constexpr int width   = std::numeric_limits<U>::digits;
+  constexpr U highbit_u = U(1) << (width - 1);
+
+  if constexpr (std::is_signed_v<T>) {
+    ASSERT_SAME_TYPE(decltype(std::shr(T(), 0)), T);
+    ASSERT_NOEXCEPT(std::shr(T(), 0));
+
+    T highbit = static_cast<T>(highbit_u);
+    assert(std::shr(highbit, 0) == highbit);
+    assert(std::shr(highbit, 1) == (highbit >> 1));
+    assert(std::shr(highbit, 2) == (highbit >> 2));
+    assert(std::shr(highbit, 3) == (highbit >> 3));
+    assert(std::shr(highbit, 4) == (highbit >> 4));
+    assert(std::shr(highbit, 5) == (highbit >> 5));
+    assert(std::shr(highbit, 6) == (highbit >> 6));
+    assert(std::shr(highbit, 7) == (highbit >> 7));
+    assert(std::shr(highbit, width - 1) == (highbit >> (width - 1)));
+
+    // Overlong right shifts sign-extend negative values and return 0 otherwise
+    assert(std::shr(T(-1), width) == T(-1));
+    assert(std::shr(T(-1), width + 1) == T(-1));
+    assert(std::shr(T(-1), std::numeric_limits<int>::max()) == T(-1));
+    assert(std::shr(T(42), width) == T(0));
+    assert(std::shr(T(42), width + 1) == T(0));
+
+    // Negative shift amounts shift left (bidirectional)
+    assert(std::shr(T(1), -1) == static_cast<T>(U(1) << 1));
+    assert(std::shr(T(1), -2) == static_cast<T>(U(1) << 2));
+    assert(std::shr(T(1), -3) == static_cast<T>(U(1) << 3));
+    assert(std::shr(T(1), -4) == static_cast<T>(U(1) << 4));
+    assert(std::shr(T(1), -5) == static_cast<T>(U(1) << 5));
+    assert(std::shr(T(1), -6) == static_cast<T>(U(1) << 6));
+    assert(std::shr(T(1), -7) == static_cast<T>(U(1) << 7));
+    assert(std::shr(T(1), -(width - 1)) == static_cast<T>(U(1) << (width - 1)));
+
+    // Negative overlong shifts always return 0
+    assert(std::shr(T(-1), -width) == T(0));
+    assert(std::shr(T(-1), -(width + 1)) == T(0));
+    assert(std::shr(T(-1), std::numeric_limits<int>::min()) == T(0));
+
+    if constexpr (width == 128) {
+      assert(std::shr(highbit, 64) == (highbit >> 64));
+      assert(std::shr(highbit, 127) == (highbit >> 127));
+      assert(std::shr(highbit, 128) == T(-1));
+      assert(std::shr(highbit, 200) == T(-1));
+      assert(std::shr(T(1), -64) == static_cast<T>(U(1) << 64));
+      assert(std::shr(T(1), -127) == static_cast<T>(U(1) << 127));
+      assert(std::shr(T(1), -128) == T(0));
+      assert(std::shr(T(1), -200) == T(0));
+    } else if constexpr (width == 256) {
+      assert(std::shr(highbit, 64) == (highbit >> 64));
+      assert(std::shr(highbit, 127) == (highbit >> 127));
+      assert(std::shr(highbit, 128) == (highbit >> 128));
+      assert(std::shr(highbit, 200) == (highbit >> 200));
+      assert(std::shr(highbit, 255) == T(-1));
+      assert(std::shr(highbit, 256) == T(-1));
+      assert(std::shr(T(1), -64) == static_cast<T>(U(1) << 64));
+      assert(std::shr(T(1), -127) == static_cast<T>(U(1) << 127));
+      assert(std::shr(T(1), -128) == static_cast<T>(U(1) << 128));
+      assert(std::shr(T(1), -200) == static_cast<T>(U(1) << 200));
+      assert(std::shr(T(1), -256) == T(0));
+      assert(std::shr(T(1), -300) == T(0));
+    }
+  } else {
+    ASSERT_SAME_TYPE(decltype(std::shr(T(), 0)), T);
+    ASSERT_NOEXCEPT(std::shr(T(), 0));
+
+    T highbit = highbit_u;
+    assert(std::shr(highbit, 0) == highbit);
+    assert(std::shr(highbit, 1) == (highbit_u >> 1));
+    assert(std::shr(highbit, 2) == (highbit_u >> 2));
+    assert(std::shr(highbit, 3) == (highbit_u >> 3));
+    assert(std::shr(highbit, 4) == (highbit_u >> 4));
+    assert(std::shr(highbit, 5) == (highbit_u >> 5));
+    assert(std::shr(highbit, 6) == (highbit_u >> 6));
+    assert(std::shr(highbit, 7) == (highbit_u >> 7));
+    assert(std::shr(highbit, width - 1) == T(1));
+
+    // Overlong right shifts return 0
+    assert(std::shr(T(~T(0)), width) == T(0));
+    assert(std::shr(T(~T(0)), width + 1) == T(0));
+    assert(std::shr(T(~T(0)), std::numeric_limits<int>::max()) == T(0));
+
+    // Negative shift amounts shift left (bidirectional)
+    assert(std::shr(T(1), -1) == T(2));
+    assert(std::shr(T(1), -2) == T(4));
+    assert(std::shr(T(1), -3) == T(8));
+    assert(std::shr(T(1), -4) == T(16));
+    assert(std::shr(T(1), -5) == T(32));
+    assert(std::shr(T(1), -6) == T(64));
+    assert(std::shr(T(1), -7) == T(128));
+    assert(std::shr(T(1), -(width - 1)) == (T(1) << (width - 1)));
+
+    // Negative overlong shifts always return 0
+    assert(std::shr(T(~T(0)), -width) == T(0));
+    assert(std::shr(T(~T(0)), -(width + 1)) == T(0));
+    assert(std::shr(T(1), std::numeric_limits<int>::min()) == T(0));
+
+    if constexpr (width == 128) {
+      assert(std::shr(highbit, 64) == (highbit_u >> 64));
+      assert(std::shr(highbit, 127) == T(1));
+      assert(std::shr(highbit, 128) == T(0));
+      assert(std::shr(highbit, 200) == T(0));
+      assert(std::shr(T(1), -64) == T(1) << 64);
+      assert(std::shr(T(1), -127) == T(1) << 127);
+      assert(std::shr(T(1), -128) == T(0));
+      assert(std::shr(T(1), -200) == T(0));
+    } else if constexpr (width == 256) {
+      assert(std::shr(highbit, 64) == (highbit_u >> 64));
+      assert(std::shr(highbit, 127) == (highbit_u >> 127));
+      assert(std::shr(highbit, 128) == (highbit_u >> 128));
+      assert(std::shr(highbit, 200) == (highbit_u >> 200));
+      assert(std::shr(highbit, 255) == T(1));
+      assert(std::shr(highbit, 256) == T(0));
+      assert(std::shr(highbit, 300) == T(0));
+      assert(std::shr(T(1), -64) == T(1) << 64);
+      assert(std::shr(T(1), -127) == T(1) << 127);
+      assert(std::shr(T(1), -128) == T(1) << 128);
+      assert(std::shr(T(1), -200) == T(1) << 200);
+      assert(std::shr(T(1), -255) == T(1) << 255);
+      assert(std::shr(T(1), -256) == T(0));
+      assert(std::shr(T(1), -300) == T(0));
+    }
+  }
+
+  return true;
+}
+
+struct A {};
+enum E1 : unsigned char { rEd };
+enum class E2 : unsigned char { red };
+
+template <class T>
+concept can_shr_value = requires(T value) { std::shr(value, 1); };
+
+template <class T>
+concept can_shr_shift = requires(T shift) { std::shr(1, shift); };
+
+int main(int, char**) {
+  static_assert(!can_shr_value<bool>);
+  static_assert(!can_shr_value<char>);
+  static_assert(!can_shr_value<wchar_t>);
+  static_assert(!can_shr_value<char8_t>);
+  static_assert(!can_shr_value<char16_t>);
+  static_assert(!can_shr_value<char32_t>);
+  static_assert(!can_shr_value<A>);
+  static_assert(!can_shr_value<A*>);
+  static_assert(!can_shr_value<E1>);
+  static_assert(!can_shr_value<E2>);
+
+  static_assert(!can_shr_shift<bool>);
+  static_assert(!can_shr_shift<char>);
+  static_assert(!can_shr_shift<wchar_t>);
+  static_assert(!can_shr_shift<char8_t>);
+  static_assert(!can_shr_shift<char16_t>);
+  static_assert(!can_shr_shift<char32_t>);
+  static_assert(!can_shr_shift<A>);
+  static_assert(!can_shr_shift<A*>);
+  static_assert(!can_shr_shift<E1>);
+  static_assert(!can_shr_shift<E2>);
+
+  constexpr auto test_type = []<class T> {
+    static_assert(test<T>());
+    test<T>();
+  };
+
+  types::for_each(types::unsigned_integer_types{}, test_type);
+  types::for_each(types::signed_integer_types{}, test_type);
+
+#if TEST_HAS_BITINT
+  using bitint_types = types::type_list<
+      signed _BitInt(32),
+      unsigned _BitInt(32),
+      signed _BitInt(64),
+      unsigned _BitInt(64)
+#  if __BITINT_MAXWIDTH__ >= 128
+          ,
+      signed _BitInt(128),
+      unsigned _BitInt(128)
+#  endif
+#  if __BITINT_MAXWIDTH__ >= 256
+          ,
+      signed _BitInt(256),
+      unsigned _BitInt(256)
+#  endif
+      >;
+  types::for_each(bitint_types{}, test_type);
+#endif
+
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 6b4acf671a0c9..8eb7f9779497d 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -248,7 +248,7 @@ def add_version_header(tc):
             "name": "__cpp_lib_bitops",
             "values": {
                 "c++20": 201907,
-                # "c++29": 202606,  # P3793R2 Better shifting
+                "c++29": 202606,  # P3793R2 Better shifting
                 # "c++29": 202607,  # P3104R6 Bit permutations
             },
             "headers": ["bit"],

>From 70141a91235e9a6d11449f6f6dfc6968e09b489e Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 10:05:27 +0200
Subject: [PATCH 02/13] Remove version number for partially implemented paper

---
 libcxx/docs/Status/Cxx29Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx29Papers.csv b/libcxx/docs/Status/Cxx29Papers.csv
index 30f0588b49e60..fb12b0dea91fc 100644
--- a/libcxx/docs/Status/Cxx29Papers.csv
+++ b/libcxx/docs/Status/Cxx29Papers.csv
@@ -11,7 +11,7 @@
 "`P3154R3 <https://wg21.link/P3154R3>`__","Deprecating signed character types in iostreams","2026-06 (Brno)","","","`#204398 <https://github.com/llvm/llvm-project/issues/204398>`__",""
 "`P3428R4 <https://wg21.link/P3428R4>`__","Hazard Pointer Batches","2026-06 (Brno)","","","`#204399 <https://github.com/llvm/llvm-project/issues/204399>`__",""
 "`P3248R5 <https://wg21.link/P3248R5>`__","Require ``[u]intptr_t``","2026-06 (Brno)","","","`#204400 <https://github.com/llvm/llvm-project/issues/204400>`__",""
-"`P3793R2 <https://wg21.link/P3793R2>`__","Better shifting","2026-06 (Brno)","|Partial|","24","`#204401 <https://github.com/llvm/llvm-project/issues/204401>`__","No `std::simd` overloads provided yet."
+"`P3793R2 <https://wg21.link/P3793R2>`__","Better shifting","2026-06 (Brno)","|Partial|","","`#204401 <https://github.com/llvm/llvm-project/issues/204401>`__","No `std::simd` overloads provided yet."
 "`P3242R4 <https://wg21.link/P3242R4>`__","Copy and fill for ``mdspan``","2026-06 (Brno)","","","`#204402 <https://github.com/llvm/llvm-project/issues/204402>`__",""
 "`P3692R4 <https://wg21.link/P3692R4>`__","How to Avoid OOTA Without Really Trying","2026-06 (Brno)","","","`#204403 <https://github.com/llvm/llvm-project/issues/204403>`__",""
 "`P3104R6 <https://wg21.link/P3104R6>`__","Bit permutations","2026-06 (Brno)","","","`#204404 <https://github.com/llvm/llvm-project/issues/204404>`__",""

>From bfa16b5cfe4423f6324f1da81e2b585eb64352d6 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 10:32:58 +0200
Subject: [PATCH 03/13] Fix broken reference to is_signed_v

---
 libcxx/include/__bit/shift.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__bit/shift.h b/libcxx/include/__bit/shift.h
index 4011b7ad83757..5644479a36239 100644
--- a/libcxx/include/__bit/shift.h
+++ b/libcxx/include/__bit/shift.h
@@ -25,7 +25,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp shl(_Tp __t, _Shift __cnt) noexcept {
   constexpr auto __n = static_cast<_Shift>(numeric_limits<make_unsigned_t<_Tp>>::digits);
-  if constexpr (is_signed_v<_Shift>) {
+  if constexpr (__is_signed_integer_v<_Shift>) {
     if (__cnt < 0) {
       if (__cnt <= -__n)
         return static_cast<_Tp>(__t < 0 ? -1 : 0);
@@ -40,7 +40,7 @@ template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
 template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp shr(_Tp __t, _Shift __cnt) noexcept {
   constexpr auto __n = static_cast<_Shift>(numeric_limits<make_unsigned_t<_Tp>>::digits);
-  if constexpr (is_signed_v<_Shift>) {
+  if constexpr (__is_signed_integer_v<_Shift>) {
     if (__cnt < 0) {
       if (__cnt <= -__n)
         return static_cast<_Tp>(0);

>From f7c4ab51bd3be9f3c37208070b780bfaf8d1fd4e Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 10:36:38 +0200
Subject: [PATCH 04/13] Add shift.h to module.modulemap.in

---
 libcxx/include/module.modulemap.in | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 9ce0c49b8b2b3..e774cbe22052f 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -931,6 +931,7 @@ module std {
     module invert_if        { header "__bit/invert_if.h" }
     module popcount         { header "__bit/popcount.h" }
     module rotate           { header "__bit/rotate.h" }
+    module shift            { header "__bit/shift.h" }
 
     header "bit"
     export *

>From 3cd099df89ac94ef1b39b4f08a746c85166de273 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 10:41:14 +0200
Subject: [PATCH 05/13] Fix formatting

---
 libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
index 0ffdcacb8d704..b229c0f07e015 100644
--- a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
@@ -23,8 +23,8 @@
 
 template <class T>
 constexpr bool test() {
-  using U             = std::make_unsigned_t<T>;
-  constexpr int width = std::numeric_limits<U>::digits;
+  using U               = std::make_unsigned_t<T>;
+  constexpr int width   = std::numeric_limits<U>::digits;
   constexpr U highbit_u = U(1) << (width - 1);
 
   if constexpr (std::is_signed_v<T>) {

>From b7d1b0fb2aa39e7d254a5924c1c56ca8821db20f Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 10:47:52 +0200
Subject: [PATCH 06/13] Fix missing version guard for [bit.shit]

---
 libcxx/modules/std/bit.inc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/modules/std/bit.inc b/libcxx/modules/std/bit.inc
index e0e845d8249c5..dd05b3963db33 100644
--- a/libcxx/modules/std/bit.inc
+++ b/libcxx/modules/std/bit.inc
@@ -22,9 +22,11 @@ export namespace std {
   using std::bit_width;
   using std::has_single_bit;
 
+#if _LIBCPP_STD_VER >= 29
   // [bit.shift], shifting
   using std::shl;
   using std::shr;
+#endif
 
   // [bit.rotate], rotating
   using std::rotl;

>From ffbe74d22564a2677b56251f04591814f87c1a98 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 16:34:58 +0200
Subject: [PATCH 07/13] Change nesting of version check and namespace std

---
 libcxx/include/__bit/shift.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__bit/shift.h b/libcxx/include/__bit/shift.h
index 5644479a36239..5552e8a00969b 100644
--- a/libcxx/include/__bit/shift.h
+++ b/libcxx/include/__bit/shift.h
@@ -18,10 +18,10 @@
 #  pragma GCC system_header
 #endif
 
-_LIBCPP_BEGIN_NAMESPACE_STD
-
 #if _LIBCPP_STD_VER >= 29
 
+_LIBCPP_BEGIN_NAMESPACE_STD
+
 template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp shl(_Tp __t, _Shift __cnt) noexcept {
   constexpr auto __n = static_cast<_Shift>(numeric_limits<make_unsigned_t<_Tp>>::digits);
@@ -53,8 +53,8 @@ template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
   return __t >> __cnt;
 }
 
-#endif // _LIBCPP_STD_VER >= 29
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 29
+
 #endif // _LIBCPP___BIT_SHIFT_H

>From 22aac2fcec158ca4ae73654695b6742e0c372329 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 17:25:33 +0200
Subject: [PATCH 08/13] Fix logic, check for signednedness to avoid all runtime
 checks

---
 libcxx/include/__bit/shift.h | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__bit/shift.h b/libcxx/include/__bit/shift.h
index 5552e8a00969b..02032dc658c87 100644
--- a/libcxx/include/__bit/shift.h
+++ b/libcxx/include/__bit/shift.h
@@ -27,8 +27,12 @@ template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
   constexpr auto __n = static_cast<_Shift>(numeric_limits<make_unsigned_t<_Tp>>::digits);
   if constexpr (__is_signed_integer_v<_Shift>) {
     if (__cnt < 0) {
-      if (__cnt <= -__n)
-        return static_cast<_Tp>(__t < 0 ? -1 : 0);
+      if (__cnt <= -__n) {
+        if constexpr (__is_signed_integer_v<_Tp>)
+          return static_cast<_Tp>(__t < 0 ? -1 : 0);
+        else
+          return static_cast<_Tp>(0);
+      }
       return __t >> -__cnt;
     }
   }
@@ -46,10 +50,13 @@ template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Shift>
         return static_cast<_Tp>(0);
       return __t << -__cnt;
     }
-    if (__cnt >= __n)
+  }
+  if (__cnt >= __n) {
+    if constexpr (__is_signed_integer_v<_Tp>)
       return static_cast<_Tp>(__t < 0 ? -1 : 0);
-  } else if (__cnt >= __n)
-    return static_cast<_Tp>(0);
+    else
+      return static_cast<_Tp>(0);
+  }
   return __t >> __cnt;
 }
 

>From f3133025c8c6326db0b19f2fc9a68778121e35b8 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Fri, 28 Aug 2026 17:26:22 +0200
Subject: [PATCH 09/13] Reorganize tests, cover unsigned shift amounts too

---
 .../numerics/bit/bitops.shift/shl.pass.cpp    | 132 +++++++++++++-----
 .../numerics/bit/bitops.shift/shr.pass.cpp    | 116 ++++++++++-----
 2 files changed, 183 insertions(+), 65 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
index b229c0f07e015..174b39062f16f 100644
--- a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
@@ -21,6 +21,40 @@
 #include "test_macros.h"
 #include "type_algorithms.h"
 
+struct A {};
+enum E1 : unsigned char { rEd };
+enum class E2 : unsigned char { red };
+
+template <class T>
+concept can_shl_value = requires(T value) { std::shl(value, 1); };
+
+static_assert(can_shl_value<int>, "Concept is broken. We should be able to shift int.");
+static_assert(!can_shl_value<bool>);
+static_assert(!can_shl_value<char>);
+static_assert(!can_shl_value<wchar_t>);
+static_assert(!can_shl_value<char8_t>);
+static_assert(!can_shl_value<char16_t>);
+static_assert(!can_shl_value<char32_t>);
+static_assert(!can_shl_value<A>);
+static_assert(!can_shl_value<A*>);
+static_assert(!can_shl_value<E1>);
+static_assert(!can_shl_value<E2>);
+
+template <class T>
+concept can_shl_shift = requires(T shift) { std::shl(1, shift); };
+
+static_assert(can_shl_shift<int>, "Concept is broken. We should be able to shift by int.");
+static_assert(!can_shl_shift<bool>);
+static_assert(!can_shl_shift<char>);
+static_assert(!can_shl_shift<wchar_t>);
+static_assert(!can_shl_shift<char8_t>);
+static_assert(!can_shl_shift<char16_t>);
+static_assert(!can_shl_shift<char32_t>);
+static_assert(!can_shl_shift<A>);
+static_assert(!can_shl_shift<A*>);
+static_assert(!can_shl_shift<E1>);
+static_assert(!can_shl_shift<E2>);
+
 template <class T>
 constexpr bool test() {
   using U               = std::make_unsigned_t<T>;
@@ -32,22 +66,36 @@ constexpr bool test() {
     ASSERT_NOEXCEPT(std::shl(T(), 0));
 
     assert(std::shl(T(1), 0) == T(1));
+    assert(std::shl(T(1), 0u) == T(1));
     assert(std::shl(T(1), 1) == T(2));
+    assert(std::shl(T(1), 1u) == T(2));
     assert(std::shl(T(1), 2) == T(4));
+    assert(std::shl(T(1), 2u) == T(4));
     assert(std::shl(T(1), 3) == T(8));
+    assert(std::shl(T(1), 3u) == T(8));
     assert(std::shl(T(1), 4) == T(16));
+    assert(std::shl(T(1), 4u) == T(16));
     assert(std::shl(T(1), 5) == T(32));
+    assert(std::shl(T(1), 5u) == T(32));
     assert(std::shl(T(1), 6) == T(64));
-    assert(std::shl(T(1), 7) == static_cast<T>(U(128)));
+    assert(std::shl(T(1), 6u) == T(64));
+    assert(std::shl(T(1), 7) == T(128));
+    assert(std::shl(T(1), 7u) == T(128));
 
     // Overlong shifts return 0
     assert(std::shl(T(1), width) == T(0));
+    assert(std::shl(T(1), static_cast<U>(width)) == T(0));
     assert(std::shl(T(1), width + 1) == T(0));
+    assert(std::shl(T(1), static_cast<U>(width + 1)) == T(0));
     assert(std::shl(T(1), width + 100) == T(0));
+    assert(std::shl(T(1), static_cast<U>(width + 100)) == T(0));
     assert(std::shl(T(1), std::numeric_limits<int>::max()) == T(0));
+    assert(std::shl(T(1), static_cast<U>(std::numeric_limits<int>::max())) == T(0));
 
     assert(std::shl(T(-1), width) == T(0));
+    assert(std::shl(T(-1), static_cast<U>(width)) == T(0));
     assert(std::shl(T(-1), width + 5) == T(0));
+    assert(std::shl(T(-1), static_cast<U>(width + 5)) == T(0));
 
     // Negative shift amounts shift right arithmetically (bidirectional)
     T highbit = static_cast<T>(highbit_u);
@@ -68,11 +116,35 @@ constexpr bool test() {
     assert(std::shl(T(42), -(width + 1)) == T(0));
 
     if constexpr (width == 128) {
+      assert(std::shl(T(1), 64) == U(1) << 64);
+      assert(std::shl(T(1), 64u) == U(1) << 64);
+      assert(std::shl(T(1), 127) == U(1) << 127);
+      assert(std::shl(T(1), 127u) == U(1) << 127);
+      assert(std::shl(T(1), 128) == T(0));
+      assert(std::shl(T(1), 128u) == T(0));
+      assert(std::shl(T(1), 200) == T(0));
+      assert(std::shl(T(1), 200u) == T(0));
+
       assert(std::shl(highbit, -64) == (highbit >> 64));
       assert(std::shl(highbit, -127) == (highbit >> 127));
       assert(std::shl(highbit, -128) == T(-1));
       assert(std::shl(highbit, -200) == T(-1));
     } else if constexpr (width == 256) {
+      assert(std::shl(T(1), 64) == U(1) << 64);
+      assert(std::shl(T(1), 64u) == U(1) << 64);
+      assert(std::shl(T(1), 127) == U(1) << 127);
+      assert(std::shl(T(1), 127u) == U(1) << 127);
+      assert(std::shl(T(1), 128) == U(1) << 128);
+      assert(std::shl(T(1), 128u) == U(1) << 128);
+      assert(std::shl(T(1), 200) == U(1) << 200);
+      assert(std::shl(T(1), 200u) == U(1) << 200);
+      assert(std::shl(T(1), 255) == U(1) << 255);
+      assert(std::shl(T(1), 255u) == U(1) << 255);
+      assert(std::shl(T(1), 256) == T(0));
+      assert(std::shl(T(1), 256u) == T(0));
+      assert(std::shl(T(1), 300) == T(0));
+      assert(std::shl(T(1), 300u) == T(0));
+
       assert(std::shl(highbit, -64) == (highbit >> 64));
       assert(std::shl(highbit, -127) == (highbit >> 127));
       assert(std::shl(highbit, -128) == (highbit >> 128));
@@ -85,22 +157,36 @@ constexpr bool test() {
     ASSERT_NOEXCEPT(std::shl(T(), 0));
 
     assert(std::shl(T(1), 0) == T(1));
+    assert(std::shl(T(1), 0u) == T(1));
     assert(std::shl(T(1), 1) == T(2));
+    assert(std::shl(T(1), 1u) == T(2));
     assert(std::shl(T(1), 2) == T(4));
+    assert(std::shl(T(1), 2u) == T(4));
     assert(std::shl(T(1), 3) == T(8));
+    assert(std::shl(T(1), 3u) == T(8));
     assert(std::shl(T(1), 4) == T(16));
+    assert(std::shl(T(1), 4u) == T(16));
     assert(std::shl(T(1), 5) == T(32));
+    assert(std::shl(T(1), 5u) == T(32));
     assert(std::shl(T(1), 6) == T(64));
+    assert(std::shl(T(1), 6u) == T(64));
     assert(std::shl(T(1), 7) == T(128));
+    assert(std::shl(T(1), 7u) == T(128));
 
     // Overlong shifts return 0
     assert(std::shl(T(1), width) == T(0));
+    assert(std::shl(T(1), static_cast<U>(width)) == T(0));
     assert(std::shl(T(1), width + 1) == T(0));
+    assert(std::shl(T(1), static_cast<U>(width + 1)) == T(0));
     assert(std::shl(T(1), width + 100) == T(0));
+    assert(std::shl(T(1), static_cast<U>(width + 100)) == T(0));
     assert(std::shl(T(1), std::numeric_limits<int>::max()) == T(0));
+    assert(std::shl(T(1), static_cast<U>(std::numeric_limits<int>::max())) == T(0));
 
     assert(std::shl(T(~T(0)), width) == T(0));
+    assert(std::shl(T(~T(0)), static_cast<U>(width)) == T(0));
     assert(std::shl(T(~T(0)), width + 5) == T(0));
+    assert(std::shl(T(~T(0)), static_cast<U>(width + 5)) == T(0));
 
     // Negative shift amounts shift right (bidirectional)
     T highbit = highbit_u;
@@ -120,63 +206,43 @@ constexpr bool test() {
 
     if constexpr (width == 128) {
       assert(std::shl(T(1), 64) == T(1) << 64);
+      assert(std::shl(T(1), 64u) == T(1) << 64);
       assert(std::shl(T(1), 127) == T(1) << 127);
+      assert(std::shl(T(1), 127u) == T(1) << 127);
       assert(std::shl(T(1), 128) == T(0));
+      assert(std::shl(T(1), 128u) == T(0));
       assert(std::shl(T(1), 200) == T(0));
+      assert(std::shl(T(1), 200u) == T(0));
+
       assert(std::shl(T(1) << 127, -1) == T(1) << 126);
       assert(std::shl(T(1) << 127, -127) == T(1));
       assert(std::shl(T(1) << 127, -128) == T(0));
       assert(std::shl(T(1) << 127, -200) == T(0));
     } else if constexpr (width == 256) {
       assert(std::shl(T(1), 64) == T(1) << 64);
+      assert(std::shl(T(1), 64u) == T(1) << 64);
       assert(std::shl(T(1), 127) == T(1) << 127);
+      assert(std::shl(T(1), 127u) == T(1) << 127);
       assert(std::shl(T(1), 128) == T(1) << 128);
+      assert(std::shl(T(1), 128u) == T(1) << 128);
       assert(std::shl(T(1), 200) == T(1) << 200);
+      assert(std::shl(T(1), 200u) == T(1) << 200);
+
       assert(std::shl(T(1) << 127, -1) == T(1) << 126);
       assert(std::shl(T(1) << 127, -127) == T(1));
       assert(std::shl(T(1) << 127, -128) == T(0));
       assert(std::shl(T(1) << 127, -200) == T(0));
       assert(std::shl(T(1), 256) == T(0));
+      assert(std::shl(T(1), 256u) == T(0));
       assert(std::shl(T(1), 300) == T(0));
+      assert(std::shl(T(1), 300u) == T(0));
     }
   }
 
   return true;
 }
 
-struct A {};
-enum E1 : unsigned char { rEd };
-enum class E2 : unsigned char { red };
-
-template <class T>
-concept can_shl_value = requires(T value) { std::shl(value, 1); };
-
-template <class T>
-concept can_shl_shift = requires(T shift) { std::shl(1, shift); };
-
 int main(int, char**) {
-  static_assert(!can_shl_value<bool>);
-  static_assert(!can_shl_value<char>);
-  static_assert(!can_shl_value<wchar_t>);
-  static_assert(!can_shl_value<char8_t>);
-  static_assert(!can_shl_value<char16_t>);
-  static_assert(!can_shl_value<char32_t>);
-  static_assert(!can_shl_value<A>);
-  static_assert(!can_shl_value<A*>);
-  static_assert(!can_shl_value<E1>);
-  static_assert(!can_shl_value<E2>);
-
-  static_assert(!can_shl_shift<bool>);
-  static_assert(!can_shl_shift<char>);
-  static_assert(!can_shl_shift<wchar_t>);
-  static_assert(!can_shl_shift<char8_t>);
-  static_assert(!can_shl_shift<char16_t>);
-  static_assert(!can_shl_shift<char32_t>);
-  static_assert(!can_shl_shift<A>);
-  static_assert(!can_shl_shift<A*>);
-  static_assert(!can_shl_shift<E1>);
-  static_assert(!can_shl_shift<E2>);
-
   constexpr auto test_type = []<class T> {
     static_assert(test<T>());
     test<T>();
diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
index b3501284bfd4d..9ddf679b33b87 100644
--- a/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
@@ -21,6 +21,39 @@
 #include "test_macros.h"
 #include "type_algorithms.h"
 
+struct A {};
+enum E1 : unsigned char { rEd };
+enum class E2 : unsigned char { red };
+
+template <class T>
+concept can_shr_value = requires(T value) { std::shr(value, 1); };
+
+static_assert(can_shr_value<int>, "Concept is broken. We should be able to shift int.");
+static_assert(!can_shr_value<bool>);
+static_assert(!can_shr_value<char>);
+static_assert(!can_shr_value<wchar_t>);
+static_assert(!can_shr_value<char8_t>);
+static_assert(!can_shr_value<char16_t>);
+static_assert(!can_shr_value<char32_t>);
+static_assert(!can_shr_value<A>);
+static_assert(!can_shr_value<A*>);
+static_assert(!can_shr_value<E1>);
+static_assert(!can_shr_value<E2>);
+
+template <class T>
+concept can_shr_shift = requires(T shift) { std::shr(1, shift); };
+
+static_assert(can_shr_shift<int>, "Concept is broken. We should be able to shift by int.");
+static_assert(!can_shr_shift<char>);
+static_assert(!can_shr_shift<wchar_t>);
+static_assert(!can_shr_shift<char8_t>);
+static_assert(!can_shr_shift<char16_t>);
+static_assert(!can_shr_shift<char32_t>);
+static_assert(!can_shr_shift<A>);
+static_assert(!can_shr_shift<A*>);
+static_assert(!can_shr_shift<E1>);
+static_assert(!can_shr_shift<E2>);
+
 template <class T>
 constexpr bool test() {
   using U               = std::make_unsigned_t<T>;
@@ -33,21 +66,35 @@ constexpr bool test() {
 
     T highbit = static_cast<T>(highbit_u);
     assert(std::shr(highbit, 0) == highbit);
+    assert(std::shr(highbit, 0u) == highbit);
     assert(std::shr(highbit, 1) == (highbit >> 1));
+    assert(std::shr(highbit, 1u) == (highbit >> 1));
     assert(std::shr(highbit, 2) == (highbit >> 2));
+    assert(std::shr(highbit, 2u) == (highbit >> 2));
     assert(std::shr(highbit, 3) == (highbit >> 3));
+    assert(std::shr(highbit, 3u) == (highbit >> 3));
     assert(std::shr(highbit, 4) == (highbit >> 4));
+    assert(std::shr(highbit, 4u) == (highbit >> 4));
     assert(std::shr(highbit, 5) == (highbit >> 5));
+    assert(std::shr(highbit, 5u) == (highbit >> 5));
     assert(std::shr(highbit, 6) == (highbit >> 6));
+    assert(std::shr(highbit, 6u) == (highbit >> 6));
     assert(std::shr(highbit, 7) == (highbit >> 7));
+    assert(std::shr(highbit, 7u) == (highbit >> 7));
     assert(std::shr(highbit, width - 1) == (highbit >> (width - 1)));
+    assert(std::shr(highbit, static_cast<unsigned>(width - 1)) == (highbit >> (width - 1)));
 
     // Overlong right shifts sign-extend negative values and return 0 otherwise
     assert(std::shr(T(-1), width) == T(-1));
+    assert(std::shr(T(-1), static_cast<unsigned>(width)) == T(-1));
     assert(std::shr(T(-1), width + 1) == T(-1));
+    assert(std::shr(T(-1), static_cast<unsigned>(width + 1)) == T(-1));
     assert(std::shr(T(-1), std::numeric_limits<int>::max()) == T(-1));
+    assert(std::shr(T(-1), static_cast<unsigned>(std::numeric_limits<int>::max())) == T(-1));
     assert(std::shr(T(42), width) == T(0));
+    assert(std::shr(T(42), static_cast<unsigned>(width)) == T(0));
     assert(std::shr(T(42), width + 1) == T(0));
+    assert(std::shr(T(42), static_cast<unsigned>(width + 1)) == T(0));
 
     // Negative shift amounts shift left (bidirectional)
     assert(std::shr(T(1), -1) == static_cast<T>(U(1) << 1));
@@ -66,20 +113,32 @@ constexpr bool test() {
 
     if constexpr (width == 128) {
       assert(std::shr(highbit, 64) == (highbit >> 64));
+      assert(std::shr(highbit, 64u) == (highbit >> 64));
       assert(std::shr(highbit, 127) == (highbit >> 127));
+      assert(std::shr(highbit, 127u) == (highbit >> 127));
       assert(std::shr(highbit, 128) == T(-1));
+      assert(std::shr(highbit, 128u) == T(-1));
       assert(std::shr(highbit, 200) == T(-1));
+      assert(std::shr(highbit, 200u) == T(-1));
+
       assert(std::shr(T(1), -64) == static_cast<T>(U(1) << 64));
       assert(std::shr(T(1), -127) == static_cast<T>(U(1) << 127));
       assert(std::shr(T(1), -128) == T(0));
       assert(std::shr(T(1), -200) == T(0));
     } else if constexpr (width == 256) {
       assert(std::shr(highbit, 64) == (highbit >> 64));
+      assert(std::shr(highbit, 64u) == (highbit >> 64));
       assert(std::shr(highbit, 127) == (highbit >> 127));
+      assert(std::shr(highbit, 127u) == (highbit >> 127));
       assert(std::shr(highbit, 128) == (highbit >> 128));
+      assert(std::shr(highbit, 128u) == (highbit >> 128));
       assert(std::shr(highbit, 200) == (highbit >> 200));
+      assert(std::shr(highbit, 200u) == (highbit >> 200));
       assert(std::shr(highbit, 255) == T(-1));
+      assert(std::shr(highbit, 255u) == T(-1));
       assert(std::shr(highbit, 256) == T(-1));
+      assert(std::shr(highbit, 256u) == T(-1));
+
       assert(std::shr(T(1), -64) == static_cast<T>(U(1) << 64));
       assert(std::shr(T(1), -127) == static_cast<T>(U(1) << 127));
       assert(std::shr(T(1), -128) == static_cast<T>(U(1) << 128));
@@ -93,19 +152,31 @@ constexpr bool test() {
 
     T highbit = highbit_u;
     assert(std::shr(highbit, 0) == highbit);
+    assert(std::shr(highbit, 0u) == highbit);
     assert(std::shr(highbit, 1) == (highbit_u >> 1));
+    assert(std::shr(highbit, 1u) == (highbit_u >> 1));
     assert(std::shr(highbit, 2) == (highbit_u >> 2));
+    assert(std::shr(highbit, 2u) == (highbit_u >> 2));
     assert(std::shr(highbit, 3) == (highbit_u >> 3));
+    assert(std::shr(highbit, 3u) == (highbit_u >> 3));
     assert(std::shr(highbit, 4) == (highbit_u >> 4));
+    assert(std::shr(highbit, 4u) == (highbit_u >> 4));
     assert(std::shr(highbit, 5) == (highbit_u >> 5));
+    assert(std::shr(highbit, 5u) == (highbit_u >> 5));
     assert(std::shr(highbit, 6) == (highbit_u >> 6));
+    assert(std::shr(highbit, 6u) == (highbit_u >> 6));
     assert(std::shr(highbit, 7) == (highbit_u >> 7));
+    assert(std::shr(highbit, 7u) == (highbit_u >> 7));
     assert(std::shr(highbit, width - 1) == T(1));
+    assert(std::shr(highbit, static_cast<unsigned>(width - 1)) == T(1));
 
     // Overlong right shifts return 0
     assert(std::shr(T(~T(0)), width) == T(0));
+    assert(std::shr(T(~T(0)), static_cast<unsigned>(width)) == T(0));
     assert(std::shr(T(~T(0)), width + 1) == T(0));
+    assert(std::shr(T(~T(0)), static_cast<unsigned>(width + 1)) == T(0));
     assert(std::shr(T(~T(0)), std::numeric_limits<int>::max()) == T(0));
+    assert(std::shr(T(~T(0)), static_cast<unsigned>(std::numeric_limits<int>::max())) == T(0));
 
     // Negative shift amounts shift left (bidirectional)
     assert(std::shr(T(1), -1) == T(2));
@@ -124,21 +195,34 @@ constexpr bool test() {
 
     if constexpr (width == 128) {
       assert(std::shr(highbit, 64) == (highbit_u >> 64));
+      assert(std::shr(highbit, 64u) == (highbit_u >> 64));
       assert(std::shr(highbit, 127) == T(1));
+      assert(std::shr(highbit, 127u) == T(1));
       assert(std::shr(highbit, 128) == T(0));
+      assert(std::shr(highbit, 128u) == T(0));
       assert(std::shr(highbit, 200) == T(0));
+      assert(std::shr(highbit, 200u) == T(0));
+
       assert(std::shr(T(1), -64) == T(1) << 64);
       assert(std::shr(T(1), -127) == T(1) << 127);
       assert(std::shr(T(1), -128) == T(0));
       assert(std::shr(T(1), -200) == T(0));
     } else if constexpr (width == 256) {
       assert(std::shr(highbit, 64) == (highbit_u >> 64));
+      assert(std::shr(highbit, 64u) == (highbit_u >> 64));
       assert(std::shr(highbit, 127) == (highbit_u >> 127));
+      assert(std::shr(highbit, 127u) == (highbit_u >> 127));
       assert(std::shr(highbit, 128) == (highbit_u >> 128));
+      assert(std::shr(highbit, 128u) == (highbit_u >> 128));
       assert(std::shr(highbit, 200) == (highbit_u >> 200));
+      assert(std::shr(highbit, 200u) == (highbit_u >> 200));
       assert(std::shr(highbit, 255) == T(1));
+      assert(std::shr(highbit, 255u) == T(1));
       assert(std::shr(highbit, 256) == T(0));
+      assert(std::shr(highbit, 256u) == T(0));
       assert(std::shr(highbit, 300) == T(0));
+      assert(std::shr(highbit, 300u) == T(0));
+
       assert(std::shr(T(1), -64) == T(1) << 64);
       assert(std::shr(T(1), -127) == T(1) << 127);
       assert(std::shr(T(1), -128) == T(1) << 128);
@@ -152,39 +236,7 @@ constexpr bool test() {
   return true;
 }
 
-struct A {};
-enum E1 : unsigned char { rEd };
-enum class E2 : unsigned char { red };
-
-template <class T>
-concept can_shr_value = requires(T value) { std::shr(value, 1); };
-
-template <class T>
-concept can_shr_shift = requires(T shift) { std::shr(1, shift); };
-
 int main(int, char**) {
-  static_assert(!can_shr_value<bool>);
-  static_assert(!can_shr_value<char>);
-  static_assert(!can_shr_value<wchar_t>);
-  static_assert(!can_shr_value<char8_t>);
-  static_assert(!can_shr_value<char16_t>);
-  static_assert(!can_shr_value<char32_t>);
-  static_assert(!can_shr_value<A>);
-  static_assert(!can_shr_value<A*>);
-  static_assert(!can_shr_value<E1>);
-  static_assert(!can_shr_value<E2>);
-
-  static_assert(!can_shr_shift<bool>);
-  static_assert(!can_shr_shift<char>);
-  static_assert(!can_shr_shift<wchar_t>);
-  static_assert(!can_shr_shift<char8_t>);
-  static_assert(!can_shr_shift<char16_t>);
-  static_assert(!can_shr_shift<char32_t>);
-  static_assert(!can_shr_shift<A>);
-  static_assert(!can_shr_shift<A*>);
-  static_assert(!can_shr_shift<E1>);
-  static_assert(!can_shr_shift<E2>);
-
   constexpr auto test_type = []<class T> {
     static_assert(test<T>());
     test<T>();

>From 605e8a66fcf68adbc5f469e877849bca5752cc40 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Sat, 12 Sep 2026 09:51:03 +0200
Subject: [PATCH 10/13] Fix generated file

---
 libcxx/docs/FeatureTestMacroTable.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 81dc64b5e4b0f..8329c6114d212 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -561,7 +561,7 @@ Status
     **C++29**
     ----------------------------------------------------------------------------
     ``__cpp_lib_bitops``                                       ``202606L``
-    ----------------------------------------------------------------------------
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_expected``                                     ``202606L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_map_lookup``                                   *unimplemented*

>From d9030bba4a0ddaeb2188841f2f8edd81725bc8f1 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Sat, 12 Sep 2026 09:59:35 +0200
Subject: [PATCH 11/13] Split test() into two overloads

---
 .../numerics/bit/bitops.shift/shl.pass.cpp    | 360 +++++++++---------
 1 file changed, 183 insertions(+), 177 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
index 174b39062f16f..3a4d9cdca01e1 100644
--- a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
@@ -17,6 +17,7 @@
 #include <cassert>
 #include <limits>
 #include <type_traits>
+#include <concepts>
 
 #include "test_macros.h"
 #include "type_algorithms.h"
@@ -55,188 +56,193 @@ static_assert(!can_shl_shift<A*>);
 static_assert(!can_shl_shift<E1>);
 static_assert(!can_shl_shift<E2>);
 
-template <class T>
+template <std::signed_integral T>
 constexpr bool test() {
   using U               = std::make_unsigned_t<T>;
   constexpr int width   = std::numeric_limits<U>::digits;
   constexpr U highbit_u = U(1) << (width - 1);
 
-  if constexpr (std::is_signed_v<T>) {
-    ASSERT_SAME_TYPE(decltype(std::shl(T(), 0)), T);
-    ASSERT_NOEXCEPT(std::shl(T(), 0));
-
-    assert(std::shl(T(1), 0) == T(1));
-    assert(std::shl(T(1), 0u) == T(1));
-    assert(std::shl(T(1), 1) == T(2));
-    assert(std::shl(T(1), 1u) == T(2));
-    assert(std::shl(T(1), 2) == T(4));
-    assert(std::shl(T(1), 2u) == T(4));
-    assert(std::shl(T(1), 3) == T(8));
-    assert(std::shl(T(1), 3u) == T(8));
-    assert(std::shl(T(1), 4) == T(16));
-    assert(std::shl(T(1), 4u) == T(16));
-    assert(std::shl(T(1), 5) == T(32));
-    assert(std::shl(T(1), 5u) == T(32));
-    assert(std::shl(T(1), 6) == T(64));
-    assert(std::shl(T(1), 6u) == T(64));
-    assert(std::shl(T(1), 7) == T(128));
-    assert(std::shl(T(1), 7u) == T(128));
-
-    // Overlong shifts return 0
-    assert(std::shl(T(1), width) == T(0));
-    assert(std::shl(T(1), static_cast<U>(width)) == T(0));
-    assert(std::shl(T(1), width + 1) == T(0));
-    assert(std::shl(T(1), static_cast<U>(width + 1)) == T(0));
-    assert(std::shl(T(1), width + 100) == T(0));
-    assert(std::shl(T(1), static_cast<U>(width + 100)) == T(0));
-    assert(std::shl(T(1), std::numeric_limits<int>::max()) == T(0));
-    assert(std::shl(T(1), static_cast<U>(std::numeric_limits<int>::max())) == T(0));
-
-    assert(std::shl(T(-1), width) == T(0));
-    assert(std::shl(T(-1), static_cast<U>(width)) == T(0));
-    assert(std::shl(T(-1), width + 5) == T(0));
-    assert(std::shl(T(-1), static_cast<U>(width + 5)) == T(0));
-
-    // Negative shift amounts shift right arithmetically (bidirectional)
-    T highbit = static_cast<T>(highbit_u);
-    assert(std::shl(highbit, -1) == (highbit >> 1));
-    assert(std::shl(highbit, -2) == (highbit >> 2));
-    assert(std::shl(highbit, -3) == (highbit >> 3));
-    assert(std::shl(highbit, -4) == (highbit >> 4));
-    assert(std::shl(highbit, -5) == (highbit >> 5));
-    assert(std::shl(highbit, -6) == (highbit >> 6));
-    assert(std::shl(highbit, -7) == (highbit >> 7));
-    assert(std::shl(highbit, -(width - 1)) == (highbit >> (width - 1)));
-
-    // Negative overlong shifts: sign-extended for negative values, 0 otherwise
-    assert(std::shl(T(-1), -width) == T(-1));
-    assert(std::shl(T(-1), -(width + 1)) == T(-1));
-    assert(std::shl(T(-1), std::numeric_limits<int>::min()) == T(-1));
-    assert(std::shl(T(42), -width) == T(0));
-    assert(std::shl(T(42), -(width + 1)) == T(0));
-
-    if constexpr (width == 128) {
-      assert(std::shl(T(1), 64) == U(1) << 64);
-      assert(std::shl(T(1), 64u) == U(1) << 64);
-      assert(std::shl(T(1), 127) == U(1) << 127);
-      assert(std::shl(T(1), 127u) == U(1) << 127);
-      assert(std::shl(T(1), 128) == T(0));
-      assert(std::shl(T(1), 128u) == T(0));
-      assert(std::shl(T(1), 200) == T(0));
-      assert(std::shl(T(1), 200u) == T(0));
-
-      assert(std::shl(highbit, -64) == (highbit >> 64));
-      assert(std::shl(highbit, -127) == (highbit >> 127));
-      assert(std::shl(highbit, -128) == T(-1));
-      assert(std::shl(highbit, -200) == T(-1));
-    } else if constexpr (width == 256) {
-      assert(std::shl(T(1), 64) == U(1) << 64);
-      assert(std::shl(T(1), 64u) == U(1) << 64);
-      assert(std::shl(T(1), 127) == U(1) << 127);
-      assert(std::shl(T(1), 127u) == U(1) << 127);
-      assert(std::shl(T(1), 128) == U(1) << 128);
-      assert(std::shl(T(1), 128u) == U(1) << 128);
-      assert(std::shl(T(1), 200) == U(1) << 200);
-      assert(std::shl(T(1), 200u) == U(1) << 200);
-      assert(std::shl(T(1), 255) == U(1) << 255);
-      assert(std::shl(T(1), 255u) == U(1) << 255);
-      assert(std::shl(T(1), 256) == T(0));
-      assert(std::shl(T(1), 256u) == T(0));
-      assert(std::shl(T(1), 300) == T(0));
-      assert(std::shl(T(1), 300u) == T(0));
-
-      assert(std::shl(highbit, -64) == (highbit >> 64));
-      assert(std::shl(highbit, -127) == (highbit >> 127));
-      assert(std::shl(highbit, -128) == (highbit >> 128));
-      assert(std::shl(highbit, -200) == (highbit >> 200));
-      assert(std::shl(highbit, -255) == T(-1));
-      assert(std::shl(highbit, -256) == T(-1));
-    }
-  } else {
-    ASSERT_SAME_TYPE(decltype(std::shl(T(), 0)), T);
-    ASSERT_NOEXCEPT(std::shl(T(), 0));
-
-    assert(std::shl(T(1), 0) == T(1));
-    assert(std::shl(T(1), 0u) == T(1));
-    assert(std::shl(T(1), 1) == T(2));
-    assert(std::shl(T(1), 1u) == T(2));
-    assert(std::shl(T(1), 2) == T(4));
-    assert(std::shl(T(1), 2u) == T(4));
-    assert(std::shl(T(1), 3) == T(8));
-    assert(std::shl(T(1), 3u) == T(8));
-    assert(std::shl(T(1), 4) == T(16));
-    assert(std::shl(T(1), 4u) == T(16));
-    assert(std::shl(T(1), 5) == T(32));
-    assert(std::shl(T(1), 5u) == T(32));
-    assert(std::shl(T(1), 6) == T(64));
-    assert(std::shl(T(1), 6u) == T(64));
-    assert(std::shl(T(1), 7) == T(128));
-    assert(std::shl(T(1), 7u) == T(128));
-
-    // Overlong shifts return 0
-    assert(std::shl(T(1), width) == T(0));
-    assert(std::shl(T(1), static_cast<U>(width)) == T(0));
-    assert(std::shl(T(1), width + 1) == T(0));
-    assert(std::shl(T(1), static_cast<U>(width + 1)) == T(0));
-    assert(std::shl(T(1), width + 100) == T(0));
-    assert(std::shl(T(1), static_cast<U>(width + 100)) == T(0));
-    assert(std::shl(T(1), std::numeric_limits<int>::max()) == T(0));
-    assert(std::shl(T(1), static_cast<U>(std::numeric_limits<int>::max())) == T(0));
-
-    assert(std::shl(T(~T(0)), width) == T(0));
-    assert(std::shl(T(~T(0)), static_cast<U>(width)) == T(0));
-    assert(std::shl(T(~T(0)), width + 5) == T(0));
-    assert(std::shl(T(~T(0)), static_cast<U>(width + 5)) == T(0));
-
-    // Negative shift amounts shift right (bidirectional)
-    T highbit = highbit_u;
-    assert(std::shl(highbit, -1) == (highbit_u >> 1));
-    assert(std::shl(highbit, -2) == (highbit_u >> 2));
-    assert(std::shl(highbit, -3) == (highbit_u >> 3));
-    assert(std::shl(highbit, -4) == (highbit_u >> 4));
-    assert(std::shl(highbit, -5) == (highbit_u >> 5));
-    assert(std::shl(highbit, -6) == (highbit_u >> 6));
-    assert(std::shl(highbit, -7) == (highbit_u >> 7));
-    assert(std::shl(highbit, -(width - 1)) == T(1));
-
-    // Negative overlong shifts: 0 for unsigned
-    assert(std::shl(T(~T(0)), -width) == T(0));
-    assert(std::shl(T(~T(0)), -(width + 1)) == T(0));
-    assert(std::shl(T(1), std::numeric_limits<int>::min()) == T(0));
-
-    if constexpr (width == 128) {
-      assert(std::shl(T(1), 64) == T(1) << 64);
-      assert(std::shl(T(1), 64u) == T(1) << 64);
-      assert(std::shl(T(1), 127) == T(1) << 127);
-      assert(std::shl(T(1), 127u) == T(1) << 127);
-      assert(std::shl(T(1), 128) == T(0));
-      assert(std::shl(T(1), 128u) == T(0));
-      assert(std::shl(T(1), 200) == T(0));
-      assert(std::shl(T(1), 200u) == T(0));
-
-      assert(std::shl(T(1) << 127, -1) == T(1) << 126);
-      assert(std::shl(T(1) << 127, -127) == T(1));
-      assert(std::shl(T(1) << 127, -128) == T(0));
-      assert(std::shl(T(1) << 127, -200) == T(0));
-    } else if constexpr (width == 256) {
-      assert(std::shl(T(1), 64) == T(1) << 64);
-      assert(std::shl(T(1), 64u) == T(1) << 64);
-      assert(std::shl(T(1), 127) == T(1) << 127);
-      assert(std::shl(T(1), 127u) == T(1) << 127);
-      assert(std::shl(T(1), 128) == T(1) << 128);
-      assert(std::shl(T(1), 128u) == T(1) << 128);
-      assert(std::shl(T(1), 200) == T(1) << 200);
-      assert(std::shl(T(1), 200u) == T(1) << 200);
-
-      assert(std::shl(T(1) << 127, -1) == T(1) << 126);
-      assert(std::shl(T(1) << 127, -127) == T(1));
-      assert(std::shl(T(1) << 127, -128) == T(0));
-      assert(std::shl(T(1) << 127, -200) == T(0));
-      assert(std::shl(T(1), 256) == T(0));
-      assert(std::shl(T(1), 256u) == T(0));
-      assert(std::shl(T(1), 300) == T(0));
-      assert(std::shl(T(1), 300u) == T(0));
-    }
+  ASSERT_SAME_TYPE(decltype(std::shl(T(), 0)), T);
+  ASSERT_NOEXCEPT(std::shl(T(), 0));
+
+  assert(std::shl(T(1), 0) == T(1));
+  assert(std::shl(T(1), 0u) == T(1));
+  assert(std::shl(T(1), 1) == T(2));
+  assert(std::shl(T(1), 1u) == T(2));
+  assert(std::shl(T(1), 2) == T(4));
+  assert(std::shl(T(1), 2u) == T(4));
+  assert(std::shl(T(1), 3) == T(8));
+  assert(std::shl(T(1), 3u) == T(8));
+  assert(std::shl(T(1), 4) == T(16));
+  assert(std::shl(T(1), 4u) == T(16));
+  assert(std::shl(T(1), 5) == T(32));
+  assert(std::shl(T(1), 5u) == T(32));
+  assert(std::shl(T(1), 6) == T(64));
+  assert(std::shl(T(1), 6u) == T(64));
+  assert(std::shl(T(1), 7) == T(128));
+  assert(std::shl(T(1), 7u) == T(128));
+
+  // Overlong shifts return 0
+  assert(std::shl(T(1), width) == T(0));
+  assert(std::shl(T(1), static_cast<U>(width)) == T(0));
+  assert(std::shl(T(1), width + 1) == T(0));
+  assert(std::shl(T(1), static_cast<U>(width + 1)) == T(0));
+  assert(std::shl(T(1), width + 100) == T(0));
+  assert(std::shl(T(1), static_cast<U>(width + 100)) == T(0));
+  assert(std::shl(T(1), std::numeric_limits<int>::max()) == T(0));
+  assert(std::shl(T(1), static_cast<U>(std::numeric_limits<int>::max())) == T(0));
+
+  assert(std::shl(T(-1), width) == T(0));
+  assert(std::shl(T(-1), static_cast<U>(width)) == T(0));
+  assert(std::shl(T(-1), width + 5) == T(0));
+  assert(std::shl(T(-1), static_cast<U>(width + 5)) == T(0));
+
+  // Negative shift amounts shift right arithmetically (bidirectional)
+  T highbit = static_cast<T>(highbit_u);
+  assert(std::shl(highbit, -1) == (highbit >> 1));
+  assert(std::shl(highbit, -2) == (highbit >> 2));
+  assert(std::shl(highbit, -3) == (highbit >> 3));
+  assert(std::shl(highbit, -4) == (highbit >> 4));
+  assert(std::shl(highbit, -5) == (highbit >> 5));
+  assert(std::shl(highbit, -6) == (highbit >> 6));
+  assert(std::shl(highbit, -7) == (highbit >> 7));
+  assert(std::shl(highbit, -(width - 1)) == (highbit >> (width - 1)));
+
+  // Negative overlong shifts: sign-extended for negative values, 0 otherwise
+  assert(std::shl(T(-1), -width) == T(-1));
+  assert(std::shl(T(-1), -(width + 1)) == T(-1));
+  assert(std::shl(T(-1), std::numeric_limits<int>::min()) == T(-1));
+  assert(std::shl(T(42), -width) == T(0));
+  assert(std::shl(T(42), -(width + 1)) == T(0));
+
+  if constexpr (width == 128) {
+    assert(std::shl(T(1), 64) == U(1) << 64);
+    assert(std::shl(T(1), 64u) == U(1) << 64);
+    assert(std::shl(T(1), 127) == U(1) << 127);
+    assert(std::shl(T(1), 127u) == U(1) << 127);
+    assert(std::shl(T(1), 128) == T(0));
+    assert(std::shl(T(1), 128u) == T(0));
+    assert(std::shl(T(1), 200) == T(0));
+    assert(std::shl(T(1), 200u) == T(0));
+
+    assert(std::shl(highbit, -64) == (highbit >> 64));
+    assert(std::shl(highbit, -127) == (highbit >> 127));
+    assert(std::shl(highbit, -128) == T(-1));
+    assert(std::shl(highbit, -200) == T(-1));
+  } else if constexpr (width == 256) {
+    assert(std::shl(T(1), 64) == U(1) << 64);
+    assert(std::shl(T(1), 64u) == U(1) << 64);
+    assert(std::shl(T(1), 127) == U(1) << 127);
+    assert(std::shl(T(1), 127u) == U(1) << 127);
+    assert(std::shl(T(1), 128) == U(1) << 128);
+    assert(std::shl(T(1), 128u) == U(1) << 128);
+    assert(std::shl(T(1), 200) == U(1) << 200);
+    assert(std::shl(T(1), 200u) == U(1) << 200);
+    assert(std::shl(T(1), 255) == U(1) << 255);
+    assert(std::shl(T(1), 255u) == U(1) << 255);
+    assert(std::shl(T(1), 256) == T(0));
+    assert(std::shl(T(1), 256u) == T(0));
+    assert(std::shl(T(1), 300) == T(0));
+    assert(std::shl(T(1), 300u) == T(0));
+
+    assert(std::shl(highbit, -64) == (highbit >> 64));
+    assert(std::shl(highbit, -127) == (highbit >> 127));
+    assert(std::shl(highbit, -128) == (highbit >> 128));
+    assert(std::shl(highbit, -200) == (highbit >> 200));
+    assert(std::shl(highbit, -255) == T(-1));
+    assert(std::shl(highbit, -256) == T(-1));
+  }
+
+  return true;
+}
+
+template <std::unsigned_integral T>
+constexpr bool test() {
+  constexpr int width = std::numeric_limits<T>::digits;
+  constexpr T highbit = T(1) << (width - 1);
+
+  ASSERT_SAME_TYPE(decltype(std::shl(T(), 0)), T);
+  ASSERT_NOEXCEPT(std::shl(T(), 0));
+
+  assert(std::shl(T(1), 0) == T(1));
+  assert(std::shl(T(1), 0u) == T(1));
+  assert(std::shl(T(1), 1) == T(2));
+  assert(std::shl(T(1), 1u) == T(2));
+  assert(std::shl(T(1), 2) == T(4));
+  assert(std::shl(T(1), 2u) == T(4));
+  assert(std::shl(T(1), 3) == T(8));
+  assert(std::shl(T(1), 3u) == T(8));
+  assert(std::shl(T(1), 4) == T(16));
+  assert(std::shl(T(1), 4u) == T(16));
+  assert(std::shl(T(1), 5) == T(32));
+  assert(std::shl(T(1), 5u) == T(32));
+  assert(std::shl(T(1), 6) == T(64));
+  assert(std::shl(T(1), 6u) == T(64));
+  assert(std::shl(T(1), 7) == T(128));
+  assert(std::shl(T(1), 7u) == T(128));
+
+  // Overlong shifts return 0
+  assert(std::shl(T(1), width) == T(0));
+  assert(std::shl(T(1), static_cast<T>(width)) == T(0));
+  assert(std::shl(T(1), width + 1) == T(0));
+  assert(std::shl(T(1), static_cast<T>(width + 1)) == T(0));
+  assert(std::shl(T(1), width + 100) == T(0));
+  assert(std::shl(T(1), static_cast<T>(width + 100)) == T(0));
+  assert(std::shl(T(1), std::numeric_limits<int>::max()) == T(0));
+  assert(std::shl(T(1), static_cast<T>(std::numeric_limits<int>::max())) == T(0));
+
+  assert(std::shl(T(~T(0)), width) == T(0));
+  assert(std::shl(T(~T(0)), static_cast<T>(width)) == T(0));
+  assert(std::shl(T(~T(0)), width + 5) == T(0));
+  assert(std::shl(T(~T(0)), static_cast<T>(width + 5)) == T(0));
+
+  // Negative shift amounts shift right (bidirectional)
+  assert(std::shl(highbit, -1) == (highbit >> 1));
+  assert(std::shl(highbit, -2) == (highbit >> 2));
+  assert(std::shl(highbit, -3) == (highbit >> 3));
+  assert(std::shl(highbit, -4) == (highbit >> 4));
+  assert(std::shl(highbit, -5) == (highbit >> 5));
+  assert(std::shl(highbit, -6) == (highbit >> 6));
+  assert(std::shl(highbit, -7) == (highbit >> 7));
+  assert(std::shl(highbit, -(width - 1)) == T(1));
+
+  // Negative overlong shifts: 0 for unsigned
+  assert(std::shl(T(~T(0)), -width) == T(0));
+  assert(std::shl(T(~T(0)), -(width + 1)) == T(0));
+  assert(std::shl(T(1), std::numeric_limits<int>::min()) == T(0));
+
+  if constexpr (width == 128) {
+    assert(std::shl(T(1), 64) == T(1) << 64);
+    assert(std::shl(T(1), 64u) == T(1) << 64);
+    assert(std::shl(T(1), 127) == T(1) << 127);
+    assert(std::shl(T(1), 127u) == T(1) << 127);
+    assert(std::shl(T(1), 128) == T(0));
+    assert(std::shl(T(1), 128u) == T(0));
+    assert(std::shl(T(1), 200) == T(0));
+    assert(std::shl(T(1), 200u) == T(0));
+
+    assert(std::shl(T(1) << 127, -1) == T(1) << 126);
+    assert(std::shl(T(1) << 127, -127) == T(1));
+    assert(std::shl(T(1) << 127, -128) == T(0));
+    assert(std::shl(T(1) << 127, -200) == T(0));
+  } else if constexpr (width == 256) {
+    assert(std::shl(T(1), 64) == T(1) << 64);
+    assert(std::shl(T(1), 64u) == T(1) << 64);
+    assert(std::shl(T(1), 127) == T(1) << 127);
+    assert(std::shl(T(1), 127u) == T(1) << 127);
+    assert(std::shl(T(1), 128) == T(1) << 128);
+    assert(std::shl(T(1), 128u) == T(1) << 128);
+    assert(std::shl(T(1), 200) == T(1) << 200);
+    assert(std::shl(T(1), 200u) == T(1) << 200);
+
+    assert(std::shl(T(1) << 127, -1) == T(1) << 126);
+    assert(std::shl(T(1) << 127, -127) == T(1));
+    assert(std::shl(T(1) << 127, -128) == T(0));
+    assert(std::shl(T(1) << 127, -200) == T(0));
+    assert(std::shl(T(1), 256) == T(0));
+    assert(std::shl(T(1), 256u) == T(0));
+    assert(std::shl(T(1), 300) == T(0));
+    assert(std::shl(T(1), 300u) == T(0));
   }
 
   return true;

>From f1524fbec78064995c0efdcbfdee4e24aff51991 Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Mon, 14 Sep 2026 13:12:43 +0200
Subject: [PATCH 12/13] Rename tests to test_signed and test_unsigned, fix
 shr.pass.cpp

---
 .../numerics/bit/bitops.shift/shl.pass.cpp    |  18 +-
 .../numerics/bit/bitops.shift/shr.pass.cpp    | 362 +++++++++---------
 2 files changed, 197 insertions(+), 183 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
index 3a4d9cdca01e1..2cd26243f0d1d 100644
--- a/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shl.pass.cpp
@@ -17,7 +17,6 @@
 #include <cassert>
 #include <limits>
 #include <type_traits>
-#include <concepts>
 
 #include "test_macros.h"
 #include "type_algorithms.h"
@@ -56,8 +55,8 @@ static_assert(!can_shl_shift<A*>);
 static_assert(!can_shl_shift<E1>);
 static_assert(!can_shl_shift<E2>);
 
-template <std::signed_integral T>
-constexpr bool test() {
+template <class T>
+constexpr bool test_signed() {
   using U               = std::make_unsigned_t<T>;
   constexpr int width   = std::numeric_limits<U>::digits;
   constexpr U highbit_u = U(1) << (width - 1);
@@ -156,8 +155,8 @@ constexpr bool test() {
   return true;
 }
 
-template <std::unsigned_integral T>
-constexpr bool test() {
+template <class T>
+constexpr bool test_unsigned() {
   constexpr int width = std::numeric_limits<T>::digits;
   constexpr T highbit = T(1) << (width - 1);
 
@@ -250,8 +249,13 @@ constexpr bool test() {
 
 int main(int, char**) {
   constexpr auto test_type = []<class T> {
-    static_assert(test<T>());
-    test<T>();
+    if constexpr (std::is_signed_v<T>) {
+      static_assert(test_signed<T>());
+      test_signed<T>();
+    } else {
+      static_assert(test_unsigned<T>());
+      test_unsigned<T>();
+    }
   };
 
   types::for_each(types::unsigned_integer_types{}, test_type);
diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
index 9ddf679b33b87..e950ea8980d80 100644
--- a/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
@@ -44,6 +44,7 @@ template <class T>
 concept can_shr_shift = requires(T shift) { std::shr(1, shift); };
 
 static_assert(can_shr_shift<int>, "Concept is broken. We should be able to shift by int.");
+static_assert(!can_shr_shift<bool>);
 static_assert(!can_shr_shift<char>);
 static_assert(!can_shr_shift<wchar_t>);
 static_assert(!can_shr_shift<char8_t>);
@@ -55,182 +56,186 @@ static_assert(!can_shr_shift<E1>);
 static_assert(!can_shr_shift<E2>);
 
 template <class T>
-constexpr bool test() {
+constexpr bool test_signed() {
   using U               = std::make_unsigned_t<T>;
   constexpr int width   = std::numeric_limits<U>::digits;
-  constexpr U highbit_u = U(1) << (width - 1);
-
-  if constexpr (std::is_signed_v<T>) {
-    ASSERT_SAME_TYPE(decltype(std::shr(T(), 0)), T);
-    ASSERT_NOEXCEPT(std::shr(T(), 0));
-
-    T highbit = static_cast<T>(highbit_u);
-    assert(std::shr(highbit, 0) == highbit);
-    assert(std::shr(highbit, 0u) == highbit);
-    assert(std::shr(highbit, 1) == (highbit >> 1));
-    assert(std::shr(highbit, 1u) == (highbit >> 1));
-    assert(std::shr(highbit, 2) == (highbit >> 2));
-    assert(std::shr(highbit, 2u) == (highbit >> 2));
-    assert(std::shr(highbit, 3) == (highbit >> 3));
-    assert(std::shr(highbit, 3u) == (highbit >> 3));
-    assert(std::shr(highbit, 4) == (highbit >> 4));
-    assert(std::shr(highbit, 4u) == (highbit >> 4));
-    assert(std::shr(highbit, 5) == (highbit >> 5));
-    assert(std::shr(highbit, 5u) == (highbit >> 5));
-    assert(std::shr(highbit, 6) == (highbit >> 6));
-    assert(std::shr(highbit, 6u) == (highbit >> 6));
-    assert(std::shr(highbit, 7) == (highbit >> 7));
-    assert(std::shr(highbit, 7u) == (highbit >> 7));
-    assert(std::shr(highbit, width - 1) == (highbit >> (width - 1)));
-    assert(std::shr(highbit, static_cast<unsigned>(width - 1)) == (highbit >> (width - 1)));
-
-    // Overlong right shifts sign-extend negative values and return 0 otherwise
-    assert(std::shr(T(-1), width) == T(-1));
-    assert(std::shr(T(-1), static_cast<unsigned>(width)) == T(-1));
-    assert(std::shr(T(-1), width + 1) == T(-1));
-    assert(std::shr(T(-1), static_cast<unsigned>(width + 1)) == T(-1));
-    assert(std::shr(T(-1), std::numeric_limits<int>::max()) == T(-1));
-    assert(std::shr(T(-1), static_cast<unsigned>(std::numeric_limits<int>::max())) == T(-1));
-    assert(std::shr(T(42), width) == T(0));
-    assert(std::shr(T(42), static_cast<unsigned>(width)) == T(0));
-    assert(std::shr(T(42), width + 1) == T(0));
-    assert(std::shr(T(42), static_cast<unsigned>(width + 1)) == T(0));
-
-    // Negative shift amounts shift left (bidirectional)
-    assert(std::shr(T(1), -1) == static_cast<T>(U(1) << 1));
-    assert(std::shr(T(1), -2) == static_cast<T>(U(1) << 2));
-    assert(std::shr(T(1), -3) == static_cast<T>(U(1) << 3));
-    assert(std::shr(T(1), -4) == static_cast<T>(U(1) << 4));
-    assert(std::shr(T(1), -5) == static_cast<T>(U(1) << 5));
-    assert(std::shr(T(1), -6) == static_cast<T>(U(1) << 6));
-    assert(std::shr(T(1), -7) == static_cast<T>(U(1) << 7));
-    assert(std::shr(T(1), -(width - 1)) == static_cast<T>(U(1) << (width - 1)));
-
-    // Negative overlong shifts always return 0
-    assert(std::shr(T(-1), -width) == T(0));
-    assert(std::shr(T(-1), -(width + 1)) == T(0));
-    assert(std::shr(T(-1), std::numeric_limits<int>::min()) == T(0));
-
-    if constexpr (width == 128) {
-      assert(std::shr(highbit, 64) == (highbit >> 64));
-      assert(std::shr(highbit, 64u) == (highbit >> 64));
-      assert(std::shr(highbit, 127) == (highbit >> 127));
-      assert(std::shr(highbit, 127u) == (highbit >> 127));
-      assert(std::shr(highbit, 128) == T(-1));
-      assert(std::shr(highbit, 128u) == T(-1));
-      assert(std::shr(highbit, 200) == T(-1));
-      assert(std::shr(highbit, 200u) == T(-1));
-
-      assert(std::shr(T(1), -64) == static_cast<T>(U(1) << 64));
-      assert(std::shr(T(1), -127) == static_cast<T>(U(1) << 127));
-      assert(std::shr(T(1), -128) == T(0));
-      assert(std::shr(T(1), -200) == T(0));
-    } else if constexpr (width == 256) {
-      assert(std::shr(highbit, 64) == (highbit >> 64));
-      assert(std::shr(highbit, 64u) == (highbit >> 64));
-      assert(std::shr(highbit, 127) == (highbit >> 127));
-      assert(std::shr(highbit, 127u) == (highbit >> 127));
-      assert(std::shr(highbit, 128) == (highbit >> 128));
-      assert(std::shr(highbit, 128u) == (highbit >> 128));
-      assert(std::shr(highbit, 200) == (highbit >> 200));
-      assert(std::shr(highbit, 200u) == (highbit >> 200));
-      assert(std::shr(highbit, 255) == T(-1));
-      assert(std::shr(highbit, 255u) == T(-1));
-      assert(std::shr(highbit, 256) == T(-1));
-      assert(std::shr(highbit, 256u) == T(-1));
-
-      assert(std::shr(T(1), -64) == static_cast<T>(U(1) << 64));
-      assert(std::shr(T(1), -127) == static_cast<T>(U(1) << 127));
-      assert(std::shr(T(1), -128) == static_cast<T>(U(1) << 128));
-      assert(std::shr(T(1), -200) == static_cast<T>(U(1) << 200));
-      assert(std::shr(T(1), -256) == T(0));
-      assert(std::shr(T(1), -300) == T(0));
-    }
-  } else {
-    ASSERT_SAME_TYPE(decltype(std::shr(T(), 0)), T);
-    ASSERT_NOEXCEPT(std::shr(T(), 0));
-
-    T highbit = highbit_u;
-    assert(std::shr(highbit, 0) == highbit);
-    assert(std::shr(highbit, 0u) == highbit);
-    assert(std::shr(highbit, 1) == (highbit_u >> 1));
-    assert(std::shr(highbit, 1u) == (highbit_u >> 1));
-    assert(std::shr(highbit, 2) == (highbit_u >> 2));
-    assert(std::shr(highbit, 2u) == (highbit_u >> 2));
-    assert(std::shr(highbit, 3) == (highbit_u >> 3));
-    assert(std::shr(highbit, 3u) == (highbit_u >> 3));
-    assert(std::shr(highbit, 4) == (highbit_u >> 4));
-    assert(std::shr(highbit, 4u) == (highbit_u >> 4));
-    assert(std::shr(highbit, 5) == (highbit_u >> 5));
-    assert(std::shr(highbit, 5u) == (highbit_u >> 5));
-    assert(std::shr(highbit, 6) == (highbit_u >> 6));
-    assert(std::shr(highbit, 6u) == (highbit_u >> 6));
-    assert(std::shr(highbit, 7) == (highbit_u >> 7));
-    assert(std::shr(highbit, 7u) == (highbit_u >> 7));
-    assert(std::shr(highbit, width - 1) == T(1));
-    assert(std::shr(highbit, static_cast<unsigned>(width - 1)) == T(1));
-
-    // Overlong right shifts return 0
-    assert(std::shr(T(~T(0)), width) == T(0));
-    assert(std::shr(T(~T(0)), static_cast<unsigned>(width)) == T(0));
-    assert(std::shr(T(~T(0)), width + 1) == T(0));
-    assert(std::shr(T(~T(0)), static_cast<unsigned>(width + 1)) == T(0));
-    assert(std::shr(T(~T(0)), std::numeric_limits<int>::max()) == T(0));
-    assert(std::shr(T(~T(0)), static_cast<unsigned>(std::numeric_limits<int>::max())) == T(0));
-
-    // Negative shift amounts shift left (bidirectional)
-    assert(std::shr(T(1), -1) == T(2));
-    assert(std::shr(T(1), -2) == T(4));
-    assert(std::shr(T(1), -3) == T(8));
-    assert(std::shr(T(1), -4) == T(16));
-    assert(std::shr(T(1), -5) == T(32));
-    assert(std::shr(T(1), -6) == T(64));
-    assert(std::shr(T(1), -7) == T(128));
-    assert(std::shr(T(1), -(width - 1)) == (T(1) << (width - 1)));
-
-    // Negative overlong shifts always return 0
-    assert(std::shr(T(~T(0)), -width) == T(0));
-    assert(std::shr(T(~T(0)), -(width + 1)) == T(0));
-    assert(std::shr(T(1), std::numeric_limits<int>::min()) == T(0));
-
-    if constexpr (width == 128) {
-      assert(std::shr(highbit, 64) == (highbit_u >> 64));
-      assert(std::shr(highbit, 64u) == (highbit_u >> 64));
-      assert(std::shr(highbit, 127) == T(1));
-      assert(std::shr(highbit, 127u) == T(1));
-      assert(std::shr(highbit, 128) == T(0));
-      assert(std::shr(highbit, 128u) == T(0));
-      assert(std::shr(highbit, 200) == T(0));
-      assert(std::shr(highbit, 200u) == T(0));
-
-      assert(std::shr(T(1), -64) == T(1) << 64);
-      assert(std::shr(T(1), -127) == T(1) << 127);
-      assert(std::shr(T(1), -128) == T(0));
-      assert(std::shr(T(1), -200) == T(0));
-    } else if constexpr (width == 256) {
-      assert(std::shr(highbit, 64) == (highbit_u >> 64));
-      assert(std::shr(highbit, 64u) == (highbit_u >> 64));
-      assert(std::shr(highbit, 127) == (highbit_u >> 127));
-      assert(std::shr(highbit, 127u) == (highbit_u >> 127));
-      assert(std::shr(highbit, 128) == (highbit_u >> 128));
-      assert(std::shr(highbit, 128u) == (highbit_u >> 128));
-      assert(std::shr(highbit, 200) == (highbit_u >> 200));
-      assert(std::shr(highbit, 200u) == (highbit_u >> 200));
-      assert(std::shr(highbit, 255) == T(1));
-      assert(std::shr(highbit, 255u) == T(1));
-      assert(std::shr(highbit, 256) == T(0));
-      assert(std::shr(highbit, 256u) == T(0));
-      assert(std::shr(highbit, 300) == T(0));
-      assert(std::shr(highbit, 300u) == T(0));
-
-      assert(std::shr(T(1), -64) == T(1) << 64);
-      assert(std::shr(T(1), -127) == T(1) << 127);
-      assert(std::shr(T(1), -128) == T(1) << 128);
-      assert(std::shr(T(1), -200) == T(1) << 200);
-      assert(std::shr(T(1), -255) == T(1) << 255);
-      assert(std::shr(T(1), -256) == T(0));
-      assert(std::shr(T(1), -300) == T(0));
-    }
+  constexpr T highbit   = T(1) << (width - 1);
+
+  ASSERT_SAME_TYPE(decltype(std::shr(T(), 0)), T);
+  ASSERT_NOEXCEPT(std::shr(T(), 0));
+
+  assert(std::shr(highbit, 0) == highbit);
+  assert(std::shr(highbit, 0u) == highbit);
+  assert(std::shr(highbit, 1) == (highbit >> 1));
+  assert(std::shr(highbit, 1u) == (highbit >> 1));
+  assert(std::shr(highbit, 2) == (highbit >> 2));
+  assert(std::shr(highbit, 2u) == (highbit >> 2));
+  assert(std::shr(highbit, 3) == (highbit >> 3));
+  assert(std::shr(highbit, 3u) == (highbit >> 3));
+  assert(std::shr(highbit, 4) == (highbit >> 4));
+  assert(std::shr(highbit, 4u) == (highbit >> 4));
+  assert(std::shr(highbit, 5) == (highbit >> 5));
+  assert(std::shr(highbit, 5u) == (highbit >> 5));
+  assert(std::shr(highbit, 6) == (highbit >> 6));
+  assert(std::shr(highbit, 6u) == (highbit >> 6));
+  assert(std::shr(highbit, 7) == (highbit >> 7));
+  assert(std::shr(highbit, 7u) == (highbit >> 7));
+  assert(std::shr(highbit, width - 1) == (highbit >> (width - 1)));
+  assert(std::shr(highbit, static_cast<unsigned>(width - 1)) == (highbit >> (width - 1)));
+
+  // Overlong right shifts sign-extend negative values and return 0 otherwise
+  assert(std::shr(T(-1), width) == T(-1));
+  assert(std::shr(T(-1), static_cast<unsigned>(width)) == T(-1));
+  assert(std::shr(T(-1), width + 1) == T(-1));
+  assert(std::shr(T(-1), static_cast<unsigned>(width + 1)) == T(-1));
+  assert(std::shr(T(-1), std::numeric_limits<int>::max()) == T(-1));
+  assert(std::shr(T(-1), static_cast<unsigned>(std::numeric_limits<int>::max())) == T(-1));
+  assert(std::shr(T(42), width) == T(0));
+  assert(std::shr(T(42), static_cast<unsigned>(width)) == T(0));
+  assert(std::shr(T(42), width + 1) == T(0));
+  assert(std::shr(T(42), static_cast<unsigned>(width + 1)) == T(0));
+
+  // Negative shift amounts shift left (bidirectional)
+  assert(std::shr(T(1), -1) == static_cast<T>(U(1) << 1));
+  assert(std::shr(T(1), -2) == static_cast<T>(U(1) << 2));
+  assert(std::shr(T(1), -3) == static_cast<T>(U(1) << 3));
+  assert(std::shr(T(1), -4) == static_cast<T>(U(1) << 4));
+  assert(std::shr(T(1), -5) == static_cast<T>(U(1) << 5));
+  assert(std::shr(T(1), -6) == static_cast<T>(U(1) << 6));
+  assert(std::shr(T(1), -7) == static_cast<T>(U(1) << 7));
+  assert(std::shr(T(1), -(width - 1)) == static_cast<T>(U(1) << (width - 1)));
+
+  // Negative overlong shifts always return 0
+  assert(std::shr(T(-1), -width) == T(0));
+  assert(std::shr(T(-1), -(width + 1)) == T(0));
+  assert(std::shr(T(-1), std::numeric_limits<int>::min()) == T(0));
+
+  if constexpr (width == 128) {
+    assert(std::shr(highbit, 64) == (highbit >> 64));
+    assert(std::shr(highbit, 64u) == (highbit >> 64));
+    assert(std::shr(highbit, 127) == (highbit >> 127));
+    assert(std::shr(highbit, 127u) == (highbit >> 127));
+    assert(std::shr(highbit, 128) == T(-1));
+    assert(std::shr(highbit, 128u) == T(-1));
+    assert(std::shr(highbit, 200) == T(-1));
+    assert(std::shr(highbit, 200u) == T(-1));
+
+    assert(std::shr(T(1), -64) == static_cast<T>(U(1) << 64));
+    assert(std::shr(T(1), -127) == static_cast<T>(U(1) << 127));
+    assert(std::shr(T(1), -128) == T(0));
+    assert(std::shr(T(1), -200) == T(0));
+  } else if constexpr (width == 256) {
+    assert(std::shr(highbit, 64) == (highbit >> 64));
+    assert(std::shr(highbit, 64u) == (highbit >> 64));
+    assert(std::shr(highbit, 127) == (highbit >> 127));
+    assert(std::shr(highbit, 127u) == (highbit >> 127));
+    assert(std::shr(highbit, 128) == (highbit >> 128));
+    assert(std::shr(highbit, 128u) == (highbit >> 128));
+    assert(std::shr(highbit, 200) == (highbit >> 200));
+    assert(std::shr(highbit, 200u) == (highbit >> 200));
+    assert(std::shr(highbit, 255) == T(-1));
+    assert(std::shr(highbit, 255u) == T(-1));
+    assert(std::shr(highbit, 256) == T(-1));
+    assert(std::shr(highbit, 256u) == T(-1));
+
+    assert(std::shr(T(1), -64) == static_cast<T>(U(1) << 64));
+    assert(std::shr(T(1), -127) == static_cast<T>(U(1) << 127));
+    assert(std::shr(T(1), -128) == static_cast<T>(U(1) << 128));
+    assert(std::shr(T(1), -200) == static_cast<T>(U(1) << 200));
+    assert(std::shr(T(1), -256) == T(0));
+    assert(std::shr(T(1), -300) == T(0));
+  }
+
+  return true;
+}
+
+template <class T>
+constexpr bool test_unsigned() {
+  constexpr int width = std::numeric_limits<T>::digits;
+  constexpr T highbit = T(1) << (width - 1);
+
+  ASSERT_SAME_TYPE(decltype(std::shr(T(), 0)), T);
+  ASSERT_NOEXCEPT(std::shr(T(), 0));
+
+  assert(std::shr(highbit, 0) == highbit);
+  assert(std::shr(highbit, 0u) == highbit);
+  assert(std::shr(highbit, 1) == (highbit >> 1));
+  assert(std::shr(highbit, 1u) == (highbit >> 1));
+  assert(std::shr(highbit, 2) == (highbit >> 2));
+  assert(std::shr(highbit, 2u) == (highbit >> 2));
+  assert(std::shr(highbit, 3) == (highbit >> 3));
+  assert(std::shr(highbit, 3u) == (highbit >> 3));
+  assert(std::shr(highbit, 4) == (highbit >> 4));
+  assert(std::shr(highbit, 4u) == (highbit >> 4));
+  assert(std::shr(highbit, 5) == (highbit >> 5));
+  assert(std::shr(highbit, 5u) == (highbit >> 5));
+  assert(std::shr(highbit, 6) == (highbit >> 6));
+  assert(std::shr(highbit, 6u) == (highbit >> 6));
+  assert(std::shr(highbit, 7) == (highbit >> 7));
+  assert(std::shr(highbit, 7u) == (highbit >> 7));
+  assert(std::shr(highbit, width - 1) == T(1));
+  assert(std::shr(highbit, static_cast<unsigned>(width - 1)) == T(1));
+
+  // Overlong right shifts return 0
+  assert(std::shr(T(~T(0)), width) == T(0));
+  assert(std::shr(T(~T(0)), static_cast<unsigned>(width)) == T(0));
+  assert(std::shr(T(~T(0)), width + 1) == T(0));
+  assert(std::shr(T(~T(0)), static_cast<unsigned>(width + 1)) == T(0));
+  assert(std::shr(T(~T(0)), std::numeric_limits<int>::max()) == T(0));
+  assert(std::shr(T(~T(0)), static_cast<unsigned>(std::numeric_limits<int>::max())) == T(0));
+
+  // Negative shift amounts shift left (bidirectional)
+  assert(std::shr(T(1), -1) == T(2));
+  assert(std::shr(T(1), -2) == T(4));
+  assert(std::shr(T(1), -3) == T(8));
+  assert(std::shr(T(1), -4) == T(16));
+  assert(std::shr(T(1), -5) == T(32));
+  assert(std::shr(T(1), -6) == T(64));
+  assert(std::shr(T(1), -7) == T(128));
+  assert(std::shr(T(1), -(width - 1)) == (T(1) << (width - 1)));
+
+  // Negative overlong shifts always return 0
+  assert(std::shr(T(~T(0)), -width) == T(0));
+  assert(std::shr(T(~T(0)), -(width + 1)) == T(0));
+  assert(std::shr(T(1), std::numeric_limits<int>::min()) == T(0));
+
+  if constexpr (width == 128) {
+    assert(std::shr(highbit, 64) == (highbit >> 64));
+    assert(std::shr(highbit, 64u) == (highbit >> 64));
+    assert(std::shr(highbit, 127) == T(1));
+    assert(std::shr(highbit, 127u) == T(1));
+    assert(std::shr(highbit, 128) == T(0));
+    assert(std::shr(highbit, 128u) == T(0));
+    assert(std::shr(highbit, 200) == T(0));
+    assert(std::shr(highbit, 200u) == T(0));
+
+    assert(std::shr(T(1), -64) == T(1) << 64);
+    assert(std::shr(T(1), -127) == T(1) << 127);
+    assert(std::shr(T(1), -128) == T(0));
+    assert(std::shr(T(1), -200) == T(0));
+  } else if constexpr (width == 256) {
+    assert(std::shr(highbit, 64) == (highbit >> 64));
+    assert(std::shr(highbit, 64u) == (highbit >> 64));
+    assert(std::shr(highbit, 127) == (highbit >> 127));
+    assert(std::shr(highbit, 127u) == (highbit >> 127));
+    assert(std::shr(highbit, 128) == (highbit >> 128));
+    assert(std::shr(highbit, 128u) == (highbit >> 128));
+    assert(std::shr(highbit, 200) == (highbit >> 200));
+    assert(std::shr(highbit, 200u) == (highbit >> 200));
+    assert(std::shr(highbit, 255) == T(1));
+    assert(std::shr(highbit, 255u) == T(1));
+    assert(std::shr(highbit, 256) == T(0));
+    assert(std::shr(highbit, 256u) == T(0));
+    assert(std::shr(highbit, 300) == T(0));
+    assert(std::shr(highbit, 300u) == T(0));
+
+    assert(std::shr(T(1), -64) == T(1) << 64);
+    assert(std::shr(T(1), -127) == T(1) << 127);
+    assert(std::shr(T(1), -128) == T(1) << 128);
+    assert(std::shr(T(1), -200) == T(1) << 200);
+    assert(std::shr(T(1), -255) == T(1) << 255);
+    assert(std::shr(T(1), -256) == T(0));
+    assert(std::shr(T(1), -300) == T(0));
   }
 
   return true;
@@ -238,8 +243,13 @@ constexpr bool test() {
 
 int main(int, char**) {
   constexpr auto test_type = []<class T> {
-    static_assert(test<T>());
-    test<T>();
+    if constexpr (std::is_signed_v<T>) {
+      static_assert(test_signed<T>());
+      test_signed<T>();
+    } else {
+      static_assert(test_unsigned<T>());
+      test_unsigned<T>();
+    }
   };
 
   types::for_each(types::unsigned_integer_types{}, test_type);

>From 873f23cbc6d0022938e8a4b5a62422286f64234f Mon Sep 17 00:00:00 2001
From: Eisenwave <me at eisenwave.net>
Date: Mon, 14 Sep 2026 13:17:40 +0200
Subject: [PATCH 13/13] Fix code formatting

---
 libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp b/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
index e950ea8980d80..9f5bc00150522 100644
--- a/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
+++ b/libcxx/test/std/numerics/bit/bitops.shift/shr.pass.cpp
@@ -57,9 +57,9 @@ static_assert(!can_shr_shift<E2>);
 
 template <class T>
 constexpr bool test_signed() {
-  using U               = std::make_unsigned_t<T>;
-  constexpr int width   = std::numeric_limits<U>::digits;
-  constexpr T highbit   = T(1) << (width - 1);
+  using U             = std::make_unsigned_t<T>;
+  constexpr int width = std::numeric_limits<U>::digits;
+  constexpr T highbit = T(1) << (width - 1);
 
   ASSERT_SAME_TYPE(decltype(std::shr(T(), 0)), T);
   ASSERT_NOEXCEPT(std::shr(T(), 0));



More information about the libcxx-commits mailing list