[libcxx-commits] [libcxx] [libc++] Fix several double-moves in the code base (PR #104616)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Aug 16 10:07:39 PDT 2024
https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/104616
This patch hardens the "test iterators" we use to test algorithms by ensuring that they don't get double-moved. As a result of this hardening, the tests started reporting multiple failures where we would double-move iterators, which are being fixed in this patch.
In particular:
- Fixed a double-move in pstl.partition
- Add coverage for begin()/end() in subrange tests
- Fix tests for ranges::ends_with and ranges::contains, which were incorrectly calling begin() twice on the same subrange containing non-copyable input iterators.
Fixes #100709
>From eaabc75e361a6049bdcf82992da8ac81f4bcd575 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 16 Aug 2024 11:35:50 -0400
Subject: [PATCH] [libc++] Fix several double-moves in the code base
This patch hardens the "test iterators" we use to test algorithms by
ensuring that they don't get double-moved. As a result of this hardening,
the tests started reporting multiple failures where we would double-move
iterators, which are being fixed in this patch.
In particular:
- Fixed a double-move in pstl.partition
- Add coverage for begin()/end() in subrange tests
- Fix tests for ranges::ends_with and ranges::contains, which were incorrectly
calling begin() twice on the same subrange containing non-copyable input
iterators.
Fixes #100709
---
libcxx/include/__algorithm/partition.h | 2 +-
libcxx/include/__pstl/backends/default.h | 2 +-
.../alg.partitions/partition.pass.cpp | 2 +
.../alg.contains/ranges.contains.pass.cpp | 38 +--
.../alg.ends_with/ranges.ends_with.pass.cpp | 260 ++++++++++--------
.../range.subrange/begin_end.pass.cpp | 54 ++++
libcxx/test/support/double_move_tracker.h | 43 +++
libcxx/test/support/test_iterators.h | 63 +++--
8 files changed, 314 insertions(+), 150 deletions(-)
create mode 100644 libcxx/test/std/ranges/range.utility/range.subrange/begin_end.pass.cpp
create mode 100644 libcxx/test/support/double_move_tracker.h
diff --git a/libcxx/include/__algorithm/partition.h b/libcxx/include/__algorithm/partition.h
index 824e49b9ec2149..bd0dccfaf92abe 100644
--- a/libcxx/include/__algorithm/partition.h
+++ b/libcxx/include/__algorithm/partition.h
@@ -29,7 +29,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<_ForwardIterator, _Forw
__partition_impl(_ForwardIterator __first, _Sentinel __last, _Predicate __pred, forward_iterator_tag) {
while (true) {
if (__first == __last)
- return std::make_pair(std::move(__first), std::move(__first));
+ return std::make_pair(__first, __first);
if (!__pred(*__first))
break;
++__first;
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 61a128805f8549..b655da51fe340b 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -163,7 +163,7 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<bool>
operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Pred&& __pred) const noexcept {
using _FindIfNot = __dispatch<__find_if_not, __current_configuration, _ExecutionPolicy>;
- auto __maybe_first = _FindIfNot()(__policy, std::move(__first), std::move(__last), __pred);
+ auto __maybe_first = _FindIfNot()(__policy, std::move(__first), __last, __pred);
if (__maybe_first == nullopt)
return nullopt;
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/partition.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/partition.pass.cpp
index 3eaeaa432c490f..87beba183a2842 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/partition.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/partition.pass.cpp
@@ -98,11 +98,13 @@ test()
int main(int, char**)
{
+ test<forward_iterator<int*> >();
test<bidirectional_iterator<int*> >();
test<random_access_iterator<int*> >();
test<int*>();
#if TEST_STD_VER >= 20
+ static_assert(test<forward_iterator<int*>>());
static_assert(test<bidirectional_iterator<int*>>());
static_assert(test<random_access_iterator<int*>>());
static_assert(test<int*>());
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.contains/ranges.contains.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.contains/ranges.contains.pass.cpp
index f710ca2c319dd6..5326985b3527e7 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.contains/ranges.contains.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.contains/ranges.contains.pass.cpp
@@ -65,12 +65,13 @@ constexpr void test_iterators() {
using ValueT = std::iter_value_t<Iter>;
{ // simple tests
ValueT a[] = {1, 2, 3, 4, 5, 6};
- auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 6)));
{
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 6)));
std::same_as<bool> decltype(auto) ret = std::ranges::contains(whole.begin(), whole.end(), 3);
assert(ret);
}
{
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 6)));
std::same_as<bool> decltype(auto) ret = std::ranges::contains(whole, 3);
assert(ret);
}
@@ -78,65 +79,70 @@ constexpr void test_iterators() {
{ // check that a range with a single element works
ValueT a[] = {32};
- auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 1)));
{
- bool ret = std::ranges::contains(whole.begin(), whole.end(), 32);
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 1)));
+ bool ret = std::ranges::contains(whole.begin(), whole.end(), 32);
assert(ret);
}
{
- bool ret = std::ranges::contains(whole, 32);
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 1)));
+ bool ret = std::ranges::contains(whole, 32);
assert(ret);
}
}
{ // check that an empty range works
std::array<ValueT, 0> a = {};
- auto whole = std::ranges::subrange(Iter(a.data()), Sent(Iter(a.data())));
{
- bool ret = std::ranges::contains(whole.begin(), whole.end(), 1);
+ auto whole = std::ranges::subrange(Iter(a.data()), Sent(Iter(a.data())));
+ bool ret = std::ranges::contains(whole.begin(), whole.end(), 1);
assert(!ret);
}
{
- bool ret = std::ranges::contains(whole, 1);
+ auto whole = std::ranges::subrange(Iter(a.data()), Sent(Iter(a.data())));
+ bool ret = std::ranges::contains(whole, 1);
assert(!ret);
}
}
{ // check that the first element matches
ValueT a[] = {32, 3, 2, 1, 0, 23, 21, 9, 40, 100};
- auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 10)));
{
- bool ret = std::ranges::contains(whole.begin(), whole.end(), 32);
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 10)));
+ bool ret = std::ranges::contains(whole.begin(), whole.end(), 32);
assert(ret);
}
{
- bool ret = std::ranges::contains(whole, 32);
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 10)));
+ bool ret = std::ranges::contains(whole, 32);
assert(ret);
}
}
{ // check that the last element matches
ValueT a[] = {3, 22, 1, 43, 99, 0, 56, 100, 32};
- auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
{
- bool ret = std::ranges::contains(whole.begin(), whole.end(), 32);
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
+ bool ret = std::ranges::contains(whole.begin(), whole.end(), 32);
assert(ret);
}
{
- bool ret = std::ranges::contains(whole, 32);
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
+ bool ret = std::ranges::contains(whole, 32);
assert(ret);
}
}
{ // no match
ValueT a[] = {13, 1, 21, 4, 5};
- auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 5)));
{
- bool ret = std::ranges::contains(whole.begin(), whole.end(), 10);
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 5)));
+ bool ret = std::ranges::contains(whole.begin(), whole.end(), 10);
assert(!ret);
}
{
- bool ret = std::ranges::contains(whole, 10);
+ auto whole = std::ranges::subrange(Iter(a), Sent(Iter(a + 5)));
+ bool ret = std::ranges::contains(whole, 10);
assert(!ret);
}
}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.ends_with/ranges.ends_with.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.ends_with/ranges.ends_with.pass.cpp
index 685bd692422ac8..199e6a786e5ba3 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.ends_with/ranges.ends_with.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.ends_with/ranges.ends_with.pass.cpp
@@ -49,7 +49,8 @@ static_assert(!HasEndsWithIt<int*, int*, int*, SentinelForNotWeaklyEqualityCompa
template <class Range1, class Range2 = UncheckedRange<int*>>
concept HasEndsWithR = requires(Range1&& range1, Range2&& range2) {
- std::ranges::ends_with(std::forward<Range1>(range1), std::forward<Range2>(range2)); };
+ std::ranges::ends_with(std::forward<Range1>(range1), std::forward<Range2>(range2));
+};
static_assert(HasEndsWithR<UncheckedRange<int*>>);
static_assert(!HasEndsWithR<ForwardRangeNotDerivedFrom>);
@@ -61,19 +62,21 @@ static_assert(!HasEndsWithR<UncheckedRange<int*>, UncheckedRange<int**>>); // no
static_assert(!HasEndsWithR<UncheckedRange<int*>, ForwardRangeNotDerivedFrom>);
static_assert(!HasEndsWithR<UncheckedRange<int*>, ForwardRangeNotSentinelSemiregular>);
-// clang-format off
template <class Iter1, class Sent1 = Iter1, class Iter2, class Sent2 = Iter2>
constexpr void test_iterators() {
{ // simple tests
- int a[] = {1, 2, 3, 4, 5, 6};
- int p[] = {4, 5, 6};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
+ int a[] = {1, 2, 3, 4, 5, 6};
+ int p[] = {4, 5, 6};
{
- [[maybe_unused]] std::same_as<bool> decltype(auto) ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
+ [[maybe_unused]] std::same_as<bool> decltype(auto) ret =
+ std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
assert(ret);
}
{
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
[[maybe_unused]] std::same_as<bool> decltype(auto) ret = std::ranges::ends_with(whole, suffix);
assert(ret);
}
@@ -82,14 +85,16 @@ constexpr void test_iterators() {
{ // suffix doesn't match
int a[] = {1, 2, 3, 4, 5, 6};
int p[] = {1, 2, 3};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
{
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
assert(!ret);
}
{
- bool ret = std::ranges::ends_with(whole, suffix);
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
+ bool ret = std::ranges::ends_with(whole, suffix);
assert(!ret);
}
}
@@ -97,14 +102,16 @@ constexpr void test_iterators() {
{ // range consists of just one element
int a[] = {1};
int p[] = {1};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 1)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 1)));
{
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 1)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 1)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
assert(ret);
}
{
- bool ret = std::ranges::ends_with(whole, suffix);
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 1)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 1)));
+ bool ret = std::ranges::ends_with(whole, suffix);
assert(ret);
}
}
@@ -112,14 +119,16 @@ constexpr void test_iterators() {
{ // suffix consists of just one element
int a[] = {5, 1, 2, 4, 3};
int p[] = {3};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 5)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 1)));
{
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 5)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 1)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
assert(ret);
}
{
- bool ret = std::ranges::ends_with(whole, suffix);
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 5)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 1)));
+ bool ret = std::ranges::ends_with(whole, suffix);
assert(ret);
}
}
@@ -127,14 +136,16 @@ constexpr void test_iterators() {
{ // range and suffix are identical
int a[] = {1, 2, 3, 4, 5, 6};
int p[] = {1, 2, 3, 4, 5, 6};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 6)));
{
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 6)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
assert(ret);
}
{
- bool ret = std::ranges::ends_with(whole, suffix);
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 6)));
+ bool ret = std::ranges::ends_with(whole, suffix);
assert(ret);
}
}
@@ -142,111 +153,128 @@ constexpr void test_iterators() {
{ // suffix is longer than range
int a[] = {3, 4, 5, 6, 7, 8};
int p[] = {1, 2, 3, 4, 5, 6, 7, 8};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 8)));
{
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 8)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
assert(!ret);
}
{
- bool ret = std::ranges::ends_with(whole, suffix);
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 8)));
+ bool ret = std::ranges::ends_with(whole, suffix);
assert(!ret);
}
- }
+ }
- { // suffix has zero length
- int a[] = {1, 2, 3, 4, 5, 6};
- std::array<int, 0> p = {};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
- auto suffix = std::ranges::subrange(Iter2(p.data()), Sent2(Iter2(p.data())));
- {
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
- assert(ret);
- }
- {
- bool ret = std::ranges::ends_with(whole, suffix);
- assert(ret);
- }
- }
+ { // suffix has zero length
+ int a[] = {1, 2, 3, 4, 5, 6};
+ std::array<int, 0> p = {};
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p.data()), Sent2(Iter2(p.data())));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ assert(ret);
+ }
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p.data()), Sent2(Iter2(p.data())));
+ bool ret = std::ranges::ends_with(whole, suffix);
+ assert(ret);
+ }
+ }
- { // range has zero length
- std::array<int, 0> a = {};
- int p[] = {1, 2, 3, 4, 5, 6, 7, 8};
- auto whole = std::ranges::subrange(Iter1(a.data()), Sent1(Iter1(a.data())));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 8)));
- {
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
- assert(!ret);
- }
- {
- bool ret = std::ranges::ends_with(whole, suffix);
- assert(!ret);
- }
- }
+ { // range has zero length
+ std::array<int, 0> a = {};
+ int p[] = {1, 2, 3, 4, 5, 6, 7, 8};
+ {
+ auto whole = std::ranges::subrange(Iter1(a.data()), Sent1(Iter1(a.data())));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 8)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ assert(!ret);
+ }
+ {
+ auto whole = std::ranges::subrange(Iter1(a.data()), Sent1(Iter1(a.data())));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 8)));
+ bool ret = std::ranges::ends_with(whole, suffix);
+ assert(!ret);
+ }
+ }
- { // subarray
- int a[] = {0, 3, 5, 10, 7, 3, 5, 89, 3, 5, 2, 1, 8, 6};
- int p[] = {3, 5};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 13)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 2)));
- {
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
- assert(!ret);
- }
- {
- bool ret = std::ranges::ends_with(whole, suffix);
- assert(!ret);
- }
- }
+ { // subarray
+ int a[] = {0, 3, 5, 10, 7, 3, 5, 89, 3, 5, 2, 1, 8, 6};
+ int p[] = {3, 5};
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 13)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 2)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ assert(!ret);
+ }
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 13)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 2)));
+ bool ret = std::ranges::ends_with(whole, suffix);
+ assert(!ret);
+ }
+ }
- { // repeated suffix
- int a[] = {8, 6, 3, 5, 1, 2};
- int p[] = {1, 2, 1, 2};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 4)));
- {
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
- assert(!ret);
- }
- {
- bool ret = std::ranges::ends_with(whole, suffix);
- assert(!ret);
- }
- }
+ { // repeated suffix
+ int a[] = {8, 6, 3, 5, 1, 2};
+ int p[] = {1, 2, 1, 2};
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 4)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end());
+ assert(!ret);
+ }
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 4)));
+ bool ret = std::ranges::ends_with(whole, suffix);
+ assert(!ret);
+ }
+ }
- { // check that the predicate is used
- int a[] = {5, 1, 3, 2, 7};
- int p[] = {-2, -7};
- auto pred = [](int l, int r) { return l * -1 == r; };
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 5)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 2)));
- {
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end(), pred);
- assert(ret);
- }
- {
- bool ret = std::ranges::ends_with(whole, suffix, pred);
- assert(ret);
- }
- }
+ { // check that the predicate is used
+ int a[] = {5, 1, 3, 2, 7};
+ int p[] = {-2, -7};
+ auto pred = [](int l, int r) { return l * -1 == r; };
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 5)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 2)));
+ bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end(), pred);
+ assert(ret);
+ }
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 5)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 2)));
+ bool ret = std::ranges::ends_with(whole, suffix, pred);
+ assert(ret);
+ }
+ }
- { // check that the projections are used
- int a[] = {1, 3, 15, 1, 2, 1};
- int p[] = {2, 1, 2};
- auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
- auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
- {
- bool ret = std::ranges::ends_with(whole.begin(), whole.end(), suffix.begin(), suffix.end(), {},
- [](int i) { return i - 3; },
- [](int i) { return i * -1; });
- assert(ret);
- }
- {
- bool ret = std::ranges::ends_with(whole, suffix, {},
- [](int i) { return i - 3; },
- [](int i) { return i * -1; });
- assert(ret);
- }
+ { // check that the projections are used
+ int a[] = {1, 3, 15, 1, 2, 1};
+ int p[] = {2, 1, 2};
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
+ bool ret = std::ranges::ends_with(
+ whole.begin(),
+ whole.end(),
+ suffix.begin(),
+ suffix.end(),
+ {},
+ [](int i) { return i - 3; },
+ [](int i) { return i * -1; });
+ assert(ret);
+ }
+ {
+ auto whole = std::ranges::subrange(Iter1(a), Sent1(Iter1(a + 6)));
+ auto suffix = std::ranges::subrange(Iter2(p), Sent2(Iter2(p + 3)));
+ bool ret = std::ranges::ends_with(whole, suffix, {}, [](int i) { return i - 3; }, [](int i) { return i * -1; });
+ assert(ret);
+ }
}
}
diff --git a/libcxx/test/std/ranges/range.utility/range.subrange/begin_end.pass.cpp b/libcxx/test/std/ranges/range.utility/range.subrange/begin_end.pass.cpp
new file mode 100644
index 00000000000000..db3f3722422d6b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.utility/range.subrange/begin_end.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// class std::ranges::subrange;
+
+#include <ranges>
+#include <cassert>
+#include <iterator>
+
+#include "test_iterators.h"
+#include "type_algorithms.h"
+
+template <class Iterator, class Sentinel>
+constexpr bool test() {
+ using Subrange = std::ranges::subrange<Iterator, Sentinel>;
+
+ // Empty subrange
+ {
+ int array[10] = {};
+ Subrange rng(Iterator(std::begin(array)), Sentinel(Iterator(std::begin(array))));
+ std::same_as<Iterator> decltype(auto) beg = rng.begin();
+ std::same_as<Sentinel> decltype(auto) end = rng.end();
+ assert(beg == Iterator(std::begin(array)));
+ assert(end == Iterator(std::begin(array)));
+ }
+
+ // Non-empty subrange
+ {
+ int array[10] = {};
+ Subrange rng(Iterator(std::begin(array)), Sentinel(Iterator(std::end(array) - 3)));
+ std::same_as<Iterator> decltype(auto) beg = rng.begin();
+ std::same_as<Sentinel> decltype(auto) end = rng.end();
+ assert(beg == Iterator(std::begin(array)));
+ assert(end == Iterator(std::end(array) - 3));
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<int*>{}, []<class Iterator> {
+ test<Iterator, sentinel_wrapper<Iterator>>();
+ static_assert(test<Iterator, sentinel_wrapper<Iterator>>());
+ });
+
+ return 0;
+}
diff --git a/libcxx/test/support/double_move_tracker.h b/libcxx/test/support/double_move_tracker.h
new file mode 100644
index 00000000000000..fac575d9df08fb
--- /dev/null
+++ b/libcxx/test/support/double_move_tracker.h
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_DOUBLE_MOVE_TRACKER_H
+#define TEST_SUPPORT_DOUBLE_MOVE_TRACKER_H
+
+#include <cassert>
+
+#include "test_macros.h"
+
+namespace support {
+
+struct double_move_tracker {
+ TEST_CONSTEXPR double_move_tracker() : moved_from_(false) {}
+
+ double_move_tracker(double_move_tracker const&) = default;
+
+ TEST_CONSTEXPR_CXX14 double_move_tracker(double_move_tracker&& other) : moved_from_(false) {
+ assert(!other.moved_from_);
+ other.moved_from_ = true;
+ }
+
+ double_move_tracker& operator=(double_move_tracker const&) = default;
+
+ TEST_CONSTEXPR_CXX14 double_move_tracker& operator=(double_move_tracker&& other) {
+ assert(!other.moved_from_);
+ other.moved_from_ = true;
+ moved_from_ = false;
+ return *this;
+ }
+
+private:
+ bool moved_from_;
+};
+
+} // namespace support
+
+#endif // TEST_SUPPORT_DOUBLE_MOVE_TRACKER_H
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 44bd4a597539de..419f1d86730b85 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -18,6 +18,7 @@
#include <type_traits>
#include <utility>
+#include "double_move_tracker.h"
#include "test_macros.h"
#include "type_algorithms.h"
@@ -28,6 +29,7 @@ template <class It>
class cpp17_output_iterator
{
It it_;
+ support::double_move_tracker tracker_;
template <class U> friend class cpp17_output_iterator;
public:
@@ -40,10 +42,13 @@ class cpp17_output_iterator
TEST_CONSTEXPR explicit cpp17_output_iterator(It it) : it_(std::move(it)) {}
template <class U>
- TEST_CONSTEXPR cpp17_output_iterator(const cpp17_output_iterator<U>& u) : it_(u.it_) {}
+ TEST_CONSTEXPR cpp17_output_iterator(const cpp17_output_iterator<U>& u) : it_(u.it_), tracker_(u.tracker_) {}
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
- TEST_CONSTEXPR_CXX14 cpp17_output_iterator(cpp17_output_iterator<U>&& u) : it_(u.it_) { u.it_ = U(); }
+ TEST_CONSTEXPR_CXX14 cpp17_output_iterator(cpp17_output_iterator<U>&& u)
+ : it_(std::move(u.it_)), tracker_(std::move(u.tracker_)) {
+ u.it_ = U();
+ }
TEST_CONSTEXPR reference operator*() const {return *it_;}
@@ -71,6 +76,7 @@ class cpp17_input_iterator
{
typedef std::iterator_traits<ItTraits> Traits;
It it_;
+ support::double_move_tracker tracker_;
template <class U, class T> friend class cpp17_input_iterator;
public:
@@ -83,10 +89,13 @@ class cpp17_input_iterator
TEST_CONSTEXPR explicit cpp17_input_iterator(It it) : it_(it) {}
template <class U, class T>
- TEST_CONSTEXPR cpp17_input_iterator(const cpp17_input_iterator<U, T>& u) : it_(u.it_) {}
+ TEST_CONSTEXPR cpp17_input_iterator(const cpp17_input_iterator<U, T>& u) : it_(u.it_), tracker_(u.tracker_) {}
template <class U, class T, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
- TEST_CONSTEXPR_CXX14 cpp17_input_iterator(cpp17_input_iterator<U, T>&& u) : it_(u.it_) { u.it_ = U(); }
+ TEST_CONSTEXPR_CXX14 cpp17_input_iterator(cpp17_input_iterator<U, T>&& u)
+ : it_(std::move(u.it_)), tracker_(std::move(u.tracker_)) {
+ u.it_ = U();
+ }
TEST_CONSTEXPR reference operator*() const {return *it_;}
@@ -114,6 +123,7 @@ template <class It>
class forward_iterator
{
It it_;
+ support::double_move_tracker tracker_;
template <class U> friend class forward_iterator;
public:
@@ -127,10 +137,13 @@ class forward_iterator
TEST_CONSTEXPR explicit forward_iterator(It it) : it_(it) {}
template <class U>
- TEST_CONSTEXPR forward_iterator(const forward_iterator<U>& u) : it_(u.it_) {}
+ TEST_CONSTEXPR forward_iterator(const forward_iterator<U>& u) : it_(u.it_), tracker_(u.tracker_) {}
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
- TEST_CONSTEXPR_CXX14 forward_iterator(forward_iterator<U>&& other) : it_(other.it_) { other.it_ = U(); }
+ TEST_CONSTEXPR_CXX14 forward_iterator(forward_iterator<U>&& other)
+ : it_(std::move(other.it_)), tracker_(std::move(other.tracker_)) {
+ other.it_ = U();
+ }
TEST_CONSTEXPR reference operator*() const {return *it_;}
@@ -154,6 +167,7 @@ template <class It>
class bidirectional_iterator
{
It it_;
+ support::double_move_tracker tracker_;
template <class U> friend class bidirectional_iterator;
public:
@@ -167,10 +181,13 @@ class bidirectional_iterator
TEST_CONSTEXPR explicit bidirectional_iterator(It it) : it_(it) {}
template <class U>
- TEST_CONSTEXPR bidirectional_iterator(const bidirectional_iterator<U>& u) : it_(u.it_) {}
+ TEST_CONSTEXPR bidirectional_iterator(const bidirectional_iterator<U>& u) : it_(u.it_), tracker_(u.tracker_) {}
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
- TEST_CONSTEXPR_CXX14 bidirectional_iterator(bidirectional_iterator<U>&& u) : it_(u.it_) { u.it_ = U(); }
+ TEST_CONSTEXPR_CXX14 bidirectional_iterator(bidirectional_iterator<U>&& u)
+ : it_(std::move(u.it_)), tracker_(std::move(u.tracker_)) {
+ u.it_ = U();
+ }
TEST_CONSTEXPR reference operator*() const {return *it_;}
@@ -196,6 +213,7 @@ template <class It>
class random_access_iterator
{
It it_;
+ support::double_move_tracker tracker_;
template <class U> friend class random_access_iterator;
public:
@@ -209,10 +227,13 @@ class random_access_iterator
TEST_CONSTEXPR explicit random_access_iterator(It it) : it_(it) {}
template <class U>
- TEST_CONSTEXPR random_access_iterator(const random_access_iterator<U>& u) : it_(u.it_) {}
+ TEST_CONSTEXPR random_access_iterator(const random_access_iterator<U>& u) : it_(u.it_), tracker_(u.tracker_) {}
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
- TEST_CONSTEXPR_CXX14 random_access_iterator(random_access_iterator<U>&& u) : it_(u.it_) { u.it_ = U(); }
+ TEST_CONSTEXPR_CXX14 random_access_iterator(random_access_iterator<U>&& u)
+ : it_(std::move(u.it_)), tracker_(std::move(u.tracker_)) {
+ u.it_ = U();
+ }
TEST_CONSTEXPR_CXX14 reference operator*() const {return *it_;}
TEST_CONSTEXPR_CXX14 reference operator[](difference_type n) const {return it_[n];}
@@ -251,6 +272,7 @@ random_access_iterator(It) -> random_access_iterator<It>;
template <std::random_access_iterator It>
class cpp20_random_access_iterator {
It it_;
+ support::double_move_tracker tracker_;
template <std::random_access_iterator>
friend class cpp20_random_access_iterator;
@@ -265,10 +287,11 @@ class cpp20_random_access_iterator {
constexpr explicit cpp20_random_access_iterator(It it) : it_(it) {}
template <class U>
- constexpr cpp20_random_access_iterator(const cpp20_random_access_iterator<U>& u) : it_(u.it_) {}
+ constexpr cpp20_random_access_iterator(const cpp20_random_access_iterator<U>& u) : it_(u.it_), tracker_(u.tracker_) {}
template <class U>
- constexpr cpp20_random_access_iterator(cpp20_random_access_iterator<U>&& u) : it_(u.it_) {
+ constexpr cpp20_random_access_iterator(cpp20_random_access_iterator<U>&& u)
+ : it_(std::move(u.it_)), tracker_(std::move(u.tracker_)) {
u.it_ = U();
}
@@ -342,6 +365,7 @@ static_assert(std::random_access_iterator<cpp20_random_access_iterator<int*>>);
template <std::contiguous_iterator It>
class contiguous_iterator {
It it_;
+ support::double_move_tracker tracker_;
template <std::contiguous_iterator U>
friend class contiguous_iterator;
@@ -360,10 +384,10 @@ class contiguous_iterator {
constexpr explicit contiguous_iterator(It it) : it_(it) {}
template <class U>
- constexpr contiguous_iterator(const contiguous_iterator<U>& u) : it_(u.it_) {}
+ constexpr contiguous_iterator(const contiguous_iterator<U>& u) : it_(u.it_), tracker_(u.tracker_) {}
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
- constexpr contiguous_iterator(contiguous_iterator<U>&& u) : it_(u.it_) {
+ constexpr contiguous_iterator(contiguous_iterator<U>&& u) : it_(std::move(u.it_)), tracker_(std::move(u.tracker_)) {
u.it_ = U();
}
@@ -435,6 +459,7 @@ class three_way_contiguous_iterator
static_assert(std::is_pointer_v<It>, "Things probably break in this case");
It it_;
+ support::double_move_tracker tracker_;
template <class U> friend class three_way_contiguous_iterator;
public:
@@ -451,10 +476,14 @@ class three_way_contiguous_iterator
constexpr explicit three_way_contiguous_iterator(It it) : it_(it) {}
template <class U>
- constexpr three_way_contiguous_iterator(const three_way_contiguous_iterator<U>& u) : it_(u.it_) {}
+ constexpr three_way_contiguous_iterator(const three_way_contiguous_iterator<U>& u)
+ : it_(u.it_), tracker_(u.tracker_) {}
template <class U, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
- constexpr three_way_contiguous_iterator(three_way_contiguous_iterator<U>&& u) : it_(u.it_) { u.it_ = U(); }
+ constexpr three_way_contiguous_iterator(three_way_contiguous_iterator<U>&& u)
+ : it_(std::move(u.it_)), tracker_(std::move(u.tracker_)) {
+ u.it_ = U();
+ }
constexpr reference operator*() const {return *it_;}
constexpr pointer operator->() const {return it_;}
@@ -671,6 +700,7 @@ template <class It>
class cpp20_input_iterator
{
It it_;
+ support::double_move_tracker tracker_;
public:
using value_type = std::iter_value_t<It>;
@@ -705,6 +735,7 @@ struct iter_value_or_void<I> {
template <class It>
class cpp20_output_iterator {
It it_;
+ support::double_move_tracker tracker_;
public:
using difference_type = std::iter_difference_t<It>;
More information about the libcxx-commits
mailing list