[libcxx-commits] [libcxx] [libc++] Implement `<bit>` parts of P3793R2 Better shifting (PR #219389)
Jan Schultke via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Aug 28 08:26:43 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 1/9] [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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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>();
More information about the libcxx-commits
mailing list