[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

GitOrigin-RevId: 87f3ff3e55e67709c3455a7ba105424be8ae84f2
diff --git a/include/queue b/include/queue
index 4e8398c..89623d5 100644
--- a/include/queue
+++ b/include/queue
@@ -43,6 +43,7 @@
     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 @@
         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 @@
 
     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 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 @@
   -> 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 @@
     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 @@
     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 @@
 
     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 @@
 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 @@
 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 @@
 #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 @@
     _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 @@
 #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 @@
 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 @@
         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 @@
                        __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 @@
 #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<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/include/stack b/include/stack
index 93bed15..d49aacb 100644
--- a/include/stack
+++ b/include/stack
@@ -42,6 +42,7 @@
     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 @@
     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 @@
 
     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 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 @@
     -> 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 @@
 
 */
 
+#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 @@
     _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 @@
     _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 @@
 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/test/libcxx/transitive_includes/cxx03.csv b/test/libcxx/transitive_includes/cxx03.csv
index 61928bd..21dd51c 100644
--- a/test/libcxx/transitive_includes/cxx03.csv
+++ b/test/libcxx/transitive_includes/cxx03.csv
@@ -777,6 +777,7 @@
 stack deque
 stack functional
 stack initializer_list
+stack limits
 stack type_traits
 stack version
 stdexcept cstdlib
diff --git a/test/libcxx/transitive_includes/cxx11.csv b/test/libcxx/transitive_includes/cxx11.csv
index d9e60a4..15044a9 100644
--- a/test/libcxx/transitive_includes/cxx11.csv
+++ b/test/libcxx/transitive_includes/cxx11.csv
@@ -778,6 +778,7 @@
 stack deque
 stack functional
 stack initializer_list
+stack limits
 stack type_traits
 stack version
 stdexcept cstdlib
diff --git a/test/libcxx/transitive_includes/cxx14.csv b/test/libcxx/transitive_includes/cxx14.csv
index e1f632e..d2bd00a 100644
--- a/test/libcxx/transitive_includes/cxx14.csv
+++ b/test/libcxx/transitive_includes/cxx14.csv
@@ -780,6 +780,7 @@
 stack deque
 stack functional
 stack initializer_list
+stack limits
 stack type_traits
 stack version
 stdexcept cstdlib
diff --git a/test/libcxx/transitive_includes/cxx17.csv b/test/libcxx/transitive_includes/cxx17.csv
index e1f632e..d2bd00a 100644
--- a/test/libcxx/transitive_includes/cxx17.csv
+++ b/test/libcxx/transitive_includes/cxx17.csv
@@ -780,6 +780,7 @@
 stack deque
 stack functional
 stack initializer_list
+stack limits
 stack type_traits
 stack version
 stdexcept cstdlib
diff --git a/test/libcxx/transitive_includes/cxx20.csv b/test/libcxx/transitive_includes/cxx20.csv
index 34171b0..2120eee 100644
--- a/test/libcxx/transitive_includes/cxx20.csv
+++ b/test/libcxx/transitive_includes/cxx20.csv
@@ -786,6 +786,7 @@
 stack deque
 stack functional
 stack initializer_list
+stack limits
 stack type_traits
 stack version
 stdexcept cstdlib
diff --git a/test/libcxx/transitive_includes/cxx23.csv b/test/libcxx/transitive_includes/cxx23.csv
index eefe23e..2efadb0 100644
--- a/test/libcxx/transitive_includes/cxx23.csv
+++ b/test/libcxx/transitive_includes/cxx23.csv
@@ -437,6 +437,7 @@
 ostream version
 queue compare
 queue cstddef
+queue cstdint
 queue deque
 queue initializer_list
 queue limits
@@ -529,8 +530,10 @@
 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/test/std/containers/container.adaptors/from_range_container_adaptors.h b/test/std/containers/container.adaptors/from_range_container_adaptors.h
new file mode 100644
index 0000000..0ad0b18
--- /dev/null
+++ b/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/test/std/containers/container.adaptors/priority.queue/priqueue.cons/deduct.pass.cpp b/test/std/containers/container.adaptors/priority.queue/priqueue.cons/deduct.pass.cpp
index 7dbeeee..7dfaa9f 100644
--- a/test/std/containers/container.adaptors/priority.queue/priqueue.cons/deduct.pass.cpp
+++ b/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 <cassert>
+#include <climits> // INT_MAX
+#include <cstddef>
+#include <iterator>
 #include <queue>
 #include <vector>
-#include <iterator>
-#include <cassert>
-#include <cstddef>
-#include <climits> // INT_MAX
 
 #include "deduction_guides_sfinae_checks.h"
 #include "test_macros.h"
@@ -238,6 +250,28 @@
         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 @@
         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/test/std/containers/container.adaptors/priority.queue/priqueue.cons/from_range.pass.cpp b/test/std/containers/container.adaptors/priority.queue/priqueue.cons/from_range.pass.cpp
new file mode 100644
index 0000000..de453f7
--- /dev/null
+++ b/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/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp b/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
new file mode 100644
index 0000000..9a57d93
--- /dev/null
+++ b/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/test/std/containers/container.adaptors/push_range_container_adaptors.h b/test/std/containers/container.adaptors/push_range_container_adaptors.h
new file mode 100644
index 0000000..37b166b
--- /dev/null
+++ b/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/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp b/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp
index e17c39e..630d444 100644
--- a/test/std/containers/container.adaptors/queue/queue.cons/deduct.pass.cpp
+++ b/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 @@
         }
     }
 
-    // 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 different 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 @@
         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/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp b/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
new file mode 100644
index 0000000..d2b21e1
--- /dev/null
+++ b/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/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp b/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
new file mode 100644
index 0000000..c3a0b67
--- /dev/null
+++ b/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/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp b/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp
index ad02a3e..bc0c594 100644
--- a/test/std/containers/container.adaptors/stack/stack.cons/deduct.pass.cpp
+++ b/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 @@
         }
     }
 
-    // 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 different 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 @@
         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/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp b/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
new file mode 100644
index 0000000..90f43dc
--- /dev/null
+++ b/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/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp b/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
new file mode 100644
index 0000000..be0d498
--- /dev/null
+++ b/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/test/support/deduction_guides_sfinae_checks.h b/test/support/deduction_guides_sfinae_checks.h
index a811322..dcc41aa 100644
--- a/test/support/deduction_guides_sfinae_checks.h
+++ b/test/support/deduction_guides_sfinae_checks.h
@@ -108,6 +108,54 @@
 #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 different 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/test/support/unwrap_container_adaptor.h b/test/support/unwrap_container_adaptor.h
new file mode 100644
index 0000000..19f7943
--- /dev/null
+++ b/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