Skip to content

Commit 029fe74

Browse files
[libc++] Fix insertion into deque from prvalue ranges
When the iterator of the source range in `*_range` functions dereferences to a prvalue, it should be at most _Cpp17InputIterator_ while possibly modeling `random_access_iterator`. When inserting such a range into a container, we should use `ranges::prev`/`ranges::next` instead of `std::prev`/`std::next` internally.
1 parent 84a796d commit 029fe74

File tree

7 files changed

+271
-2
lines changed

7 files changed

+271
-2
lines changed

libcxx/include/deque

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ template <class T, class Allocator, class Predicate>
185185
# include <__algorithm/copy_n.h>
186186
# include <__algorithm/equal.h>
187187
# include <__algorithm/fill_n.h>
188+
# include <__algorithm/iterator_operations.h>
188189
# include <__algorithm/lexicographical_compare.h>
189190
# include <__algorithm/lexicographical_compare_three_way.h>
190191
# include <__algorithm/max.h>
@@ -1916,6 +1917,12 @@ template <class _Tp, class _Allocator>
19161917
template <class _BiIter>
19171918
_LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator
19181919
deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f, _BiIter __l, size_type __n) {
1920+
# if _LIBCPP_STD_VER >= 20
1921+
using _Ops = _IterOps<
1922+
conditional_t<__has_bidirectional_iterator_category<_BiIter>::value, _ClassicAlgPolicy, _RangeAlgPolicy>>;
1923+
# else
1924+
using _Ops = _IterOps<_ClassicAlgPolicy>;
1925+
# endif
19191926
size_type __pos = __p - begin();
19201927
size_type __to_end = size() - __pos;
19211928
allocator_type& __a = __alloc();
@@ -1928,7 +1935,7 @@ deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f,
19281935
iterator __i = __old_begin;
19291936
_BiIter __m = __f;
19301937
if (__n > __pos) {
1931-
__m = __pos < __n / 2 ? std::prev(__l, __pos) : std::next(__f, __n - __pos);
1938+
__m = __pos < __n / 2 ? _Ops::prev(__l, __pos) : _Ops::next(__f, __n - __pos);
19321939
for (_BiIter __j = __m; __j != __f; --__start_, ++__size())
19331940
__alloc_traits::construct(__a, std::addressof(*--__i), *--__j);
19341941
__n = __pos;
@@ -1955,7 +1962,7 @@ deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f,
19551962
_BiIter __m = __l;
19561963
size_type __de = size() - __pos;
19571964
if (__n > __de) {
1958-
__m = __de < __n / 2 ? std::next(__f, __de) : std::prev(__l, __n - __de);
1965+
__m = __de < __n / 2 ? _Ops::next(__f, __de) : _Ops::prev(__l, __n - __de);
19591966
for (_BiIter __j = __m; __j != __l; ++__i, (void)++__j, ++__size())
19601967
__alloc_traits::construct(__a, std::addressof(*__i), *__j);
19611968
__n = __de;

libcxx/test/std/containers/from_range_helpers.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
#define SUPPORT_FROM_RANGE_HELPERS_H
1111

1212
#include <array>
13+
#include <concepts>
1314
#include <cstddef>
1415
#include <iterator>
16+
#include <ranges>
1517
#include <type_traits>
18+
#include <utility>
1619
#include <vector>
1720

1821
#include "min_allocator.h"
@@ -50,6 +53,39 @@ constexpr auto wrap_input(std::vector<T>& input) {
5053
return std::ranges::subrange(std::move(b), std::move(e));
5154
}
5255

56+
// https://llvm.org/PR159943
57+
struct DecayCopy {
58+
template <class T>
59+
requires std::convertible_to<T, std::decay_t<T>>
60+
static constexpr std::decay_t<T> operator()(T&& t) {
61+
return std::forward<T>(t);
62+
}
63+
};
64+
65+
template <class Iter, class Sent, std::ranges::input_range Range>
66+
constexpr auto wrap_input_decay(Range&& input) {
67+
auto b = Iter(std::ranges::begin(input));
68+
auto e = Sent(Iter(std::ranges::end(input)));
69+
if constexpr (std::is_reference_v<std::iter_reference_t<Iter>>)
70+
return std::ranges::subrange(std::move(b), std::move(e)) | std::views::transform(DecayCopy{});
71+
else
72+
return std::ranges::subrange(std::move(b), std::move(e));
73+
}
74+
75+
template <class Iter, class Sent, class T, std::size_t N>
76+
constexpr auto wrap_input_decay(std::array<T, N>& input) {
77+
auto b = Iter(input.data());
78+
auto e = Sent(Iter(input.data() + input.size()));
79+
return std::ranges::subrange(std::move(b), std::move(e)) | std::views::transform(DecayCopy{});
80+
}
81+
82+
template <class Iter, class Sent, class T>
83+
constexpr auto wrap_input_decay(std::vector<T>& input) {
84+
auto b = Iter(input.data());
85+
auto e = Sent(Iter(input.data() + input.size()));
86+
return std::ranges::subrange(std::move(b), std::move(e)) | std::views::transform(DecayCopy{});
87+
}
88+
5389
struct KeyValue {
5490
int key; // Only the key is considered for equality comparison.
5591
char value; // Allows distinguishing equivalent instances.

libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ int main(int, char**) {
2929
test_sequence_append_range<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
3030
LIBCPP_ASSERT(c.__invariants());
3131
});
32+
test_sequence_append_range_decay<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
33+
LIBCPP_ASSERT(c.__invariants());
34+
});
3235
});
3336
test_sequence_append_range_move_only<std::deque>();
3437

libcxx/test/std/containers/sequences/deque/deque.modifiers/assign_range.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ int main(int, char**) {
2828
test_sequence_assign_range<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
2929
LIBCPP_ASSERT(c.__invariants());
3030
});
31+
test_sequence_assign_range_decay<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
32+
LIBCPP_ASSERT(c.__invariants());
33+
});
3134
});
3235
test_sequence_assign_range_move_only<std::deque>();
3336

libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_range.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ int main(int, char**) {
3333
test_sequence_insert_range<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
3434
LIBCPP_ASSERT(c.__invariants());
3535
});
36+
test_sequence_insert_range_decay<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
37+
LIBCPP_ASSERT(c.__invariants());
38+
});
3639
});
3740
test_sequence_insert_range_move_only<std::deque>();
3841

libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ int main(int, char**) {
2929
test_sequence_prepend_range<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
3030
LIBCPP_ASSERT(c.__invariants());
3131
});
32+
test_sequence_prepend_range_decay<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
33+
LIBCPP_ASSERT(c.__invariants());
34+
});
3235
});
3336
test_sequence_prepend_range_move_only<std::deque>();
3437

libcxx/test/std/containers/sequences/insert_range_sequence_containers.h

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,76 @@ constexpr void test_sequence_insert_range(Validate validate) {
464464
}
465465
}
466466

467+
// https://llvm.org/PR159943
468+
template <class Container, class Iter, class Sent, class Validate>
469+
constexpr void test_sequence_insert_range_decay(Validate validate) {
470+
using T = Container::value_type;
471+
using D = Container::difference_type;
472+
auto get_pos = [](auto& c, auto& test_case) { return std::ranges::next(c.begin(), static_cast<D>(test_case.index)); };
473+
474+
auto test = [&](auto& test_case) {
475+
Container c(test_case.initial.begin(), test_case.initial.end());
476+
auto in = wrap_input_decay<Iter, Sent>(test_case.input);
477+
auto pos = get_pos(c, test_case);
478+
479+
auto result = c.insert_range(pos, in);
480+
assert(result == get_pos(c, test_case));
481+
validate(c);
482+
return std::ranges::equal(c, test_case.expected);
483+
};
484+
485+
{ // Empty container.
486+
// empty_c.insert_range(end, empty_range)
487+
assert(test(EmptyContainer_EmptyRange<T>));
488+
// empty_c.insert_range(end, one_element_range)
489+
assert(test(EmptyContainer_OneElementRange<T>));
490+
// empty_c.insert_range(end, mid_range)
491+
assert(test(EmptyContainer_MidRange<T>));
492+
}
493+
494+
{ // One-element container.
495+
// one_element_c.insert_range(begin, empty_range)
496+
assert(test(OneElementContainer_Begin_EmptyRange<T>));
497+
// one_element_c.insert_range(end, empty_range)
498+
assert(test(OneElementContainer_End_EmptyRange<T>));
499+
// one_element_c.insert_range(begin, one_element_range)
500+
assert(test(OneElementContainer_Begin_OneElementRange<T>));
501+
// one_element_c.insert_range(end, one_element_range)
502+
assert(test(OneElementContainer_End_OneElementRange<T>));
503+
// one_element_c.insert_range(begin, mid_range)
504+
assert(test(OneElementContainer_Begin_MidRange<T>));
505+
// one_element_c.insert_range(end, mid_range)
506+
assert(test(OneElementContainer_End_MidRange<T>));
507+
}
508+
509+
{ // Full container.
510+
// full_container.insert_range(begin, empty_range)
511+
assert(test(FullContainer_Begin_EmptyRange<T>));
512+
// full_container.insert_range(mid, empty_range)
513+
assert(test(FullContainer_Mid_EmptyRange<T>));
514+
// full_container.insert_range(end, empty_range)
515+
assert(test(FullContainer_End_EmptyRange<T>));
516+
// full_container.insert_range(begin, one_element_range)
517+
assert(test(FullContainer_Begin_OneElementRange<T>));
518+
// full_container.insert_range(end, one_element_range)
519+
assert(test(FullContainer_Mid_OneElementRange<T>));
520+
// full_container.insert_range(end, one_element_range)
521+
assert(test(FullContainer_End_OneElementRange<T>));
522+
// full_container.insert_range(begin, mid_range)
523+
assert(test(FullContainer_Begin_MidRange<T>));
524+
// full_container.insert_range(mid, mid_range)
525+
assert(test(FullContainer_Mid_MidRange<T>));
526+
// full_container.insert_range(end, mid_range)
527+
assert(test(FullContainer_End_MidRange<T>));
528+
// full_container.insert_range(begin, long_range)
529+
assert(test(FullContainer_Begin_LongRange<T>));
530+
// full_container.insert_range(mid, long_range)
531+
assert(test(FullContainer_Mid_LongRange<T>));
532+
// full_container.insert_range(end, long_range)
533+
assert(test(FullContainer_End_LongRange<T>));
534+
}
535+
}
536+
467537
template <class Container, class Iter, class Sent, class Validate>
468538
constexpr void test_sequence_prepend_range(Validate validate) {
469539
using T = typename Container::value_type;
@@ -507,6 +577,50 @@ constexpr void test_sequence_prepend_range(Validate validate) {
507577
}
508578
}
509579

580+
// https://llvm.org/PR159943
581+
template <class Container, class Iter, class Sent, class Validate>
582+
constexpr void test_sequence_prepend_range_decay(Validate validate) {
583+
using T = Container::value_type;
584+
585+
auto test = [&](auto& test_case) {
586+
Container c(test_case.initial.begin(), test_case.initial.end());
587+
auto in = wrap_input_decay<Iter, Sent>(test_case.input);
588+
589+
c.prepend_range(in);
590+
validate(c);
591+
return std::ranges::equal(c, test_case.expected);
592+
};
593+
594+
{ // Empty container.
595+
// empty_c.prepend_range(empty_range)
596+
assert(test(EmptyContainer_EmptyRange<T>));
597+
// empty_c.prepend_range(one_element_range)
598+
assert(test(EmptyContainer_OneElementRange<T>));
599+
// empty_c.prepend_range(mid_range)
600+
assert(test(EmptyContainer_MidRange<T>));
601+
}
602+
603+
{ // One-element container.
604+
// one_element_c.prepend_range(empty_range)
605+
assert(test(OneElementContainer_Begin_EmptyRange<T>));
606+
// one_element_c.prepend_range(one_element_range)
607+
assert(test(OneElementContainer_Begin_OneElementRange<T>));
608+
// one_element_c.prepend_range(mid_range)
609+
assert(test(OneElementContainer_Begin_MidRange<T>));
610+
}
611+
612+
{ // Full container.
613+
// full_container.prepend_range(empty_range)
614+
assert(test(FullContainer_Begin_EmptyRange<T>));
615+
// full_container.prepend_range(one_element_range)
616+
assert(test(FullContainer_Begin_OneElementRange<T>));
617+
// full_container.prepend_range(mid_range)
618+
assert(test(FullContainer_Begin_MidRange<T>));
619+
// full_container.prepend_range(long_range)
620+
assert(test(FullContainer_Begin_LongRange<T>));
621+
}
622+
}
623+
510624
template <class Container, class Iter, class Sent, class Validate>
511625
constexpr void test_sequence_append_range(Validate validate) {
512626
using T = typename Container::value_type;
@@ -550,6 +664,50 @@ constexpr void test_sequence_append_range(Validate validate) {
550664
}
551665
}
552666

667+
// https://llvm.org/PR159943
668+
template <class Container, class Iter, class Sent, class Validate>
669+
constexpr void test_sequence_append_range_decay(Validate validate) {
670+
using T = Container::value_type;
671+
672+
auto test = [&](auto& test_case) {
673+
Container c(test_case.initial.begin(), test_case.initial.end());
674+
auto in = wrap_input_decay<Iter, Sent>(test_case.input);
675+
676+
c.append_range(in);
677+
validate(c);
678+
return std::ranges::equal(c, test_case.expected);
679+
};
680+
681+
{ // Empty container.
682+
// empty_c.append_range(empty_range)
683+
assert(test(EmptyContainer_EmptyRange<T>));
684+
// empty_c.append_range(one_element_range)
685+
assert(test(EmptyContainer_OneElementRange<T>));
686+
// empty_c.append_range(mid_range)
687+
assert(test(EmptyContainer_MidRange<T>));
688+
}
689+
690+
{ // One-element container.
691+
// one_element_c.append_range(empty_range)
692+
assert(test(OneElementContainer_End_EmptyRange<T>));
693+
// one_element_c.append_range(one_element_range)
694+
assert(test(OneElementContainer_End_OneElementRange<T>));
695+
// one_element_c.append_range(mid_range)
696+
assert(test(OneElementContainer_End_MidRange<T>));
697+
}
698+
699+
{ // Full container.
700+
// full_container.append_range(empty_range)
701+
assert(test(FullContainer_End_EmptyRange<T>));
702+
// full_container.append_range(one_element_range)
703+
assert(test(FullContainer_End_OneElementRange<T>));
704+
// full_container.append_range(mid_range)
705+
assert(test(FullContainer_End_MidRange<T>));
706+
// full_container.append_range(long_range)
707+
assert(test(FullContainer_End_LongRange<T>));
708+
}
709+
}
710+
553711
template <class Container, class Iter, class Sent, class Validate>
554712
constexpr void test_sequence_assign_range(Validate validate) {
555713
using T = typename Container::value_type;
@@ -605,6 +763,62 @@ constexpr void test_sequence_assign_range(Validate validate) {
605763
}
606764
}
607765

766+
// https://llvm.org/PR159943
767+
template <class Container, class Iter, class Sent, class Validate>
768+
constexpr void test_sequence_assign_range_decay(Validate validate) {
769+
using T = Container::value_type;
770+
771+
auto& initial_empty = EmptyContainer_EmptyRange<T>.initial;
772+
auto& initial_one_element = OneElementContainer_Begin_EmptyRange<T>.initial;
773+
auto& initial_full = FullContainer_Begin_EmptyRange<T>.initial;
774+
auto& input_empty = FullContainer_Begin_EmptyRange<T>.input;
775+
auto& input_one_element = FullContainer_Begin_OneElementRange<T>.input;
776+
auto& input_mid_range = FullContainer_Begin_MidRange<T>.input;
777+
auto& input_long_range = FullContainer_Begin_LongRange<T>.input;
778+
779+
auto test = [&](auto& initial, auto& input) {
780+
Container c(initial.begin(), initial.end());
781+
auto in = wrap_input_decay<Iter, Sent>(input);
782+
783+
c.assign_range(in);
784+
validate(c);
785+
return std::ranges::equal(c, input);
786+
};
787+
788+
{ // Empty container.
789+
// empty_container.assign_range(empty_range)
790+
assert(test(initial_empty, input_empty));
791+
// empty_container.assign_range(one_element_range)
792+
assert(test(initial_empty, input_one_element));
793+
// empty_container.assign_range(mid_range)
794+
assert(test(initial_empty, input_mid_range));
795+
// empty_container.assign_range(long_range)
796+
assert(test(initial_empty, input_long_range));
797+
}
798+
799+
{ // One-element container.
800+
// one_element_container.assign_range(empty_range)
801+
assert(test(initial_one_element, input_empty));
802+
// one_element_container.assign_range(one_element_range)
803+
assert(test(initial_one_element, input_one_element));
804+
// one_element_container.assign_range(mid_range)
805+
assert(test(initial_one_element, input_mid_range));
806+
// one_element_container.assign_range(long_range)
807+
assert(test(initial_one_element, input_long_range));
808+
}
809+
810+
{ // Full container.
811+
// full_container.assign_range(empty_range)
812+
assert(test(initial_full, input_empty));
813+
// full_container.assign_range(one_element_range)
814+
assert(test(initial_full, input_one_element));
815+
// full_container.assign_range(mid_range)
816+
assert(test(initial_full, input_mid_range));
817+
// full_container.assign_range(long_range)
818+
assert(test(initial_full, input_long_range));
819+
}
820+
}
821+
608822
// Move-only types.
609823

610824
template <template <class...> class Container>

0 commit comments

Comments
 (0)