[libcxx-commits] [libcxx] [libc++] Fix ambiguous call in {ranges, std}::find (PR #122641)
Peng Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Feb 4 20:15:51 PST 2025
https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/122641
>From ff2d21675dc59733151a76190e871c7c5daffc30 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Fri, 10 Jan 2025 15:19:08 -0500
Subject: [PATCH 1/2] Fix ambiguous call in {ranges, std}::find
---
libcxx/include/__algorithm/find.h | 10 +--
libcxx/include/__bit/countr.h | 90 ++++++++++++++++---
libcxx/include/__fwd/bit_reference.h | 6 ++
.../alg.nonmodifying/alg.find/find.pass.cpp | 30 +++++++
.../alg.find/ranges.find.pass.cpp | 70 +++++++++++----
libcxx/test/support/sized_allocator.h | 58 ++++++++++++
6 files changed, 229 insertions(+), 35 deletions(-)
create mode 100644 libcxx/test/support/sized_allocator.h
diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index 24b8b2f96443c95..23c38b7d6e80bb0 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -106,10 +106,10 @@ __find_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_ty
if (__first.__ctz_ != 0) {
__storage_type __clz_f = static_cast<__storage_type>(__bits_per_word - __first.__ctz_);
__storage_type __dn = std::min(__clz_f, __n);
- __storage_type __m = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz_f - __dn));
+ __storage_type __m = std::__middle_mask<__storage_type>(__first.__ctz_, __clz_f - __dn);
__storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_) & __m;
if (__b)
- return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
+ return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
if (__n == __dn)
return __first + __n;
__n -= __dn;
@@ -119,14 +119,14 @@ __find_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_ty
for (; __n >= __bits_per_word; ++__first.__seg_, __n -= __bits_per_word) {
__storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_);
if (__b)
- return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
+ return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
}
// do last partial word
if (__n > 0) {
- __storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
+ __storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
__storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_) & __m;
if (__b)
- return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
+ return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
}
return _It(__first.__seg_, static_cast<unsigned>(__n));
}
diff --git a/libcxx/include/__bit/countr.h b/libcxx/include/__bit/countr.h
index 2f7571133bd03a3..1035710600d23b0 100644
--- a/libcxx/include/__bit/countr.h
+++ b/libcxx/include/__bit/countr.h
@@ -15,6 +15,8 @@
#include <__bit/rotate.h>
#include <__concepts/arithmetic.h>
#include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_unsigned.h>
#include <limits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -38,20 +40,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
return __builtin_ctzll(__x);
}
-template <class _Tp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero(_Tp __t) _NOEXCEPT {
-#if __has_builtin(__builtin_ctzg)
- return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
-#else // __has_builtin(__builtin_ctzg)
- if (__t == 0)
- return numeric_limits<_Tp>::digits;
- if (sizeof(_Tp) <= sizeof(unsigned int))
+#if _LIBCPP_STD_VER >= 17
+// Implementation using constexpr if for C++ standards >= 17
+
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
+template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+ if constexpr (sizeof(_Tp) <= sizeof(unsigned int)) {
return std::__libcpp_ctz(static_cast<unsigned int>(__t));
- else if (sizeof(_Tp) <= sizeof(unsigned long))
+ } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long)) {
return std::__libcpp_ctz(static_cast<unsigned long>(__t));
- else if (sizeof(_Tp) <= sizeof(unsigned long long))
+ } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) {
return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
- else {
+ } else {
int __ret = 0;
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
while (static_cast<unsigned long long>(__t) == 0uLL) {
@@ -60,7 +61,72 @@ template <class _Tp>
}
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
}
-#endif // __has_builtin(__builtin_ctzg)
+}
+
+#else
+// Equivalent SFINAE-based implementation for older C++ standards < 17
+
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
+template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && sizeof(_Tp) <= sizeof(unsigned int), int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+ return std::__libcpp_ctz(static_cast<unsigned int>(__t));
+}
+
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
+template < class _Tp,
+ __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned int)) &&
+ sizeof(_Tp) <= sizeof(unsigned long),
+ int> = 0 >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+ return std::__libcpp_ctz(static_cast<unsigned long>(__t));
+}
+
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
+template < class _Tp,
+ __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long)) &&
+ sizeof(_Tp) <= sizeof(unsigned long long),
+ int> = 0 >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+ return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
+}
+
+# if _LIBCPP_STD_VER == 11
+
+// Recursive constexpr implementation for C++11 due to limited constexpr support
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
+template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+ unsigned long long __ull = static_cast<unsigned long long>(__t);
+ const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
+ return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz(__ull);
+}
+
+# else
+
+// Loop-based constexpr implementation for C++14 (and non-constexpr for C++03, 98)
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
+template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+ int __ret = 0;
+ const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
+ while (static_cast<unsigned long long>(__t) == 0uLL) {
+ __ret += __ulldigits;
+ __t >>= __ulldigits;
+ }
+ return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
+}
+
+# endif // _LIBCPP_STD_VER == 11
+
+#endif // _LIBCPP_STD_VER >= 17
+
+template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero(_Tp __t) _NOEXCEPT {
+#if __has_builtin(__builtin_ctzg)
+ return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
+#else
+ return __t != 0 ? __countr_zero_impl(__t) : numeric_limits<_Tp>::digits;
+#endif
}
#if _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__fwd/bit_reference.h b/libcxx/include/__fwd/bit_reference.h
index 30462b6ce4c92f8..6932effd95f30e2 100644
--- a/libcxx/include/__fwd/bit_reference.h
+++ b/libcxx/include/__fwd/bit_reference.h
@@ -10,6 +10,8 @@
#define _LIBCPP___FWD_BIT_REFERENCE_H
#include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_unsigned.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -23,6 +25,10 @@ class __bit_iterator;
template <class, class = void>
struct __size_difference_type_traits;
+template <class _StorageType, __enable_if_t<is_unsigned<_StorageType>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _StorageType
+__find_in_masked_range(_StorageType __word, unsigned __ctz, unsigned __clz, bool __find_val);
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___FWD_BIT_REFERENCE_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
index c41246522fdebac..6940ff97a0ba5b6 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
@@ -14,6 +14,7 @@
// MSVC warning C4389: '==': signed/unsigned mismatch
// MSVC warning C4805: '==': unsafe mix of type 'char' and type 'bool' in operation
// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4245 /wd4305 /wd4310 /wd4389 /wd4805
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
// <algorithm>
@@ -28,6 +29,7 @@
#include <vector>
#include <type_traits>
+#include "sized_allocator.h"
#include "test_macros.h"
#include "test_iterators.h"
#include "type_algorithms.h"
@@ -206,6 +208,33 @@ struct TestIntegerPromotions {
}
};
+TEST_CONSTEXPR_CXX20 void test_bititer_with_custom_sized_types() {
+ {
+ using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
+ std::vector<bool, Alloc> in(100, false, Alloc(1));
+ in[in.size() - 2] = true;
+ assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
+ }
+ {
+ using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
+ std::vector<bool, Alloc> in(200, false, Alloc(1));
+ in[in.size() - 2] = true;
+ assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
+ }
+ {
+ using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
+ std::vector<bool, Alloc> in(200, false, Alloc(1));
+ in[in.size() - 2] = true;
+ assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
+ }
+ {
+ using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
+ std::vector<bool, Alloc> in(200, false, Alloc(1));
+ in[in.size() - 2] = true;
+ assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
+ }
+}
+
TEST_CONSTEXPR_CXX20 bool test() {
types::for_each(types::integer_types(), TestTypes<char>());
types::for_each(types::integer_types(), TestTypes<int>());
@@ -226,6 +255,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
#endif
types::for_each(types::integral_types(), TestIntegerPromotions());
+ test_bititer_with_custom_sized_types();
return true;
}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
index 4ae049c3ec00115..a2af568be37c049 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
@@ -31,6 +31,7 @@
#include <vector>
#include "almost_satisfies_types.h"
+#include "sized_allocator.h"
#include "test_iterators.h"
struct NotEqualityComparable {};
@@ -66,14 +67,14 @@ constexpr void test_iterators() {
using ValueT = std::iter_value_t<It>;
{ // simple test
{
- ValueT a[] = {1, 2, 3, 4};
+ ValueT a[] = {1, 2, 3, 4};
std::same_as<It> auto ret = std::ranges::find(It(a), Sent(It(a + 4)), 4);
assert(base(ret) == a + 3);
assert(*ret == 4);
}
{
- ValueT a[] = {1, 2, 3, 4};
- auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
+ ValueT a[] = {1, 2, 3, 4};
+ auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
std::same_as<It> auto ret = std::ranges::find(range, 4);
assert(base(ret) == a + 3);
assert(*ret == 4);
@@ -83,13 +84,13 @@ constexpr void test_iterators() {
{ // check that an empty range works
{
std::array<ValueT, 0> a = {};
- auto ret = std::ranges::find(It(a.data()), Sent(It(a.data())), 1);
+ auto ret = std::ranges::find(It(a.data()), Sent(It(a.data())), 1);
assert(base(ret) == a.data());
}
{
std::array<ValueT, 0> a = {};
- auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data())));
- auto ret = std::ranges::find(range, 1);
+ auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data())));
+ auto ret = std::ranges::find(range, 1);
assert(base(ret) == a.data());
}
}
@@ -97,12 +98,12 @@ constexpr void test_iterators() {
{ // check that last is returned with no match
{
ValueT a[] = {1, 1, 1};
- auto ret = std::ranges::find(a, a + 3, 0);
+ auto ret = std::ranges::find(a, a + 3, 0);
assert(ret == a + 3);
}
{
ValueT a[] = {1, 1, 1};
- auto ret = std::ranges::find(a, 0);
+ auto ret = std::ranges::find(a, 0);
assert(ret == a + 3);
}
}
@@ -120,6 +121,33 @@ class TriviallyComparable {
bool operator==(const TriviallyComparable&) const = default;
};
+constexpr void test_bititer_with_custom_sized_types() {
+ {
+ using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
+ std::vector<bool, Alloc> in(100, false, Alloc(1));
+ in[in.size() - 2] = true;
+ assert(std::ranges::find(in, true) == in.end() - 2);
+ }
+ {
+ using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
+ std::vector<bool, Alloc> in(200, false, Alloc(1));
+ in[in.size() - 2] = true;
+ assert(std::ranges::find(in, true) == in.end() - 2);
+ }
+ {
+ using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
+ std::vector<bool, Alloc> in(200, false, Alloc(1));
+ in[in.size() - 2] = true;
+ assert(std::ranges::find(in, true) == in.end() - 2);
+ }
+ {
+ using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
+ std::vector<bool, Alloc> in(200, false, Alloc(1));
+ in[in.size() - 2] = true;
+ assert(std::ranges::find(in, true) == in.end() - 2);
+ }
+}
+
constexpr bool test() {
types::for_each(types::type_list<char, wchar_t, int, long, TriviallyComparable<char>, TriviallyComparable<wchar_t>>{},
[]<class T> {
@@ -148,7 +176,7 @@ constexpr bool test() {
int comp;
int other;
};
- S a[] = { {0, 0}, {0, 2}, {0, 1} };
+ S a[] = {{0, 0}, {0, 2}, {0, 1}};
auto ret = std::ranges::find(a, 0, &S::comp);
assert(ret == a);
assert(ret->comp == 0);
@@ -159,7 +187,7 @@ constexpr bool test() {
int comp;
int other;
};
- S a[] = { {0, 0}, {0, 2}, {0, 1} };
+ S a[] = {{0, 0}, {0, 2}, {0, 1}};
auto ret = std::ranges::find(a, a + 3, 0, &S::comp);
assert(ret == a);
assert(ret->comp == 0);
@@ -169,7 +197,7 @@ constexpr bool test() {
{
// check that an iterator is returned with a borrowing range
- int a[] = {1, 2, 3, 4};
+ int a[] = {1, 2, 3, 4};
std::same_as<int*> auto ret = std::ranges::find(std::views::all(a), 1);
assert(ret == a);
assert(*ret == 1);
@@ -178,23 +206,31 @@ constexpr bool test() {
{
// count invocations of the projection
{
- int a[] = {1, 2, 3, 4};
+ int a[] = {1, 2, 3, 4};
int projection_count = 0;
- auto ret = std::ranges::find(a, a + 4, 2, [&](int i) { ++projection_count; return i; });
+ auto ret = std::ranges::find(a, a + 4, 2, [&](int i) {
+ ++projection_count;
+ return i;
+ });
assert(ret == a + 1);
assert(*ret == 2);
assert(projection_count == 2);
}
{
- int a[] = {1, 2, 3, 4};
+ int a[] = {1, 2, 3, 4};
int projection_count = 0;
- auto ret = std::ranges::find(a, 2, [&](int i) { ++projection_count; return i; });
+ auto ret = std::ranges::find(a, 2, [&](int i) {
+ ++projection_count;
+ return i;
+ });
assert(ret == a + 1);
assert(*ret == 2);
assert(projection_count == 2);
}
}
+ test_bititer_with_custom_sized_types();
+
return true;
}
@@ -210,9 +246,7 @@ class Comparable {
return size;
}()) {}
- bool operator==(const Comparable& other) const {
- return comparable_data[other.index_] == comparable_data[index_];
- }
+ bool operator==(const Comparable& other) const { return comparable_data[other.index_] == comparable_data[index_]; }
friend bool operator==(const Comparable& lhs, long long rhs) { return comparable_data[lhs.index_] == rhs; }
};
diff --git a/libcxx/test/support/sized_allocator.h b/libcxx/test/support/sized_allocator.h
new file mode 100644
index 000000000000000..a877252e82962c7
--- /dev/null
+++ b/libcxx/test/support/sized_allocator.h
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SUPPORT_SIZED_ALLOCATOR_H
+#define TEST_SUPPORT_SIZED_ALLOCATOR_H
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <new>
+
+#include "test_macros.h"
+
+template <typename T, typename SIZE_TYPE = std::size_t, typename DIFF_TYPE = std::ptrdiff_t>
+class sized_allocator {
+ template <typename U, typename Sz, typename Diff>
+ friend class sized_allocator;
+
+public:
+ using value_type = T;
+ using size_type = SIZE_TYPE;
+ using difference_type = DIFF_TYPE;
+ using propagate_on_container_swap = std::true_type;
+
+ TEST_CONSTEXPR_CXX20 explicit sized_allocator(int d = 0) : data_(d) {}
+
+ template <typename U, typename Sz, typename Diff>
+ TEST_CONSTEXPR_CXX20 sized_allocator(const sized_allocator<U, Sz, Diff>& a) TEST_NOEXCEPT : data_(a.data_) {}
+
+ TEST_CONSTEXPR_CXX20 T* allocate(size_type n) {
+ if (n > max_size())
+ TEST_THROW(std::bad_array_new_length());
+ return std::allocator<T>().allocate(n);
+ }
+
+ TEST_CONSTEXPR_CXX20 void deallocate(T* p, size_type n) TEST_NOEXCEPT { std::allocator<T>().deallocate(p, n); }
+
+ TEST_CONSTEXPR size_type max_size() const TEST_NOEXCEPT {
+ return std::numeric_limits<size_type>::max() / sizeof(value_type);
+ }
+
+private:
+ int data_;
+
+ TEST_CONSTEXPR friend bool operator==(const sized_allocator& a, const sized_allocator& b) {
+ return a.data_ == b.data_;
+ }
+ TEST_CONSTEXPR friend bool operator!=(const sized_allocator& a, const sized_allocator& b) {
+ return a.data_ != b.data_;
+ }
+};
+
+#endif
>From c5c8107a0167649eaab50680eb2dc3d53da0f7c6 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Wed, 15 Jan 2025 19:11:16 -0500
Subject: [PATCH 2/2] Refactor
---
libcxx/include/__algorithm/find.h | 2 +-
libcxx/include/__bit/countr.h | 49 +++++++------------
libcxx/include/__bit_reference | 32 ++++++++++++
libcxx/include/__fwd/bit_reference.h | 10 ++--
.../alg.nonmodifying/alg.find/find.pass.cpp | 8 +--
.../alg.find/ranges.find.pass.cpp | 8 +--
6 files changed, 64 insertions(+), 45 deletions(-)
diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index 23c38b7d6e80bb0..a7d9374b3a1c89d 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -106,7 +106,7 @@ __find_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_ty
if (__first.__ctz_ != 0) {
__storage_type __clz_f = static_cast<__storage_type>(__bits_per_word - __first.__ctz_);
__storage_type __dn = std::min(__clz_f, __n);
- __storage_type __m = std::__middle_mask<__storage_type>(__first.__ctz_, __clz_f - __dn);
+ __storage_type __m = std::__middle_mask<__storage_type>(__clz_f - __dn, __first.__ctz_);
__storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_) & __m;
if (__b)
return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
diff --git a/libcxx/include/__bit/countr.h b/libcxx/include/__bit/countr.h
index 1035710600d23b0..fe95e9752a9842b 100644
--- a/libcxx/include/__bit/countr.h
+++ b/libcxx/include/__bit/countr.h
@@ -40,12 +40,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
return __builtin_ctzll(__x);
}
-#if _LIBCPP_STD_VER >= 17
-// Implementation using constexpr if for C++ standards >= 17
+#ifndef _LIBCPP_CXX03_LANG
+// constexpr implementation for C++11 and later
-// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
+// Precondition: __t != 0 (the caller __countr_zero handles __t == 0 as a special case)
template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr int __countr_zero_impl(_Tp __t) _NOEXCEPT {
if constexpr (sizeof(_Tp) <= sizeof(unsigned int)) {
return std::__libcpp_ctz(static_cast<unsigned int>(__t));
} else if constexpr (sizeof(_Tp) <= sizeof(unsigned long)) {
@@ -53,6 +53,12 @@ template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
} else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) {
return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
} else {
+# if _LIBCPP_STD_VER == 11
+ // A recursive constexpr implementation for C++11
+ unsigned long long __ull = static_cast<unsigned long long>(__t);
+ const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
+ return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz(__ull);
+# else
int __ret = 0;
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
while (static_cast<unsigned long long>(__t) == 0uLL) {
@@ -61,52 +67,35 @@ template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
}
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
}
+#endif // _LIBCPP_STD_VER == 11
}
#else
-// Equivalent SFINAE-based implementation for older C++ standards < 17
+// Equivalent implementation using SFINAE-based overloading for C++03
-// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && sizeof(_Tp) <= sizeof(unsigned int), int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI int __countr_zero_impl(_Tp __t) _NOEXCEPT {
return std::__libcpp_ctz(static_cast<unsigned int>(__t));
}
-// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
template < class _Tp,
__enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned int)) &&
sizeof(_Tp) <= sizeof(unsigned long),
int> = 0 >
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI int __countr_zero_impl(_Tp __t) _NOEXCEPT {
return std::__libcpp_ctz(static_cast<unsigned long>(__t));
}
-// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
template < class _Tp,
__enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long)) &&
sizeof(_Tp) <= sizeof(unsigned long long),
int> = 0 >
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI int __countr_zero_impl(_Tp __t) _NOEXCEPT {
return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
}
-# if _LIBCPP_STD_VER == 11
-
-// Recursive constexpr implementation for C++11 due to limited constexpr support
-// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
-template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
- unsigned long long __ull = static_cast<unsigned long long>(__t);
- const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
- return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz(__ull);
-}
-
-# else
-
-// Loop-based constexpr implementation for C++14 (and non-constexpr for C++03, 98)
-// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI int __countr_zero_impl(_Tp __t) _NOEXCEPT {
int __ret = 0;
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
while (static_cast<unsigned long long>(__t) == 0uLL) {
@@ -116,16 +105,14 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero_impl(_Tp _
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
}
-# endif // _LIBCPP_STD_VER == 11
-
-#endif // _LIBCPP_STD_VER >= 17
+#endif // _LIBCPP_CXX03_LANG
template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero(_Tp __t) _NOEXCEPT {
#if __has_builtin(__builtin_ctzg)
return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
#else
- return __t != 0 ? __countr_zero_impl(__t) : numeric_limits<_Tp>::digits;
+ return __t != 0 ? std::__countr_zero_impl(__t) : numeric_limits<_Tp>::digits;
#endif
}
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index bb8d4725c398059..6210db5420c7ca9 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -14,7 +14,9 @@
#include <__algorithm/copy_backward.h>
#include <__algorithm/copy_n.h>
#include <__algorithm/min.h>
+#include <__assert>
#include <__bit/countr.h>
+#include <__bit/invert_if.h>
#include <__compare/ordering.h>
#include <__config>
#include <__cstddef/ptrdiff_t.h>
@@ -25,9 +27,11 @@
#include <__memory/pointer_traits.h>
#include <__type_traits/conditional.h>
#include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_unsigned.h>
#include <__type_traits/void_t.h>
#include <__utility/pair.h>
#include <__utility/swap.h>
+#include <climits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -58,6 +62,34 @@ struct __size_difference_type_traits<_Cp, __void_t<typename _Cp::difference_type
using size_type = typename _Cp::size_type;
};
+template <class _StorageType>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask(unsigned __clz) {
+ static_assert(is_unsigned<_StorageType>::value, "__trailing_mask only works with unsigned types");
+ return static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) >> __clz);
+}
+
+template <class _StorageType>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __clz, unsigned __ctz) {
+ static_assert(is_unsigned<_StorageType>::value, "__middle_mask only works with unsigned types");
+ return static_cast<_StorageType>(
+ static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __ctz) &
+ std::__trailing_mask<_StorageType>(__clz));
+}
+
+template <bool _ToFind, class _StoragePointer>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR unsigned
+__find_in_masked_range(_StoragePointer __word, unsigned __clz, unsigned __ctz) {
+ static_assert(is_unsigned<typename pointer_traits<_StoragePointer>::element_type>::value,
+ "__find_in_masked_range must be called with unsigned types");
+ using _StorageType = typename pointer_traits<_StoragePointer>::element_type;
+ _LIBCPP_ASSERT_VALID_INPUT_RANGE(
+ __ctz + __clz < sizeof(_StorageType) * CHAR_BIT, "__find_in_masked_range called with invalid range");
+ _StorageType __m = static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) >> __clz) &
+ static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __ctz);
+ _StorageType __b = std::__invert_if<!_ToFind>(*__word) & __m;
+ return static_cast<unsigned>(std::__countr_zero(__b));
+}
+
template <class _Cp, bool = __has_storage_type<_Cp>::value>
class __bit_reference {
using __storage_type _LIBCPP_NODEBUG = typename _Cp::__storage_type;
diff --git a/libcxx/include/__fwd/bit_reference.h b/libcxx/include/__fwd/bit_reference.h
index 6932effd95f30e2..220366351ba7523 100644
--- a/libcxx/include/__fwd/bit_reference.h
+++ b/libcxx/include/__fwd/bit_reference.h
@@ -10,8 +10,6 @@
#define _LIBCPP___FWD_BIT_REFERENCE_H
#include <__config>
-#include <__type_traits/enable_if.h>
-#include <__type_traits/is_unsigned.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -25,9 +23,11 @@ class __bit_iterator;
template <class, class = void>
struct __size_difference_type_traits;
-template <class _StorageType, __enable_if_t<is_unsigned<_StorageType>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _StorageType
-__find_in_masked_range(_StorageType __word, unsigned __ctz, unsigned __clz, bool __find_val);
+template <class _StorageType>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask(unsigned __clz);
+
+template <class _StorageType>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __clz, unsigned __ctz);
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
index 6940ff97a0ba5b6..599b6355af565b1 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
@@ -208,7 +208,7 @@ struct TestIntegerPromotions {
}
};
-TEST_CONSTEXPR_CXX20 void test_bititer_with_custom_sized_types() {
+TEST_CONSTEXPR_CXX20 void test_bit_iterator_with_custom_sized_types() {
{
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
std::vector<bool, Alloc> in(100, false, Alloc(1));
@@ -217,7 +217,7 @@ TEST_CONSTEXPR_CXX20 void test_bititer_with_custom_sized_types() {
}
{
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
- std::vector<bool, Alloc> in(200, false, Alloc(1));
+ std::vector<bool, Alloc> in(199, false, Alloc(1));
in[in.size() - 2] = true;
assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
}
@@ -229,7 +229,7 @@ TEST_CONSTEXPR_CXX20 void test_bititer_with_custom_sized_types() {
}
{
using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
- std::vector<bool, Alloc> in(200, false, Alloc(1));
+ std::vector<bool, Alloc> in(257, false, Alloc(1));
in[in.size() - 2] = true;
assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
}
@@ -255,7 +255,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
#endif
types::for_each(types::integral_types(), TestIntegerPromotions());
- test_bititer_with_custom_sized_types();
+ test_bit_iterator_with_custom_sized_types();
return true;
}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
index a2af568be37c049..d596e3a5c7970b8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
@@ -121,7 +121,7 @@ class TriviallyComparable {
bool operator==(const TriviallyComparable&) const = default;
};
-constexpr void test_bititer_with_custom_sized_types() {
+constexpr void test_bit_iterator_with_custom_sized_types() {
{
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
std::vector<bool, Alloc> in(100, false, Alloc(1));
@@ -130,7 +130,7 @@ constexpr void test_bititer_with_custom_sized_types() {
}
{
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
- std::vector<bool, Alloc> in(200, false, Alloc(1));
+ std::vector<bool, Alloc> in(199, false, Alloc(1));
in[in.size() - 2] = true;
assert(std::ranges::find(in, true) == in.end() - 2);
}
@@ -142,7 +142,7 @@ constexpr void test_bititer_with_custom_sized_types() {
}
{
using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
- std::vector<bool, Alloc> in(200, false, Alloc(1));
+ std::vector<bool, Alloc> in(257, false, Alloc(1));
in[in.size() - 2] = true;
assert(std::ranges::find(in, true) == in.end() - 2);
}
@@ -229,7 +229,7 @@ constexpr bool test() {
}
}
- test_bititer_with_custom_sized_types();
+ test_bit_iterator_with_custom_sized_types();
return true;
}
More information about the libcxx-commits
mailing list