[libcxx-commits] [libcxx] 87f3ff3 - [libc++][ranges] Implement the changes to container adaptors from P1206 (`ranges::to`):
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jun 5 18:59:49 PDT 2023
Author: varconst
Date: 2023-06-05T18:57:25-07:00
New Revision: 87f3ff3e55e67709c3455a7ba105424be8ae84f2
URL: https://github.com/llvm/llvm-project/commit/87f3ff3e55e67709c3455a7ba105424be8ae84f2
DIFF: https://github.com/llvm/llvm-project/commit/87f3ff3e55e67709c3455a7ba105424be8ae84f2.diff
LOG: [libc++][ranges] Implement the changes to container adaptors from P1206 (`ranges::to`):
- add the `from_range_t` constructors and the related deduction guides;
- add the `push_range` member function.
(Note: this patch is split from https://reviews.llvm.org/D142335)
Reviewed By: #libc, ldionne
Differential Revision: https://reviews.llvm.org/D149829
Added:
libcxx/test/std/containers/container.adaptors/from_range_container_adaptors.h
libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/from_range.pass.cpp
libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
libcxx/test/support/unwrap_container_adaptor.h
Modified:
libcxx/include/queue
libcxx/include/stack
libcxx/test/libcxx/transitive_includes/cxx03.csv
libcxx/test/libcxx/transitive_includes/cxx11.csv
libcxx/test/libcxx/transitive_includes/cxx14.csv
libcxx/test/libcxx/transitive_includes/cxx17.csv
libcxx/test/libcxx/transitive_includes/cxx20.csv
libcxx/test/libcxx/transitive_includes/cxx23.csv
libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/deduct.pass.cpp
libcxx/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp
libcxx/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp
libcxx/test/support/deduction_guides_sfinae_checks.h
Removed:
################################################################################
diff --git a/libcxx/include/queue b/libcxx/include/queue
index 4e8398cf0fa4a..89623d554d6e5 100644
--- a/libcxx/include/queue
+++ b/libcxx/include/queue
@@ -43,6 +43,7 @@ public:
explicit queue(container_type&& c)
template<class InputIterator>
queue(InputIterator first, InputIterator last); // since C++23
+ template<container-compatible-range<T> R> queue(from_range_t, R&& rg); // since C++23
template <class Alloc>
explicit queue(const Alloc& a);
template <class Alloc>
@@ -55,6 +56,8 @@ public:
queue(queue&& q, const Alloc& a);
template <class InputIterator, class Alloc>
queue(InputIterator first, InputIterator last, const Alloc&); // since C++23
+ template<container-compatible-range<T> R, class Alloc>
+ queue(from_range_t, R&& rg, const Alloc&); // since C++23
bool empty() const;
size_type size() const;
@@ -66,6 +69,8 @@ public:
void push(const value_type& v);
void push(value_type&& v);
+ template<container-compatible-range<T> R>
+ void push_range(R&& rg); // C++23
template <class... Args> reference emplace(Args&&... args); // reference in C++17
void pop();
@@ -78,6 +83,9 @@ template<class Container>
template<class InputIterator>
queue(InputIterator, InputIterator) -> queue<iter-value-type<InputIterator>>; // since C++23
+template<ranges::input_range R>
+ queue(from_range_t, R&&) -> queue<ranges::range_value_t<R>>; // since C++23
+
template<class Container, class Allocator>
queue(Container, Allocator) -> queue<typename Container::value_type, Container>; // C++17
@@ -86,6 +94,10 @@ template<class InputIterator, class Allocator>
-> queue<iter-value-type<InputIterator>,
deque<iter-value-type<InputIterator>, Allocator>>; // since C++23
+template<ranges::input_range R, class Allocator>
+ queue(from_range_t, R&&, Allocator)
+ -> queue<ranges::range_value_t<R>, deque<ranges::range_value_t<R>, Allocator>>; // since C++23
+
template <class T, class Container>
bool operator==(const queue<T, Container>& x,const queue<T, Container>& y);
@@ -138,6 +150,8 @@ public:
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last,
const Compare& comp, Container&& c);
+ template <container-compatible-range<T> R>
+ priority_queue(from_range_t, R&& rg, const Compare& x = Compare()); // since C++23
template <class Alloc>
explicit priority_queue(const Alloc& a);
template <class Alloc>
@@ -160,6 +174,10 @@ public:
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last,
const Compare& comp, Container&& c, const Alloc& a);
+ template <container-compatible-range<T> R, class Alloc>
+ priority_queue(from_range_t, R&& rg, const Compare&, const Alloc&); // since C++23
+ template <container-compatible-range<T> R, class Alloc>
+ priority_queue(from_range_t, R&& rg, const Alloc&); // since C++23
template <class Alloc>
priority_queue(const priority_queue& q, const Alloc& a);
template <class Alloc>
@@ -171,6 +189,8 @@ public:
void push(const value_type& v);
void push(value_type&& v);
+ template<container-compatible-range<T> R>
+ void push_range(R&& rg); // C++23
template <class... Args> void emplace(Args&&... args);
void pop();
@@ -189,6 +209,10 @@ template<class InputIterator,
priority_queue(InputIterator, InputIterator, Compare = Compare(), Container = Container())
-> priority_queue<iter-value-type<InputIterator>, Container, Compare>; // C++17
+template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>>
+ priority_queue(from_range_t, R&&, Compare = Compare())
+ -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>>, Compare>; // C++23
+
template<class Compare, class Container, class Allocator>
priority_queue(Compare, Container, Allocator)
-> priority_queue<typename Container::value_type, Container, Compare>; // C++17
@@ -208,6 +232,15 @@ template<class InputIterator, class Compare, class Container, class Allocator>
priority_queue(InputIterator, InputIterator, Compare, Container, Allocator)
-> priority_queue<typename Container::value_type, Container, Compare>; // C++17
+template<ranges::input_range R, class Compare, class Allocator>
+ priority_queue(from_range_t, R&&, Compare, Allocator)
+ -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>, Allocator>,
+ Compare>; // C++23
+
+template<ranges::input_range R, class Allocator>
+ priority_queue(from_range_t, R&&, Allocator)
+ -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>, Allocator>>; // C++23
+
template <class T, class Container, class Compare>
void swap(priority_queue<T, Container, Compare>& x,
priority_queue<T, Container, Compare>& y)
@@ -220,11 +253,17 @@ template <class T, class Container, class Compare>
#include <__algorithm/make_heap.h>
#include <__algorithm/pop_heap.h>
#include <__algorithm/push_heap.h>
+#include <__algorithm/ranges_copy.h>
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__functional/operations.h>
+#include <__iterator/back_insert_iterator.h>
#include <__iterator/iterator_traits.h>
#include <__memory/uses_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
#include <__utility/forward.h>
#include <deque>
#include <vector>
@@ -283,12 +322,24 @@ public:
_LIBCPP_HIDE_FROM_ABI
queue(_InputIterator __first, _InputIterator __last) : c(__first, __last) {}
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ queue(from_range_t, _Range&& __range) : c(from_range, std::forward<_Range>(__range)) {}
+
template <class _InputIterator,
class _Alloc,
class = __enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = __enable_if_t<uses_allocator<container_type, _Alloc>::value>>
_LIBCPP_HIDE_FROM_ABI
queue(_InputIterator __first, _InputIterator __second, const _Alloc& __alloc) : c(__first, __second, __alloc) {}
+
+ template <_ContainerCompatibleRange<_Tp> _Range,
+ class _Alloc,
+ class = __enable_if_t<uses_allocator<container_type, _Alloc>::value>>
+ _LIBCPP_HIDE_FROM_ABI
+ queue(from_range_t, _Range&& __range, const _Alloc& __alloc)
+ : c(from_range, std::forward<_Range>(__range), __alloc) {}
+
#endif
_LIBCPP_INLINE_VISIBILITY
@@ -360,6 +411,21 @@ public:
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
void push(value_type&& __v) {c.push_back(_VSTD::move(__v));}
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void push_range(_Range&& __range) {
+ if constexpr (requires (container_type& __c) {
+ __c.append_range(std::forward<_Range>(__range));
+ }) {
+ c.append_range(std::forward<_Range>(__range));
+ } else {
+ ranges::copy(std::forward<_Range>(__range), std::back_inserter(c));
+ }
+ }
+#endif
+
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
#if _LIBCPP_STD_VER >= 17
@@ -418,12 +484,22 @@ template <class _InputIterator,
queue(_InputIterator, _InputIterator)
-> queue<__iter_value_type<_InputIterator>>;
+template <ranges::input_range _Range>
+queue(from_range_t, _Range&&)
+ -> queue<ranges::range_value_t<_Range>>;
+
template <class _InputIterator,
class _Alloc,
class = __enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = __enable_if_t<__is_allocator<_Alloc>::value>>
queue(_InputIterator, _InputIterator, _Alloc)
-> queue<__iter_value_type<_InputIterator>, deque<__iter_value_type<_InputIterator>, _Alloc>>;
+
+template <ranges::input_range _Range,
+ class _Alloc,
+ class = __enable_if_t<__is_allocator<_Alloc>::value>>
+queue(from_range_t, _Range&&, _Alloc)
+ -> queue<ranges::range_value_t<_Range>, deque<ranges::range_value_t<_Range>, _Alloc>>;
#endif
template <class _Tp, class _Container>
@@ -557,6 +633,17 @@ public:
priority_queue(_InputIter __f, _InputIter __l,
const value_compare& __comp, container_type&& __c);
#endif // _LIBCPP_CXX03_LANG
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ priority_queue(from_range_t, _Range&& __range, const value_compare& __comp = value_compare())
+ : c(from_range, std::forward<_Range>(__range)),
+ comp(__comp) {
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+#endif
+
template <class _Alloc>
_LIBCPP_INLINE_VISIBILITY
explicit priority_queue(const _Alloc& __a,
@@ -611,6 +698,30 @@ public:
__enable_if_t<uses_allocator<container_type, _Alloc>::value>* = 0);
#endif // _LIBCPP_CXX03_LANG
+#if _LIBCPP_STD_VER >= 23
+
+ template <_ContainerCompatibleRange<_Tp> _Range,
+ class _Alloc,
+ class = enable_if_t<uses_allocator<_Container, _Alloc>::value>>
+ _LIBCPP_HIDE_FROM_ABI
+ priority_queue(from_range_t, _Range&& __range, const value_compare& __comp, const _Alloc& __a)
+ : c(from_range, std::forward<_Range>(__range), __a),
+ comp(__comp) {
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+
+ template <_ContainerCompatibleRange<_Tp> _Range,
+ class _Alloc,
+ class = enable_if_t<uses_allocator<_Container, _Alloc>::value>>
+ _LIBCPP_HIDE_FROM_ABI
+ priority_queue(from_range_t, _Range&& __range, const _Alloc& __a)
+ : c(from_range, std::forward<_Range>(__range), __a),
+ comp() {
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+
+#endif
+
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
bool empty() const {return c.empty();}
_LIBCPP_INLINE_VISIBILITY
@@ -623,6 +734,23 @@ public:
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
void push(value_type&& __v);
+
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void push_range(_Range&& __range) {
+ if constexpr (requires (container_type& __c) {
+ __c.append_range(std::forward<_Range>(__range));
+ }) {
+ c.append_range(std::forward<_Range>(__range));
+ } else {
+ ranges::copy(std::forward<_Range>(__range), std::back_inserter(c));
+ }
+
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+#endif
+
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
void emplace(_Args&&... __args);
@@ -695,6 +823,31 @@ priority_queue(_InputIterator, _InputIterator, _Compare, _Container, _Alloc)
-> priority_queue<typename _Container::value_type, _Container, _Compare>;
#endif
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range,
+ class _Compare = less<ranges::range_value_t<_Range>>,
+ class = enable_if_t<!__is_allocator<_Compare>::value>>
+priority_queue(from_range_t, _Range&&, _Compare = _Compare())
+ -> priority_queue<ranges::range_value_t<_Range>, vector<ranges::range_value_t<_Range>>, _Compare>;
+
+template <ranges::input_range _Range,
+ class _Compare,
+ class _Alloc,
+ class = enable_if_t<!__is_allocator<_Compare>::value>,
+ class = enable_if_t<__is_allocator<_Alloc>::value>>
+priority_queue(from_range_t, _Range&&, _Compare, _Alloc)
+ -> priority_queue<ranges::range_value_t<_Range>, vector<ranges::range_value_t<_Range>, _Alloc>,
+ _Compare>;
+
+template <ranges::input_range _Range,
+ class _Alloc,
+ class = enable_if_t<__is_allocator<_Alloc>::value>>
+priority_queue(from_range_t, _Range&&, _Alloc)
+ -> priority_queue<ranges::range_value_t<_Range>, vector<ranges::range_value_t<_Range>, _Alloc>>;
+
+#endif
+
template <class _Tp, class _Container, class _Compare>
inline
priority_queue<_Tp, _Container, _Compare>::priority_queue(const _Compare& __comp,
diff --git a/libcxx/include/stack b/libcxx/include/stack
index 93bed1555d0f2..d49aacb5f2c1d 100644
--- a/libcxx/include/stack
+++ b/libcxx/include/stack
@@ -42,6 +42,7 @@ public:
explicit stack(const container_type& c);
explicit stack(container_type&& c);
template <class InputIterator> stack(InputIterator first, InputIterator last); // since C++23
+ template<container-compatible-range<T> R> stack(from_range_t, R&& rg); // since C++23
template <class Alloc> explicit stack(const Alloc& a);
template <class Alloc> stack(const container_type& c, const Alloc& a);
template <class Alloc> stack(container_type&& c, const Alloc& a);
@@ -49,6 +50,8 @@ public:
template <class Alloc> stack(stack&& c, const Alloc& a);
template<class InputIterator, class Alloc>
stack(InputIterator first, InputIterator last, const Alloc&); // since C++23
+ template<container-compatible-range<T> R, class Alloc>
+ stack(from_range_t, R&& rg, const Alloc&); // since C++23
bool empty() const;
size_type size() const;
@@ -57,6 +60,8 @@ public:
void push(const value_type& x);
void push(value_type&& x);
+ template<container-compatible-range<T> R>
+ void push_range(R&& rg); // C++23
template <class... Args> reference emplace(Args&&... args); // reference in C++17
void pop();
@@ -69,6 +74,9 @@ template<class Container>
template<class InputIterator>
stack(InputIterator, InputIterator) -> stack<iter-value-type<InputIterator>>; // since C++23
+template<ranges::input_range R>
+ stack(from_range_t, R&&) -> stack<ranges::range_value_t<R>>; // since C++23
+
template<class Container, class Allocator>
stack(Container, Allocator) -> stack<typename Container::value_type, Container>; // C++17
@@ -77,6 +85,10 @@ template<class InputIterator, class Allocator>
-> stack<iter-value-type<InputIterator>,
deque<iter-value-type<InputIterator>, Allocator>>; // since C++23
+template<ranges::input_range R, class Allocator>
+ stack(from_range_t, R&&, Allocator)
+ -> stack<ranges::range_value_t<R>, deque<ranges::range_value_t<R>, Allocator>>; // since C++23
+
template <class T, class Container>
bool operator==(const stack<T, Container>& x, const stack<T, Container>& y);
template <class T, class Container>
@@ -98,10 +110,16 @@ template <class T, class Container>
*/
+#include <__algorithm/ranges_copy.h>
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
+#include <__iterator/back_insert_iterator.h>
#include <__iterator/iterator_traits.h>
#include <__memory/uses_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
#include <__type_traits/is_same.h>
#include <__utility/forward.h>
#include <deque>
@@ -210,12 +228,24 @@ public:
_LIBCPP_HIDE_FROM_ABI
stack(_InputIterator __first, _InputIterator __last) : c(__first, __last) {}
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ stack(from_range_t, _Range&& __range) : c(from_range, std::forward<_Range>(__range)) {}
+
template <class _InputIterator,
class _Alloc,
class = __enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = __enable_if_t<uses_allocator<container_type, _Alloc>::value>>
_LIBCPP_HIDE_FROM_ABI
stack(_InputIterator __first, _InputIterator __last, const _Alloc& __alloc) : c(__first, __last, __alloc) {}
+
+ template <_ContainerCompatibleRange<_Tp> _Range,
+ class _Alloc,
+ class = __enable_if_t<uses_allocator<container_type, _Alloc>::value>>
+ _LIBCPP_HIDE_FROM_ABI
+ stack(from_range_t, _Range&& __range, const _Alloc& __alloc)
+ : c(from_range, std::forward<_Range>(__range), __alloc) {}
+
#endif
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
@@ -233,6 +263,20 @@ public:
_LIBCPP_INLINE_VISIBILITY
void push(value_type&& __v) {c.push_back(_VSTD::move(__v));}
+#if _LIBCPP_STD_VER >= 23
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ _LIBCPP_HIDE_FROM_ABI
+ void push_range(_Range&& __range) {
+ if constexpr (requires (container_type& __c) {
+ __c.append_range(std::forward<_Range>(__range));
+ }) {
+ c.append_range(std::forward<_Range>(__range));
+ } else {
+ ranges::copy(std::forward<_Range>(__range), std::back_inserter(c));
+ }
+ }
+#endif
+
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
#if _LIBCPP_STD_VER >= 17
@@ -290,12 +334,22 @@ template<class _InputIterator,
stack(_InputIterator, _InputIterator)
-> stack<__iter_value_type<_InputIterator>>;
+template <ranges::input_range _Range>
+stack(from_range_t, _Range&&) -> stack<ranges::range_value_t<_Range>>;
+
template<class _InputIterator,
class _Alloc,
class = __enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = __enable_if_t<__is_allocator<_Alloc>::value>>
stack(_InputIterator, _InputIterator, _Alloc)
-> stack<__iter_value_type<_InputIterator>, deque<__iter_value_type<_InputIterator>, _Alloc>>;
+
+template <ranges::input_range _Range,
+ class _Alloc,
+ class = __enable_if_t<__is_allocator<_Alloc>::value>>
+stack(from_range_t, _Range&&, _Alloc)
+ -> stack<ranges::range_value_t<_Range>, deque<ranges::range_value_t<_Range>, _Alloc>>;
+
#endif
template <class _Tp, class _Container>
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 61928bd560933..21dd51c5323eb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -777,6 +777,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
+stack limits
stack type_traits
stack version
stdexcept cstdlib
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index d9e60a4d0f391..15044a90cf500 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -778,6 +778,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
+stack limits
stack type_traits
stack version
stdexcept cstdlib
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index e1f632ecbcd14..d2bd00a5b8796 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -780,6 +780,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
+stack limits
stack type_traits
stack version
stdexcept cstdlib
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index e1f632ecbcd14..d2bd00a5b8796 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -780,6 +780,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
+stack limits
stack type_traits
stack version
stdexcept cstdlib
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 34171b06dd08e..2120eeeeaadc5 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -786,6 +786,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
+stack limits
stack type_traits
stack version
stdexcept cstdlib
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index eefe23ed9a245..2efadb0a456bf 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -437,6 +437,7 @@ ostream typeinfo
ostream version
queue compare
queue cstddef
+queue cstdint
queue deque
queue initializer_list
queue limits
@@ -529,8 +530,10 @@ sstream string
sstream version
stack compare
stack cstddef
+stack cstdint
stack deque
stack initializer_list
+stack limits
stack version
stdexcept iosfwd
stop_token atomic
diff --git a/libcxx/test/std/containers/container.adaptors/from_range_container_adaptors.h b/libcxx/test/std/containers/container.adaptors/from_range_container_adaptors.h
new file mode 100644
index 0000000000000..0ad0b18f575c0
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/from_range_container_adaptors.h
@@ -0,0 +1,229 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUPPORT_FROM_RANGE_CONTAINER_ADAPTORS_H
+#define SUPPORT_FROM_RANGE_CONTAINER_ADAPTORS_H
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <queue>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "../from_range_helpers.h"
+#include "MoveOnly.h"
+#include "almost_satisfies_types.h"
+#include "count_new.h"
+#include "test_macros.h"
+#include "unwrap_container_adaptor.h"
+
+template <class Container, class Range>
+concept HasFromRangeCtr = requires (Range&& range) {
+ Container(std::from_range, std::forward<Range>(range));
+ Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_constraints() {
+ // Input range with the same value type.
+ static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>);
+ // Input range with a convertible value type.
+ static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>);
+ // Not an input range.
+ static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>);
+ static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>);
+ static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>);
+
+ return true;
+}
+
+template <template <class ...> class Adaptor,
+ template <class ...> class UnderlyingContainer,
+ class T,
+ class Iter,
+ class Sent,
+ class Alloc>
+constexpr void test_container_adaptor_with_input(std::vector<T>&& input) {
+ auto b = Iter(input.data());
+ auto e = Iter(input.data() + input.size());
+ std::ranges::subrange in(std::move(b), Sent(std::move(e)));
+
+ { // (range)
+ Adaptor<T> adaptor(std::from_range, in);
+ UnwrapAdaptor<Adaptor<T>> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::equal(in, c));
+ LIBCPP_ASSERT(c.__invariants());
+ }
+
+ { // (range, allocator)
+ using C = UnderlyingContainer<T, Alloc>;
+ Alloc alloc;
+ Adaptor<T, C> adaptor(std::from_range, in, alloc);
+ UnwrapAdaptor<Adaptor<T, C>> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::equal(in, c));
+ LIBCPP_ASSERT(c.__invariants());
+ }
+}
+
+template <template <class ...> class UnderlyingContainer,
+ class T,
+ class Iter,
+ class Sent,
+ class Comp,
+ class Alloc>
+constexpr void test_priority_queue_with_input(std::vector<T>&& input) {
+ auto b = Iter(input.data());
+ auto e = Iter(input.data() + input.size());
+ std::ranges::subrange in(std::move(b), Sent(std::move(e)));
+
+ { // (range)
+ std::priority_queue<T> adaptor(std::from_range, in);
+ UnwrapAdaptor<std::priority_queue<T>> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ LIBCPP_ASSERT(c.__invariants());
+ }
+
+ { // (range, comp)
+ using C = UnderlyingContainer<T>;
+ Comp comp;
+
+ std::priority_queue<T, C, Comp> adaptor(std::from_range, in, comp);
+ UnwrapAdaptor<std::priority_queue<T, C, Comp>> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ LIBCPP_ASSERT(c.__invariants());
+ assert(unwrap_adaptor.get_comparator() == comp);
+ }
+
+ { // (range, allocator)
+ using C = UnderlyingContainer<T, Alloc>;
+ Alloc alloc;
+
+ std::priority_queue<T, C> adaptor(std::from_range, in, alloc);
+ UnwrapAdaptor<std::priority_queue<T, C>> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ LIBCPP_ASSERT(c.__invariants());
+ }
+
+ { // (range, comp, alloc)
+ using C = UnderlyingContainer<T, Alloc>;
+ Comp comp;
+ Alloc alloc;
+
+ std::priority_queue<T, C, Comp> adaptor(std::from_range, in, comp, alloc);
+ UnwrapAdaptor<std::priority_queue<T, C, Comp>> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+
+ assert(c.get_allocator() == alloc);
+ assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+ assert(std::ranges::is_permutation(input, c));
+ LIBCPP_ASSERT(c.__invariants());
+ assert(unwrap_adaptor.get_comparator() == comp);
+ }
+}
+
+template <template <class ...> class Adaptor,
+ template <class ...> class UnderlyingContainer,
+ class T,
+ class Iter,
+ class Sent,
+ class Alloc>
+constexpr void test_container_adaptor() {
+ auto test_with_input = &test_container_adaptor_with_input<Adaptor, UnderlyingContainer, T, Iter, Sent, Alloc>;
+
+ // Normal input.
+ test_with_input({0, 5, 12, 7, -1, 8, 26});
+ // Empty input.
+ test_with_input({});
+ // Single-element input.
+ test_with_input({5});
+}
+
+template <template <class ...> class UnderlyingContainer,
+ class T,
+ class Iter,
+ class Sent,
+ class Comp,
+ class Alloc>
+constexpr void test_priority_queue() {
+ auto test_with_input = &test_priority_queue_with_input<UnderlyingContainer, T, Iter, Sent, Comp, Alloc>;
+
+ // Normal input.
+ test_with_input({0, 5, 12, 7, -1, 8, 26});
+ // Empty input.
+ test_with_input({});
+ // Single-element input.
+ test_with_input({5});
+}
+
+template <template <class ...> class Container>
+constexpr void test_container_adaptor_move_only() {
+ MoveOnly input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ [[maybe_unused]] Container<MoveOnly> c(std::from_range, in);
+}
+
+template <template <class ...> class Adaptor>
+void test_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using T = ThrowingCopy<3>;
+ T::reset();
+ T in[5];
+
+ try {
+ Adaptor<T, std::vector<T>> c(std::from_range, in);
+ assert(false); // The constructor call above should throw.
+
+ } catch (int) {
+ assert(T::created_by_copying == 3);
+ assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <template <class ...> class Adaptor, class T>
+void test_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ T in[] = {0, 1};
+
+ try {
+ using C = std::vector<T, ThrowingAllocator<T>>;
+ ThrowingAllocator<T> alloc;
+
+ globalMemCounter.reset();
+ Adaptor<T, C> c(std::from_range, in, alloc);
+ assert(false); // The constructor call should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+#endif // SUPPORT_FROM_RANGE_CONTAINER_ADAPTORS_H
diff --git a/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/deduct.pass.cpp b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/deduct.pass.cpp
index 7dbeeee735d1f..7dfaa9f38af3d 100644
--- a/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/deduct.pass.cpp
@@ -22,14 +22,26 @@
// template<class Compare, class Container, class Allocator>
// priority_queue(Compare, Container, Allocator)
// -> priority_queue<typename Container::value_type, Container, Compare>;
+//
+// template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>>
+// priority_queue(from_range_t, R&&, Compare = Compare())
+// -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>>, Compare>; // C++23
+//
+// template<ranges::input_range R, class Compare, class Allocator>
+// priority_queue(from_range_t, R&&, Compare, Allocator)
+// -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>, Allocator>,
+// Compare>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+// priority_queue(from_range_t, R&&, Allocator)
+// -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>, Allocator>>; // C++23
-
-#include <queue>
-#include <vector>
-#include <iterator>
#include <cassert>
-#include <cstddef>
#include <climits> // INT_MAX
+#include <cstddef>
+#include <iterator>
+#include <queue>
+#include <vector>
#include "deduction_guides_sfinae_checks.h"
#include "test_macros.h"
@@ -238,6 +250,28 @@ int main(int, char**)
std::priority_queue pri(a, a+2, Comp(), std::move(cont), ConvertibleToAlloc(2));
static_assert(std::is_same_v<decltype(pri), std::priority_queue<T, Cont, Comp>>);
}
+
+#if TEST_STD_VER >= 23
+ { // (from_range, range)
+ std::priority_queue c(std::from_range, Cont());
+ static_assert(std::is_same_v<decltype(c), std::priority_queue<T>>);
+ }
+
+ { // (from_range, range, compare)
+ std::priority_queue c(std::from_range, Cont(), Comp());
+ static_assert(std::is_same_v<decltype(c), std::priority_queue<T, std::vector<T>, Comp>>);
+ }
+
+ { // (from_range, range, compare, alloc)
+ std::priority_queue c(std::from_range, Cont(), Comp(), Alloc(2));
+ static_assert(std::is_same_v<decltype(c), std::priority_queue<T, std::vector<T, Alloc>, Comp>>);
+ }
+
+ { // (from_range, range, alloc)
+ std::priority_queue c(std::from_range, Cont(), Alloc(2));
+ static_assert(std::is_same_v<decltype(c), std::priority_queue<T, std::vector<T, Alloc>>>);
+ }
+#endif // TEST_STD_VER >= 23
}
// Deduction guides should be SFINAE'd away when given:
@@ -354,6 +388,39 @@ int main(int, char**)
static_assert(SFINAEs_away<std::priority_queue, AllocAsComp, Cont>);
// Cannot deduce from (comp, ALLOC_as_cont)
static_assert(SFINAEs_away<std::priority_queue, Comp, AllocAsCont>);
+
+#if TEST_STD_VER >= 23
+ using Range = RangeT<int>;
+ using BadRange = BadRangeT<int>;
+
+ // (from_range, range)
+ //
+ // Cannot deduce from (from_range, BAD_range)
+ static_assert(SFINAEs_away<std::priority_queue, BadRange>);
+
+ // (from_range, range, compare)
+ //
+ // Cannot deduce from (from_range, BAD_range, compare)
+ static_assert(SFINAEs_away<std::priority_queue, BadRange, Comp>);
+
+ // (from_range, range, compare, alloc)
+ //
+ // Cannot deduce from (from_range, BAD_range, compare, alloc)
+ static_assert(SFINAEs_away<std::priority_queue, BadRange, Comp, Alloc>);
+ // Cannot deduce from (from_range, range, compare, BAD_alloc)
+ static_assert(SFINAEs_away<std::priority_queue, Range, Comp, BadAlloc>);
+ // Cannot deduce from (from_range, range, compare, DIFFERENT_alloc)
+ static_assert(SFINAEs_away<std::priority_queue, Range, Comp, DiffAlloc>);
+
+ // (from_range, range, alloc)
+ //
+ // Cannot deduce from (from_range, BAD_range, alloc)
+ static_assert(SFINAEs_away<std::priority_queue, BadRange, Alloc>);
+ // Cannot deduce from (from_range, range, BAD_alloc)
+ static_assert(SFINAEs_away<std::priority_queue, Range, BadAlloc>);
+ // Cannot deduce from (from_range, range, DIFFERENT_alloc)
+ static_assert(SFINAEs_away<std::priority_queue, Range, DiffAlloc>);
+#endif // TEST_STD_VER >= 23
}
return 0;
diff --git a/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/from_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/from_range.pass.cpp
new file mode 100644
index 0000000000000..de453f72dc8b3
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.cons/from_range.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <queue>
+
+#include "../../from_range_container_adaptors.h"
+#include "../../../test_compare.h"
+#include "test_macros.h"
+
+// template <container-compatible-range<T> R>
+// priority_queue(from_range_t, R&& rg, const Compare& x = Compare()); // since C++23
+// template <container-compatible-range<T> R, class Alloc>
+// priority_queue(from_range_t, R&& rg, const Compare&, const Alloc&); // since C++23
+// template <container-compatible-range<T> R, class Alloc>
+// priority_queue(from_range_t, R&& rg, const Alloc&); // since C++23
+
+template <class Range>
+concept PriorityQueueHasFromRangeCtr = requires (Range&& range) {
+ std::priority_queue<int>(std::from_range, std::forward<Range>(range));
+ std::priority_queue<int>(std::from_range, std::forward<Range>(range), std::less<int>());
+ std::priority_queue<int>(std::from_range, std::forward<Range>(range), std::less<int>(), std::allocator<int>());
+ std::priority_queue<int>(std::from_range, std::forward<Range>(range), std::allocator<int>());
+};
+
+constexpr bool test_constraints_priority_queue() {
+ // Input range with the same value type.
+ static_assert(PriorityQueueHasFromRangeCtr<InputRange<int>>);
+ // Input range with a convertible value type.
+ static_assert(PriorityQueueHasFromRangeCtr<InputRange<double>>);
+ // Input range with a non-convertible value type.
+ static_assert(!PriorityQueueHasFromRangeCtr<InputRange<Empty>>);
+ // Not an input range.
+ static_assert(!PriorityQueueHasFromRangeCtr<InputRangeNotDerivedFrom>);
+ static_assert(!PriorityQueueHasFromRangeCtr<InputRangeNotIndirectlyReadable>);
+ static_assert(!PriorityQueueHasFromRangeCtr<InputRangeNotInputOrOutputIterator>);
+
+ return true;
+}
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_priority_queue<std::vector, int, Iter, Sent, test_less<int>, Alloc>();
+ });
+ test_container_adaptor_move_only<std::priority_queue>();
+
+ static_assert(test_constraints_priority_queue());
+
+ test_exception_safety_throwing_copy<std::priority_queue>();
+ test_exception_safety_throwing_allocator<std::priority_queue, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
new file mode 100644
index 0000000000000..9a57d930e69d9
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// void push_range(R&& rg); // C++23
+
+#include <queue>
+
+#include "../../push_range_container_adaptors.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_push_range<std::priority_queue<int, std::vector<int, Alloc>>, Iter, Sent>(/*is_result_heapified=*/true);
+ });
+ test_push_range_move_only<std::priority_queue>();
+ test_push_range_inserter_choice<std::priority_queue, int>(/*is_result_heapified=*/true);
+
+ static_assert(test_constraints_push_range<std::priority_queue, int, double>());
+
+ test_push_range_exception_safety_throwing_copy<std::priority_queue>();
+ test_push_range_exception_safety_throwing_allocator<std::priority_queue, std::vector, int>();
+
+ return 0;
+}
+
diff --git a/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h b/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
new file mode 100644
index 0000000000000..37b166b7df289
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
@@ -0,0 +1,299 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUPPORT_PUSH_RANGE_CONTAINER_ADAPTORS_H
+#define SUPPORT_PUSH_RANGE_CONTAINER_ADAPTORS_H
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+#include <initializer_list>
+#include <ranges>
+#include <type_traits>
+#include <vector>
+
+#include "../from_range_helpers.h"
+#include "../insert_range_helpers.h"
+#include "MoveOnly.h"
+#include "almost_satisfies_types.h"
+#include "count_new.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "unwrap_container_adaptor.h"
+
+template <class Container, class Range>
+concept HasPushRange = requires (Container& c, Range&& range) {
+ c.push_range(range);
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_constraints_push_range() {
+ // Input range with the same value type.
+ static_assert(HasPushRange<Container<T>, InputRange<T>>);
+ // Input range with a convertible value type.
+ static_assert(HasPushRange<Container<T>, InputRange<U>>);
+ // Input range with a non-convertible value type.
+ static_assert(!HasPushRange<Container<T>, InputRange<Empty>>);
+ // Not an input range.
+ static_assert(!HasPushRange<Container<T>, InputRangeNotDerivedFrom>);
+ static_assert(!HasPushRange<Container<T>, InputRangeNotIndirectlyReadable>);
+ static_assert(!HasPushRange<Container<T>, InputRangeNotInputOrOutputIterator>);
+
+ return true;
+}
+
+// Empty container.
+
+template <class T>
+TestCase<T> constexpr EmptyContainer_EmptyRange {
+ .initial = {}, .input = {}, .expected = {}
+};
+
+template <class T> constexpr TestCase<T> EmptyContainer_OneElementRange {
+ .initial = {}, .input = {5}, .expected = {5}
+};
+
+template <class T> constexpr TestCase<T> EmptyContainer_MidRange {
+ .initial = {}, .input = {5, 3, 1, 7, 9}, .expected = {5, 3, 1, 7, 9}
+};
+
+// One-element container.
+
+template <class T> constexpr TestCase<T> OneElementContainer_EmptyRange {
+ .initial = {3}, .input = {}, .expected = {3}
+};
+
+template <class T> constexpr TestCase<T> OneElementContainer_OneElementRange {
+ .initial = {3}, .input = {-5}, .expected = {3, -5}
+};
+
+template <class T> constexpr TestCase<T> OneElementContainer_MidRange {
+ .initial = {3}, .input = {-5, -3, -1, -7, -9}, .expected = {3, -5, -3, -1, -7, -9}
+};
+
+// Full container.
+
+template <class T> constexpr TestCase<T> FullContainer_EmptyRange {
+ .initial = {11, 29, 35, 14, 84}, .input = {}, .expected = {11, 29, 35, 14, 84}
+};
+
+template <class T> constexpr TestCase<T> FullContainer_OneElementRange {
+ .initial = {11, 29, 35, 14, 84}, .input = {-5}, .expected = {11, 29, 35, 14, 84, -5}
+};
+
+template <class T> constexpr TestCase<T> FullContainer_MidRange {
+ .initial = {11, 29, 35, 14, 84},
+ .input = {-5, -3, -1, -7, -9},
+ .expected = {11, 29, 35, 14, 84, -5, -3, -1, -7, -9}
+};
+
+template <class T> constexpr TestCase<T> FullContainer_LongRange {
+ .initial = {11, 29, 35, 14, 84},
+ .input = {-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48},
+ .expected = {
+ 11, 29, 35, 14, 84, -5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48
+ }
+};
+
+// Container adaptors tests.
+
+template <class Adaptor, class Iter, class Sent>
+constexpr void test_push_range(bool is_result_heapified = false) {
+ using T = typename Adaptor::value_type;
+
+ auto test = [&](auto& test_case) {
+ Adaptor adaptor(test_case.initial.begin(), test_case.initial.end());
+ auto in = wrap_input<Iter, Sent>(test_case.input);
+
+ adaptor.push_range(in);
+ UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+
+ if (is_result_heapified) {
+ assert(std::ranges::is_heap(c));
+ return std::ranges::is_permutation(c, test_case.expected);
+ } else {
+ return std::ranges::equal(c, test_case.expected);
+ }
+ };
+
+ { // Empty container.
+ // empty_c.push_range(empty_range)
+ assert(test(EmptyContainer_EmptyRange<T>));
+ // empty_c.push_range(one_element_range)
+ assert(test(EmptyContainer_OneElementRange<T>));
+ // empty_c.push_range(mid_range)
+ assert(test(EmptyContainer_MidRange<T>));
+ }
+
+ { // One-element container.
+ // one_element_c.push_range(empty_range)
+ assert(test(OneElementContainer_EmptyRange<T>));
+ // one_element_c.push_range(one_element_range)
+ assert(test(OneElementContainer_OneElementRange<T>));
+ // one_element_c.push_range(mid_range)
+ assert(test(OneElementContainer_MidRange<T>));
+ }
+
+ { // Full container.
+ // full_container.push_range(empty_range)
+ assert(test(FullContainer_EmptyRange<T>));
+ // full_container.push_range(one_element_range)
+ assert(test(FullContainer_OneElementRange<T>));
+ // full_container.push_range(mid_range)
+ assert(test(FullContainer_MidRange<T>));
+ // full_container.push_range(long_range)
+ assert(test(FullContainer_LongRange<T>));
+ }
+}
+
+// Move-only types.
+
+template <template <class ...> class Container>
+constexpr void test_push_range_move_only() {
+ MoveOnly input[5];
+ std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+ Container<MoveOnly> c;
+ c.push_range(in);
+}
+
+// Check that `append_range` is preferred if available and `push_back` is used as a fallback.
+
+enum class InserterChoice {
+ Invalid,
+ PushBack,
+ AppendRange
+};
+
+template <class T, InserterChoice Inserter>
+struct Container {
+ InserterChoice inserter_choice = InserterChoice::Invalid;
+
+ using value_type = T;
+ using iterator = T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using size_type = std::size_t;
+
+ static constexpr int Capacity = 8;
+ int size_ = 0;
+ value_type buffer_[Capacity] = {};
+
+ iterator begin() { return buffer_; }
+ iterator end() { return buffer_ + size_; }
+ size_type size() const { return size_; }
+
+ template <class U>
+ void push_back(U val)
+ requires (Inserter >= InserterChoice::PushBack) {
+ inserter_choice = InserterChoice::PushBack;
+ buffer_[size_] = val;
+ ++size_;
+ }
+
+ template <std::ranges::input_range Range>
+ void append_range(Range&& range)
+ requires (Inserter >= InserterChoice::AppendRange) {
+ assert(size() + std::ranges::distance(range) <= Capacity);
+
+ inserter_choice = InserterChoice::AppendRange;
+
+ for (auto&& e : range) {
+ buffer_[size_] = e;
+ ++size_;
+ }
+ }
+
+ friend bool operator==(const Container&, const Container&) = default;
+};
+
+template <template <class ...> class AdaptorT, class T>
+void test_push_range_inserter_choice(bool is_result_heapified = false) {
+ { // `append_range` is preferred if available.
+ using BaseContainer = Container<T, InserterChoice::AppendRange>;
+ using Adaptor = AdaptorT<T, BaseContainer>;
+ T in[] = {1, 2, 3, 4, 5};
+
+ Adaptor adaptor;
+ adaptor.push_range(in);
+
+ UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+ assert(c.inserter_choice == InserterChoice::AppendRange);
+ if (is_result_heapified) {
+ assert(std::ranges::is_heap(c));
+ assert(std::ranges::is_permutation(c, in));
+ } else {
+ assert(std::ranges::equal(c, in));
+ }
+ }
+
+ { // `push_back` is used as a fallback (via `back_inserter`).
+ using BaseContainer = Container<T, InserterChoice::PushBack>;
+ using Adaptor = AdaptorT<T, BaseContainer>;
+ T in[] = {1, 2, 3, 4, 5};
+
+ Adaptor adaptor;
+ adaptor.push_range(in);
+
+ UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
+ auto& c = unwrap_adaptor.get_container();
+ assert(c.inserter_choice == InserterChoice::PushBack);
+ if (is_result_heapified) {
+ assert(std::ranges::is_heap(c));
+ assert(std::ranges::is_permutation(c, in));
+ } else {
+ assert(std::ranges::equal(c, in));
+ }
+ }
+}
+
+// Exception safety.
+
+template <template <class ...> class Container>
+void test_push_range_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ using T = ThrowingCopy<3>;
+ T::reset();
+ T in[5];
+
+ try {
+ Container<T> c;
+ c.push_range(in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(T::created_by_copying == 3);
+ assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
+ }
+#endif
+}
+
+template <template <class ...> class Adaptor, template <class ...> class BaseContainer, class T>
+void test_push_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ T in[] = {0, 1};
+
+ try {
+ globalMemCounter.reset();
+ Adaptor<T, BaseContainer<T, ThrowingAllocator<T>>> c;
+ c.push_range(in);
+ assert(false); // The function call above should throw.
+
+ } catch (int) {
+ assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+ }
+#endif
+}
+
+#endif // SUPPORT_PUSH_RANGE_CONTAINER_ADAPTORS_H
diff --git a/libcxx/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp b/libcxx/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp
index e17c39e5faff7..630d444cbff86 100644
--- a/libcxx/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp
@@ -14,7 +14,13 @@
//
// template<class Container, class Allocator>
// queue(Container, Allocator) -> queue<typename Container::value_type, Container>;
-
+//
+// template<ranges::input_range R>
+// queue(from_range_t, R&&) -> queue<ranges::range_value_t<R>>; // since C++23
+//
+// template<ranges::input_range R, class Allocator>
+// queue(from_range_t, R&&, Allocator)
+// -> queue<ranges::range_value_t<R>, deque<ranges::range_value_t<R>, Allocator>>; // since C++23
#include <array>
#include <queue>
@@ -134,29 +140,7 @@ int main(int, char**)
}
}
- // Deduction guides should be SFINAE'd away when given:
- // - a "bad" allocator (that is, a type not qualifying as an allocator);
- // - an allocator instead of a container;
- // - an allocator and a container that uses a
diff erent allocator.
- {
- using Cont = std::list<int>;
- using Alloc = std::allocator<int>;
- using DiffAlloc = test_allocator<int>;
- using Iter = int*;
-
- struct NotIter{};
- struct NotAlloc {};
-
- static_assert(SFINAEs_away<std::queue, Alloc, NotAlloc>);
- static_assert(SFINAEs_away<std::queue, Cont, NotAlloc>);
- static_assert(SFINAEs_away<std::queue, Cont, DiffAlloc>);
- static_assert(SFINAEs_away<std::queue, Iter, NotIter>);
-#if TEST_STD_VER > 20
- static_assert(SFINAEs_away<std::queue, Iter, NotIter, Alloc>);
- static_assert(SFINAEs_away<std::queue, Iter, Iter, NotAlloc>);
-#endif
- }
-#if TEST_STD_VER > 20
+#if TEST_STD_VER >= 23
{
typedef short T;
typedef test_allocator<T> Alloc;
@@ -170,6 +154,22 @@ int main(int, char**)
static_assert(std::is_same_v<decltype(q), std::queue<T, std::deque<T, Alloc>>>);
}
}
+
+ {
+ {
+ std::queue c(std::from_range, std::array<int, 0>());
+ static_assert(std::is_same_v<decltype(c), std::queue<int>>);
+ }
+
+ {
+ using Alloc = test_allocator<int>;
+ std::queue c(std::from_range, std::array<int, 0>(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::queue<int, std::deque<int, Alloc>>>);
+ }
+ }
#endif
+
+ ContainerAdaptorDeductionGuidesSfinaeAway<std::queue, std::queue<int>>();
+
return 0;
}
diff --git a/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
new file mode 100644
index 0000000000000..d2b21e16c2461
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <queue>
+
+#include "../../from_range_container_adaptors.h"
+#include "test_macros.h"
+
+// template<container-compatible-range<T> R> queue(from_range_t, R&& rg); // since C++23
+// template<container-compatible-range<T> R, class Alloc>
+// queue(from_range_t, R&& rg, const Alloc&); // since C++23
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_container_adaptor<std::queue, std::deque, int, Iter, Sent, Alloc>();
+ });
+ test_container_adaptor_move_only<std::queue>();
+
+ static_assert(test_constraints<std::queue, int, double>());
+
+ test_exception_safety_throwing_copy<std::queue>();
+ test_exception_safety_throwing_allocator<std::queue, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
new file mode 100644
index 0000000000000..c3a0b676ca320
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// void push_range(R&& rg); // C++23
+
+#include <queue>
+
+#include "../../push_range_container_adaptors.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_push_range<std::queue<int, std::deque<int, Alloc>>, Iter, Sent>();
+ });
+ test_push_range_move_only<std::queue>();
+ test_push_range_inserter_choice<std::queue, int>();
+
+ static_assert(test_constraints_push_range<std::queue, int, double>());
+
+ test_push_range_exception_safety_throwing_copy<std::queue>();
+ test_push_range_exception_safety_throwing_allocator<std::queue, std::deque, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp b/libcxx/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp
index ad02a3ef5f55a..bc0c594a3c641 100644
--- a/libcxx/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp
@@ -14,7 +14,13 @@
//
// template<class Container, class Allocator>
// stack(Container, Allocator) -> stack<typename Container::value_type, Container>;
-
+//
+// template<ranges::input_range R>
+// stack(from_range_t, R&&) -> stack<ranges::range_value_t<R>>; // since C++23
+//
+// template<ranges::input_range R, class Allocator>
+// stack(from_range_t, R&&, Allocator)
+// -> stack<ranges::range_value_t<R>, deque<ranges::range_value_t<R>, Allocator>>; // since C++23
#include <array>
#include <stack>
@@ -138,30 +144,7 @@ int main(int, char**)
}
}
- // Deduction guides should be SFINAE'd away when given:
- // - a "bad" allocator (that is, a type not qualifying as an allocator);
- // - an allocator instead of a container;
- // - an allocator and a container that uses a
diff erent allocator.
- {
- using Cont = std::list<int>;
- using Alloc = std::allocator<int>;
- using DiffAlloc = test_allocator<int>;
- using Iter = int;
-
- struct NotIter {};
- struct NotAlloc {};
-
- static_assert(SFINAEs_away<std::stack, Alloc, Alloc>);
- static_assert(SFINAEs_away<std::stack, Cont, NotAlloc>);
- static_assert(SFINAEs_away<std::stack, Cont, DiffAlloc>);
- static_assert(SFINAEs_away<std::stack, Iter, NotIter>);
-#if TEST_STD_VER > 20
- static_assert(SFINAEs_away<std::stack, Iter, NotIter, Alloc>);
- static_assert(SFINAEs_away<std::stack, Iter, Iter, NotAlloc>);
-#endif
- }
-
-#if TEST_STD_VER > 20
+#if TEST_STD_VER >= 23
{
typedef short T;
typedef test_allocator<T> Alloc;
@@ -175,7 +158,22 @@ int main(int, char**)
static_assert(std::is_same_v<decltype(s), std::stack<T, std::deque<T, Alloc>>>);
}
}
+
+ {
+ {
+ std::stack c(std::from_range, std::array<int, 0>());
+ static_assert(std::is_same_v<decltype(c), std::stack<int>>);
+ }
+
+ {
+ using Alloc = test_allocator<int>;
+ std::stack c(std::from_range, std::array<int, 0>(), Alloc());
+ static_assert(std::is_same_v<decltype(c), std::stack<int, std::deque<int, Alloc>>>);
+ }
+ }
#endif
+ ContainerAdaptorDeductionGuidesSfinaeAway<std::stack, std::stack<int>>();
+
return 0;
}
diff --git a/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
new file mode 100644
index 0000000000000..90f43dc56e499
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <stack>
+
+#include "../../from_range_container_adaptors.h"
+#include "test_macros.h"
+
+// template<container-compatible-range<T> R> stack(from_range_t, R&& rg); // since C++23
+// template<container-compatible-range<T> R, class Alloc>
+// stack(from_range_t, R&& rg, const Alloc&); // since C++23
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+ test_container_adaptor<std::stack, std::deque, int, Iter, Sent, Alloc>();
+ });
+ test_container_adaptor_move_only<std::stack>();
+
+ static_assert(test_constraints<std::stack, int, double>());
+
+ test_exception_safety_throwing_copy<std::stack>();
+ test_exception_safety_throwing_allocator<std::stack, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
new file mode 100644
index 0000000000000..be0d4985ef820
--- /dev/null
+++ b/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<container-compatible-range<T> R>
+// void push_range(R&& rg); // C++23
+
+#include <stack>
+
+#include "../../push_range_container_adaptors.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+ for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+ test_push_range<std::stack<int, std::deque<int, Alloc>>, Iter, Sent>();
+ });
+ test_push_range_move_only<std::stack>();
+ test_push_range_inserter_choice<std::stack, int>();
+
+ static_assert(test_constraints_push_range<std::stack, int, double>());
+
+ test_push_range_exception_safety_throwing_copy<std::stack>();
+ test_push_range_exception_safety_throwing_allocator<std::stack, std::deque, int>();
+
+ return 0;
+}
diff --git a/libcxx/test/support/deduction_guides_sfinae_checks.h b/libcxx/test/support/deduction_guides_sfinae_checks.h
index a811322c1a5ef..dcc41aa1b4ba7 100644
--- a/libcxx/test/support/deduction_guides_sfinae_checks.h
+++ b/libcxx/test/support/deduction_guides_sfinae_checks.h
@@ -108,6 +108,54 @@ constexpr void SequenceContainerDeductionGuidesSfinaeAway() {
#endif
}
+// Deduction guides should be SFINAE'd away when given:
+// - a "bad" allocator (that is, a type not qualifying as an allocator);
+// - an allocator instead of a container;
+// - an allocator and a container that uses a
diff erent allocator;
+// - a range not satisfying the `input_range` concept.
+template<template<typename ...> class Container, typename InstantiatedContainer>
+constexpr void ContainerAdaptorDeductionGuidesSfinaeAway() {
+ using T = typename InstantiatedContainer::value_type;
+ using Alloc = std::allocator<T>;
+ using Iter = T*;
+
+ using BadIter = int;
+ using BadAlloc = Empty;
+
+ // (container) -- no constraints.
+
+ // (container, alloc)
+ //
+ // Cannot deduce from (container, BAD_alloc)
+ static_assert(SFINAEs_away<Container, std::vector<T>, BadAlloc>);
+
+ // (iter, iter)
+ //
+ // Cannot deduce from (BAD_iter, BAD_iter)
+ LIBCPP_STATIC_ASSERT(SFINAEs_away<Container, BadIter, BadIter>);
+
+#if TEST_STD_VER >= 23
+ using BadRange = BadRangeT<T>;
+
+ // (iter, iter, alloc)
+ //
+ // Cannot deduce from (BAD_iter, BAD_iter, alloc)
+ LIBCPP_STATIC_ASSERT(SFINAEs_away<Container, BadIter, BadIter, Alloc>);
+ // Cannot deduce from (iter, iter, BAD_alloc)
+ static_assert(SFINAEs_away<Container, Iter, Iter, BadAlloc>);
+
+ // (from_range, range)
+ //
+ // Cannot deduce from (BAD_range)
+ static_assert(SFINAEs_away<Container, std::from_range_t, BadRange>);
+
+ // (from_range, range, alloc)
+ //
+ // Cannot deduce from (range, BAD_alloc)
+ static_assert(SFINAEs_away<Container, std::from_range_t, RangeT<int>, BadAlloc>);
+#endif
+}
+
// For associative containers the deduction guides should be SFINAE'd away when
// given:
// - "bad" input iterators (that is, a type not qualifying as an input
diff --git a/libcxx/test/support/unwrap_container_adaptor.h b/libcxx/test/support/unwrap_container_adaptor.h
new file mode 100644
index 0000000000000..19f79430045da
--- /dev/null
+++ b/libcxx/test/support/unwrap_container_adaptor.h
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUPPORT_UNWRAP_CONTAINER_ADAPTOR_H
+#define SUPPORT_UNWRAP_CONTAINER_ADAPTOR_H
+
+// Allows accessing the underlying container of the given adaptor.
+template <class Adaptor>
+struct UnwrapAdaptor : Adaptor {
+ UnwrapAdaptor() = default;
+
+ UnwrapAdaptor(Adaptor&& adaptor) : Adaptor(std::move(adaptor)) {}
+ // `c` is a protected member variable of the base class.
+ decltype(auto) get_container() {
+ return (UnwrapAdaptor::c); // Put into parentheses to make sure the function returns a reference.
+ }
+
+ // TODO: make this work pre-C++20.
+ decltype(auto) get_comparator()
+ requires requires {
+ UnwrapAdaptor::c;
+ } {
+ return (UnwrapAdaptor::comp); // Put into parentheses to make sure the function returns a reference.
+ }
+};
+
+#endif // SUPPORT_UNWRAP_CONTAINER_ADAPTOR_H
More information about the libcxx-commits
mailing list