Skip to content

Commit f27f167

Browse files
committed
Fix ambiguous call in {ranges, std}::find
1 parent 022c9c9 commit f27f167

File tree

4 files changed

+165
-35
lines changed

4 files changed

+165
-35
lines changed

libcxx/include/__algorithm/find.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,10 @@ __find_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_ty
106106
if (__first.__ctz_ != 0) {
107107
__storage_type __clz_f = static_cast<__storage_type>(__bits_per_word - __first.__ctz_);
108108
__storage_type __dn = std::min(__clz_f, __n);
109-
__storage_type __m = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz_f - __dn));
109+
__storage_type __m = std::__middle_mask<__storage_type>(__first.__ctz_, __clz_f - __dn);
110110
__storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_) & __m;
111111
if (__b)
112-
return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
112+
return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
113113
if (__n == __dn)
114114
return __first + __n;
115115
__n -= __dn;
@@ -119,14 +119,14 @@ __find_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_ty
119119
for (; __n >= __bits_per_word; ++__first.__seg_, __n -= __bits_per_word) {
120120
__storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_);
121121
if (__b)
122-
return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
122+
return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
123123
}
124124
// do last partial word
125125
if (__n > 0) {
126-
__storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
126+
__storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
127127
__storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_) & __m;
128128
if (__b)
129-
return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
129+
return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
130130
}
131131
return _It(__first.__seg_, static_cast<unsigned>(__n));
132132
}

libcxx/include/__bit/countr.h

Lines changed: 78 additions & 12 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,20 +40,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3840
return __builtin_ctzll(__x);
3941
}
4042

41-
template <class _Tp>
42-
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero(_Tp __t) _NOEXCEPT {
43-
#if __has_builtin(__builtin_ctzg)
44-
return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
45-
#else // __has_builtin(__builtin_ctzg)
46-
if (__t == 0)
47-
return numeric_limits<_Tp>::digits;
48-
if (sizeof(_Tp) <= sizeof(unsigned int))
43+
#if _LIBCPP_STD_VER >= 17
44+
// Implementation using constexpr if for C++ standards >= 17
45+
46+
// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
47+
template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
48+
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int __countr_zero_impl(_Tp __t) _NOEXCEPT {
49+
if constexpr (sizeof(_Tp) <= sizeof(unsigned int)) {
4950
return std::__libcpp_ctz(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_ctz(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_ctz(static_cast<unsigned long long>(__t));
54-
else {
55+
} else {
5556
int __ret = 0;
5657
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
5758
while (static_cast<unsigned long long>(__t) == 0uLL) {
@@ -60,7 +61,72 @@ template <class _Tp>
6061
}
6162
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
6263
}
63-
#endif // __has_builtin(__builtin_ctzg)
64+
}
65+
66+
#else
67+
// Equivalent SFINAE-based implementation for older C++ standards < 17
68+
69+
// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
70+
template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && sizeof(_Tp) <= sizeof(unsigned int), int> = 0>
71+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
72+
return std::__libcpp_ctz(static_cast<unsigned int>(__t));
73+
}
74+
75+
// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
76+
template < class _Tp,
77+
__enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned int)) &&
78+
sizeof(_Tp) <= sizeof(unsigned long),
79+
int> = 0 >
80+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
81+
return std::__libcpp_ctz(static_cast<unsigned long>(__t));
82+
}
83+
84+
// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
85+
template < class _Tp,
86+
__enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long)) &&
87+
sizeof(_Tp) <= sizeof(unsigned long long),
88+
int> = 0 >
89+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
90+
return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
91+
}
92+
93+
# if _LIBCPP_STD_VER == 11
94+
95+
// Recursive constexpr implementation for C++11 due to limited constexpr support
96+
// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
97+
template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
98+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
99+
unsigned long long __ull = static_cast<unsigned long long>(__t);
100+
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
101+
return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz(__ull);
102+
}
103+
104+
# else
105+
106+
// Loop-based constexpr implementation for C++14 (and non-constexpr for C++03, 98)
107+
// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
108+
template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
109+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero_impl(_Tp __t) _NOEXCEPT {
110+
int __ret = 0;
111+
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
112+
while (static_cast<unsigned long long>(__t) == 0uLL) {
113+
__ret += __ulldigits;
114+
__t >>= __ulldigits;
115+
}
116+
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
117+
}
118+
119+
# endif // _LIBCPP_STD_VER == 11
120+
121+
#endif // _LIBCPP_STD_VER >= 17
122+
123+
template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
124+
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero(_Tp __t) _NOEXCEPT {
125+
#if __has_builtin(__builtin_ctzg)
126+
return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
127+
#else
128+
return __t != 0 ? __countr_zero_impl(__t) : numeric_limits<_Tp>::digits;
129+
#endif
64130
}
65131

66132
#if _LIBCPP_STD_VER >= 20

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// MSVC warning C4389: '==': signed/unsigned mismatch
1515
// MSVC warning C4805: '==': unsafe mix of type 'char' and type 'bool' in operation
1616
// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4245 /wd4305 /wd4310 /wd4389 /wd4805
17+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
1718

1819
// <algorithm>
1920

@@ -28,6 +29,7 @@
2829
#include <vector>
2930
#include <type_traits>
3031

32+
#include "sized_allocator.h"
3133
#include "test_macros.h"
3234
#include "test_iterators.h"
3335
#include "type_algorithms.h"
@@ -206,6 +208,33 @@ struct TestIntegerPromotions {
206208
}
207209
};
208210

211+
TEST_CONSTEXPR_CXX20 void test_bititer_with_custom_sized_types() {
212+
{
213+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
214+
std::vector<bool, Alloc> in(100, false, Alloc(1));
215+
in[in.size() - 2] = true;
216+
assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
217+
}
218+
{
219+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
220+
std::vector<bool, Alloc> in(200, false, Alloc(1));
221+
in[in.size() - 2] = true;
222+
assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
223+
}
224+
{
225+
using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
226+
std::vector<bool, Alloc> in(200, false, Alloc(1));
227+
in[in.size() - 2] = true;
228+
assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
229+
}
230+
{
231+
using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
232+
std::vector<bool, Alloc> in(200, false, Alloc(1));
233+
in[in.size() - 2] = true;
234+
assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
235+
}
236+
}
237+
209238
TEST_CONSTEXPR_CXX20 bool test() {
210239
types::for_each(types::integer_types(), TestTypes<char>());
211240
types::for_each(types::integer_types(), TestTypes<int>());
@@ -226,6 +255,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
226255
#endif
227256

228257
types::for_each(types::integral_types(), TestIntegerPromotions());
258+
test_bititer_with_custom_sized_types();
229259

230260
return true;
231261
}

libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <vector>
3232

3333
#include "almost_satisfies_types.h"
34+
#include "sized_allocator.h"
3435
#include "test_iterators.h"
3536

3637
struct NotEqualityComparable {};
@@ -66,14 +67,14 @@ constexpr void test_iterators() {
6667
using ValueT = std::iter_value_t<It>;
6768
{ // simple test
6869
{
69-
ValueT a[] = {1, 2, 3, 4};
70+
ValueT a[] = {1, 2, 3, 4};
7071
std::same_as<It> auto ret = std::ranges::find(It(a), Sent(It(a + 4)), 4);
7172
assert(base(ret) == a + 3);
7273
assert(*ret == 4);
7374
}
7475
{
75-
ValueT a[] = {1, 2, 3, 4};
76-
auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
76+
ValueT a[] = {1, 2, 3, 4};
77+
auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
7778
std::same_as<It> auto ret = std::ranges::find(range, 4);
7879
assert(base(ret) == a + 3);
7980
assert(*ret == 4);
@@ -83,26 +84,26 @@ constexpr void test_iterators() {
8384
{ // check that an empty range works
8485
{
8586
std::array<ValueT, 0> a = {};
86-
auto ret = std::ranges::find(It(a.data()), Sent(It(a.data())), 1);
87+
auto ret = std::ranges::find(It(a.data()), Sent(It(a.data())), 1);
8788
assert(base(ret) == a.data());
8889
}
8990
{
9091
std::array<ValueT, 0> a = {};
91-
auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data())));
92-
auto ret = std::ranges::find(range, 1);
92+
auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data())));
93+
auto ret = std::ranges::find(range, 1);
9394
assert(base(ret) == a.data());
9495
}
9596
}
9697

9798
{ // check that last is returned with no match
9899
{
99100
ValueT a[] = {1, 1, 1};
100-
auto ret = std::ranges::find(a, a + 3, 0);
101+
auto ret = std::ranges::find(a, a + 3, 0);
101102
assert(ret == a + 3);
102103
}
103104
{
104105
ValueT a[] = {1, 1, 1};
105-
auto ret = std::ranges::find(a, 0);
106+
auto ret = std::ranges::find(a, 0);
106107
assert(ret == a + 3);
107108
}
108109
}
@@ -120,6 +121,33 @@ class TriviallyComparable {
120121
bool operator==(const TriviallyComparable&) const = default;
121122
};
122123

124+
constexpr void test_bititer_with_custom_sized_types() {
125+
{
126+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
127+
std::vector<bool, Alloc> in(100, false, Alloc(1));
128+
in[in.size() - 2] = true;
129+
assert(std::ranges::find(in, true) == in.end() - 2);
130+
}
131+
{
132+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
133+
std::vector<bool, Alloc> in(200, false, Alloc(1));
134+
in[in.size() - 2] = true;
135+
assert(std::ranges::find(in, true) == in.end() - 2);
136+
}
137+
{
138+
using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
139+
std::vector<bool, Alloc> in(200, false, Alloc(1));
140+
in[in.size() - 2] = true;
141+
assert(std::ranges::find(in, true) == in.end() - 2);
142+
}
143+
{
144+
using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
145+
std::vector<bool, Alloc> in(200, false, Alloc(1));
146+
in[in.size() - 2] = true;
147+
assert(std::ranges::find(in, true) == in.end() - 2);
148+
}
149+
}
150+
123151
constexpr bool test() {
124152
types::for_each(types::type_list<char, wchar_t, int, long, TriviallyComparable<char>, TriviallyComparable<wchar_t>>{},
125153
[]<class T> {
@@ -148,7 +176,7 @@ constexpr bool test() {
148176
int comp;
149177
int other;
150178
};
151-
S a[] = { {0, 0}, {0, 2}, {0, 1} };
179+
S a[] = {{0, 0}, {0, 2}, {0, 1}};
152180
auto ret = std::ranges::find(a, 0, &S::comp);
153181
assert(ret == a);
154182
assert(ret->comp == 0);
@@ -159,7 +187,7 @@ constexpr bool test() {
159187
int comp;
160188
int other;
161189
};
162-
S a[] = { {0, 0}, {0, 2}, {0, 1} };
190+
S a[] = {{0, 0}, {0, 2}, {0, 1}};
163191
auto ret = std::ranges::find(a, a + 3, 0, &S::comp);
164192
assert(ret == a);
165193
assert(ret->comp == 0);
@@ -169,7 +197,7 @@ constexpr bool test() {
169197

170198
{
171199
// check that an iterator is returned with a borrowing range
172-
int a[] = {1, 2, 3, 4};
200+
int a[] = {1, 2, 3, 4};
173201
std::same_as<int*> auto ret = std::ranges::find(std::views::all(a), 1);
174202
assert(ret == a);
175203
assert(*ret == 1);
@@ -178,23 +206,31 @@ constexpr bool test() {
178206
{
179207
// count invocations of the projection
180208
{
181-
int a[] = {1, 2, 3, 4};
209+
int a[] = {1, 2, 3, 4};
182210
int projection_count = 0;
183-
auto ret = std::ranges::find(a, a + 4, 2, [&](int i) { ++projection_count; return i; });
211+
auto ret = std::ranges::find(a, a + 4, 2, [&](int i) {
212+
++projection_count;
213+
return i;
214+
});
184215
assert(ret == a + 1);
185216
assert(*ret == 2);
186217
assert(projection_count == 2);
187218
}
188219
{
189-
int a[] = {1, 2, 3, 4};
220+
int a[] = {1, 2, 3, 4};
190221
int projection_count = 0;
191-
auto ret = std::ranges::find(a, 2, [&](int i) { ++projection_count; return i; });
222+
auto ret = std::ranges::find(a, 2, [&](int i) {
223+
++projection_count;
224+
return i;
225+
});
192226
assert(ret == a + 1);
193227
assert(*ret == 2);
194228
assert(projection_count == 2);
195229
}
196230
}
197231

232+
test_bititer_with_custom_sized_types();
233+
198234
return true;
199235
}
200236

@@ -210,9 +246,7 @@ class Comparable {
210246
return size;
211247
}()) {}
212248

213-
bool operator==(const Comparable& other) const {
214-
return comparable_data[other.index_] == comparable_data[index_];
215-
}
249+
bool operator==(const Comparable& other) const { return comparable_data[other.index_] == comparable_data[index_]; }
216250

217251
friend bool operator==(const Comparable& lhs, long long rhs) { return comparable_data[lhs.index_] == rhs; }
218252
};

0 commit comments

Comments
 (0)