[libcxx-commits] [libcxx] d807661 - [libc++] Fix bitset conversion functions and refactor constructor (#121348)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jun 24 09:38:13 PDT 2025


Author: Peng Liu
Date: 2025-06-24T12:38:10-04:00
New Revision: d80766152c496ca7a0991486e35aa8ed52d0e422

URL: https://github.com/llvm/llvm-project/commit/d80766152c496ca7a0991486e35aa8ed52d0e422
DIFF: https://github.com/llvm/llvm-project/commit/d80766152c496ca7a0991486e35aa8ed52d0e422.diff

LOG: [libc++] Fix bitset conversion functions and refactor constructor (#121348)

This patch addresses several implementation issues in `bitset`'s
conversion functions `to_ullong` and `to_ulong`, and refactors its
converting constructor `__bitset(unsigned long long __v)` to a more
generic and elegant implementation.

Added: 
    

Modified: 
    libcxx/include/bitset
    libcxx/test/std/utilities/template.bitset/bitset.members/to_ullong.pass.cpp
    libcxx/test/std/utilities/template.bitset/bitset.members/to_ulong.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/bitset b/libcxx/include/bitset
index 6be476e2b69d8..d109f27af58d6 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -136,6 +136,7 @@ template <size_t N> struct hash<std::bitset<N>>;
 #  include <__algorithm/fill.h>
 #  include <__algorithm/fill_n.h>
 #  include <__algorithm/find.h>
+#  include <__algorithm/min.h>
 #  include <__assert>
 #  include <__bit/countr.h>
 #  include <__bit/invert_if.h>
@@ -146,7 +147,11 @@ template <size_t N> struct hash<std::bitset<N>>;
 #  include <__functional/hash.h>
 #  include <__functional/identity.h>
 #  include <__functional/unary_function.h>
+#  include <__tuple/tuple_indices.h>
+#  include <__type_traits/enable_if.h>
+#  include <__type_traits/integral_constant.h>
 #  include <__type_traits/is_char_like_type.h>
+#  include <__utility/integer_sequence.h>
 #  include <climits>
 #  include <stdexcept>
 #  include <string_view>
@@ -220,11 +225,42 @@ protected:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void operator^=(const __bitset& __v) _NOEXCEPT;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void flip() _NOEXCEPT;
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
-    return to_ulong(integral_constant < bool, _Size< sizeof(unsigned long) * CHAR_BIT>());
+    if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long) * CHAR_BIT) {
+      if (auto __e = __make_iter(_Size); std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true) != __e)
+        std::__throw_overflow_error("__bitset<_N_words, _Size>::to_ulong overflow error");
+    }
+
+    static_assert(sizeof(__storage_type) >= sizeof(unsigned long),
+                  "libc++ only supports platforms where sizeof(size_t) >= sizeof(unsigned long), such as 32-bit and "
+                  "64-bit platforms. If you're interested in supporting a platform where that is not the case, please "
+                  "contact the libc++ developers.");
+    return static_cast<unsigned long>(__first_[0]);
   }
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const {
-    return to_ullong(integral_constant < bool, _Size< sizeof(unsigned long long) * CHAR_BIT>());
+    // Check for overflow if _Size does not fit in unsigned long long
+    if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long long) * CHAR_BIT) {
+      if (auto __e = __make_iter(_Size);
+          std::find(__make_iter(sizeof(unsigned long long) * CHAR_BIT), __e, true) != __e)
+        std::__throw_overflow_error("__bitset<_N_words, _Size>::to_ullong overflow error");
+    }
+
+    // At this point, the effective bitset size (excluding leading zeros) fits in unsigned long long
+
+    if _LIBCPP_CONSTEXPR (sizeof(__storage_type) >= sizeof(unsigned long long)) {
+      // If __storage_type is at least as large as unsigned long long, the result spans only one word
+      return static_cast<unsigned long long>(__first_[0]);
+    } else {
+      // Otherwise, the result spans multiple words which are concatenated
+      const size_t __ull_words = (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1;
+      const size_t __n_words   = _N_words < __ull_words ? _N_words : __ull_words;
+      unsigned long long __r   = static_cast<unsigned long long>(__first_[0]);
+      for (size_t __i = 1; __i < __n_words; ++__i)
+        __r |= static_cast<unsigned long long>(__first_[__i]) << (__bits_per_word * __i);
+      return __r;
+    }
   }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool all() const _NOEXCEPT { return !__scan_bits(__bit_not()); }
@@ -255,16 +291,6 @@ private:
       return ~__x;
     }
   };
-#  ifdef _LIBCPP_CXX03_LANG
-  void __init(unsigned long long __v, false_type) _NOEXCEPT;
-  _LIBCPP_HIDE_FROM_ABI void __init(unsigned long long __v, true_type) _NOEXCEPT;
-#  endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong(false_type) const;
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong(true_type) const;
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(false_type) const;
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type) const;
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type, false_type) const;
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type, true_type) const;
 
   template <typename _Proj>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __scan_bits(_Proj __proj) const _NOEXCEPT {
@@ -282,6 +308,15 @@ private:
     }
     return false;
   }
+
+#  ifdef _LIBCPP_CXX03_LANG
+  void __init(unsigned long long __v, false_type) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI void __init(unsigned long long __v, true_type) _NOEXCEPT;
+#  else
+  template <size_t... _Indices>
+  _LIBCPP_HIDE_FROM_ABI constexpr __bitset(unsigned long long __v, std::__tuple_indices<_Indices...>) _NOEXCEPT
+      : __first_{static_cast<__storage_type>(__v >> (_Indices * __bits_per_word))...} {}
+#  endif // _LIBCPP_CXX03_LANG
 };
 
 template <size_t _N_words, size_t _Size>
@@ -316,21 +351,15 @@ inline _LIBCPP_HIDE_FROM_ABI void __bitset<_N_words, _Size>::__init(unsigned lon
 template <size_t _N_words, size_t _Size>
 inline _LIBCPP_CONSTEXPR __bitset<_N_words, _Size>::__bitset(unsigned long long __v) _NOEXCEPT
 #  ifndef _LIBCPP_CXX03_LANG
-#    if __SIZEOF_SIZE_T__ == 8
-    : __first_{__v}
-#    elif __SIZEOF_SIZE_T__ == 4
-    : __first_{static_cast<__storage_type>(__v),
-               _Size >= 2 * __bits_per_word
-                   ? static_cast<__storage_type>(__v >> __bits_per_word)
-                   : static_cast<__storage_type>((__v >> __bits_per_word) &
-                                                 (__storage_type(1) << (_Size - __bits_per_word)) - 1)}
-#    else
-#      error This constructor has not been ported to this platform
-#    endif
+    : __bitset(__v,
+               std::__make_indices_imp< (_N_words < (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1)
+                                            ? _N_words
+                                            : (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1,
+                                        0>{})
 #  endif
 {
 #  ifdef _LIBCPP_CXX03_LANG
-  __init(__v, integral_constant<bool, sizeof(unsigned long long) <= sizeof(__storage_type)>());
+  __init(__v, _BoolConstant<sizeof(unsigned long long) <= sizeof(__storage_type)>());
 #  endif
 }
 
@@ -369,58 +398,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __bitset<_N_words, _Siz
     *__p ^= (__storage_type(1) << __n) - 1;
 }
 
-template <size_t _N_words, size_t _Size>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long
-__bitset<_N_words, _Size>::to_ulong(false_type) const {
-  __const_iterator __e = __make_iter(_Size);
-  __const_iterator __i = std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true);
-  if (__i != __e)
-    std::__throw_overflow_error("bitset to_ulong overflow error");
-
-  return __first_[0];
-}
-
-template <size_t _N_words, size_t _Size>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long
-__bitset<_N_words, _Size>::to_ulong(true_type) const {
-  return __first_[0];
-}
-
-template <size_t _N_words, size_t _Size>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
-__bitset<_N_words, _Size>::to_ullong(false_type) const {
-  __const_iterator __e = __make_iter(_Size);
-  __const_iterator __i = std::find(__make_iter(sizeof(unsigned long long) * CHAR_BIT), __e, true);
-  if (__i != __e)
-    std::__throw_overflow_error("bitset to_ullong overflow error");
-
-  return to_ullong(true_type());
-}
-
-template <size_t _N_words, size_t _Size>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
-__bitset<_N_words, _Size>::to_ullong(true_type) const {
-  return to_ullong(true_type(), integral_constant<bool, sizeof(__storage_type) < sizeof(unsigned long long)>());
-}
-
-template <size_t _N_words, size_t _Size>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
-__bitset<_N_words, _Size>::to_ullong(true_type, false_type) const {
-  return __first_[0];
-}
-
-template <size_t _N_words, size_t _Size>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
-__bitset<_N_words, _Size>::to_ullong(true_type, true_type) const {
-  unsigned long long __r = __first_[0];
-  _LIBCPP_DIAGNOSTIC_PUSH
-  _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshift-count-overflow")
-  for (size_t __i = 1; __i < sizeof(unsigned long long) / sizeof(__storage_type); ++__i)
-    __r |= static_cast<unsigned long long>(__first_[__i]) << (sizeof(__storage_type) * CHAR_BIT);
-  _LIBCPP_DIAGNOSTIC_POP
-  return __r;
-}
-
 template <size_t _N_words, size_t _Size>
 inline size_t __bitset<_N_words, _Size>::__hash_code() const _NOEXCEPT {
   size_t __h = 0;
@@ -479,8 +456,25 @@ protected:
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void flip() _NOEXCEPT;
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const;
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
+    if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long) * CHAR_BIT) {
+      if (auto __e = __make_iter(_Size); std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true) != __e)
+        __throw_overflow_error("__bitset<1, _Size>::to_ulong overflow error");
+    }
+    return static_cast<unsigned long>(__first_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const {
+    // If _Size exceeds the size of unsigned long long, check for overflow
+    if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long long) * CHAR_BIT) {
+      if (auto __e = __make_iter(_Size);
+          std::find(__make_iter(sizeof(unsigned long long) * CHAR_BIT), __e, true) != __e)
+        __throw_overflow_error("__bitset<1, _Size>::to_ullong overflow error");
+    }
+
+    // If _Size fits or no overflow, directly cast to unsigned long long
+    return static_cast<unsigned long long>(__first_);
+  }
 
   template <bool _Sparse, class _CharT, class _Traits, class _Allocator>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
@@ -507,8 +501,10 @@ inline _LIBCPP_CONSTEXPR __bitset<1, _Size>::__bitset() _NOEXCEPT : __first_(0)
 
 template <size_t _Size>
 inline _LIBCPP_CONSTEXPR __bitset<1, _Size>::__bitset(unsigned long long __v) _NOEXCEPT
-    : __first_(_Size == __bits_per_word ? static_cast<__storage_type>(__v)
-                                        : static_cast<__storage_type>(__v) & ((__storage_type(1) << _Size) - 1)) {}
+    // TODO: We must refer to __bits_per_word in order to work around an issue with the GDB pretty-printers.
+    //       Without it, the pretty-printers complain about a missing __bits_per_word member. This needs to
+    //       be investigated further.
+    : __first_(_Size == __bits_per_word ? static_cast<__storage_type>(__v) : static_cast<__storage_type>(__v)) {}
 
 template <size_t _Size>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void
@@ -533,16 +529,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __bitset<1, _Siz
   __first_ ^= ~__storage_type(0) >> (__bits_per_word - _Size);
 }
 
-template <size_t _Size>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long __bitset<1, _Size>::to_ulong() const {
-  return __first_;
-}
-
-template <size_t _Size>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long __bitset<1, _Size>::to_ullong() const {
-  return __first_;
-}
-
 template <size_t _Size>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool __bitset<1, _Size>::all() const _NOEXCEPT {
   __storage_type __m = ~__storage_type(0) >> (__bits_per_word - _Size);
@@ -633,8 +619,6 @@ class bitset : private __bitset<_Size == 0 ? 0 : (_Size - 1) / (sizeof(size_t) *
 public:
   static const unsigned __n_words = _Size == 0 ? 0 : (_Size - 1) / (sizeof(size_t) * CHAR_BIT) + 1;
   typedef __bitset<__n_words, _Size> __base;
-
-public:
   typedef typename __base::reference reference;
   typedef typename __base::__const_reference __const_reference;
 
@@ -713,8 +697,10 @@ public:
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");
     return __base::__make_ref(__p);
   }
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const;
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const { return __base::to_ulong(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const {
+    return __base::to_ullong();
+  }
   template <class _CharT, class _Traits, class _Allocator>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
   to_string(_CharT __zero = _CharT('0'), _CharT __one = _CharT('1')) const;
@@ -850,16 +836,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bitset<_Size>& bitset<_Size>
   return *this;
 }
 
-template <size_t _Size>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long bitset<_Size>::to_ulong() const {
-  return __base::to_ulong();
-}
-
-template <size_t _Size>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long bitset<_Size>::to_ullong() const {
-  return __base::to_ullong();
-}
-
 template <size_t _Size>
 template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>

diff  --git a/libcxx/test/std/utilities/template.bitset/bitset.members/to_ullong.pass.cpp b/libcxx/test/std/utilities/template.bitset/bitset.members/to_ullong.pass.cpp
index cbc8da96be1b1..5df6103d79e7a 100644
--- a/libcxx/test/std/utilities/template.bitset/bitset.members/to_ullong.pass.cpp
+++ b/libcxx/test/std/utilities/template.bitset/bitset.members/to_ullong.pass.cpp
@@ -13,36 +13,38 @@
 #include <type_traits>
 #include <climits>
 #include <cassert>
+#include <stdexcept>
 
 #include "test_macros.h"
 
 template <std::size_t N>
 TEST_CONSTEXPR_CXX23 void test_to_ullong() {
-    const std::size_t M = sizeof(unsigned long long) * CHAR_BIT < N ? sizeof(unsigned long long) * CHAR_BIT : N;
-    const bool is_M_zero = std::integral_constant<bool, M == 0>::value; // avoid compiler warnings
-    const std::size_t X = is_M_zero ? sizeof(unsigned long long) * CHAR_BIT - 1 : sizeof(unsigned long long) * CHAR_BIT - M;
-    const unsigned long long max = is_M_zero ? 0 : (unsigned long long)(-1) >> X;
-    unsigned long long tests[] = {
-        0,
-        std::min<unsigned long long>(1, max),
-        std::min<unsigned long long>(2, max),
-        std::min<unsigned long long>(3, max),
-        std::min(max, max-3),
-        std::min(max, max-2),
-        std::min(max, max-1),
-        max
-    };
-    for (unsigned long long j : tests) {
-         std::bitset<N> v(j);
-        assert(j == v.to_ullong());
-    }
-    { // test values bigger than can fit into the bitset
-        const unsigned long long val = 0x55AAAAFFFFAAAA55ULL;
-        const bool canFit = N < sizeof(unsigned long long) * CHAR_BIT;
-        const unsigned long long mask = canFit ? (1ULL << (canFit ? N : 0)) - 1 : (unsigned long long)(-1); // avoid compiler warnings
-        std::bitset<N> v(val);
-        assert(v.to_ullong() == (val & mask)); // we shouldn't return bit patterns from outside the limits of the bitset.
-    }
+  const std::size_t M  = sizeof(unsigned long long) * CHAR_BIT < N ? sizeof(unsigned long long) * CHAR_BIT : N;
+  const bool is_M_zero = std::integral_constant < bool, M == 0 > ::value; // avoid compiler warnings
+  const std::size_t X =
+      is_M_zero ? sizeof(unsigned long long) * CHAR_BIT - 1 : sizeof(unsigned long long) * CHAR_BIT - M;
+  const unsigned long long max = is_M_zero ? 0 : (unsigned long long)(-1) >> X;
+  unsigned long long tests[]   = {
+      0,
+      std::min<unsigned long long>(1, max),
+      std::min<unsigned long long>(2, max),
+      std::min<unsigned long long>(3, max),
+      std::min(max, max - 3),
+      std::min(max, max - 2),
+      std::min(max, max - 1),
+      max};
+  for (unsigned long long j : tests) {
+    std::bitset<N> v(j);
+    assert(j == v.to_ullong());
+  }
+  { // test values bigger than can fit into the bitset
+    const unsigned long long val = 0x55AAAAFFFFAAAA55ULL;
+    const bool canFit            = N < sizeof(unsigned long long) * CHAR_BIT;
+    const unsigned long long mask =
+        canFit ? (1ULL << (canFit ? N : 0)) - 1 : (unsigned long long)(-1); // avoid compiler warnings
+    std::bitset<N> v(val);
+    assert(v.to_ullong() == (val & mask)); // we shouldn't return bit patterns from outside the limits of the bitset.
+  }
 }
 
 TEST_CONSTEXPR_CXX23 bool test() {
@@ -56,6 +58,22 @@ TEST_CONSTEXPR_CXX23 bool test() {
   test_to_ullong<65>();
   test_to_ullong<1000>();
 
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    // bitset has true bits beyond the size of unsigned long long
+    std::bitset<std::numeric_limits<unsigned long long>::digits + 1> q(0);
+    q.flip();
+    try {
+      q.to_ullong(); // throws
+      assert(false);
+    } catch (const std::overflow_error&) {
+      // expected
+    } catch (...) {
+      assert(false);
+    }
+  }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/template.bitset/bitset.members/to_ulong.pass.cpp b/libcxx/test/std/utilities/template.bitset/bitset.members/to_ulong.pass.cpp
index 17a5e7eb51aa2..d943030a16287 100644
--- a/libcxx/test/std/utilities/template.bitset/bitset.members/to_ulong.pass.cpp
+++ b/libcxx/test/std/utilities/template.bitset/bitset.members/to_ulong.pass.cpp
@@ -14,37 +14,37 @@
 #include <limits>
 #include <climits>
 #include <cassert>
+#include <stdexcept>
 
 #include "test_macros.h"
 
 template <std::size_t N>
 TEST_CONSTEXPR_CXX23 void test_to_ulong() {
-    const std::size_t M = sizeof(unsigned long) * CHAR_BIT < N ? sizeof(unsigned long) * CHAR_BIT : N;
-    const bool is_M_zero = std::integral_constant<bool, M == 0>::value; // avoid compiler warnings
-    const std::size_t X = is_M_zero ? sizeof(unsigned long) * CHAR_BIT - 1 : sizeof(unsigned long) * CHAR_BIT - M;
-    const std::size_t max = is_M_zero ? 0 : std::size_t(std::numeric_limits<unsigned long>::max()) >> X;
-    std::size_t tests[] = {
-        0,
-        std::min<std::size_t>(1, max),
-        std::min<std::size_t>(2, max),
-        std::min<std::size_t>(3, max),
-        std::min(max, max-3),
-        std::min(max, max-2),
-        std::min(max, max-1),
-        max
-    };
-    for (std::size_t j : tests) {
-        std::bitset<N> v(j);
-        assert(j == v.to_ulong());
-    }
+  const std::size_t M   = sizeof(unsigned long) * CHAR_BIT < N ? sizeof(unsigned long) * CHAR_BIT : N;
+  const bool is_M_zero  = std::integral_constant < bool, M == 0 > ::value; // avoid compiler warnings
+  const std::size_t X   = is_M_zero ? sizeof(unsigned long) * CHAR_BIT - 1 : sizeof(unsigned long) * CHAR_BIT - M;
+  const std::size_t max = is_M_zero ? 0 : std::size_t(std::numeric_limits<unsigned long>::max()) >> X;
+  std::size_t tests[]   = {
+      0,
+      std::min<std::size_t>(1, max),
+      std::min<std::size_t>(2, max),
+      std::min<std::size_t>(3, max),
+      std::min(max, max - 3),
+      std::min(max, max - 2),
+      std::min(max, max - 1),
+      max};
+  for (std::size_t j : tests) {
+    std::bitset<N> v(j);
+    assert(j == v.to_ulong());
+  }
 
-    { // test values bigger than can fit into the bitset
-        const unsigned long val = 0x5AFFFFA5UL;
-        const bool canFit = N < sizeof(unsigned long) * CHAR_BIT;
-        const unsigned long mask = canFit ? (1UL << (canFit ? N : 0)) - 1 : (unsigned long)(-1); // avoid compiler warnings
-        std::bitset<N> v(val);
-        assert(v.to_ulong() == (val & mask)); // we shouldn't return bit patterns from outside the limits of the bitset.
-    }
+  { // test values bigger than can fit into the bitset
+    const unsigned long val  = 0x5AFFFFA5UL;
+    const bool canFit        = N < sizeof(unsigned long) * CHAR_BIT;
+    const unsigned long mask = canFit ? (1UL << (canFit ? N : 0)) - 1 : (unsigned long)(-1); // avoid compiler warnings
+    std::bitset<N> v(val);
+    assert(v.to_ulong() == (val & mask)); // we shouldn't return bit patterns from outside the limits of the bitset.
+  }
 }
 
 TEST_CONSTEXPR_CXX23 bool test() {
@@ -58,6 +58,22 @@ TEST_CONSTEXPR_CXX23 bool test() {
   test_to_ulong<65>();
   test_to_ulong<1000>();
 
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    // bitset has true bits beyond the size of unsigned long
+    std::bitset<std::numeric_limits<unsigned long>::digits + 1> q(0);
+    q.flip();
+    try {
+      q.to_ulong(); // throws
+      assert(false);
+    } catch (const std::overflow_error&) {
+      // expected
+    } catch (...) {
+      assert(false);
+    }
+  }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
   return true;
 }
 


        


More information about the libcxx-commits mailing list