[libcxx-commits] [libcxx] [libc++] Fix insertion into `deque` from prvalue ranges (PR #160022)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Mon Sep 22 10:13:03 PDT 2025


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/160022

>From 029fe7413c7bc42721db531c62130700fd36d853 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 22 Sep 2025 12:00:40 +0800
Subject: [PATCH 1/3] [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.
---
 libcxx/include/deque                          |  11 +-
 .../test/std/containers/from_range_helpers.h  |  36 +++
 .../deque.modifiers/append_range.pass.cpp     |   3 +
 .../deque.modifiers/assign_range.pass.cpp     |   3 +
 .../deque.modifiers/insert_range.pass.cpp     |   3 +
 .../deque.modifiers/prepend_range.pass.cpp    |   3 +
 .../insert_range_sequence_containers.h        | 214 ++++++++++++++++++
 7 files changed, 271 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index 98d1dbbddb7e8..ea83c50bceea0 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -185,6 +185,7 @@ template <class T, class Allocator, class Predicate>
 #  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>
@@ -1916,6 +1917,12 @@ template <class _Tp, class _Allocator>
 template <class _BiIter>
 _LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator
 deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f, _BiIter __l, size_type __n) {
+#  if _LIBCPP_STD_VER >= 20
+  using _Ops = _IterOps<
+      conditional_t<__has_bidirectional_iterator_category<_BiIter>::value, _ClassicAlgPolicy, _RangeAlgPolicy>>;
+#  else
+  using _Ops = _IterOps<_ClassicAlgPolicy>;
+#  endif
   size_type __pos     = __p - begin();
   size_type __to_end  = size() - __pos;
   allocator_type& __a = __alloc();
@@ -1928,7 +1935,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 +1962,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..e1895a528efb2 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 <array>
+#include <concepts>
 #include <cstddef>
 #include <iterator>
+#include <ranges>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "min_allocator.h"
@@ -50,6 +53,39 @@ constexpr auto wrap_input(std::vector<T>& input) {
   return std::ranges::subrange(std::move(b), std::move(e));
 }
 
+// https://llvm.org/PR159943
+struct DecayCopy {
+  template <class T>
+    requires std::convertible_to<T, std::decay_t<T>>
+  static constexpr std::decay_t<T> operator()(T&& t) {
+    return std::forward<T>(t);
+  }
+};
+
+template <class Iter, class Sent, std::ranges::input_range Range>
+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<std::iter_reference_t<Iter>>)
+    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 <class Iter, class Sent, class T, std::size_t N>
+constexpr auto wrap_input_decay(std::array<T, N>& 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 <class Iter, class Sent, class T>
+constexpr auto wrap_input_decay(std::vector<T>& 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<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
       LIBCPP_ASSERT(c.__invariants());
     });
+    test_sequence_append_range_decay<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
+      LIBCPP_ASSERT(c.__invariants());
+    });
   });
   test_sequence_append_range_move_only<std::deque>();
 
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<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
       LIBCPP_ASSERT(c.__invariants());
     });
+    test_sequence_assign_range_decay<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
+      LIBCPP_ASSERT(c.__invariants());
+    });
   });
   test_sequence_assign_range_move_only<std::deque>();
 
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<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
       LIBCPP_ASSERT(c.__invariants());
     });
+    test_sequence_insert_range_decay<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
+      LIBCPP_ASSERT(c.__invariants());
+    });
   });
   test_sequence_insert_range_move_only<std::deque>();
 
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<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
       LIBCPP_ASSERT(c.__invariants());
     });
+    test_sequence_prepend_range_decay<std::deque<int, Alloc>, Iter, Sent>([]([[maybe_unused]] auto&& c) {
+      LIBCPP_ASSERT(c.__invariants());
+    });
   });
   test_sequence_prepend_range_move_only<std::deque>();
 
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 <class Container, class Iter, class Sent, class Validate>
+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<D>(test_case.index)); };
+
+  auto test = [&](auto& test_case) {
+    Container c(test_case.initial.begin(), test_case.initial.end());
+    auto in  = wrap_input_decay<Iter, Sent>(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<T>));
+    // empty_c.insert_range(end, one_element_range)
+    assert(test(EmptyContainer_OneElementRange<T>));
+    // empty_c.insert_range(end, mid_range)
+    assert(test(EmptyContainer_MidRange<T>));
+  }
+
+  { // One-element container.
+    // one_element_c.insert_range(begin, empty_range)
+    assert(test(OneElementContainer_Begin_EmptyRange<T>));
+    // one_element_c.insert_range(end, empty_range)
+    assert(test(OneElementContainer_End_EmptyRange<T>));
+    // one_element_c.insert_range(begin, one_element_range)
+    assert(test(OneElementContainer_Begin_OneElementRange<T>));
+    // one_element_c.insert_range(end, one_element_range)
+    assert(test(OneElementContainer_End_OneElementRange<T>));
+    // one_element_c.insert_range(begin, mid_range)
+    assert(test(OneElementContainer_Begin_MidRange<T>));
+    // one_element_c.insert_range(end, mid_range)
+    assert(test(OneElementContainer_End_MidRange<T>));
+  }
+
+  { // Full container.
+    // full_container.insert_range(begin, empty_range)
+    assert(test(FullContainer_Begin_EmptyRange<T>));
+    // full_container.insert_range(mid, empty_range)
+    assert(test(FullContainer_Mid_EmptyRange<T>));
+    // full_container.insert_range(end, empty_range)
+    assert(test(FullContainer_End_EmptyRange<T>));
+    // full_container.insert_range(begin, one_element_range)
+    assert(test(FullContainer_Begin_OneElementRange<T>));
+    // full_container.insert_range(end, one_element_range)
+    assert(test(FullContainer_Mid_OneElementRange<T>));
+    // full_container.insert_range(end, one_element_range)
+    assert(test(FullContainer_End_OneElementRange<T>));
+    // full_container.insert_range(begin, mid_range)
+    assert(test(FullContainer_Begin_MidRange<T>));
+    // full_container.insert_range(mid, mid_range)
+    assert(test(FullContainer_Mid_MidRange<T>));
+    // full_container.insert_range(end, mid_range)
+    assert(test(FullContainer_End_MidRange<T>));
+    // full_container.insert_range(begin, long_range)
+    assert(test(FullContainer_Begin_LongRange<T>));
+    // full_container.insert_range(mid, long_range)
+    assert(test(FullContainer_Mid_LongRange<T>));
+    // full_container.insert_range(end, long_range)
+    assert(test(FullContainer_End_LongRange<T>));
+  }
+}
+
 template <class Container, class Iter, class Sent, class Validate>
 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 <class Container, class Iter, class Sent, class Validate>
+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<Iter, Sent>(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<T>));
+    // empty_c.prepend_range(one_element_range)
+    assert(test(EmptyContainer_OneElementRange<T>));
+    // empty_c.prepend_range(mid_range)
+    assert(test(EmptyContainer_MidRange<T>));
+  }
+
+  { // One-element container.
+    // one_element_c.prepend_range(empty_range)
+    assert(test(OneElementContainer_Begin_EmptyRange<T>));
+    // one_element_c.prepend_range(one_element_range)
+    assert(test(OneElementContainer_Begin_OneElementRange<T>));
+    // one_element_c.prepend_range(mid_range)
+    assert(test(OneElementContainer_Begin_MidRange<T>));
+  }
+
+  { // Full container.
+    // full_container.prepend_range(empty_range)
+    assert(test(FullContainer_Begin_EmptyRange<T>));
+    // full_container.prepend_range(one_element_range)
+    assert(test(FullContainer_Begin_OneElementRange<T>));
+    // full_container.prepend_range(mid_range)
+    assert(test(FullContainer_Begin_MidRange<T>));
+    // full_container.prepend_range(long_range)
+    assert(test(FullContainer_Begin_LongRange<T>));
+  }
+}
+
 template <class Container, class Iter, class Sent, class Validate>
 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 <class Container, class Iter, class Sent, class Validate>
+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<Iter, Sent>(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<T>));
+    // empty_c.append_range(one_element_range)
+    assert(test(EmptyContainer_OneElementRange<T>));
+    // empty_c.append_range(mid_range)
+    assert(test(EmptyContainer_MidRange<T>));
+  }
+
+  { // One-element container.
+    // one_element_c.append_range(empty_range)
+    assert(test(OneElementContainer_End_EmptyRange<T>));
+    // one_element_c.append_range(one_element_range)
+    assert(test(OneElementContainer_End_OneElementRange<T>));
+    // one_element_c.append_range(mid_range)
+    assert(test(OneElementContainer_End_MidRange<T>));
+  }
+
+  { // Full container.
+    // full_container.append_range(empty_range)
+    assert(test(FullContainer_End_EmptyRange<T>));
+    // full_container.append_range(one_element_range)
+    assert(test(FullContainer_End_OneElementRange<T>));
+    // full_container.append_range(mid_range)
+    assert(test(FullContainer_End_MidRange<T>));
+    // full_container.append_range(long_range)
+    assert(test(FullContainer_End_LongRange<T>));
+  }
+}
+
 template <class Container, class Iter, class Sent, class Validate>
 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 <class Container, class Iter, class Sent, class Validate>
+constexpr void test_sequence_assign_range_decay(Validate validate) {
+  using T = Container::value_type;
+
+  auto& initial_empty       = EmptyContainer_EmptyRange<T>.initial;
+  auto& initial_one_element = OneElementContainer_Begin_EmptyRange<T>.initial;
+  auto& initial_full        = FullContainer_Begin_EmptyRange<T>.initial;
+  auto& input_empty         = FullContainer_Begin_EmptyRange<T>.input;
+  auto& input_one_element   = FullContainer_Begin_OneElementRange<T>.input;
+  auto& input_mid_range     = FullContainer_Begin_MidRange<T>.input;
+  auto& input_long_range    = FullContainer_Begin_LongRange<T>.input;
+
+  auto test = [&](auto& initial, auto& input) {
+    Container c(initial.begin(), initial.end());
+    auto in = wrap_input_decay<Iter, Sent>(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 <template <class...> class Container>

>From bf74931328b6f2625da532b07f0b42e071b30378 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 22 Sep 2025 15:31:48 +0800
Subject: [PATCH 2/3] Pass policy tags via template parameters/arguments

---
 libcxx/include/deque | 21 ++++++++-------------
 1 file changed, 8 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index ea83c50bceea0..da137964d6f8e 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -833,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<size_type>(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<size_type>(ranges::distance(__range));
@@ -1203,10 +1203,10 @@ private:
   template <class _Iterator>
   _LIBCPP_HIDE_FROM_ABI iterator __insert_with_size(const_iterator __p, _Iterator __f, size_type __n);
 
-  template <class _BiIter, class _Sentinel>
+  template <class _IterOpsPolicy, class _BiIter, class _Sentinel>
   _LIBCPP_HIDE_FROM_ABI iterator
   __insert_bidirectional(const_iterator __p, _BiIter __f, _Sentinel __sent, size_type __n);
-  template <class _BiIter>
+  template <class _IterOpsPolicy, class _BiIter>
   _LIBCPP_HIDE_FROM_ABI iterator __insert_bidirectional(const_iterator __p, _BiIter __f, _BiIter __l, size_type __n);
 
   template <class _InpIter, __enable_if_t<__has_exactly_input_iterator_category<_InpIter>::value, int> = 0>
@@ -1903,26 +1903,21 @@ deque<_Tp, _Allocator>::__insert_with_size(const_iterator __p, _Iterator __f, si
 template <class _Tp, class _Allocator>
 template <class _BiIter, __enable_if_t<__has_bidirectional_iterator_category<_BiIter>::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 <class _Tp, class _Allocator>
-template <class _BiIter, class _Sentinel>
+template <class _IterOpsPolicy, class _BiIter, class _Sentinel>
 _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<_IterOpsPolicy>(__p, __f, _IterOps<_IterOpsPolicy>::next(__f, __n), __n);
 }
 
 template <class _Tp, class _Allocator>
-template <class _BiIter>
+template <class _IterOpsPolicy, class _BiIter>
 _LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator
 deque<_Tp, _Allocator>::__insert_bidirectional(const_iterator __p, _BiIter __f, _BiIter __l, size_type __n) {
-#  if _LIBCPP_STD_VER >= 20
-  using _Ops = _IterOps<
-      conditional_t<__has_bidirectional_iterator_category<_BiIter>::value, _ClassicAlgPolicy, _RangeAlgPolicy>>;
-#  else
-  using _Ops = _IterOps<_ClassicAlgPolicy>;
-#  endif
+  using _Ops          = _IterOps<_IterOpsPolicy>;
   size_type __pos     = __p - begin();
   size_type __to_end  = size() - __pos;
   allocator_type& __a = __alloc();

>From 45a26008841562000633700e8f4cfd6cbb3f8df9 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 23 Sep 2025 01:12:26 +0800
Subject: [PATCH 3/3] Change policy template parameter name; add issue
 description

---
 libcxx/include/deque                            | 12 ++++++------
 libcxx/test/std/containers/from_range_helpers.h |  2 ++
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index da137964d6f8e..e7f58a43b78a0 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -1203,10 +1203,10 @@ private:
   template <class _Iterator>
   _LIBCPP_HIDE_FROM_ABI iterator __insert_with_size(const_iterator __p, _Iterator __f, size_type __n);
 
-  template <class _IterOpsPolicy, class _BiIter, class _Sentinel>
+  template <class _AlgPolicy, class _BiIter, class _Sentinel>
   _LIBCPP_HIDE_FROM_ABI iterator
   __insert_bidirectional(const_iterator __p, _BiIter __f, _Sentinel __sent, size_type __n);
-  template <class _IterOpsPolicy, class _BiIter>
+  template <class _AlgPolicy, class _BiIter>
   _LIBCPP_HIDE_FROM_ABI iterator __insert_bidirectional(const_iterator __p, _BiIter __f, _BiIter __l, size_type __n);
 
   template <class _InpIter, __enable_if_t<__has_exactly_input_iterator_category<_InpIter>::value, int> = 0>
@@ -1907,17 +1907,17 @@ typename deque<_Tp, _Allocator>::iterator deque<_Tp, _Allocator>::insert(const_i
 }
 
 template <class _Tp, class _Allocator>
-template <class _IterOpsPolicy, class _BiIter, class _Sentinel>
+template <class _AlgPolicy, class _BiIter, class _Sentinel>
 _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<_IterOpsPolicy>(__p, __f, _IterOps<_IterOpsPolicy>::next(__f, __n), __n);
+  return __insert_bidirectional<_AlgPolicy>(__p, __f, _IterOps<_AlgPolicy>::next(__f, __n), __n);
 }
 
 template <class _Tp, class _Allocator>
-template <class _IterOpsPolicy, class _BiIter>
+template <class _AlgPolicy, class _BiIter>
 _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<_IterOpsPolicy>;
+  using _Ops          = _IterOps<_AlgPolicy>;
   size_type __pos     = __p - begin();
   size_type __to_end  = size() - __pos;
   allocator_type& __a = __alloc();
diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h
index e1895a528efb2..1ef0d1ccde191 100644
--- a/libcxx/test/std/containers/from_range_helpers.h
+++ b/libcxx/test/std/containers/from_range_helpers.h
@@ -54,6 +54,8 @@ constexpr auto wrap_input(std::vector<T>& input) {
 }
 
 // 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 <class T>
     requires std::convertible_to<T, std::decay_t<T>>



More information about the libcxx-commits mailing list