https://gcc.gnu.org/g:7a4fced2b122bde47fed2d99fb8e3cf197f9c46f

commit r15-4853-g7a4fced2b122bde47fed2d99fb8e3cf197f9c46f
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Tue Oct 8 21:15:18 2024 +0100

    libstdc++: Add P1206R7 from_range members to std::list and 
std::forward_list [PR111055]
    
    This is another piece of P1206R7, adding new members to std::list and
    std::forward_list.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/111055
            * include/bits/forward_list.h
            (forward_list(from_range, R&&, const Alloc&), assign_range)
            (prepend_range, insert_range_after): Define.
            * include/bits/stl_list.h (list(from_range, R&&, const Alloc&))
            (assign_range, prepend_range, append_range, insert_range):
            Define.
            * include/debug/forward_list
            (forward_list(from_range, R&&, const Alloc&), assign_range)
            (prepend_range, insert_range_after): Define.
            * include/debug/list (list(from_range, R&&, const Alloc&))
            (assign_range, prepend_range, append_range, insert_range):
            Define.
            * testsuite/23_containers/forward_list/cons/from_range.cc: New
            test.
            * testsuite/23_containers/forward_list/modifiers/assign_range.cc:
            New test.
            * 
testsuite/23_containers/forward_list/modifiers/insert_range_after.cc:
            New test.
            * testsuite/23_containers/forward_list/modifiers/prepend_range.cc:
            New test.
            * testsuite/23_containers/list/cons/from_range.cc: New test.
            * testsuite/23_containers/list/modifiers/append_range.cc: New
            test.
            * testsuite/23_containers/list/modifiers/assign/assign_range.cc:
            New test.
            * testsuite/23_containers/list/modifiers/insert/insert_range.cc:
            New test.
            * testsuite/23_containers/list/modifiers/prepend_range.cc: New
            test.
    
    Reviewed-by: Patrick Palka <ppa...@redhat.com>

Diff:
---
 libstdc++-v3/include/bits/forward_list.h           | 121 +++++++++++++++++++
 libstdc++-v3/include/bits/stl_list.h               | 128 +++++++++++++++++++++
 libstdc++-v3/include/debug/forward_list            |  63 ++++++++++
 libstdc++-v3/include/debug/list                    |  56 +++++++++
 .../23_containers/forward_list/cons/from_range.cc  | 100 ++++++++++++++++
 .../forward_list/modifiers/assign_range.cc         |  97 ++++++++++++++++
 .../forward_list/modifiers/insert_range_after.cc   |  93 +++++++++++++++
 .../forward_list/modifiers/prepend_range.cc        |  86 ++++++++++++++
 .../23_containers/list/cons/from_range.cc          | 100 ++++++++++++++++
 .../23_containers/list/modifiers/append_range.cc   |  86 ++++++++++++++
 .../list/modifiers/assign/assign_range.cc          |  99 ++++++++++++++++
 .../list/modifiers/insert/insert_range.cc          |  98 ++++++++++++++++
 .../23_containers/list/modifiers/prepend_range.cc  |  86 ++++++++++++++
 13 files changed, 1213 insertions(+)

diff --git a/libstdc++-v3/include/bits/forward_list.h 
b/libstdc++-v3/include/bits/forward_list.h
index eee773c02121..c9238cef96fa 100644
--- a/libstdc++-v3/include/bits/forward_list.h
+++ b/libstdc++-v3/include/bits/forward_list.h
@@ -42,6 +42,10 @@
 #include <bits/allocator.h>
 #include <ext/alloc_traits.h>
 #include <ext/aligned_buffer.h>
+#if __glibcxx_ranges_to_container // C++ >= 23
+# include <bits/ranges_base.h> // ranges::begin, ranges::distance etc.
+# include <bits/ranges_util.h> // ranges::subrange
+#endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -564,6 +568,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        : _Base(_Node_alloc_type(__al))
        { _M_range_initialize(__first, __last); }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      /**
+       * @brief Construct a forward_list from a range.
+       * @param __rg An input range with elements that are convertible to
+       *             the forward_list's value_type.
+       * @param __a An allocator.
+       *
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       forward_list(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc())
+       : _Base(_Node_alloc_type(__a))
+       {
+         _Node_base* __to = &this->_M_impl._M_head;
+         auto __first = ranges::begin(__rg);
+         const auto __last = ranges::end(__rg);
+         for (; __first != __last; ++__first)
+           {
+             __to->_M_next = this->_M_create_node(*__first);
+             __to = __to->_M_next;
+           }
+       }
+#endif // ranges_to_container
+
       /**
        *  @brief  The %forward_list copy constructor.
        *  @param  __list  A %forward_list of identical element and allocator
@@ -675,6 +703,39 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
          _M_assign(__first, __last, __assignable());
        }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      /**
+       * @brief Assign a range to a forward_list.
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       void
+       assign_range(_Rg&& __rg)
+       {
+         static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>);
+
+         auto __first = ranges::begin(__rg);
+         const auto __last = ranges::end(__rg);
+         iterator __prev = before_begin();
+         iterator __curr = begin();
+         const iterator __end = end();
+
+         while (__curr != __end && __first != __last)
+           {
+             *__curr = *__first;
+             __prev = __curr;
+             ++__first;
+             ++__curr;
+           }
+
+         if (__curr != __end)
+           erase_after(__prev, __end);
+         else
+           insert_range_after(__prev,
+                              ranges::subrange(std::move(__first), __last));
+       }
+#endif // ranges_to_container
+
       /**
        *  @brief  Assigns a given value to a %forward_list.
        *  @param  __n  Number of elements to be assigned.
@@ -888,6 +949,33 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       push_front(_Tp&& __val)
       { this->_M_insert_after(cbefore_begin(), std::move(__val)); }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      /**
+       * @brief Insert a range at the beginning of a forward_list.
+       * @param __rg An input range with elements that are convertible to
+       *             the forward_list's value_type.
+       *
+       * The inserted elements will be in the same order as in the range,
+       * so they are not reversed as would happen with a simple loop calling
+       * emplace_front for each element of the range.
+       *
+       * No iterators to existing elements are invalidated by this function.
+       * If the insertion fails due to an exception, no elements will be added
+       * and so the list will be unchanged.
+       *
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       void
+       prepend_range(_Rg&& __rg)
+       {
+         forward_list __tmp(from_range, std::forward<_Rg>(__rg),
+                            get_allocator());
+         if (!__tmp.empty())
+           splice_after(before_begin(), __tmp);
+       }
+#endif // ranges_to_container
+
       /**
        *  @brief  Removes first element.
        *
@@ -1004,6 +1092,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       insert_after(const_iterator __pos, std::initializer_list<_Tp> __il)
       { return insert_after(__pos, __il.begin(), __il.end()); }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      /**
+       * @brief Insert a rangeinto a forward_list.
+       * @param  __position An iterator.
+       * @param  __rg An input range of elements that can be converted to
+       *              the forward_list's value type.
+       * @return An iterator pointing to the last element inserted,
+       *         or `__position` if the range is empty.
+       *
+       * Inserts the elements of `__rg` after `__position`.
+       * No iterators to existing elements are invalidated by this function.
+       * If the insertion fails due to an exception, no elements will be added
+       * and so the list will be unchanged.
+       *
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       iterator
+       insert_range_after(const_iterator __position, _Rg&& __rg)
+       {
+         forward_list __tmp(from_range, std::forward<_Rg>(__rg),
+                            get_allocator());
+         return _M_splice_after(__position, __tmp.before_begin(), __tmp.end());
+       }
+#endif // ranges_to_container
+
       /**
        *  @brief  Removes the element pointed to by the iterator following
        *          @c pos.
@@ -1438,6 +1552,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
           typename = _RequireAllocator<_Allocator>>
     forward_list(_InputIterator, _InputIterator, _Allocator = _Allocator())
       -> forward_list<_ValT, _Allocator>;
+
+#if __glibcxx_ranges_to_container // C++ >= 23
+  template<ranges::input_range _Rg,
+          typename _Allocator = allocator<ranges::range_value_t<_Rg>>>
+    forward_list(from_range_t, _Rg&&, _Allocator = _Allocator())
+      -> forward_list<ranges::range_value_t<_Rg>, _Allocator>;
+#endif
 #endif
 
   /**
diff --git a/libstdc++-v3/include/bits/stl_list.h 
b/libstdc++-v3/include/bits/stl_list.h
index 8b2521960a8f..3c313cec1d89 100644
--- a/libstdc++-v3/include/bits/stl_list.h
+++ b/libstdc++-v3/include/bits/stl_list.h
@@ -64,6 +64,10 @@
 #include <bits/allocated_ptr.h>
 #include <ext/aligned_buffer.h>
 #endif
+#if __glibcxx_ranges_to_container // C++ >= 23
+# include <bits/ranges_base.h> // ranges::begin, ranges::distance etc.
+# include <bits/ranges_util.h> // ranges::subrange
+#endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -892,6 +896,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        }
 #endif
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      /**
+       * @brief Construct a list from a range.
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       list(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc())
+       : _Base(_Node_alloc_type(__a))
+       {
+         auto __first = ranges::begin(__rg);
+         const auto __last = ranges::end(__rg);
+         for (; __first != __last; ++__first)
+           emplace_back(*__first);
+       }
+#endif
+
 #if __cplusplus >= 201103L
       /**
        *  No explicit dtor needed as the _Base dtor takes care of
@@ -951,6 +971,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       }
 #endif
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      /**
+       * @brief Assign a range to a list.
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       void
+       assign_range(_Rg&& __rg)
+       {
+         static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>);
+
+         iterator __first1 = begin();
+         const iterator __last1 = end();
+         auto __first2 = ranges::begin(__rg);
+         const auto __last2 = ranges::end(__rg);
+         for (; __first1 != __last1 && __first2 != __last2;
+              ++__first1, (void)++__first2)
+           *__first1 = *__first2;
+         if (__first2 == __last2)
+           erase(__first1, __last1);
+         else
+           insert_range(__last1,
+                        ranges::subrange(std::move(__first2), __last2));
+       }
+#endif
+
       /**
        *  @brief  Assigns a given value to a %list.
        *  @param  __n  Number of elements to be assigned.
@@ -1275,6 +1321,50 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        }
 #endif
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      /**
+       * @brief Insert a range at the beginning of a list.
+       * @param  __rg An input range of elements that can be converted to
+       *              the list's value type.
+       *
+       * Inserts the elements of `__rg` at the beginning of the list.
+       * No iterators to existing elements are invalidated by this function.
+       * If the insertion fails due to an exception, no elements will be added
+       * and so the list will be unchanged.
+       *
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       void
+       prepend_range(_Rg&& __rg)
+       {
+         list __tmp(from_range, std::forward<_Rg>(__rg), get_allocator());
+         if (!__tmp.empty())
+           splice(begin(), __tmp);
+       }
+
+      /**
+       * @brief Insert a range at the end of a list.
+       * @param  __rg An input range of elements that can be converted to
+       *              the list's value type.
+       *
+       * Inserts the elements of `__rg` at the end of the list.
+       * No iterators to existing elements are invalidated by this function.
+       * If the insertion fails due to an exception, no elements will be added
+       * and so the list will be unchanged.
+       *
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       void
+       append_range(_Rg&& __rg)
+       {
+         list __tmp(from_range, std::forward<_Rg>(__rg), get_allocator());
+         if (!__tmp.empty())
+           splice(end(), __tmp);
+       }
+#endif
+
       /**
        *  @brief  Removes first element.
        *
@@ -1505,6 +1595,37 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        }
 #endif
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      /**
+       * @brief Insert a range into a list.
+       * @param  __position An iterator.
+       * @param  __rg An input range of elements that can be converted to
+       *              the list's value type.
+       * @return An iterator pointing to the first element inserted,
+       *         or `__position` if the range is empty.
+       *
+       * Inserts the elements of `__rg` before `__position`.
+       * No iterators to existing elements are invalidated by this function.
+       * If the insertion fails due to an exception, no elements will be added
+       * and so the list will be unchanged.
+       *
+       * @since C++23
+       */
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       iterator
+       insert_range(const_iterator __position, _Rg&& __rg)
+       {
+         list __tmp(from_range, std::forward<_Rg>(__rg), get_allocator());
+         if (!__tmp.empty())
+           {
+             auto __it = __tmp.begin();
+             splice(__position, __tmp);
+             return __it;
+           }
+         return __position._M_const_cast();
+       }
+#endif
+
       /**
        *  @brief  Remove element at given position.
        *  @param  __position  Iterator pointing to element to be erased.
@@ -2102,6 +2223,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
           typename = _RequireAllocator<_Allocator>>
     list(_InputIterator, _InputIterator, _Allocator = _Allocator())
       -> list<_ValT, _Allocator>;
+
+#if __glibcxx_ranges_to_container // C++ >= 23
+  template<ranges::input_range _Rg,
+          typename _Allocator = allocator<ranges::range_value_t<_Rg>>>
+    list(from_range_t, _Rg&&, _Allocator = _Allocator())
+      -> list<ranges::range_value_t<_Rg>, _Allocator>;
+#endif
 #endif
 
 _GLIBCXX_END_NAMESPACE_CXX11
diff --git a/libstdc++-v3/include/debug/forward_list 
b/libstdc++-v3/include/debug/forward_list
index 3f94a9098e86..73389c466771 100644
--- a/libstdc++-v3/include/debug/forward_list
+++ b/libstdc++-v3/include/debug/forward_list
@@ -267,6 +267,13 @@ namespace __debug
                __gnu_debug::__base(__last), __al)
        { }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       forward_list(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc())
+       : _Base(std::from_range, std::forward<_Rg>(__rg), __a)
+       { }
+#endif
+
       forward_list(const forward_list&) = default;
 
       forward_list(forward_list&&) = default;
@@ -311,6 +318,39 @@ namespace __debug
          this->_M_invalidate_all();
        }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       void
+       assign_range(_Rg&& __rg)
+       {
+         // Have to reimplement this function, so that we use the debug
+         // version of erase_after, which invalidates the correct iterators.
+
+         static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>);
+
+         auto __first = ranges::begin(__rg);
+         const auto __last = ranges::end(__rg);
+         auto __prev = _Base::before_begin();
+         auto __curr = _Base::begin();
+         const auto __end = _Base::end();
+
+         while (__curr != __end && __first != __last)
+           {
+             *__curr = *__first;
+             __prev = __curr;
+             ++__first;
+             ++__curr;
+           }
+
+         if (__curr != __end)
+           erase_after(const_iterator(__prev, this), end());
+         else
+           _Base::insert_range_after(__prev,
+                                     ranges::subrange(std::move(__first),
+                                                      __last));
+       }
+#endif
+
       void
       assign(size_type __n, const _Tp& __val)
       {
@@ -400,6 +440,10 @@ namespace __debug
       using _Base::emplace_front;
       using _Base::push_front;
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      using _Base::prepend_range;
+#endif
+
       void
       pop_front()
       {
@@ -468,6 +512,18 @@ namespace __debug
        return { _Base::insert_after(__pos.base(), __il), this };
       }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       iterator
+       insert_range_after(const_iterator __position, _Rg&& __rg)
+       {
+         auto __ret
+           = _Base::insert_range_after(__position.base(),
+                                       std::forward<_Rg>(__rg));
+         return { __ret, this };
+       }
+#endif
+
       iterator
       erase_after(const_iterator __pos)
       {
@@ -860,6 +916,13 @@ namespace __debug
           typename = _RequireAllocator<_Allocator>>
     forward_list(size_t, _Tp, _Allocator = _Allocator())
       -> forward_list<_Tp, _Allocator>;
+
+#if __glibcxx_ranges_to_container // C++ >= 23
+  template<ranges::input_range _Rg,
+          typename _Allocator = allocator<ranges::range_value_t<_Rg>>>
+    forward_list(from_range_t, _Rg&&, _Allocator = _Allocator())
+      -> forward_list<ranges::range_value_t<_Rg>, _Allocator>;
+#endif
 #endif
 
   template<typename _Tp, typename _Alloc>
diff --git a/libstdc++-v3/include/debug/list b/libstdc++-v3/include/debug/list
index 60752b4dc8b1..4028a3d39856 100644
--- a/libstdc++-v3/include/debug/list
+++ b/libstdc++-v3/include/debug/list
@@ -160,6 +160,13 @@ namespace __debug
                __gnu_debug::__base(__last), __a)
        { }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       list(from_range_t, _Rg&& __rg, const _Allocator& __a = _Allocator())
+       : _Base(std::from_range, std::forward<_Rg>(__rg), __a)
+       { }
+#endif
+
       list(_Base_ref __x)
       : _Base(__x._M_ref) { }
 
@@ -207,6 +214,32 @@ namespace __debug
          this->_M_invalidate_all();
        }
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       void
+       assign_range(_Rg&& __rg)
+       {
+         // Have to reimplement this function, so that we use the debug
+         // version of erase, which invalidates the correct iterators.
+
+         static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>);
+
+         auto __first1 = _Base::begin();
+         const auto __last1 = _Base::end();
+         auto __first2 = ranges::begin(__rg);
+         const auto __last2 = ranges::end(__rg);
+         for (; __first1 != __last1 && __first2 != __last2;
+              ++__first1, (void)++__first2)
+           *__first1 = *__first2;
+         if (__first2 == __last2)
+           erase(const_iterator(__first1, this),
+                 const_iterator(__last1, this));
+         else
+           _Base::insert_range(__last1,
+                               ranges::subrange(std::move(__first2), __last2));
+       }
+#endif
+
       void
       assign(size_type __n, const _Tp& __t)
       {
@@ -401,6 +434,11 @@ namespace __debug
       using _Base::emplace_front;
 #endif
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      using _Base::prepend_range;
+      using _Base::append_range;
+#endif
+
       void
       pop_front() _GLIBCXX_NOEXCEPT
       {
@@ -511,6 +549,17 @@ namespace __debug
        }
 #endif
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+      template<__detail::__container_compatible_range<_Tp> _Rg>
+       iterator
+       insert_range(const_iterator __position, _Rg&& __rg)
+       {
+         auto __ret = _Base::insert_range(__position.base(),
+                                          std::forward<_Rg>(__rg));
+         return { __ret, this };
+       }
+#endif
+
     private:
       _Base_iterator
 #if __cplusplus >= 201103L
@@ -920,6 +969,13 @@ namespace __debug
           typename = _RequireAllocator<_Allocator>>
     list(size_t, _Tp, _Allocator = _Allocator())
       -> list<_Tp, _Allocator>;
+
+#if __glibcxx_ranges_to_container // C++ >= 23
+  template<ranges::input_range _Rg,
+          typename _Allocator = allocator<ranges::range_value_t<_Rg>>>
+    list(from_range_t, _Rg&&, _Allocator = _Allocator())
+      -> list<ranges::range_value_t<_Rg>, _Allocator>;
+#endif
 #endif
 
   template<typename _Tp, typename _Alloc>
diff --git 
a/libstdc++-v3/testsuite/23_containers/forward_list/cons/from_range.cc 
b/libstdc++-v3/testsuite/23_containers/forward_list/cons/from_range.cc
new file mode 100644
index 000000000000..65b378e69773
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/cons/from_range.cc
@@ -0,0 +1,100 @@
+// { dg-do run { target c++23 } }
+
+#include <forward_list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+void
+test_deduction_guide(long* p)
+{
+  __gnu_test::test_input_range<long> r(p, p);
+  std::forward_list v(std::from_range, r);
+  static_assert(std::is_same_v<decltype(v), std::forward_list<long>>);
+
+  using Alloc = __gnu_test::SimpleAllocator<long>;
+  Alloc alloc;
+  std::forward_list v2(std::from_range, r, alloc);
+  static_assert(std::is_same_v<decltype(v2), std::forward_list<long, Alloc>>);
+}
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test(Alloc alloc)
+{
+  // The forward_list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::forward_list<V, Alloc>& l, std::span<T> r) {
+    if (std::distance(l.begin(), l.end()) != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  std::forward_list<V, Alloc> v0(std::from_range, Range(a, a+0));
+  VERIFY( v0.empty() );
+  VERIFY( v0.get_allocator() == Alloc() );
+
+  std::forward_list<V, Alloc> v4(std::from_range, Range(a, a+4));
+  VERIFY( eq(v4, {a, 4}) );
+  VERIFY( v4.get_allocator() == Alloc() );
+
+  std::forward_list<V, Alloc> v9(std::from_range, Range(a, a+9), alloc);
+  VERIFY( eq(v9, {a, 9}) );
+  VERIFY( v9.get_allocator() == alloc );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range>(std::allocator<int>());
+  do_test<Range>(__gnu_test::uneq_allocator<int>(42));
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to bool
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range>(std::allocator<int>());
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/assign_range.cc 
b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/assign_range.cc
new file mode 100644
index 000000000000..d8352539724b
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/assign_range.cc
@@ -0,0 +1,97 @@
+// { dg-do run { target c++23 } }
+
+#include <forward_list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test()
+{
+  // The forward_list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::forward_list<V, Alloc>& l, std::span<T> r) {
+    if (std::distance(l.begin(), l.end()) != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  // assign to empty forward_list
+  std::forward_list<V, Alloc> v;
+  v.assign_range(Range(a, a));
+  VERIFY( v.empty() );
+  v.assign_range(Range(a, a+4));
+  VERIFY( eq(v, {a, 4}) );
+  v.clear();
+  v.assign_range(Range(a));
+  VERIFY( eq(v, a) );
+
+  // assign to non-empty forward_list
+  v.assign_range(Range(a, a+4));
+  VERIFY( eq(v, {a, 4}) );
+  v.assign_range(Range(a)); // larger than size()
+  VERIFY( eq(v, a) );
+  v.assign_range(Range(a, a+4)); // smaller than size()
+  VERIFY( eq(v, {a, 4}) );
+  v.assign_range(Range(a+2, a+6)); // equal to size()
+  VERIFY( eq(v, {a+2, 4}) );
+  v.assign_range(Range(a, a));
+  VERIFY( v.empty() );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range, std::allocator<int>>();
+  do_test<Range, __gnu_test::SimpleAllocator<int>>();
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to int
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range, std::allocator<int>>();
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/insert_range_after.cc
 
b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/insert_range_after.cc
new file mode 100644
index 000000000000..32580a6ed1e2
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/insert_range_after.cc
@@ -0,0 +1,93 @@
+// { dg-do run { target c++23 } }
+
+#include <forward_list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test()
+{
+  // The forward_list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::forward_list<V, Alloc>& l, std::span<T> r) {
+    if (std::distance(l.begin(), l.end()) != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  std::forward_list<V, Alloc> v;
+  v.insert_range_after(v.before_begin(), Range(a, a));
+  VERIFY( v.empty() );
+  v.insert_range_after(v.before_begin(), Range(a, a+4));
+  VERIFY( eq(v, {a, a+4}) );
+  v.clear();
+  v.insert_range_after(v.before_begin(), Range(a+4, a+9));
+  VERIFY( eq(v, {a+4, a+9}) );
+  v.insert_range_after(v.before_begin(), Range(a, a+4));
+  VERIFY( eq(v, a) );
+  v.clear();
+  v.insert_range_after(v.before_begin(), Range(a, a+3));
+  v.insert_range_after(std::next(v.before_begin(), 3), Range(a+6, a+9));
+  v.insert_range_after(std::next(v.before_begin(), 3), Range(a+3, a+6));
+  VERIFY( eq(v, a) );
+  v.insert_range_after(std::next(v.before_begin(), 2), Range(a, a));
+  VERIFY( eq(v, a) );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range, std::allocator<int>>();
+  do_test<Range, __gnu_test::SimpleAllocator<int>>();
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to int
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range, std::allocator<int>>();
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/prepend_range.cc 
b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/prepend_range.cc
new file mode 100644
index 000000000000..ae38fb7e791f
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/prepend_range.cc
@@ -0,0 +1,86 @@
+// { dg-do run { target c++23 } }
+
+#include <forward_list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test()
+{
+  // The forward_list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::forward_list<V, Alloc>& l, std::span<T> r) {
+    if (std::distance(l.begin(), l.end()) != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  std::forward_list<V, Alloc> v;
+  v.prepend_range(Range(a+4, a+9));
+  VERIFY( eq(v, {a+4, 5}) );
+  v.prepend_range(Range(a, a+4));
+  VERIFY( eq(v, a) );
+  v.prepend_range(Range(a, a));
+  VERIFY( eq(v, a) );
+  v.clear();
+  v.prepend_range(Range(a, a));
+  VERIFY( v.empty() );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range, std::allocator<int>>();
+  do_test<Range, __gnu_test::SimpleAllocator<int>>();
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to int
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range, std::allocator<int>>();
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/list/cons/from_range.cc 
b/libstdc++-v3/testsuite/23_containers/list/cons/from_range.cc
new file mode 100644
index 000000000000..31448b9122b4
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/list/cons/from_range.cc
@@ -0,0 +1,100 @@
+// { dg-do run { target c++23 } }
+
+#include <list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+void
+test_deduction_guide(long* p)
+{
+  __gnu_test::test_input_range<long> r(p, p);
+  std::list v(std::from_range, r);
+  static_assert(std::is_same_v<decltype(v), std::list<long>>);
+
+  using Alloc = __gnu_test::SimpleAllocator<long>;
+  Alloc alloc;
+  std::list v2(std::from_range, r, alloc);
+  static_assert(std::is_same_v<decltype(v2), std::list<long, Alloc>>);
+}
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test(Alloc alloc)
+{
+  // The list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) {
+    if (l.size() != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  std::list<V, Alloc> v0(std::from_range, Range(a, a+0));
+  VERIFY( v0.empty() );
+  VERIFY( v0.get_allocator() == Alloc() );
+
+  std::list<V, Alloc> v4(std::from_range, Range(a, a+4));
+  VERIFY( eq(v4, {a, 4}) );
+  VERIFY( v4.get_allocator() == Alloc() );
+
+  std::list<V, Alloc> v9(std::from_range, Range(a, a+9), alloc);
+  VERIFY( eq(v9, {a, 9}) );
+  VERIFY( v9.get_allocator() == alloc );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range>(std::allocator<int>());
+  do_test<Range>(__gnu_test::uneq_allocator<int>(42));
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to bool
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range>(std::allocator<int>());
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/list/modifiers/append_range.cc 
b/libstdc++-v3/testsuite/23_containers/list/modifiers/append_range.cc
new file mode 100644
index 000000000000..9ddfc7dbea38
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/list/modifiers/append_range.cc
@@ -0,0 +1,86 @@
+// { dg-do run { target c++23 } }
+
+#include <list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test()
+{
+  // The list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) {
+    if (l.size() != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  std::list<V, Alloc> v;
+  v.append_range(Range(a, a+4));
+  VERIFY( eq(v, {a, 4}) );
+  v.append_range(Range(a+4, a+9));
+  VERIFY( eq(v, a) );
+  v.append_range(Range(a, a));
+  VERIFY( eq(v, a) );
+  v.clear();
+  v.append_range(Range(a, a));
+  VERIFY( v.empty() );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range, std::allocator<int>>();
+  do_test<Range, __gnu_test::SimpleAllocator<int>>();
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to int
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range, std::allocator<int>>();
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/list/modifiers/assign/assign_range.cc 
b/libstdc++-v3/testsuite/23_containers/list/modifiers/assign/assign_range.cc
new file mode 100644
index 000000000000..84b9f07a8eb3
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/list/modifiers/assign/assign_range.cc
@@ -0,0 +1,99 @@
+// { dg-do run { target c++23 } }
+
+#include <list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test()
+{
+  // The list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) {
+    if (l.size() != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  // assign to empty list
+  std::list<V, Alloc> v;
+  v.assign_range(Range(a, a));
+  VERIFY( v.empty() );
+  v.assign_range(Range(a, a+4));
+  VERIFY( eq(v, {a, 4}) );
+  v.clear();
+  v.assign_range(Range(a));
+  VERIFY( eq(v, a) );
+
+  // assign to non-empty list
+  v.assign_range(Range(a, a+4));
+  VERIFY( eq(v, {a, 4}) );
+  v.assign_range(Range(a)); // larger than size()
+  VERIFY( eq(v, a) );
+  v.assign_range(Range(a, a+4)); // smaller than size()
+  VERIFY( eq(v, {a, 4}) );
+  v.clear();
+  v.resize(4);
+  v.assign_range(Range(a, a+4)); // equal to size()
+  VERIFY( eq(v, {a, 4}) );
+  v.assign_range(Range(a, a));
+  VERIFY( v.empty() );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range, std::allocator<int>>();
+  do_test<Range, __gnu_test::SimpleAllocator<int>>();
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to int
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range, std::allocator<int>>();
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/list/modifiers/insert/insert_range.cc 
b/libstdc++-v3/testsuite/23_containers/list/modifiers/insert/insert_range.cc
new file mode 100644
index 000000000000..a15d51d74668
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/list/modifiers/insert/insert_range.cc
@@ -0,0 +1,98 @@
+// { dg-do run { target c++23 } }
+
+#include <list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test()
+{
+  // The list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) {
+    if (l.size() != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  std::list<V, Alloc> v;
+  v.insert_range(v.begin(), Range(a, a));
+  VERIFY( v.empty() );
+  v.insert_range(v.begin(), Range(a, a+4));
+  VERIFY( eq(v, {a, a+4}) );
+  v.clear();
+  v.insert_range(v.begin(), Range(a+4, a+9));
+  VERIFY( eq(v, {a+4, a+9}) );
+  v.insert_range(v.begin(), Range(a, a+4));
+  VERIFY( eq(v, a) );
+  v.clear();
+  v.insert_range(v.begin(), Range(a, a+3));
+  v.insert_range(v.end(), Range(a+6, a+9));
+  v.insert_range(std::next(v.begin(), 3), Range(a+3, a+6));
+  VERIFY( eq(v, a) );
+  v.resize(3);
+  v.insert_range(std::next(v.begin()), Range(a+4, a+9));
+  v.insert_range(std::next(v.begin()), Range(a+1, a+4));
+  v.resize(9);
+  VERIFY( eq(v, a) );
+  v.insert_range(std::next(v.begin(), 6), Range(a, a));
+  VERIFY( eq(v, a) );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range, std::allocator<int>>();
+  do_test<Range, __gnu_test::SimpleAllocator<int>>();
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to int
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range, std::allocator<int>>();
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/list/modifiers/prepend_range.cc 
b/libstdc++-v3/testsuite/23_containers/list/modifiers/prepend_range.cc
new file mode 100644
index 000000000000..fe217e195731
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/list/modifiers/prepend_range.cc
@@ -0,0 +1,86 @@
+// { dg-do run { target c++23 } }
+
+#include <list>
+#include <span>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+#include <testsuite_allocator.h>
+
+template<typename Range, typename Alloc>
+constexpr void
+do_test()
+{
+  // The list's value_type.
+  using V = typename std::allocator_traits<Alloc>::value_type;
+
+  // The range's value_type.
+  using T = std::ranges::range_value_t<Range>;
+  T a[]{1,2,3,4,5,6,7,8,9};
+
+  auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) {
+    if (l.size() != r.size())
+      return false;
+    unsigned i = 0;
+    for (auto& e : l)
+      if (e != r[i++])
+       return false;
+    return true;
+  };
+
+  std::list<V, Alloc> v;
+  v.prepend_range(Range(a+4, a+9));
+  VERIFY( eq(v, {a+4, 5}) );
+  v.prepend_range(Range(a, a+4));
+  VERIFY( eq(v, a) );
+  v.prepend_range(Range(a, a));
+  VERIFY( eq(v, a) );
+  v.clear();
+  v.prepend_range(Range(a, a));
+  VERIFY( v.empty() );
+}
+
+template<typename Range>
+void
+do_test_a()
+{
+  do_test<Range, std::allocator<int>>();
+  do_test<Range, __gnu_test::SimpleAllocator<int>>();
+}
+
+bool
+test_ranges()
+{
+  using namespace __gnu_test;
+
+  do_test_a<test_forward_range<int>>();
+  do_test_a<test_forward_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>();
+
+  do_test_a<test_input_range<int>>();
+  do_test_a<test_input_sized_range<int>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>();
+
+  do_test_a<test_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>();
+  do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>();
+
+  do_test_a<test_forward_range<short>>();
+  do_test_a<test_input_range<short>>();
+
+  // Not lvalue-convertible to int
+  struct C {
+    C(int v) : val(v) { }
+    operator int() && { return val; }
+    bool operator==(int b) const { return b == val; }
+    int val;
+  };
+  using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>;
+  do_test<rvalue_input_range, std::allocator<int>>();
+
+  return true;
+}
+
+int main()
+{
+  test_ranges();
+}

Reply via email to