Skip to content

Commit fad654f

Browse files
committed
Fix ambiguous call in ranges::count & std::count for vector<bool>::iterator
1 parent 60d8e6f commit fad654f

File tree

6 files changed

+188
-50
lines changed

6 files changed

+188
-50
lines changed

libcxx/include/__algorithm/count.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,18 @@ __count_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_t
5555
if (__first.__ctz_ != 0) {
5656
__storage_type __clz_f = static_cast<__storage_type>(__bits_per_word - __first.__ctz_);
5757
__storage_type __dn = std::min(__clz_f, __n);
58-
__storage_type __m = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz_f - __dn));
59-
__r = std::__libcpp_popcount(std::__invert_if<!_ToCount>(*__first.__seg_) & __m);
58+
__storage_type __m = std::__middle_mask<__storage_type>(__clz_f - __dn, __first.__ctz_);
59+
__r = std::__popcount(__storage_type(std::__invert_if<!_ToCount>(*__first.__seg_) & __m));
6060
__n -= __dn;
6161
++__first.__seg_;
6262
}
6363
// do middle whole words
6464
for (; __n >= __bits_per_word; ++__first.__seg_, __n -= __bits_per_word)
65-
__r += std::__libcpp_popcount(std::__invert_if<!_ToCount>(*__first.__seg_));
65+
__r += std::__popcount(__storage_type(std::__invert_if<!_ToCount>(*__first.__seg_)));
6666
// do last partial word
6767
if (__n > 0) {
68-
__storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
69-
__r += std::__libcpp_popcount(std::__invert_if<!_ToCount>(*__first.__seg_) & __m);
68+
__storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
69+
__r += std::__popcount(__storage_type(std::__invert_if<!_ToCount>(*__first.__seg_) & __m));
7070
}
7171
return __r;
7272
}

libcxx/include/__bit/popcount.h

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <__bit/rotate.h>
1616
#include <__concepts/arithmetic.h>
1717
#include <__config>
18+
#include <__type_traits/enable_if.h>
19+
#include <__type_traits/is_unsigned.h>
1820
#include <limits>
1921

2022
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -38,31 +40,83 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_popcount(unsigned lo
3840
return __builtin_popcountll(__x);
3941
}
4042

41-
#if _LIBCPP_STD_VER >= 20
43+
#ifndef _LIBCPP_CXX03_LANG
44+
// constexpr implementation for C++11 and later
4245

43-
template <__libcpp_unsigned_integer _Tp>
44-
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr int popcount(_Tp __t) noexcept {
45-
# if __has_builtin(__builtin_popcountg)
46-
return __builtin_popcountg(__t);
47-
# else // __has_builtin(__builtin_popcountg)
48-
if (sizeof(_Tp) <= sizeof(unsigned int))
46+
template <class _Tp>
47+
_LIBCPP_HIDE_FROM_ABI constexpr int __popcount(_Tp __t) _NOEXCEPT {
48+
static_assert(is_unsigned<_Tp>::value, "__popcount only works with unsigned types");
49+
if constexpr (sizeof(_Tp) <= sizeof(unsigned int)) {
4950
return std::__libcpp_popcount(static_cast<unsigned int>(__t));
50-
else if (sizeof(_Tp) <= sizeof(unsigned long))
51+
} else if constexpr (sizeof(_Tp) <= sizeof(unsigned long)) {
5152
return std::__libcpp_popcount(static_cast<unsigned long>(__t));
52-
else if (sizeof(_Tp) <= sizeof(unsigned long long))
53+
} else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) {
5354
return std::__libcpp_popcount(static_cast<unsigned long long>(__t));
54-
else {
55+
} else {
56+
# if _LIBCPP_STD_VER == 11
57+
// A recursive constexpr implementation for C++11
58+
return __t != 0 ? std::__libcpp_popcount(static_cast<unsigned long long>(__t)) +
59+
std::__popcount<_Tp>(__t >> numeric_limits<unsigned long long>::digits)
60+
: 0;
61+
# else
5562
int __ret = 0;
5663
while (__t != 0) {
5764
__ret += std::__libcpp_popcount(static_cast<unsigned long long>(__t));
58-
__t >>= numeric_limits<unsigned long long>::digits;
65+
__t >>= std::numeric_limits<unsigned long long>::digits;
5966
}
6067
return __ret;
68+
# endif
6169
}
62-
# endif // __has_builtin(__builtin_popcountg)
6370
}
6471

65-
#endif // _LIBCPP_STD_VER >= 20
72+
#else
73+
// Implementation for C++03
74+
75+
template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && sizeof(_Tp) <= sizeof(unsigned int), int> = 0>
76+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __popcount(_Tp __t) {
77+
return std::__libcpp_popcount(static_cast<unsigned int>(__t));
78+
}
79+
80+
template < class _Tp,
81+
__enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned int)) &&
82+
sizeof(_Tp) <= sizeof(unsigned long),
83+
int> = 0 >
84+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __popcount(_Tp __t) {
85+
return std::__libcpp_popcount(static_cast<unsigned long>(__t));
86+
}
87+
88+
template < class _Tp,
89+
__enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long)) &&
90+
sizeof(_Tp) <= sizeof(unsigned long long),
91+
int> = 0 >
92+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __popcount(_Tp __t) {
93+
return std::__libcpp_popcount(static_cast<unsigned long long>(__t));
94+
}
95+
96+
template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
97+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __popcount(_Tp __t) {
98+
int __ret = 0;
99+
while (__t != 0) {
100+
__ret += std::__libcpp_popcount(static_cast<unsigned long long>(__t));
101+
__t >>= std::numeric_limits<unsigned long long>::digits;
102+
}
103+
return __ret;
104+
}
105+
106+
#endif // _LIBCPP_CXX03_LANG
107+
108+
#if _LIBCPP_STD_VER >= 20
109+
110+
template <__libcpp_unsigned_integer _Tp>
111+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr int popcount(_Tp __t) noexcept {
112+
# if __has_builtin(__builtin_popcountg)
113+
return __builtin_popcountg(__t);
114+
# else
115+
return std::__popcount(__t);
116+
# endif
117+
}
118+
119+
#endif
66120

67121
_LIBCPP_END_NAMESPACE_STD
68122

libcxx/include/__bit_reference

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ struct __size_difference_type_traits<_Cp, __void_t<typename _Cp::difference_type
6262
using size_type = typename _Cp::size_type;
6363
};
6464

65+
template <class _StorageType>
66+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask(unsigned __clz) {
67+
static_assert(is_unsigned<_StorageType>::value, "__trailing_mask only works with unsigned types");
68+
return static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) >> __clz);
69+
}
70+
71+
template <class _StorageType>
72+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __clz, unsigned __ctz) {
73+
static_assert(is_unsigned<_StorageType>::value, "__middle_mask only works with unsigned types");
74+
return static_cast<_StorageType>(
75+
static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __ctz) &
76+
std::__trailing_mask<_StorageType>(__clz));
77+
}
78+
6579
// This function is designed to operate correctly even for smaller integral types like `uint8_t`, `uint16_t`,
6680
// or `unsigned short`. Casting back to _StorageType is crucial to prevent undefined behavior that can arise
6781
// from integral promotions.

libcxx/include/__fwd/bit_reference.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ template <class _StoragePointer>
3030
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
3131
__fill_masked_range(_StoragePointer __word, unsigned __ctz, unsigned __clz, bool __fill_val);
3232

33+
template <class _StorageType>
34+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask(unsigned __clz);
35+
36+
template <class _StorageType>
37+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __clz, unsigned __ctz);
38+
3339
_LIBCPP_END_NAMESPACE_STD
3440

3541
#endif // _LIBCPP___FWD_BIT_REFERENCE_H

libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515

1616
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=20000000
1717
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=80000000
18+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
1819

1920
#include <algorithm>
2021
#include <cassert>
2122
#include <cstddef>
2223
#include <vector>
2324

25+
#include "sized_allocator.h"
2426
#include "test_macros.h"
2527
#include "test_iterators.h"
2628
#include "type_algorithms.h"
@@ -36,6 +38,29 @@ struct Test {
3638
}
3739
};
3840

41+
TEST_CONSTEXPR_CXX20 void test_bit_iterator_with_custom_sized_types() {
42+
{
43+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
44+
std::vector<bool, Alloc> in(100, true, Alloc(1));
45+
assert(std::count(in.begin(), in.end(), true) == 100);
46+
}
47+
{
48+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
49+
std::vector<bool, Alloc> in(199, true, Alloc(1));
50+
assert(std::count(in.begin(), in.end(), true) == 199);
51+
}
52+
{
53+
using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
54+
std::vector<bool, Alloc> in(200, true, Alloc(1));
55+
assert(std::count(in.begin(), in.end(), true) == 200);
56+
}
57+
{
58+
using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
59+
std::vector<bool, Alloc> in(257, true, Alloc(1));
60+
assert(std::count(in.begin(), in.end(), true) == 257);
61+
}
62+
}
63+
3964
TEST_CONSTEXPR_CXX20 bool test() {
4065
types::for_each(types::cpp17_input_iterator_list<const int*>(), Test());
4166

@@ -51,6 +76,8 @@ TEST_CONSTEXPR_CXX20 bool test() {
5176
}
5277
}
5378

79+
test_bit_iterator_with_custom_sized_types();
80+
5481
return true;
5582
}
5683

0 commit comments

Comments
 (0)