[libcxx-commits] [libcxx] [libc++][ranges] Fix `ranges::join_view` segmented iterator trait (PR #158347)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Sep 19 01:06:39 PDT 2025
https://github.com/lbonn updated https://github.com/llvm/llvm-project/pull/158347
>From d8fb7159fd65a7a3ced817394ab5c6c62f9abd1b Mon Sep 17 00:00:00 2001
From: Laurent Bonnans <github at lbonnans.net>
Date: Fri, 19 Sep 2025 09:23:13 +0200
Subject: [PATCH] [libc++][ranges] Fix `ranges::join_view` segmented iterator
trait
The outer iterator needs to move to the next segment when calling
__compose.
Without this change, `find_segment_if` would never reach the end of the
join_view which caused erroneous result when calling `ranges::find` on a
join_view of bidirectional ranges.
---
libcxx/include/__ranges/join_view.h | 9 +-
.../alg.find/ranges.find.pass.cpp | 110 +++++++++++-------
2 files changed, 78 insertions(+), 41 deletions(-)
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 327b349f476a7..7ca1bfb851953 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -410,8 +410,13 @@ struct __segmented_iterator_traits<_JoinViewIterator> {
static constexpr _LIBCPP_HIDE_FROM_ABI _JoinViewIterator
__compose(__segment_iterator __seg_iter, __local_iterator __local_iter) {
- return _JoinViewIterator(
- std::move(__seg_iter).__get_data(), std::move(__seg_iter).__get_iter(), std::move(__local_iter));
+ auto&& __parent = std::move(__seg_iter).__get_data();
+ auto&& __outer = std::move(__seg_iter).__get_iter();
+ if (__local_iter == ranges::end(*__outer)) {
+ ++__outer;
+ return _JoinViewIterator(*__parent, __outer);
+ }
+ return _JoinViewIterator(__parent, __outer, std::move(__local_iter));
}
};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
index 5b4abc45b6f4f..fbd603753352a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
@@ -272,57 +272,89 @@ class Comparable {
friend bool operator==(const Comparable& lhs, long long rhs) { return comparable_data[lhs.index_] == rhs; }
};
-void test_deque() {
- { // empty deque
- std::deque<int> data;
- assert(std::ranges::find(data, 4) == data.end());
- assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
- }
-
- { // single element - match
- std::deque<int> data = {4};
- assert(std::ranges::find(data, 4) == data.begin());
- assert(std::ranges::find(data.begin(), data.end(), 4) == data.begin());
- }
-
- { // single element - no match
- std::deque<int> data = {3};
- assert(std::ranges::find(data, 4) == data.end());
- assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
- }
-
- // many elements
- for (auto size : {2, 3, 1023, 1024, 1025, 2047, 2048, 2049}) {
- { // last element match
+void test_segmented_iterator_types() {
+ // deque
+ {
+ { // empty deque
std::deque<int> data;
- data.resize(size);
- std::fill(data.begin(), data.end(), 3);
- data[size - 1] = 4;
- assert(std::ranges::find(data, 4) == data.end() - 1);
- assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 1);
+ assert(std::ranges::find(data, 4) == data.end());
+ assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
}
- { // second-last element match
- std::deque<int> data;
- data.resize(size);
- std::fill(data.begin(), data.end(), 3);
- data[size - 2] = 4;
- assert(std::ranges::find(data, 4) == data.end() - 2);
- assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 2);
+ { // single element - match
+ std::deque<int> data = {4};
+ assert(std::ranges::find(data, 4) == data.begin());
+ assert(std::ranges::find(data.begin(), data.end(), 4) == data.begin());
}
- { // no match
- std::deque<int> data;
- data.resize(size);
- std::fill(data.begin(), data.end(), 3);
+ { // single element - no match
+ std::deque<int> data = {3};
assert(std::ranges::find(data, 4) == data.end());
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
}
+
+ // many elements
+ for (auto size : {2, 3, 1023, 1024, 1025, 2047, 2048, 2049}) {
+ { // last element match
+ std::deque<int> data;
+ data.resize(size);
+ std::fill(data.begin(), data.end(), 3);
+ data[size - 1] = 4;
+ assert(std::ranges::find(data, 4) == data.end() - 1);
+ assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 1);
+ }
+
+ { // second-last element match
+ std::deque<int> data;
+ data.resize(size);
+ std::fill(data.begin(), data.end(), 3);
+ data[size - 2] = 4;
+ assert(std::ranges::find(data, 4) == data.end() - 2);
+ assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 2);
+ }
+
+ { // no match
+ std::deque<int> data;
+ data.resize(size);
+ std::fill(data.begin(), data.end(), 3);
+ assert(std::ranges::find(data, 4) == data.end());
+ assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
+ }
+ }
+ }
+ // join_view
+ // See https://llvm.org/PR158279
+ {
+ {
+ int data[2][1] = {{4}, {0}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::ranges::begin(joined));
+ }
+ {
+ int data[3][1] = {{0}, {4}, {0}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::next(std::ranges::begin(joined)));
+ }
+ {
+ int data[3][1] = {{3}, {0}, {0}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::ranges::end(joined));
+ }
+ {
+ std::vector<std::vector<int>> data = {{}, {}, {3, 4}, {}, {}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::ranges::next(std::ranges::begin(joined)));
+ }
+ {
+ std::vector<std::vector<int>> data = {{}, {}, {3, 5}, {}, {}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::ranges::end(joined));
+ }
}
}
int main(int, char**) {
- test_deque();
+ test_segmented_iterator_types();
test();
static_assert(test());
More information about the libcxx-commits
mailing list