[libcxx-commits] [libcxx] b4ecfd3 - [libc++] Forward to std::memcmp for trivially comparable types in equal
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Feb 21 08:11:27 PST 2023
Author: Nikolas Klauser
Date: 2023-02-21T17:11:21+01:00
New Revision: b4ecfd3c4675ac45d48a97590d4489a1d29c3848
URL: https://github.com/llvm/llvm-project/commit/b4ecfd3c4675ac45d48a97590d4489a1d29c3848
DIFF: https://github.com/llvm/llvm-project/commit/b4ecfd3c4675ac45d48a97590d4489a1d29c3848.diff
LOG: [libc++] Forward to std::memcmp for trivially comparable types in equal
Reviewed By: #libc, ldionne
Spies: ldionne, Mordante, libcxx-commits
Differential Revision: https://reviews.llvm.org/D139554
Added:
libcxx/benchmarks/algorithms/equal.bench.cpp
libcxx/include/__type_traits/is_equality_comparable.h
libcxx/include/__type_traits/predicate_traits.h
libcxx/test/libcxx/type_traits/is_trivially_comparable.compile.pass.cpp
Modified:
libcxx/benchmarks/CMakeLists.txt
libcxx/docs/ReleaseNotes.rst
libcxx/include/CMakeLists.txt
libcxx/include/__algorithm/comp.h
libcxx/include/__algorithm/equal.h
libcxx/include/__algorithm/make_projected.h
libcxx/include/__algorithm/ranges_equal.h
libcxx/include/__algorithm/unwrap_iter.h
libcxx/include/__algorithm/unwrap_range.h
libcxx/include/__functional/identity.h
libcxx/include/__functional/operations.h
libcxx/include/__functional/ranges_operations.h
libcxx/include/__string/constexpr_c_functions.h
libcxx/include/module.modulemap.in
libcxx/test/libcxx/private_headers.verify.cpp
libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/equal.pass.cpp
libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/ranges.equal.pass.cpp
libcxx/test/std/algorithms/robust_against_adl.compile.pass.cpp
libcxx/test/support/test_iterators.h
libcxx/test/support/type_algorithms.h
Removed:
################################################################################
diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt
index 2c3c39ef5345a..572f210d71004 100644
--- a/libcxx/benchmarks/CMakeLists.txt
+++ b/libcxx/benchmarks/CMakeLists.txt
@@ -107,7 +107,7 @@ endif()
add_library( cxx-benchmarks-flags-libcxx INTERFACE)
target_link_libraries( cxx-benchmarks-flags-libcxx INTERFACE cxx-benchmarks-flags)
target_compile_options(cxx-benchmarks-flags-libcxx INTERFACE ${SANITIZER_FLAGS} -Wno-user-defined-literals -Wno-suggest-override)
-target_link_options( cxx-benchmarks-flags-libcxx INTERFACE -nodefaultlibs "-L${BENCHMARK_LIBCXX_INSTALL}/lib" ${SANITIZER_FLAGS})
+target_link_options( cxx-benchmarks-flags-libcxx INTERFACE -nodefaultlibs "-L${BENCHMARK_LIBCXX_INSTALL}/lib" "-L${BENCHMARK_LIBCXX_INSTALL}/lib64" ${SANITIZER_FLAGS})
set(libcxx_benchmark_targets)
@@ -158,6 +158,7 @@ endfunction()
#==============================================================================
set(BENCHMARK_TESTS
algorithms.partition_point.bench.cpp
+ algorithms/equal.bench.cpp
algorithms/lower_bound.bench.cpp
algorithms/make_heap.bench.cpp
algorithms/make_heap_then_sort_heap.bench.cpp
diff --git a/libcxx/benchmarks/algorithms/equal.bench.cpp b/libcxx/benchmarks/algorithms/equal.bench.cpp
new file mode 100644
index 0000000000000..6d63d8c48ce1e
--- /dev/null
+++ b/libcxx/benchmarks/algorithms/equal.bench.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <benchmark/benchmark.h>
+#include <vector>
+
+static void bm_equal_iter(benchmark::State& state) {
+ std::vector<char> vec1(state.range(), '1');
+ std::vector<char> vec2(state.range(), '1');
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(vec1);
+ benchmark::DoNotOptimize(vec2);
+ benchmark::DoNotOptimize(std::equal(vec1.begin(), vec1.end(), vec2.begin()));
+ }
+}
+BENCHMARK(bm_equal_iter)->DenseRange(1, 8)->Range(16, 1 << 20);
+
+static void bm_equal(benchmark::State& state) {
+ std::vector<char> vec1(state.range(), '1');
+ std::vector<char> vec2(state.range(), '1');
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(vec1);
+ benchmark::DoNotOptimize(vec2);
+ benchmark::DoNotOptimize(std::equal(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
+ }
+}
+BENCHMARK(bm_equal)->DenseRange(1, 8)->Range(16, 1 << 20);
+
+static void bm_ranges_equal(benchmark::State& state) {
+ std::vector<char> vec1(state.range(), '1');
+ std::vector<char> vec2(state.range(), '1');
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(vec1);
+ benchmark::DoNotOptimize(vec2);
+ benchmark::DoNotOptimize(std::ranges::equal(vec1, vec2));
+ }
+}
+BENCHMARK(bm_ranges_equal)->DenseRange(1, 8)->Range(16, 1 << 20);
+
+BENCHMARK_MAIN();
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index acd5c16070323..cce18ddb46b17 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -42,6 +42,8 @@ Implemented Papers
Improvements and New Features
-----------------------------
+- ``std::equal`` and ``std::ranges::equal`` are now forwarding to ``std::memcmp`` for integral types and pointers,
+ which can lead up to 40x performance improvements.
- ``std::string_view`` now provides iterators that check for out-of-bounds accesses when the safe
libc++ mode is enabled.
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 7a62cc951a6d1..6fdaa6a84b1f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -638,6 +638,7 @@ set(files
__type_traits/is_destructible.h
__type_traits/is_empty.h
__type_traits/is_enum.h
+ __type_traits/is_equality_comparable.h
__type_traits/is_final.h
__type_traits/is_floating_point.h
__type_traits/is_function.h
@@ -702,6 +703,7 @@ set(files
__type_traits/nat.h
__type_traits/negation.h
__type_traits/noexcept_move_assign_container.h
+ __type_traits/predicate_traits.h
__type_traits/promote.h
__type_traits/rank.h
__type_traits/remove_all_extents.h
diff --git a/libcxx/include/__algorithm/comp.h b/libcxx/include/__algorithm/comp.h
index af8eb7b5d76b1..1001e42a3169b 100644
--- a/libcxx/include/__algorithm/comp.h
+++ b/libcxx/include/__algorithm/comp.h
@@ -10,6 +10,8 @@
#define _LIBCPP___ALGORITHM_COMP_H
#include <__config>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/predicate_traits.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -24,6 +26,9 @@ struct __equal_to {
}
};
+template <class _Lhs, class _Rhs>
+struct __is_trivial_equality_predicate<__equal_to, _Lhs, _Rhs> : true_type {};
+
template <class _T1, class _T2 = _T1>
struct __less
{
diff --git a/libcxx/include/__algorithm/equal.h b/libcxx/include/__algorithm/equal.h
index e8d8a57c10882..19be7909b8244 100644
--- a/libcxx/include/__algorithm/equal.h
+++ b/libcxx/include/__algorithm/equal.h
@@ -11,9 +11,20 @@
#define _LIBCPP___ALGORITHM_EQUAL_H
#include <__algorithm/comp.h>
+#include <__algorithm/unwrap_iter.h>
#include <__config>
+#include <__functional/identity.h>
+#include <__functional/invoke.h>
#include <__iterator/distance.h>
#include <__iterator/iterator_traits.h>
+#include <__string/constexpr_c_functions.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_equality_comparable.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/predicate_traits.h>
+#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -22,23 +33,42 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
-_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
-equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate __pred) {
+_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_iter_impl(
+ _InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate& __pred) {
for (; __first1 != __last1; ++__first1, (void)++__first2)
if (!__pred(*__first1, *__first2))
return false;
return true;
}
+template <
+ class _Tp,
+ class _Up,
+ class _BinaryPredicate,
+ __enable_if_t<__is_trivial_equality_predicate<_BinaryPredicate, _Tp, _Up>::value && !is_volatile<_Tp>::value &&
+ !is_volatile<_Up>::value && __is_trivially_equality_comparable<_Tp, _Up>::value,
+ int> = 0>
+_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
+__equal_iter_impl(_Tp* __first1, _Tp* __last1, _Up* __first2, _BinaryPredicate&) {
+ return std::__constexpr_memcmp(__first1, __first2, (__last1 - __first1) * sizeof(_Tp)) == 0;
+}
+
+template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
+equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate __pred) {
+ return std::__equal_iter_impl(
+ std::__unwrap_iter(__first1), std::__unwrap_iter(__last1), std::__unwrap_iter(__first2), __pred);
+}
+
template <class _InputIterator1, class _InputIterator2>
-_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2) {
return std::equal(__first1, __last1, __first2, __equal_to());
}
#if _LIBCPP_STD_VER >= 14
template <class _BinaryPredicate, class _InputIterator1, class _InputIterator2>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
__equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2,
_BinaryPredicate __pred, input_iterator_tag, input_iterator_tag) {
for (; __first1 != __last1 && __first2 != __last2; ++__first1, (void)++__first2)
@@ -47,19 +77,52 @@ __equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __fir
return __first1 == __last1 && __first2 == __last2;
}
+template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Pred, class _Proj1, class _Proj2>
+_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_impl(
+ _Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, _Pred& __comp, _Proj1& __proj1, _Proj2& __proj2) {
+ while (__first1 != __last1 && __first2 != __last2) {
+ if (!std::__invoke(__comp, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
+ return false;
+ ++__first1;
+ ++__first2;
+ }
+ return __first1 == __last1 && __first2 == __last2;
+}
+
+template <class _Tp,
+ class _Up,
+ class _Pred,
+ class _Proj1,
+ class _Proj2,
+ __enable_if_t<__is_trivial_equality_predicate<_Pred, _Tp, _Up>::value && __is_identity<_Proj1>::value &&
+ __is_identity<_Proj2>::value && !is_volatile<_Tp>::value && !is_volatile<_Up>::value &&
+ __is_trivially_equality_comparable<_Tp, _Up>::value,
+ int> = 0>
+_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_impl(
+ _Tp* __first1, _Tp* __last1, _Up* __first2, _Up*, _Pred&, _Proj1&, _Proj2&) {
+ return std::__constexpr_memcmp(__first1, __first2, (__last1 - __first1) * sizeof(_Tp)) == 0;
+}
+
template <class _BinaryPredicate, class _RandomAccessIterator1, class _RandomAccessIterator2>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
__equal(_RandomAccessIterator1 __first1, _RandomAccessIterator1 __last1, _RandomAccessIterator2 __first2,
_RandomAccessIterator2 __last2, _BinaryPredicate __pred, random_access_iterator_tag,
random_access_iterator_tag) {
if (_VSTD::distance(__first1, __last1) != _VSTD::distance(__first2, __last2))
return false;
- return _VSTD::equal<_RandomAccessIterator1, _RandomAccessIterator2,
- _BinaryPredicate&>(__first1, __last1, __first2, __pred);
+ __identity __proj;
+ return std::__equal_impl(
+ std::__unwrap_iter(__first1),
+ std::__unwrap_iter(__last1),
+ std::__unwrap_iter(__first2),
+ std::__unwrap_iter(__last2),
+ __pred,
+ __proj,
+ __proj);
}
template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
-_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2,
_BinaryPredicate __pred) {
return _VSTD::__equal<_BinaryPredicate&>(
@@ -68,7 +131,7 @@ equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first
}
template <class _InputIterator1, class _InputIterator2>
-_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2) {
return std::__equal(
__first1,
diff --git a/libcxx/include/__algorithm/make_projected.h b/libcxx/include/__algorithm/make_projected.h
index d363310a14c9c..f4ca4c9d4a7a8 100644
--- a/libcxx/include/__algorithm/make_projected.h
+++ b/libcxx/include/__algorithm/make_projected.h
@@ -55,25 +55,12 @@ struct _ProjectedPred {
};
-template <class _Pred, class _Proj, class = void>
-struct __can_use_pristine_comp : false_type {};
-
-template <class _Pred, class _Proj>
-struct __can_use_pristine_comp<_Pred, _Proj, __enable_if_t<
- !is_member_pointer<typename decay<_Pred>::type>::value && (
-#if _LIBCPP_STD_VER >= 20
- is_same<typename decay<_Proj>::type, identity>::value ||
-#endif
- is_same<typename decay<_Proj>::type, __identity>::value
- )
-> > : true_type {};
-
-template <class _Pred, class _Proj>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static
-__enable_if_t<
- !__can_use_pristine_comp<_Pred, _Proj>::value,
- _ProjectedPred<_Pred, _Proj>
->
+template <class _Pred,
+ class _Proj,
+ __enable_if_t<!(!is_member_pointer<typename decay<_Pred>::type>::value &&
+ __is_identity<typename decay<_Proj>::type>::value),
+ int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static _ProjectedPred<_Pred, _Proj>
__make_projected(_Pred& __pred, _Proj& __proj) {
return _ProjectedPred<_Pred, _Proj>(__pred, __proj);
}
@@ -81,13 +68,12 @@ __make_projected(_Pred& __pred, _Proj& __proj) {
// Avoid creating the functor and just use the pristine comparator -- for certain algorithms, this would enable
// optimizations that rely on the type of the comparator. Additionally, this results in less layers of indirection in
// the call stack when the comparator is invoked, even in an unoptimized build.
-template <class _Pred, class _Proj>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static
-__enable_if_t<
- __can_use_pristine_comp<_Pred, _Proj>::value,
- _Pred&
->
-__make_projected(_Pred& __pred, _Proj&) {
+template <class _Pred,
+ class _Proj,
+ __enable_if_t<!is_member_pointer<typename decay<_Pred>::type>::value &&
+ __is_identity<typename decay<_Proj>::type>::value,
+ int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static _Pred& __make_projected(_Pred& __pred, _Proj&) {
return __pred;
}
@@ -102,7 +88,7 @@ namespace ranges {
template <class _Comp, class _Proj1, class _Proj2>
_LIBCPP_HIDE_FROM_ABI constexpr static
decltype(auto) __make_projected_comp(_Comp& __comp, _Proj1& __proj1, _Proj2& __proj2) {
- if constexpr (same_as<decay_t<_Proj1>, identity> && same_as<decay_t<_Proj2>, identity> &&
+ if constexpr (__is_identity<decay_t<_Proj1>>::value && __is_identity<decay_t<_Proj2>>::value &&
!is_member_pointer_v<decay_t<_Comp>>) {
// Avoid creating the lambda and just use the pristine comparator -- for certain algorithms, this would enable
// optimizations that rely on the type of the comparator.
diff --git a/libcxx/include/__algorithm/ranges_equal.h b/libcxx/include/__algorithm/ranges_equal.h
index 94f9d1d980abe..87544531c70bc 100644
--- a/libcxx/include/__algorithm/ranges_equal.h
+++ b/libcxx/include/__algorithm/ranges_equal.h
@@ -9,6 +9,8 @@
#ifndef _LIBCPP___ALGORITHM_RANGES_EQUAL_H
#define _LIBCPP___ALGORITHM_RANGES_EQUAL_H
+#include <__algorithm/equal.h>
+#include <__algorithm/unwrap_range.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
@@ -31,29 +33,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
namespace __equal {
struct __fn {
-private:
- template <class _Iter1, class _Sent1,
- class _Iter2, class _Sent2,
- class _Pred,
- class _Proj1,
- class _Proj2>
- _LIBCPP_HIDE_FROM_ABI constexpr static
- bool __equal_impl(_Iter1 __first1, _Sent1 __last1,
- _Iter2 __first2, _Sent2 __last2,
- _Pred& __pred,
- _Proj1& __proj1,
- _Proj2& __proj2) {
- while (__first1 != __last1 && __first2 != __last2) {
- if (!std::invoke(__pred, std::invoke(__proj1, *__first1), std::invoke(__proj2, *__first2)))
- return false;
- ++__first1;
- ++__first2;
- }
- return __first1 == __last1 && __first2 == __last2;
- }
-
-public:
-
template <input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
class _Pred = ranges::equal_to,
@@ -70,11 +49,13 @@ struct __fn {
if (__last1 - __first1 != __last2 - __first2)
return false;
}
- return __equal_impl(std::move(__first1), std::move(__last1),
- std::move(__first2), std::move(__last2),
- __pred,
- __proj1,
- __proj2);
+ auto __unwrapped1 = std::__unwrap_range(std::move(__first1), std::move(__last1));
+ auto __unwrapped2 = std::__unwrap_range(std::move(__first2), std::move(__last2));
+ return std::__equal_impl(std::move(__unwrapped1.first), std::move(__unwrapped1.second),
+ std::move(__unwrapped2.first), std::move(__unwrapped2.second),
+ __pred,
+ __proj1,
+ __proj2);
}
template <input_range _Range1,
@@ -93,11 +74,13 @@ struct __fn {
if (ranges::distance(__range1) != ranges::distance(__range2))
return false;
}
- return __equal_impl(ranges::begin(__range1), ranges::end(__range1),
- ranges::begin(__range2), ranges::end(__range2),
- __pred,
- __proj1,
- __proj2);
+ auto __unwrapped1 = std::__unwrap_range(ranges::begin(__range1), ranges::end(__range1));
+ auto __unwrapped2 = std::__unwrap_range(ranges::begin(__range2), ranges::end(__range2));
+ return std::__equal_impl(std::move(__unwrapped1.first), std::move(__unwrapped1.second),
+ std::move(__unwrapped2.first), std::move(__unwrapped2.second),
+ __pred,
+ __proj1,
+ __proj2);
return false;
}
};
diff --git a/libcxx/include/__algorithm/unwrap_iter.h b/libcxx/include/__algorithm/unwrap_iter.h
index 0f661e10a74d2..81f2942f00292 100644
--- a/libcxx/include/__algorithm/unwrap_iter.h
+++ b/libcxx/include/__algorithm/unwrap_iter.h
@@ -64,6 +64,14 @@ decltype(_Impl::__unwrap(std::declval<_Iter>())) __unwrap_iter(_Iter __i) _NOEXC
return _Impl::__unwrap(__i);
}
+// Allow input_iterators to be passed to __unwrap_iter (but not __rewrap_iter)
+#if _LIBCPP_STD_VER >= 20
+template <class _Iter, __enable_if_t<!is_copy_constructible<_Iter>::value, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI constexpr _Iter __unwrap_iter(_Iter __i) noexcept {
+ return __i;
+}
+#endif
+
template <class _OrigIter, class _Iter, class _Impl = __unwrap_iter_impl<_OrigIter> >
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _OrigIter __rewrap_iter(_OrigIter __orig_iter, _Iter __iter) _NOEXCEPT {
return _Impl::__rewrap(std::move(__orig_iter), std::move(__iter));
diff --git a/libcxx/include/__algorithm/unwrap_range.h b/libcxx/include/__algorithm/unwrap_range.h
index b6d316fd8cb28..2c75c8f49de93 100644
--- a/libcxx/include/__algorithm/unwrap_range.h
+++ b/libcxx/include/__algorithm/unwrap_range.h
@@ -43,7 +43,7 @@ struct __unwrap_range_impl {
}
_LIBCPP_HIDE_FROM_ABI static constexpr auto
- __rewrap(_Iter __orig_iter, decltype(std::__unwrap_iter(__orig_iter)) __iter)
+ __rewrap(_Iter __orig_iter, decltype(std::__unwrap_iter(std::move(__orig_iter))) __iter)
requires random_access_iterator<_Iter> && sized_sentinel_for<_Sent, _Iter>
{
return std::__rewrap_iter(std::move(__orig_iter), std::move(__iter));
diff --git a/libcxx/include/__functional/identity.h b/libcxx/include/__functional/identity.h
index 3edfb82f4bb54..421ba2f3275a1 100644
--- a/libcxx/include/__functional/identity.h
+++ b/libcxx/include/__functional/identity.h
@@ -11,6 +11,7 @@
#define _LIBCPP___FUNCTIONAL_IDENTITY_H
#include <__config>
+#include <__type_traits/integral_constant.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -19,6 +20,9 @@
_LIBCPP_BEGIN_NAMESPACE_STD
+template <class _Tp>
+struct __is_identity : false_type {};
+
struct __identity {
template <class _Tp>
_LIBCPP_NODISCARD _LIBCPP_CONSTEXPR _Tp&& operator()(_Tp&& __t) const _NOEXCEPT {
@@ -28,6 +32,9 @@ struct __identity {
using is_transparent = void;
};
+template <>
+struct __is_identity<__identity> : true_type {};
+
#if _LIBCPP_STD_VER >= 20
struct identity {
@@ -39,6 +46,10 @@ struct identity {
using is_transparent = void;
};
+
+template <>
+struct __is_identity<identity> : true_type {};
+
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/operations.h b/libcxx/include/__functional/operations.h
index 78c12603f2931..3d0c3641bf4a5 100644
--- a/libcxx/include/__functional/operations.h
+++ b/libcxx/include/__functional/operations.h
@@ -13,6 +13,8 @@
#include <__config>
#include <__functional/binary_function.h>
#include <__functional/unary_function.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/predicate_traits.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -341,6 +343,14 @@ struct _LIBCPP_TEMPLATE_VIS equal_to<void>
};
#endif
+template <class _Tp>
+struct __is_trivial_equality_predicate<equal_to<_Tp>, _Tp, _Tp> : true_type {};
+
+#if _LIBCPP_STD_VER >= 14
+template <class _Tp>
+struct __is_trivial_equality_predicate<equal_to<>, _Tp, _Tp> : true_type {};
+#endif
+
#if _LIBCPP_STD_VER >= 14
template <class _Tp = void>
#else
diff --git a/libcxx/include/__functional/ranges_operations.h b/libcxx/include/__functional/ranges_operations.h
index dbb35d92ed956..77b6a6ddbb5f8 100644
--- a/libcxx/include/__functional/ranges_operations.h
+++ b/libcxx/include/__functional/ranges_operations.h
@@ -13,6 +13,8 @@
#include <__concepts/equality_comparable.h>
#include <__concepts/totally_ordered.h>
#include <__config>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/predicate_traits.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -93,6 +95,9 @@ struct greater_equal {
} // namespace ranges
+template <class _Lhs, class _Rhs>
+struct __is_trivial_equality_predicate<ranges::equal_to, _Lhs, _Rhs> : true_type {};
+
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__string/constexpr_c_functions.h b/libcxx/include/__string/constexpr_c_functions.h
index 927b823997525..8b1abb75d59b2 100644
--- a/libcxx/include/__string/constexpr_c_functions.h
+++ b/libcxx/include/__string/constexpr_c_functions.h
@@ -11,6 +11,8 @@
#include <__config>
#include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_equality_comparable.h>
+#include <__type_traits/is_same.h>
#include <cstddef>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -33,21 +35,32 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_st
return __builtin_strlen(__str);
}
-template <class _Tp>
+template <class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
-__constexpr_memcmp(const _Tp* __lhs, const _Tp* __rhs, size_t __count) {
-#ifdef _LIBCPP_COMPILER_GCC
+__constexpr_memcmp(const _Tp* __lhs, const _Up* __rhs, size_t __count) {
+ static_assert(
+ __is_trivially_equality_comparable<_Tp, _Up>::value, "_Tp and _Up have to be trivially equality comparable");
+
if (__libcpp_is_constant_evaluated()) {
- for (; __count; --__count, ++__lhs, ++__rhs) {
+#ifdef _LIBCPP_COMPILER_CLANG_BASED
+ if (sizeof(_Tp) == 1 && !is_same<_Tp, bool>::value)
+ return __builtin_memcmp(__lhs, __rhs, __count);
+#endif
+
+ while (__count != 0) {
if (*__lhs < *__rhs)
return -1;
if (*__rhs < *__lhs)
return 1;
+
+ __count -= sizeof(_Tp);
+ ++__lhs;
+ ++__rhs;
}
return 0;
+ } else {
+ return __builtin_memcmp(__lhs, __rhs, __count);
}
-#endif
- return __builtin_memcmp(__lhs, __rhs, __count);
}
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const char*
diff --git a/libcxx/include/__type_traits/is_equality_comparable.h b/libcxx/include/__type_traits/is_equality_comparable.h
new file mode 100644
index 0000000000000..2293eee9d9d09
--- /dev/null
+++ b/libcxx/include/__type_traits/is_equality_comparable.h
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 _LIBCPP___TYPE_TRAITS_IS_EQUAILITY_COMPARABLE_H
+#define _LIBCPP___TYPE_TRAITS_IS_EQUAILITY_COMPARABLE_H
+
+#include <__config>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_integral.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/void_t.h>
+#include <__utility/declval.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp, class _Up, class = void>
+struct __is_equality_comparable : false_type {};
+
+template <class _Tp, class _Up>
+struct __is_equality_comparable<_Tp, _Up, __void_t<decltype(std::declval<_Tp>() == std::declval<_Up>())> > : true_type {
+};
+
+// A type is_trivially_equality_comparable if the expression `a == b` is equivalent to `std::memcmp(&a, &b, sizeof(T))`
+// (with `a` and `b` being of type `T`). There is no compiler built-in to check this, so we can only do this for known
+// types. In particular, these are the integral types and raw pointers.
+//
+// The following types are not trivially equality comparable:
+// floating-point types:
diff erent bit-patterns can compare equal. (e.g 0.0 and -0.0)
+// enums: The user is allowed to specialize operator== for enums
+// pointers that don't have the same type (ignoring cv-qualifiers): pointers to virtual bases are equality comparable,
+// but don't have the same bit-pattern. An exception to this is comparing to a void-pointer. There the bit-pattern is
+// always compared.
+
+template <class _Tp, class _Up>
+struct __is_trivially_equality_comparable
+ : integral_constant<bool,
+ __is_equality_comparable<_Tp, _Up>::value && is_integral<_Tp>::value &&
+ is_same<__remove_cv_t<_Tp>, __remove_cv_t<_Up> >::value> {};
+
+// TODO: Use is_pointer_inverconvertible_base_of
+template <class _Tp, class _Up>
+struct __is_trivially_equality_comparable<_Tp*, _Up*>
+ : integral_constant<
+ bool,
+ __is_equality_comparable<_Tp*, _Up*>::value &&
+ (is_same<__remove_cv_t<_Tp>, __remove_cv_t<_Up> >::value || is_void<_Tp>::value || is_void<_Up>::value)> {
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_IS_EQUAILITY_COMPARABLE_H
diff --git a/libcxx/include/__type_traits/predicate_traits.h b/libcxx/include/__type_traits/predicate_traits.h
new file mode 100644
index 0000000000000..872608e6ac3be
--- /dev/null
+++ b/libcxx/include/__type_traits/predicate_traits.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 _LIBCPP___TYPE_TRAITS_PREDICATE_TRAITS
+#define _LIBCPP___TYPE_TRAITS_PREDICATE_TRAITS
+
+#include <__config>
+#include <__type_traits/integral_constant.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Pred, class _Lhs, class _Rhs>
+struct __is_trivial_equality_predicate : false_type {};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_PREDICATE_TRAITS
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 10b2c4f3d9b89..1ffddbbe24ba6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -604,7 +604,10 @@ module std [system] {
module unique { private header "__algorithm/unique.h" }
module unique_copy { private header "__algorithm/unique_copy.h" }
module unwrap_iter { private header "__algorithm/unwrap_iter.h" }
- module unwrap_range { private header "__algorithm/unwrap_range.h" }
+ module unwrap_range {
+ private header "__algorithm/unwrap_range.h"
+ export utility.__utility.pair
+ }
module upper_bound { private header "__algorithm/upper_bound.h" }
}
}
@@ -1491,6 +1494,10 @@ module std [system] {
module is_destructible { private header "__type_traits/is_destructible.h" }
module is_empty { private header "__type_traits/is_empty.h" }
module is_enum { private header "__type_traits/is_enum.h" }
+ module is_equality_comparable {
+ private header "__type_traits/is_equality_comparable.h"
+ export integral_constant
+ }
module is_final { private header "__type_traits/is_final.h" }
module is_floating_point { private header "__type_traits/is_floating_point.h" }
module is_function { private header "__type_traits/is_function.h" }
@@ -1561,6 +1568,7 @@ module std [system] {
module nat { private header "__type_traits/nat.h" }
module negation { private header "__type_traits/negation.h" }
module noexcept_move_assign_container { private header "__type_traits/noexcept_move_assign_container.h" }
+ module predicate_traits { private header "__type_traits/predicate_traits.h" }
module promote { private header "__type_traits/promote.h" }
module rank { private header "__type_traits/rank.h" }
module remove_all_extents { private header "__type_traits/remove_all_extents.h" }
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 926448d05c3a2..0f611ce5eec75 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -651,6 +651,7 @@ END-SCRIPT
#include <__type_traits/is_destructible.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_destructible.h'}}
#include <__type_traits/is_empty.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_empty.h'}}
#include <__type_traits/is_enum.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_enum.h'}}
+#include <__type_traits/is_equality_comparable.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_equality_comparable.h'}}
#include <__type_traits/is_final.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_final.h'}}
#include <__type_traits/is_floating_point.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_floating_point.h'}}
#include <__type_traits/is_function.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_function.h'}}
@@ -715,6 +716,7 @@ END-SCRIPT
#include <__type_traits/nat.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/nat.h'}}
#include <__type_traits/negation.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/negation.h'}}
#include <__type_traits/noexcept_move_assign_container.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/noexcept_move_assign_container.h'}}
+#include <__type_traits/predicate_traits.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/predicate_traits.h'}}
#include <__type_traits/promote.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/promote.h'}}
#include <__type_traits/rank.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/rank.h'}}
#include <__type_traits/remove_all_extents.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/remove_all_extents.h'}}
diff --git a/libcxx/test/libcxx/type_traits/is_trivially_comparable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_trivially_comparable.compile.pass.cpp
new file mode 100644
index 0000000000000..c0aac60d5648e
--- /dev/null
+++ b/libcxx/test/libcxx/type_traits/is_trivially_comparable.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -Wno-private-header
+
+#include <__type_traits/is_equality_comparable.h>
+
+enum Enum : int {};
+enum class EnumClass : int {};
+
+static_assert(std::__is_trivially_equality_comparable<int, int>::value, "");
+static_assert(std::__is_trivially_equality_comparable<const int, int>::value, "");
+static_assert(std::__is_trivially_equality_comparable<int, const int>::value, "");
+
+static_assert(std::__is_trivially_equality_comparable<unsigned int, unsigned int>::value, "");
+static_assert(std::__is_trivially_equality_comparable<const unsigned int, unsigned int>::value, "");
+static_assert(!std::__is_trivially_equality_comparable<unsigned int, int>::value, "");
+
+static_assert(!std::__is_trivially_equality_comparable<long, int>::value, "");
+static_assert(!std::__is_trivially_equality_comparable<int, long>::value, "");
+
+static_assert(std::__is_trivially_equality_comparable<int*, int*>::value, "");
+static_assert(std::__is_trivially_equality_comparable<int*, void*>::value, "");
+static_assert(!std::__is_trivially_equality_comparable<int*, long*>::value, "");
+
+static_assert(!std::__is_trivially_equality_comparable<Enum, int>::value, "");
+static_assert(!std::__is_trivially_equality_comparable<EnumClass, int>::value, "");
+
+static_assert(!std::__is_trivially_equality_comparable<float, int>::value, "");
+static_assert(!std::__is_trivially_equality_comparable<double, long long>::value, "");
+
+static_assert(!std::__is_trivially_equality_comparable<float, int>::value, "");
+
+static_assert(!std::__is_trivially_equality_comparable<float, float>::value, "");
+static_assert(!std::__is_trivially_equality_comparable<double, double>::value, "");
+static_assert(!std::__is_trivially_equality_comparable<long double, long double>::value, "");
+
+struct S {
+ char c;
+};
+
+struct S2 {
+ char c;
+};
+
+struct VirtualBase : virtual S {};
+struct NonVirtualBase : S, S2 {};
+
+static_assert(!std::__is_trivially_equality_comparable<S*, VirtualBase*>::value, "");
+static_assert(!std::__is_trivially_equality_comparable<S2*, VirtualBase*>::value, "");
+
+// This is trivially_equality_comparable, but we can't detect it currently
+static_assert(!std::__is_trivially_equality_comparable<S*, NonVirtualBase*>::value, "");
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/equal.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/equal.pass.cpp
index dfa425d974df9..c182feb8df834 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/equal.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/equal.pass.cpp
@@ -20,68 +20,119 @@
#include <algorithm>
#include <cassert>
+#include <functional>
-#include "test_macros.h"
#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class UnderlyingType, class Iter1>
+struct Test {
+ template <class Iter2>
+ TEST_CONSTEXPR_CXX20 void operator()() {
+ UnderlyingType a[] = {0, 1, 2, 3, 4, 5};
+ const unsigned s = sizeof(a) / sizeof(a[0]);
+ UnderlyingType b[s] = {0, 1, 2, 5, 4, 5};
+
+ assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a)));
+ assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b)));
+
+#if TEST_STD_VER >= 14
+ assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a), std::equal_to<>()));
+ assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b), std::equal_to<>()));
+
+ assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s)));
+ assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s - 1)));
+ assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b), Iter2(b + s)));
-template <class Iter1, class Iter2 = Iter1>
-void test_equal() {
- int a[] = {0, 1, 2, 3, 4, 5};
- const unsigned s = sizeof(a) / sizeof(a[0]);
- int b[s] = {0, 1, 2, 5, 4, 5};
+ assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s), std::equal_to<>()));
+ assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s - 1), std::equal_to<>()));
+ assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b), Iter2(b + s), std::equal_to<>()));
+#endif
+ }
+};
- assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a)));
- assert(std::equal(Iter2(a), Iter2(a + s), Iter1(a)));
- assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b)));
+struct TestNarrowingEqualTo {
+ template <class UnderlyingType>
+ TEST_CONSTEXPR_CXX20 void operator()() {
+ UnderlyingType a[] = {
+ UnderlyingType(0x1000),
+ UnderlyingType(0x1001),
+ UnderlyingType(0x1002),
+ UnderlyingType(0x1003),
+ UnderlyingType(0x1004)};
+ UnderlyingType b[] = {
+ UnderlyingType(0x1600),
+ UnderlyingType(0x1601),
+ UnderlyingType(0x1602),
+ UnderlyingType(0x1603),
+ UnderlyingType(0x1604)};
+ assert(std::equal(a, a + 5, b, std::equal_to<char>()));
#if TEST_STD_VER >= 14
- assert(std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s)));
- assert(std::equal(Iter2(a), Iter2(a + s), Iter1(a), Iter1(a + s)));
- assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(a), Iter2(a + s - 1)));
- assert(!std::equal(Iter1(a), Iter1(a + s), Iter2(b), Iter2(b + s)));
+ assert(std::equal(a, a + 5, b, b + 5, std::equal_to<char>()));
#endif
+ }
+};
+
+template <class UnderlyingType, class TypeList>
+struct TestIter2 {
+ template <class Iter1>
+ TEST_CONSTEXPR_CXX20 void operator()() {
+ meta::for_each(TypeList(), Test<UnderlyingType, Iter1>());
+ }
+};
+
+struct AddressCompare {
+ int i = 0;
+ TEST_CONSTEXPR_CXX20 AddressCompare(int) {}
+
+ operator char() { return static_cast<char>(i); }
+
+ friend TEST_CONSTEXPR_CXX20 bool operator==(const AddressCompare& lhs, const AddressCompare& rhs) {
+ return &lhs == &rhs;
+ }
+
+ friend TEST_CONSTEXPR_CXX20 bool operator!=(const AddressCompare& lhs, const AddressCompare& rhs) {
+ return &lhs != &rhs;
+ }
+};
+
+TEST_CONSTEXPR_CXX20 bool test() {
+ meta::for_each(meta::cpp17_input_iterator_list<int*>(), TestIter2<int, meta::cpp17_input_iterator_list<int*> >());
+ meta::for_each(meta::cpp17_input_iterator_list<char*>(), TestIter2<char, meta::cpp17_input_iterator_list<char*> >());
+ meta::for_each(meta::cpp17_input_iterator_list<AddressCompare*>(),
+ TestIter2<AddressCompare, meta::cpp17_input_iterator_list<AddressCompare*> >());
+
+ meta::for_each(meta::integral_types(), TestNarrowingEqualTo());
+
+ return true;
}
-#if TEST_STD_VER > 17
-TEST_CONSTEXPR bool test_constexpr() {
- int ia[] = {1, 3, 6, 7};
- int ib[] = {1, 3};
- int ic[] = {1, 3, 5, 7};
- typedef cpp17_input_iterator<int*> II;
- typedef bidirectional_iterator<int*> BI;
-
- return !std::equal(std::begin(ia), std::end(ia), std::begin(ic))
- && !std::equal(std::begin(ia), std::end(ia), std::begin(ic), std::end(ic))
- && std::equal(std::begin(ib), std::end(ib), std::begin(ic))
- && !std::equal(std::begin(ib), std::end(ib), std::begin(ic), std::end(ic))
-
- && std::equal(II(std::begin(ib)), II(std::end(ib)), II(std::begin(ic)))
- && !std::equal(BI(std::begin(ib)), BI(std::end(ib)), BI(std::begin(ic)), BI(std::end(ic)))
- ;
- }
+struct Base {};
+struct Derived : virtual Base {};
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 20
+ static_assert(test());
#endif
+ meta::for_each(meta::as_pointers<meta::cv_qualified_versions<int> >(),
+ TestIter2<int, meta::as_pointers<meta::cv_qualified_versions<int> > >());
+ meta::for_each(meta::as_pointers<meta::cv_qualified_versions<char> >(),
+ TestIter2<char, meta::as_pointers<meta::cv_qualified_versions<char> > >());
-int main(int, char**)
-{
- test_equal<cpp17_input_iterator<const int*> >();
- test_equal<random_access_iterator<const int*> >();
-
- // Test all combinations of cv-qualifiers:
- test_equal<int*>();
- test_equal<int*, const int*>();
- test_equal<int*, volatile int*>();
- test_equal<int*, const volatile int*>();
- test_equal<const int*>();
- test_equal<const int*, volatile int*>();
- test_equal<const int*, const volatile int*>();
- test_equal<volatile int*>();
- test_equal<volatile int*, const volatile int*>();
- test_equal<const volatile int*>();
-
-#if TEST_STD_VER > 17
- static_assert(test_constexpr());
+ {
+ Derived d;
+ Derived* a[] = {&d, nullptr};
+ Base* b[] = {&d, nullptr};
+
+ assert(std::equal(a, a + 2, b));
+#if TEST_STD_VER >= 14
+ assert(std::equal(a, a + 2, b, b + 2));
#endif
+ }
return 0;
}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/ranges.equal.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/ranges.equal.pass.cpp
index 9e9f82ef904b1..828f75ea11d35 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/ranges.equal.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.equal/ranges.equal.pass.cpp
@@ -88,6 +88,21 @@ constexpr void test_iterators() {
}
}
+ { // check that false is returned for non-equal ranges
+ {
+ int a[] = {1, 2, 3, 4};
+ int b[] = {1, 2, 4, 4};
+ assert(!std::ranges::equal(Iter1(a), Sent1(Iter1(a + 4)), Iter2(b), Sent2(Iter2(b + 4))));
+ }
+ {
+ int a[] = {1, 2, 3, 4};
+ int b[] = {1, 2, 4, 4};
+ auto range1 = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 4)));
+ auto range2 = std::ranges::subrange(Iter2(b), Sent2(Iter2(b + 4)));
+ assert(!std::ranges::equal(range1, range2));
+ }
+ }
+
{ // check that the predicate is used (return true)
{
int a[] = {1, 2, 3, 4};
diff --git a/libcxx/test/std/algorithms/robust_against_adl.compile.pass.cpp b/libcxx/test/std/algorithms/robust_against_adl.compile.pass.cpp
index 2d5ece6cb6650..1411796d65963 100644
--- a/libcxx/test/std/algorithms/robust_against_adl.compile.pass.cpp
+++ b/libcxx/test/std/algorithms/robust_against_adl.compile.pass.cpp
@@ -8,6 +8,11 @@
// <algorithm>
+// https://buildkite.com/llvm-project/libcxx-ci/builds/15823#0184fc0b-d56b-4774-9e1d-35fe24e09e37
+// It seems like the CI gcc version is buggy. I can't reproduce the failure on my system or on
+// godbolt (https://godbolt.org/z/rsPv8e8fn).
+// UNSUPPORTED: gcc-12
+
#include <algorithm>
#include <cstddef>
#include <functional>
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 588a24f37ac74..ab64619143ec2 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1418,23 +1418,33 @@ template <std::ranges::input_range R>
requires std::ranges::viewable_range<R&&>
ProxyRange(R&&) -> ProxyRange<std::views::all_t<R&&>>;
+#endif // TEST_STD_VER > 17
+
namespace meta {
template <class Ptr>
-using random_access_iterator_list = type_list<Ptr, contiguous_iterator<Ptr>, random_access_iterator<Ptr>>;
+using random_access_iterator_list =
+ type_list<Ptr,
+#if TEST_STD_VER >= 20
+ contiguous_iterator<Ptr>,
+#endif
+ random_access_iterator<Ptr> >;
template <class Ptr>
using bidirectional_iterator_list =
- concatenate_t<random_access_iterator_list<Ptr>, type_list<bidirectional_iterator<Ptr>>>;
+ concatenate_t<random_access_iterator_list<Ptr>, type_list<bidirectional_iterator<Ptr> > >;
+
+template <class Ptr>
+using forward_iterator_list = concatenate_t<bidirectional_iterator_list<Ptr>, type_list<forward_iterator<Ptr> > >;
template <class Ptr>
-using forward_iterator_list = concatenate_t<bidirectional_iterator_list<Ptr>, type_list<forward_iterator<Ptr>>>;
+using cpp17_input_iterator_list = concatenate_t<forward_iterator_list<Ptr>, type_list<cpp17_input_iterator<Ptr> > >;
+#if TEST_STD_VER >= 20
template <class Ptr>
using cpp20_input_iterator_list =
concatenate_t<forward_iterator_list<Ptr>, type_list<cpp20_input_iterator<Ptr>, cpp17_input_iterator<Ptr>>>;
-
+#endif
} // namespace meta
-#endif // TEST_STD_VER > 17
#endif // SUPPORT_TEST_ITERATORS_H
diff --git a/libcxx/test/support/type_algorithms.h b/libcxx/test/support/type_algorithms.h
index 823978b54376c..a066e54ce50cb 100644
--- a/libcxx/test/support/type_algorithms.h
+++ b/libcxx/test/support/type_algorithms.h
@@ -100,6 +100,20 @@ using integral_types = concatenate_t<character_types, signed_integer_types, unsi
using floating_point_types = type_list<float, double, long double>;
using arithmetic_types = concatenate_t<integral_types, floating_point_types>;
+
+template <class T>
+using cv_qualified_versions = type_list<T, const T, volatile T, const volatile T>;
+
+template <class T>
+struct type_list_as_pointers;
+
+template <class... Types>
+struct type_list_as_pointers<type_list<Types...> > {
+ using type = type_list<Types*...>;
+};
+
+template <class T>
+using as_pointers = typename type_list_as_pointers<T>::type;
} // namespace meta
#endif // TEST_SUPPORT_TYPE_ALGORITHMS_H
More information about the libcxx-commits
mailing list