Skip to content

Commit ec17470

Browse files
lbonndyung
authored andcommitted
[libc++][ranges] Fix ranges::join_view segmented iterator trait (#158347)
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. Other specializations using the segmented iterator trait were likely to be affected as well. Fixes #158279 Fixes #93180 (cherry picked from commit d1b5607)
1 parent 220bac1 commit ec17470

File tree

2 files changed

+89
-41
lines changed

2 files changed

+89
-41
lines changed

libcxx/include/__ranges/join_view.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,13 @@ struct __segmented_iterator_traits<_JoinViewIterator> {
410410

411411
static constexpr _LIBCPP_HIDE_FROM_ABI _JoinViewIterator
412412
__compose(__segment_iterator __seg_iter, __local_iterator __local_iter) {
413-
return _JoinViewIterator(
414-
std::move(__seg_iter).__get_data(), std::move(__seg_iter).__get_iter(), std::move(__local_iter));
413+
auto&& __parent = std::move(__seg_iter).__get_data();
414+
auto&& __outer = std::move(__seg_iter).__get_iter();
415+
if (__local_iter == ranges::end(*__outer)) {
416+
++__outer;
417+
return _JoinViewIterator(*__parent, __outer);
418+
}
419+
return _JoinViewIterator(__parent, __outer, std::move(__local_iter));
415420
}
416421
};
417422

libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp

Lines changed: 82 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -272,57 +272,100 @@ class Comparable {
272272
friend bool operator==(const Comparable& lhs, long long rhs) { return comparable_data[lhs.index_] == rhs; }
273273
};
274274

275-
void test_deque() {
276-
{ // empty deque
277-
std::deque<int> data;
278-
assert(std::ranges::find(data, 4) == data.end());
279-
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
280-
}
281-
282-
{ // single element - match
283-
std::deque<int> data = {4};
284-
assert(std::ranges::find(data, 4) == data.begin());
285-
assert(std::ranges::find(data.begin(), data.end(), 4) == data.begin());
286-
}
287-
288-
{ // single element - no match
289-
std::deque<int> data = {3};
290-
assert(std::ranges::find(data, 4) == data.end());
291-
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
292-
}
293-
294-
// many elements
295-
for (auto size : {2, 3, 1023, 1024, 1025, 2047, 2048, 2049}) {
296-
{ // last element match
275+
void test_segmented_iterator_types() {
276+
// Test the optimized find algorithm for types that implement the segment iterator trait
277+
// deque
278+
{
279+
{ // empty deque
297280
std::deque<int> data;
298-
data.resize(size);
299-
std::fill(data.begin(), data.end(), 3);
300-
data[size - 1] = 4;
301-
assert(std::ranges::find(data, 4) == data.end() - 1);
302-
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 1);
281+
assert(std::ranges::find(data, 4) == data.end());
282+
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
303283
}
304284

305-
{ // second-last element match
306-
std::deque<int> data;
307-
data.resize(size);
308-
std::fill(data.begin(), data.end(), 3);
309-
data[size - 2] = 4;
310-
assert(std::ranges::find(data, 4) == data.end() - 2);
311-
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 2);
285+
{ // single element - match
286+
std::deque<int> data = {4};
287+
assert(std::ranges::find(data, 4) == data.begin());
288+
assert(std::ranges::find(data.begin(), data.end(), 4) == data.begin());
312289
}
313290

314-
{ // no match
315-
std::deque<int> data;
316-
data.resize(size);
317-
std::fill(data.begin(), data.end(), 3);
291+
{ // single element - no match
292+
std::deque<int> data = {3};
318293
assert(std::ranges::find(data, 4) == data.end());
319294
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
320295
}
296+
297+
// many elements
298+
for (auto size : {2, 3, 1023, 1024, 1025, 2047, 2048, 2049}) {
299+
{ // last element match
300+
std::deque<int> data;
301+
data.resize(size);
302+
std::fill(data.begin(), data.end(), 3);
303+
data[size - 1] = 4;
304+
assert(std::ranges::find(data, 4) == data.end() - 1);
305+
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 1);
306+
}
307+
308+
{ // second-last element match
309+
std::deque<int> data;
310+
data.resize(size);
311+
std::fill(data.begin(), data.end(), 3);
312+
data[size - 2] = 4;
313+
assert(std::ranges::find(data, 4) == data.end() - 2);
314+
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 2);
315+
}
316+
317+
{ // no match
318+
std::deque<int> data;
319+
data.resize(size);
320+
std::fill(data.begin(), data.end(), 3);
321+
assert(std::ranges::find(data, 4) == data.end());
322+
assert(std::ranges::find(data.begin(), data.end(), 4) == data.end());
323+
}
324+
}
325+
}
326+
// join_view ranges adaptor
327+
{
328+
{ // single element - match
329+
int data[1][1] = {{4}};
330+
auto joined = std::views::join(data);
331+
assert(std::ranges::find(joined, 4) == std::ranges::begin(joined));
332+
}
333+
{ // single element - no match
334+
// (reproducer for https://llvm.org/PR158279, where the iterator would never reach the end sentinel)
335+
int data[1][1] = {{3}};
336+
auto joined = std::views::join(data);
337+
assert(std::ranges::find(joined, 4) == std::ranges::end(joined));
338+
}
339+
{ // several sub-arrays of size 1 - match
340+
int data[3][1] = {{0}, {4}, {0}};
341+
auto joined = std::views::join(data);
342+
assert(std::ranges::find(joined, 4) == std::next(std::ranges::begin(joined)));
343+
}
344+
{ // several sub-arrays of size 2 - match in second element of an array
345+
int data[3][2] = {{0, 0}, {0, 4}, {0, 0}};
346+
auto joined = std::views::join(data);
347+
assert(std::ranges::find(joined, 4) == std::ranges::next(std::ranges::begin(joined), 3));
348+
}
349+
{ // vector of empty vectors
350+
std::vector<std::vector<int>> data = {{}, {}};
351+
auto joined = std::views::join(data);
352+
assert(std::ranges::find(joined, 4) == std::ranges::end(joined));
353+
}
354+
{ // vector of variably sized vectors - match
355+
std::vector<std::vector<int>> data = {{}, {}, {3, 4}, {}, {}};
356+
auto joined = std::views::join(data);
357+
assert(std::ranges::find(joined, 4) == std::ranges::next(std::ranges::begin(joined)));
358+
}
359+
{ // vector of variably sized vectors - no match
360+
std::vector<std::vector<int>> data = {{}, {}, {3, 5}, {}, {}};
361+
auto joined = std::views::join(data);
362+
assert(std::ranges::find(joined, 4) == std::ranges::end(joined));
363+
}
321364
}
322365
}
323366

324367
int main(int, char**) {
325-
test_deque();
368+
test_segmented_iterator_types();
326369
test();
327370
static_assert(test());
328371

0 commit comments

Comments
 (0)