[libcxx-commits] [libcxx] [libc++][ranges] Fix `ranges::join_view` segmented iterator trait (PR #158347)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Sep 24 05:54:46 PDT 2025
https://github.com/lbonn updated https://github.com/llvm/llvm-project/pull/158347
>From b41db68fe58024e7a55cda56496d8c4fef1f4eb9 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 | 121 ++++++++++++------
2 files changed, 89 insertions(+), 41 deletions(-)
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 327b349f476a7..364f056d8d2cf 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..3303b4a76f467 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,100 @@ 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() {
+ // Test the optimized find algorithm for types that implement the segment iterator trait
+ // 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 ranges adaptor
+ {
+ { // single element - match
+ int data[1][1] = {{4}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::ranges::begin(joined));
+ }
+ { // single element - no match
+ // (reproducer for https://llvm.org/PR158279, where the iterator would never reach the end sentinel)
+ int data[1][1] = {{3}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::ranges::end(joined));
+ }
+ { // several sub-arrays of size 1 - match
+ 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)));
+ }
+ { // several sub-arrays of size 2 - match in second element of an array
+ int data[3][2] = {{0, 0}, {0, 4}, {0, 0}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::ranges::next(std::ranges::begin(joined), 3));
+ }
+ { // vector of empty vectors
+ std::vector<std::vector<int>> data = {{}, {}};
+ auto joined = std::views::join(data);
+ assert(std::ranges::find(joined, 4) == std::ranges::end(joined));
+ }
+ { // vector of variably sized vectors - match
+ 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)));
+ }
+ { // vector of variably sized vectors - no match
+ 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