diff --git a/libcxx/include/deque b/libcxx/include/deque index 98d1dbbddb7e8..e7f58a43b78a0 100644 --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -185,6 +185,7 @@ template # include <__algorithm/copy_n.h> # include <__algorithm/equal.h> # include <__algorithm/fill_n.h> +# include <__algorithm/iterator_operations.h> # include <__algorithm/lexicographical_compare.h> # include <__algorithm/lexicographical_compare_three_way.h> # include <__algorithm/max.h> @@ -832,7 +833,7 @@ public: _LIBCPP_HIDE_FROM_ABI iterator insert_range(const_iterator __position, _Range&& __range) { if constexpr (ranges::bidirectional_range<_Range>) { auto __n = static_cast(ranges::distance(__range)); - return __insert_bidirectional(__position, ranges::begin(__range), ranges::end(__range), __n); + return __insert_bidirectional<_RangeAlgPolicy>(__position, ranges::begin(__range), ranges::end(__range), __n); } else if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) { auto __n = static_cast(ranges::distance(__range)); @@ -1202,10 +1203,10 @@ private: template _LIBCPP_HIDE_FROM_ABI iterator __insert_with_size(const_iterator __p, _Iterator __f, size_type __n); - template + template _LIBCPP_HIDE_FROM_ABI iterator __insert_bidirectional(const_iterator __p, _BiIter __f, _Sentinel __sent, size_type __n); - template + template _LIBCPP_HIDE_FROM_ABI iterator __insert_bidirectional(const_iterator __p, _BiIter __f, _BiIter __l, size_type __n); template ::value, int> = 0> @@ -1902,20 +1903,21 @@ deque<_Tp, _Allocator>::__insert_with_size(const_iterator __p, _Iterator __f, si template template ::value, int> > typename deque<_Tp, _Allocator>::iterator deque<_Tp, _Allocator>::insert(const_iterator __p, _BiIter __f, _BiIter __l) { - return __insert_bidirectional(__p, __f, __l, std::distance(__f, __l)); + return __insert_bidirectional<_ClassicAlgPolicy>(__p, __f, __l, std::distance(__f, __l)); } template -template +template _LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f, _Sentinel, size_type __n) { - return __insert_bidirectional(__p, __f, std::next(__f, __n), __n); + return __insert_bidirectional<_AlgPolicy>(__p, __f, _IterOps<_AlgPolicy>::next(__f, __n), __n); } template -template +template _LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f, _BiIter __l, size_type __n) { + using _Ops = _IterOps<_AlgPolicy>; size_type __pos = __p - begin(); size_type __to_end = size() - __pos; allocator_type& __a = __alloc(); @@ -1928,7 +1930,7 @@ deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f, iterator __i = __old_begin; _BiIter __m = __f; if (__n > __pos) { - __m = __pos < __n / 2 ? std::prev(__l, __pos) : std::next(__f, __n - __pos); + __m = __pos < __n / 2 ? _Ops::prev(__l, __pos) : _Ops::next(__f, __n - __pos); for (_BiIter __j = __m; __j != __f; --__start_, ++__size()) __alloc_traits::construct(__a, std::addressof(*--__i), *--__j); __n = __pos; @@ -1955,7 +1957,7 @@ deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f, _BiIter __m = __l; size_type __de = size() - __pos; if (__n > __de) { - __m = __de < __n / 2 ? std::next(__f, __de) : std::prev(__l, __n - __de); + __m = __de < __n / 2 ? _Ops::next(__f, __de) : _Ops::prev(__l, __n - __de); for (_BiIter __j = __m; __j != __l; ++__i, (void)++__j, ++__size()) __alloc_traits::construct(__a, std::addressof(*__i), *__j); __n = __de; diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h index edf1d6ce528d7..1ef0d1ccde191 100644 --- a/libcxx/test/std/containers/from_range_helpers.h +++ b/libcxx/test/std/containers/from_range_helpers.h @@ -10,9 +10,12 @@ #define SUPPORT_FROM_RANGE_HELPERS_H #include +#include #include #include +#include #include +#include #include #include "min_allocator.h" @@ -50,6 +53,41 @@ constexpr auto wrap_input(std::vector& input) { return std::ranges::subrange(std::move(b), std::move(e)); } +// https://llvm.org/PR159943 +// Verify container insertion/assignment from ranges whose iterators dereference to prvalues. +// Especially, `std::prev` should be avoided when inserting such a `bidirectional_range`. +struct DecayCopy { + template + requires std::convertible_to> + static constexpr std::decay_t operator()(T&& t) { + return std::forward(t); + } +}; + +template +constexpr auto wrap_input_decay(Range&& input) { + auto b = Iter(std::ranges::begin(input)); + auto e = Sent(Iter(std::ranges::end(input))); + if constexpr (std::is_reference_v>) + return std::ranges::subrange(std::move(b), std::move(e)) | std::views::transform(DecayCopy{}); + else + return std::ranges::subrange(std::move(b), std::move(e)); +} + +template +constexpr auto wrap_input_decay(std::array& input) { + auto b = Iter(input.data()); + auto e = Sent(Iter(input.data() + input.size())); + return std::ranges::subrange(std::move(b), std::move(e)) | std::views::transform(DecayCopy{}); +} + +template +constexpr auto wrap_input_decay(std::vector& input) { + auto b = Iter(input.data()); + auto e = Sent(Iter(input.data() + input.size())); + return std::ranges::subrange(std::move(b), std::move(e)) | std::views::transform(DecayCopy{}); +} + struct KeyValue { int key; // Only the key is considered for equality comparison. char value; // Allows distinguishing equivalent instances. diff --git a/libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp index 56a1d226db46f..69f295264f107 100644 --- a/libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp @@ -29,6 +29,9 @@ int main(int, char**) { test_sequence_append_range, Iter, Sent>([]([[maybe_unused]] auto&& c) { LIBCPP_ASSERT(c.__invariants()); }); + test_sequence_append_range_decay, Iter, Sent>([]([[maybe_unused]] auto&& c) { + LIBCPP_ASSERT(c.__invariants()); + }); }); test_sequence_append_range_move_only(); diff --git a/libcxx/test/std/containers/sequences/deque/deque.modifiers/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.modifiers/assign_range.pass.cpp index 744e03a7b983e..6952133d30fd2 100644 --- a/libcxx/test/std/containers/sequences/deque/deque.modifiers/assign_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/deque/deque.modifiers/assign_range.pass.cpp @@ -28,6 +28,9 @@ int main(int, char**) { test_sequence_assign_range, Iter, Sent>([]([[maybe_unused]] auto&& c) { LIBCPP_ASSERT(c.__invariants()); }); + test_sequence_assign_range_decay, Iter, Sent>([]([[maybe_unused]] auto&& c) { + LIBCPP_ASSERT(c.__invariants()); + }); }); test_sequence_assign_range_move_only(); diff --git a/libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_range.pass.cpp index 7681eb63b9076..9964f6432ea8b 100644 --- a/libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/deque/deque.modifiers/insert_range.pass.cpp @@ -33,6 +33,9 @@ int main(int, char**) { test_sequence_insert_range, Iter, Sent>([]([[maybe_unused]] auto&& c) { LIBCPP_ASSERT(c.__invariants()); }); + test_sequence_insert_range_decay, Iter, Sent>([]([[maybe_unused]] auto&& c) { + LIBCPP_ASSERT(c.__invariants()); + }); }); test_sequence_insert_range_move_only(); diff --git a/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp index 3154cd389d2f0..d6cb116a9c77f 100644 --- a/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp @@ -29,6 +29,9 @@ int main(int, char**) { test_sequence_prepend_range, Iter, Sent>([]([[maybe_unused]] auto&& c) { LIBCPP_ASSERT(c.__invariants()); }); + test_sequence_prepend_range_decay, Iter, Sent>([]([[maybe_unused]] auto&& c) { + LIBCPP_ASSERT(c.__invariants()); + }); }); test_sequence_prepend_range_move_only(); diff --git a/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h b/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h index 9f404c46df778..3a82e7049fcca 100644 --- a/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h +++ b/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h @@ -464,6 +464,76 @@ constexpr void test_sequence_insert_range(Validate validate) { } } +// https://llvm.org/PR159943 +template +constexpr void test_sequence_insert_range_decay(Validate validate) { + using T = Container::value_type; + using D = Container::difference_type; + auto get_pos = [](auto& c, auto& test_case) { return std::ranges::next(c.begin(), static_cast(test_case.index)); }; + + auto test = [&](auto& test_case) { + Container c(test_case.initial.begin(), test_case.initial.end()); + auto in = wrap_input_decay(test_case.input); + auto pos = get_pos(c, test_case); + + auto result = c.insert_range(pos, in); + assert(result == get_pos(c, test_case)); + validate(c); + return std::ranges::equal(c, test_case.expected); + }; + + { // Empty container. + // empty_c.insert_range(end, empty_range) + assert(test(EmptyContainer_EmptyRange)); + // empty_c.insert_range(end, one_element_range) + assert(test(EmptyContainer_OneElementRange)); + // empty_c.insert_range(end, mid_range) + assert(test(EmptyContainer_MidRange)); + } + + { // One-element container. + // one_element_c.insert_range(begin, empty_range) + assert(test(OneElementContainer_Begin_EmptyRange)); + // one_element_c.insert_range(end, empty_range) + assert(test(OneElementContainer_End_EmptyRange)); + // one_element_c.insert_range(begin, one_element_range) + assert(test(OneElementContainer_Begin_OneElementRange)); + // one_element_c.insert_range(end, one_element_range) + assert(test(OneElementContainer_End_OneElementRange)); + // one_element_c.insert_range(begin, mid_range) + assert(test(OneElementContainer_Begin_MidRange)); + // one_element_c.insert_range(end, mid_range) + assert(test(OneElementContainer_End_MidRange)); + } + + { // Full container. + // full_container.insert_range(begin, empty_range) + assert(test(FullContainer_Begin_EmptyRange)); + // full_container.insert_range(mid, empty_range) + assert(test(FullContainer_Mid_EmptyRange)); + // full_container.insert_range(end, empty_range) + assert(test(FullContainer_End_EmptyRange)); + // full_container.insert_range(begin, one_element_range) + assert(test(FullContainer_Begin_OneElementRange)); + // full_container.insert_range(end, one_element_range) + assert(test(FullContainer_Mid_OneElementRange)); + // full_container.insert_range(end, one_element_range) + assert(test(FullContainer_End_OneElementRange)); + // full_container.insert_range(begin, mid_range) + assert(test(FullContainer_Begin_MidRange)); + // full_container.insert_range(mid, mid_range) + assert(test(FullContainer_Mid_MidRange)); + // full_container.insert_range(end, mid_range) + assert(test(FullContainer_End_MidRange)); + // full_container.insert_range(begin, long_range) + assert(test(FullContainer_Begin_LongRange)); + // full_container.insert_range(mid, long_range) + assert(test(FullContainer_Mid_LongRange)); + // full_container.insert_range(end, long_range) + assert(test(FullContainer_End_LongRange)); + } +} + template constexpr void test_sequence_prepend_range(Validate validate) { using T = typename Container::value_type; @@ -507,6 +577,50 @@ constexpr void test_sequence_prepend_range(Validate validate) { } } +// https://llvm.org/PR159943 +template +constexpr void test_sequence_prepend_range_decay(Validate validate) { + using T = Container::value_type; + + auto test = [&](auto& test_case) { + Container c(test_case.initial.begin(), test_case.initial.end()); + auto in = wrap_input_decay(test_case.input); + + c.prepend_range(in); + validate(c); + return std::ranges::equal(c, test_case.expected); + }; + + { // Empty container. + // empty_c.prepend_range(empty_range) + assert(test(EmptyContainer_EmptyRange)); + // empty_c.prepend_range(one_element_range) + assert(test(EmptyContainer_OneElementRange)); + // empty_c.prepend_range(mid_range) + assert(test(EmptyContainer_MidRange)); + } + + { // One-element container. + // one_element_c.prepend_range(empty_range) + assert(test(OneElementContainer_Begin_EmptyRange)); + // one_element_c.prepend_range(one_element_range) + assert(test(OneElementContainer_Begin_OneElementRange)); + // one_element_c.prepend_range(mid_range) + assert(test(OneElementContainer_Begin_MidRange)); + } + + { // Full container. + // full_container.prepend_range(empty_range) + assert(test(FullContainer_Begin_EmptyRange)); + // full_container.prepend_range(one_element_range) + assert(test(FullContainer_Begin_OneElementRange)); + // full_container.prepend_range(mid_range) + assert(test(FullContainer_Begin_MidRange)); + // full_container.prepend_range(long_range) + assert(test(FullContainer_Begin_LongRange)); + } +} + template constexpr void test_sequence_append_range(Validate validate) { using T = typename Container::value_type; @@ -550,6 +664,50 @@ constexpr void test_sequence_append_range(Validate validate) { } } +// https://llvm.org/PR159943 +template +constexpr void test_sequence_append_range_decay(Validate validate) { + using T = Container::value_type; + + auto test = [&](auto& test_case) { + Container c(test_case.initial.begin(), test_case.initial.end()); + auto in = wrap_input_decay(test_case.input); + + c.append_range(in); + validate(c); + return std::ranges::equal(c, test_case.expected); + }; + + { // Empty container. + // empty_c.append_range(empty_range) + assert(test(EmptyContainer_EmptyRange)); + // empty_c.append_range(one_element_range) + assert(test(EmptyContainer_OneElementRange)); + // empty_c.append_range(mid_range) + assert(test(EmptyContainer_MidRange)); + } + + { // One-element container. + // one_element_c.append_range(empty_range) + assert(test(OneElementContainer_End_EmptyRange)); + // one_element_c.append_range(one_element_range) + assert(test(OneElementContainer_End_OneElementRange)); + // one_element_c.append_range(mid_range) + assert(test(OneElementContainer_End_MidRange)); + } + + { // Full container. + // full_container.append_range(empty_range) + assert(test(FullContainer_End_EmptyRange)); + // full_container.append_range(one_element_range) + assert(test(FullContainer_End_OneElementRange)); + // full_container.append_range(mid_range) + assert(test(FullContainer_End_MidRange)); + // full_container.append_range(long_range) + assert(test(FullContainer_End_LongRange)); + } +} + template constexpr void test_sequence_assign_range(Validate validate) { using T = typename Container::value_type; @@ -605,6 +763,62 @@ constexpr void test_sequence_assign_range(Validate validate) { } } +// https://llvm.org/PR159943 +template +constexpr void test_sequence_assign_range_decay(Validate validate) { + using T = Container::value_type; + + auto& initial_empty = EmptyContainer_EmptyRange.initial; + auto& initial_one_element = OneElementContainer_Begin_EmptyRange.initial; + auto& initial_full = FullContainer_Begin_EmptyRange.initial; + auto& input_empty = FullContainer_Begin_EmptyRange.input; + auto& input_one_element = FullContainer_Begin_OneElementRange.input; + auto& input_mid_range = FullContainer_Begin_MidRange.input; + auto& input_long_range = FullContainer_Begin_LongRange.input; + + auto test = [&](auto& initial, auto& input) { + Container c(initial.begin(), initial.end()); + auto in = wrap_input_decay(input); + + c.assign_range(in); + validate(c); + return std::ranges::equal(c, input); + }; + + { // Empty container. + // empty_container.assign_range(empty_range) + assert(test(initial_empty, input_empty)); + // empty_container.assign_range(one_element_range) + assert(test(initial_empty, input_one_element)); + // empty_container.assign_range(mid_range) + assert(test(initial_empty, input_mid_range)); + // empty_container.assign_range(long_range) + assert(test(initial_empty, input_long_range)); + } + + { // One-element container. + // one_element_container.assign_range(empty_range) + assert(test(initial_one_element, input_empty)); + // one_element_container.assign_range(one_element_range) + assert(test(initial_one_element, input_one_element)); + // one_element_container.assign_range(mid_range) + assert(test(initial_one_element, input_mid_range)); + // one_element_container.assign_range(long_range) + assert(test(initial_one_element, input_long_range)); + } + + { // Full container. + // full_container.assign_range(empty_range) + assert(test(initial_full, input_empty)); + // full_container.assign_range(one_element_range) + assert(test(initial_full, input_one_element)); + // full_container.assign_range(mid_range) + assert(test(initial_full, input_mid_range)); + // full_container.assign_range(long_range) + assert(test(initial_full, input_long_range)); + } +} + // Move-only types. template