Skip to content
Open
134 changes: 75 additions & 59 deletions libcxx/include/__algorithm/sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
#include <__algorithm/partial_sort.h>
#include <__algorithm/unwrap_iter.h>
#include <__assert>
#include <__bit/bit_log2.h>
#include <__bit/blsr.h>
#include <__bit/countl.h>
#include <__bit/countr.h>
#include <__config>
#include <__cstddef/ptrdiff_t.h>
#include <__cstddef/size_t.h>
#include <__debug_utils/randomize_range.h>
#include <__debug_utils/strict_weak_ordering_check.h>
#include <__functional/operations.h>
Expand All @@ -34,6 +37,7 @@
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_copyable.h>
#include <__type_traits/make_unsigned.h>
#include <__utility/move.h>
#include <__utility/pair.h>
#include <climits>
Expand Down Expand Up @@ -138,7 +142,7 @@ template <class,
class _Compare,
class _RandomAccessIterator,
__enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI void
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__sort4(_RandomAccessIterator __x1,
_RandomAccessIterator __x2,
_RandomAccessIterator __x3,
Expand All @@ -155,7 +159,7 @@ template <class _AlgPolicy,
class _Compare,
class _RandomAccessIterator,
__enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI void
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__sort4(_RandomAccessIterator __x1,
_RandomAccessIterator __x2,
_RandomAccessIterator __x3,
Expand All @@ -180,7 +184,7 @@ template <class _AlgPolicy,
class _Compare,
class _RandomAccessIterator,
__enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI void
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__sort5(_RandomAccessIterator __x1,
_RandomAccessIterator __x2,
_RandomAccessIterator __x3,
Expand All @@ -199,7 +203,7 @@ template <class _AlgPolicy,
class _Compare,
class _RandomAccessIterator,
__enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI void
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__sort5(_RandomAccessIterator __x1,
_RandomAccessIterator __x2,
_RandomAccessIterator __x3,
Expand Down Expand Up @@ -238,7 +242,7 @@ __selection_sort(_BidirectionalIterator __first, _BidirectionalIterator __last,
// Sort the iterator range [__first, __last) using the comparator __comp using
// the insertion sort algorithm.
template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__insertion_sort(_BidirectionalIterator __first, _BidirectionalIterator __last, _Compare __comp) {
using _Ops = _IterOps<_AlgPolicy>;

Expand Down Expand Up @@ -268,7 +272,7 @@ __insertion_sort(_BidirectionalIterator __first, _BidirectionalIterator __last,
// Assumes that there is an element in the position (__first - 1) and that each
// element in the input range is greater or equal to the element at __first - 1.
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
_LIBCPP_HIDE_FROM_ABI void
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__insertion_sort_unguarded(_RandomAccessIterator const __first, _RandomAccessIterator __last, _Compare __comp) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
Expand Down Expand Up @@ -296,7 +300,7 @@ __insertion_sort_unguarded(_RandomAccessIterator const __first, _RandomAccessIte
}

template <class _AlgPolicy, class _Comp, class _RandomAccessIterator>
_LIBCPP_HIDE_FROM_ABI bool
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
__insertion_sort_incomplete(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp __comp) {
using _Ops = _IterOps<_AlgPolicy>;

Expand Down Expand Up @@ -350,7 +354,7 @@ __insertion_sort_incomplete(_RandomAccessIterator __first, _RandomAccessIterator
}

template <class _AlgPolicy, class _RandomAccessIterator>
inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos(
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap_bitmap_pos(
_RandomAccessIterator __first, _RandomAccessIterator __last, uint64_t& __left_bitset, uint64_t& __right_bitset) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename std::iterator_traits<_RandomAccessIterator>::difference_type difference_type;
Expand All @@ -368,7 +372,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos(
template <class _Compare,
class _RandomAccessIterator,
class _ValueType = typename iterator_traits<_RandomAccessIterator>::value_type>
inline _LIBCPP_HIDE_FROM_ABI void
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__populate_left_bitset(_RandomAccessIterator __first, _Compare __comp, _ValueType& __pivot, uint64_t& __left_bitset) {
// Possible vectorization. With a proper "-march" flag, the following loop
// will be compiled into a set of SIMD instructions.
Expand All @@ -384,7 +388,7 @@ __populate_left_bitset(_RandomAccessIterator __first, _Compare __comp, _ValueTyp
template <class _Compare,
class _RandomAccessIterator,
class _ValueType = typename iterator_traits<_RandomAccessIterator>::value_type>
inline _LIBCPP_HIDE_FROM_ABI void
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__populate_right_bitset(_RandomAccessIterator __lm1, _Compare __comp, _ValueType& __pivot, uint64_t& __right_bitset) {
// Possible vectorization. With a proper "-march" flag, the following loop
// will be compiled into a set of SIMD instructions.
Expand All @@ -401,7 +405,7 @@ template <class _AlgPolicy,
class _Compare,
class _RandomAccessIterator,
class _ValueType = typename iterator_traits<_RandomAccessIterator>::value_type>
inline _LIBCPP_HIDE_FROM_ABI void __bitset_partition_partial_blocks(
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __bitset_partition_partial_blocks(
_RandomAccessIterator& __first,
_RandomAccessIterator& __lm1,
_Compare __comp,
Expand Down Expand Up @@ -448,7 +452,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __bitset_partition_partial_blocks(
}

template <class _AlgPolicy, class _RandomAccessIterator>
inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos_within(
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap_bitmap_pos_within(
_RandomAccessIterator& __first, _RandomAccessIterator& __lm1, uint64_t& __left_bitset, uint64_t& __right_bitset) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename std::iterator_traits<_RandomAccessIterator>::difference_type difference_type;
Expand Down Expand Up @@ -489,7 +493,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos_within(
// __bitset_partition uses bitsets for storing outcomes of the comparisons
// between the pivot and other elements.
template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
_LIBCPP_HIDE_FROM_ABI std::pair<_RandomAccessIterator, bool>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 std::pair<_RandomAccessIterator, bool>
__bitset_partition(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename std::iterator_traits<_RandomAccessIterator>::value_type value_type;
Expand Down Expand Up @@ -581,7 +585,7 @@ __bitset_partition(_RandomAccessIterator __first, _RandomAccessIterator __last,
// the provided range is already sorted, false otherwise. We assume that the
// length of the range is at least three elements.
template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
_LIBCPP_HIDE_FROM_ABI std::pair<_RandomAccessIterator, bool>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 std::pair<_RandomAccessIterator, bool>
__partition_with_equals_on_right(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
Expand Down Expand Up @@ -649,7 +653,7 @@ __partition_with_equals_on_right(_RandomAccessIterator __first, _RandomAccessIte
// Similar to the above function. Elements equivalent to the pivot are put to
// the left of the pivot. Returns the iterator to the pivot element.
template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
_LIBCPP_HIDE_FROM_ABI _RandomAccessIterator
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator
__partition_with_equals_on_left(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
Expand Down Expand Up @@ -712,11 +716,12 @@ __partition_with_equals_on_left(_RandomAccessIterator __first, _RandomAccessIter
// The implementation is partly based on Orson Peters' pattern-defeating
// quicksort, published at: <https://github.com/orlp/pdqsort>.
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator, bool _UseBitSetPartition>
void __introsort(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Compare __comp,
typename iterator_traits<_RandomAccessIterator>::difference_type __depth,
bool __leftmost = true) {
_LIBCPP_CONSTEXPR_SINCE_CXX20 void
__introsort(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Compare __comp,
typename iterator_traits<_RandomAccessIterator>::difference_type __depth,
bool __leftmost = true) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
using _Comp_ref = __comp_ref_type<_Compare>;
Expand Down Expand Up @@ -826,25 +831,6 @@ void __introsort(_RandomAccessIterator __first,
}
}

template <typename _Number>
inline _LIBCPP_HIDE_FROM_ABI _Number __log2i(_Number __n) {
if (__n == 0)
return 0;
if (sizeof(__n) <= sizeof(unsigned))
return sizeof(unsigned) * CHAR_BIT - 1 - __libcpp_clz(static_cast<unsigned>(__n));
if (sizeof(__n) <= sizeof(unsigned long))
return sizeof(unsigned long) * CHAR_BIT - 1 - __libcpp_clz(static_cast<unsigned long>(__n));
if (sizeof(__n) <= sizeof(unsigned long long))
return sizeof(unsigned long long) * CHAR_BIT - 1 - __libcpp_clz(static_cast<unsigned long long>(__n));

_Number __log2 = 0;
while (__n > 1) {
__log2++;
__n >>= 1;
}
return __log2;
}

template <class _Comp, class _RandomAccessIterator>
void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);

Expand Down Expand Up @@ -877,8 +863,10 @@ __sort<__less<long double>&, long double*>(long double*, long double*, __less<lo
template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__sort_dispatch(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp& __comp) {
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
difference_type __depth_limit = 2 * std::__log2i(__last - __first);
using difference_type = typename iterator_traits<_RandomAccessIterator>::difference_type;
using __size_type = __make_unsigned_t<difference_type>;
difference_type __depth_limit =
static_cast<difference_type>(2 * std::__bit_log2(static_cast<__size_type>(__last - __first)));

// Only use bitset partitioning for arithmetic types. We should also check
// that the default comparator is in use so that we are sure that there are no
Expand Down Expand Up @@ -912,30 +900,63 @@ using __sort_is_specialized_in_library _LIBCPP_NODEBUG = __is_any_of<
long double>;

template <class _AlgPolicy, class _Type, __enable_if_t<__sort_is_specialized_in_library<_Type>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __sort_dispatch(_Type* __first, _Type* __last, __less<>&) {
__less<_Type> __comp;
std::__sort<__less<_Type>&, _Type*>(__first, __last, __comp);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __sort_dispatch(_Type* __first, _Type* __last, __less<>&) {
#if _LIBCPP_STD_VER >= 20
if (std::is_constant_evaluated()) {
auto __depth_limit = static_cast<ptrdiff_t>(2 * std::__bit_log2(static_cast<size_t>(__last - __first)));
std::__introsort<_ClassicAlgPolicy, ranges::less, _Type*, __use_branchless_sort<ranges::less, _Type*>>(
__first, __last, ranges::less{}, __depth_limit);
} else
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this. How is this changing anything compared to the general __libcpp_is_constant_evaluated? IIUC you claim this has got something to do with instantiating an undefined template, but this is instantiating __sort exactly the same as the old code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guarded this with _LIBCPP_STD_VER >= 20 because ranges::less is used in __sort and I wanted the same operations to be performed. And given _LIBCPP_STD_VER >= 20 is required, IMO its a bit better to use std::is_constant_evaluated. (Or even C++23 if consteval?)

IIUC you claim this has got something to do with instantiating an undefined template, but this is instantiating __sort exactly the same as the old code.

No, the changes in __sort_dispatch didn't change instantiation of __sort. I just attempted to add constexpr-friend branches to them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then please revert these changes. It's not at all obvious to me what changes actually fix anything (and FWIW I'm not convinced these changes are a good idea).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think the changes here "fix" an ironic inconsistency.

Some functions needs to be constexpr to make instantiation sort involving consteval-only operations well-formed, while without these changes they are not even called in constant evaluation. If we keep these functions not called in constant evaluation, maybe someone will get confused and attempt to remove constexpr in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand. How does it make a difference whether some functions that aren't constexpr are instantiated or not? How does it not make a difference when they can't be instantiated because they're not defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it not make a difference when they can't be instantiated because they're not defined?

No, the error is not about undefined functions. It is about functions templates that are defined but not constexpr (i.e. __introsort etc. in the status quo).

I don't understand. How does it make a difference whether some functions that aren't constexpr are instantiated or not?

The expression __last - __first inside __introsort can't be a constant expression because __first and __last are function parameters. When the operator- for the iterator is a consteval function and __introsort is not constexpr, __last - __first would be required to be a constant expression, which makes instantiation of __introsort ill-formed.

Marking __introsort constexpr makes the __introsort specialization gets immediate-escalated and becomes an immediate function, and thus its instantiation can be well-formed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it not make a difference when they can't be instantiated because they're not defined?

No, the error is not about undefined functions. It is about functions templates that are defined but not constexpr (i.e. __introsort etc. in the status quo).

I don't understand. How does it make a difference whether some functions that aren't constexpr are instantiated or not?

The expression __last - __first inside __introsort can't be a constant expression because __first and __last are function parameters. When the operator- for the iterator is a consteval function and __introsort is not constexpr, __last - __first would be required to be a constant expression, which makes instantiation of __introsort ill-formed.

Marking __introsort constexpr makes the __introsort specialization gets immediate-escalated and becomes an immediate function, and thus its instantiation can be well-formed.

That is quite unfortunate. IMO it shouldn't make a difference whether we define a function or not. Anyways, can't we just replace the if (__libcpp_is_constant_evaluated()) with if consteval? That would make the diff significantly smaller.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyways, can't we just replace the if (__libcpp_is_constant_evaluated()) with if consteval?

This doesn't work, because instantiation of __introsort is still triggered. AFAIK __introsort needs to be instantiated for most iterator types, and there's no mechanism to avoid instantiation of some function only during constant evaluation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, IMO this is CWG issue territory. It can't be that consteval goes this viral. This basically means that we can't have non-constexpr code in constexpr functions, which is simply not acceptable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, IMO this is CWG issue territory. It can't be that consteval goes this viral. This basically means that we can't have non-constexpr code in constexpr functions, which is simply not acceptable.

The crux is the combination of non-constant consteval operations and non-constexpr functions. Currently non-constexpr functions, along with non-template non-lambda non-defaulted constexpr functions, are never immediate-escalated. Perhaps it's also possible to escalate some non-constexpr functions to "error-on-call-only" things.

However, is it a bad thing to use __introsort in constant evaluation? If not, it seems plausible to mark it and its depended functions _LIBCPP_CONSTEXPR_SINCE_CXX20.

{
__less<_Type> __comp;
std::__sort<__less<_Type>&, _Type*>(__first, __last, __comp);
}
}

template <class _AlgPolicy, class _Type, __enable_if_t<__sort_is_specialized_in_library<_Type>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __sort_dispatch(_Type* __first, _Type* __last, less<_Type>&) {
__less<_Type> __comp;
std::__sort<__less<_Type>&, _Type*>(__first, __last, __comp);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __sort_dispatch(_Type* __first, _Type* __last, less<_Type>&) {
#if _LIBCPP_STD_VER >= 20
if (std::is_constant_evaluated()) {
auto __depth_limit = static_cast<ptrdiff_t>(2 * std::__bit_log2(static_cast<size_t>(__last - __first)));
std::__introsort<_ClassicAlgPolicy, ranges::less, _Type*, __use_branchless_sort<ranges::less, _Type*>>(
__first, __last, ranges::less{}, __depth_limit);
} else
#endif
{
__less<_Type> __comp;
std::__sort<__less<_Type>&, _Type*>(__first, __last, __comp);
}
}

#if _LIBCPP_STD_VER >= 14
template <class _AlgPolicy, class _Type, __enable_if_t<__sort_is_specialized_in_library<_Type>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __sort_dispatch(_Type* __first, _Type* __last, less<>&) {
__less<_Type> __comp;
std::__sort<__less<_Type>&, _Type*>(__first, __last, __comp);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __sort_dispatch(_Type* __first, _Type* __last, less<>&) {
# if _LIBCPP_STD_VER >= 20
if (std::is_constant_evaluated()) {
auto __depth_limit = static_cast<ptrdiff_t>(2 * std::__bit_log2(static_cast<size_t>(__last - __first)));
std::__introsort<_ClassicAlgPolicy, ranges::less, _Type*, __use_branchless_sort<ranges::less, _Type*>>(
__first, __last, ranges::less{}, __depth_limit);
} else
# endif
{
__less<_Type> __comp;
std::__sort<__less<_Type>&, _Type*>(__first, __last, __comp);
}
}
#endif

#if _LIBCPP_STD_VER >= 20
template <class _AlgPolicy, class _Type, __enable_if_t<__sort_is_specialized_in_library<_Type>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __sort_dispatch(_Type* __first, _Type* __last, ranges::less&) {
__less<_Type> __comp;
std::__sort<__less<_Type>&, _Type*>(__first, __last, __comp);
_LIBCPP_HIDE_FROM_ABI constexpr void __sort_dispatch(_Type* __first, _Type* __last, ranges::less&) {
if (std::is_constant_evaluated()) {
auto __depth_limit = static_cast<ptrdiff_t>(2 * std::__bit_log2(static_cast<size_t>(__last - __first)));
std::__introsort<_ClassicAlgPolicy, ranges::less, _Type*, __use_branchless_sort<ranges::less, _Type*>>(
__first, __last, ranges::less{}, __depth_limit);
} else {
__less<_Type> __comp;
std::__sort<__less<_Type>&, _Type*>(__first, __last, __comp);
}
}
#endif

Expand All @@ -944,12 +965,7 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
__sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp& __comp) {
std::__debug_randomize_range<_AlgPolicy>(__first, __last);

if (__libcpp_is_constant_evaluated()) {
std::__partial_sort<_AlgPolicy>(
std::__unwrap_iter(__first), std::__unwrap_iter(__last), std::__unwrap_iter(__last), __comp);
} else {
std::__sort_dispatch<_AlgPolicy>(std::__unwrap_iter(__first), std::__unwrap_iter(__last), __comp);
}
std::__sort_dispatch<_AlgPolicy>(std::__unwrap_iter(__first), std::__unwrap_iter(__last), __comp);
std::__check_strict_weak_ordering_sorted(std::__unwrap_iter(__first), std::__unwrap_iter(__last), __comp);
}

Expand Down
6 changes: 1 addition & 5 deletions libcxx/include/__bit/bit_log2.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,12 @@

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 14

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp __bit_log2(_Tp __t) noexcept {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __bit_log2(_Tp __t) _NOEXCEPT {
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__bit_log2 requires an unsigned integer type");
return numeric_limits<_Tp>::digits - 1 - std::__countl_zero(__t);
}

#endif // _LIBCPP_STD_VER >= 14

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___BIT_BIT_LOG2_H
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ constexpr void test_iterators() {
test_iterators_1<random_access_iterator<int*>>();
test_iterators_1<contiguous_iterator<int*>>();
test_iterators_1<int*>();
#if __cpp_consteval >= 202211L // TODO: Remove this guard when MSVC implements P2564R3.
test_iterators_1<consteval_random_access_iterator<int*>>();
test_iterators_1<consteval_contiguous_iterator<int*>>();
#endif
}

constexpr bool test() {
Expand Down Expand Up @@ -187,8 +191,6 @@ constexpr bool test() {
[[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) result = std::ranges::sort(std::array{1, 2, 3});
}

// TODO: Enable the tests once the implementation switched to use iter_move/iter_swap
/*
{ // ProxyIterator
{
std::array in = {2, 1, 3};
Expand All @@ -205,7 +207,6 @@ constexpr bool test() {
assert((in == std::array{1, 2, 3}));
}
}
*/

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,39 @@ int main(int, char**)
static_assert(test_pointers<17, int, int**>());
static_assert(test_pointers<17, int, random_access_iterator<int**>>());
static_assert(test_pointers<17, int, contiguous_iterator<int**>>());

// Test consteval-only operations
# if __cpp_consteval >= 202211L // TODO: Remove this guard when MSVC implements P2564R3.
(void)test<7, int, consteval_random_access_iterator<int*>>();
(void)test<7, int, consteval_contiguous_iterator<int*>>();
(void)test<LargeN, int, consteval_random_access_iterator<int*>>();
(void)test<LargeN, int, consteval_contiguous_iterator<int*>>();
(void)test<7, MoveOnly, consteval_random_access_iterator<MoveOnly*>>();
(void)test<7, MoveOnly, consteval_contiguous_iterator<MoveOnly*>>();
(void)test<LargeN, MoveOnly, consteval_random_access_iterator<MoveOnly*>>();
(void)test<LargeN, MoveOnly, consteval_contiguous_iterator<MoveOnly*>>();
(void)test_pointers<17, char, consteval_random_access_iterator<char**>>();
(void)test_pointers<17, char, consteval_contiguous_iterator<char**>>();
(void)test_pointers<17, const char, consteval_random_access_iterator<const char**>>();
(void)test_pointers<17, const char, consteval_contiguous_iterator<const char**>>();
(void)test_pointers<17, int, consteval_random_access_iterator<int**>>();
(void)test_pointers<17, int, consteval_contiguous_iterator<int**>>();

static_assert(test<7, int, consteval_random_access_iterator<int*>>());
static_assert(test<7, int, consteval_contiguous_iterator<int*>>());
static_assert(test<LargeN, int, consteval_random_access_iterator<int*>>());
static_assert(test<LargeN, int, consteval_contiguous_iterator<int*>>());
static_assert(test<7, MoveOnly, consteval_random_access_iterator<MoveOnly*>>());
static_assert(test<7, MoveOnly, consteval_contiguous_iterator<MoveOnly*>>());
static_assert(test<LargeN, MoveOnly, consteval_random_access_iterator<MoveOnly*>>());
static_assert(test<LargeN, MoveOnly, consteval_contiguous_iterator<MoveOnly*>>());
static_assert(test_pointers<17, char, consteval_random_access_iterator<char**>>());
static_assert(test_pointers<17, char, consteval_contiguous_iterator<char**>>());
static_assert(test_pointers<17, const char, consteval_random_access_iterator<const char**>>());
static_assert(test_pointers<17, const char, consteval_contiguous_iterator<const char**>>());
static_assert(test_pointers<17, int, consteval_random_access_iterator<int**>>());
static_assert(test_pointers<17, int, consteval_contiguous_iterator<int**>>());
# endif
#endif

return 0;
Expand Down
Loading