[libcxx-commits] [libcxx] ef70fe4 - [libc++][ranges] Implement the changes to node-based containers from P1206 (`ranges::to`):

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 18 11:01:35 PDT 2023


Author: varconst
Date: 2023-07-18T11:01:10-07:00
New Revision: ef70fe4d264d20abdd8476605650479a96a62071

URL: https://github.com/llvm/llvm-project/commit/ef70fe4d264d20abdd8476605650479a96a62071
DIFF: https://github.com/llvm/llvm-project/commit/ef70fe4d264d20abdd8476605650479a96a62071.diff

LOG: [libc++][ranges] Implement the changes to node-based containers from P1206 (`ranges::to`):

- add the `from_range_t` constructors and the related deduction guides;
- add the `insert_range`/`assign_range`/etc. member functions.

(Note: this patch is split from https://reviews.llvm.org/D142335)

Differential Revision: https://reviews.llvm.org/D149830

Added: 
    libcxx/include/__iterator/ranges_iterator_traits.h
    libcxx/test/std/containers/associative/from_range_associative_containers.h
    libcxx/test/std/containers/associative/map/map.cons/from_range.pass.cpp
    libcxx/test/std/containers/associative/map/map.modifiers/insert_range.pass.cpp
    libcxx/test/std/containers/associative/multimap/multimap.cons/from_range.pass.cpp
    libcxx/test/std/containers/associative/multimap/multimap.modifiers/insert_range.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
    libcxx/test/std/containers/associative/set/insert_range.pass.cpp
    libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
    libcxx/test/std/containers/insert_range_maps_sets.h
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_range.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp
    libcxx/test/std/containers/sequences/list/exception_safety.pass.cpp
    libcxx/test/std/containers/sequences/list/list.cons/from_range.pass.cpp
    libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp
    libcxx/test/std/containers/sequences/list/list.modifiers/assign_range.pass.cpp
    libcxx/test/std/containers/sequences/list/list.modifiers/insert_range.pass.cpp
    libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp
    libcxx/test/std/containers/unord/from_range_unordered_containers.h
    libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/from_range.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_iter_iter.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/from_range.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_iter_iter.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/insert_iter_iter.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/from_range.pass.cpp
    libcxx/test/std/containers/unord/unord.set/insert_iter_iter.pass.cpp
    libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/from_range.pass.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/forward_list
    libcxx/include/list
    libcxx/include/map
    libcxx/include/module.modulemap.in
    libcxx/include/set
    libcxx/include/unordered_map
    libcxx/include/unordered_set
    libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp
    libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
    libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
    libcxx/test/std/containers/insert_range_helpers.h
    libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp
    libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp
    libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp
    libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp
    libcxx/test/support/deduction_guides_sfinae_checks.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 640de9a8235c46..cfb367e3a4a480 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -465,6 +465,7 @@ set(files
   __iterator/permutable.h
   __iterator/prev.h
   __iterator/projected.h
+  __iterator/ranges_iterator_traits.h
   __iterator/readable_traits.h
   __iterator/reverse_access.h
   __iterator/reverse_iterator.h

diff  --git a/libcxx/include/__iterator/ranges_iterator_traits.h b/libcxx/include/__iterator/ranges_iterator_traits.h
new file mode 100644
index 00000000000000..a30864199df76b
--- /dev/null
+++ b/libcxx/include/__iterator/ranges_iterator_traits.h
@@ -0,0 +1,42 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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 _LIBCPP___ITERATOR_RANGES_ITERATOR_TRAITS_H
+#define _LIBCPP___ITERATOR_RANGES_ITERATOR_TRAITS_H
+
+#include <__config>
+#include <__fwd/pair.h>
+#include <__ranges/concepts.h>
+#include <__type_traits/add_const.h>
+#include <__type_traits/remove_const.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range>
+using __range_key_type = __remove_const_t<typename ranges::range_value_t<_Range>::first_type>;
+
+template <ranges::input_range _Range>
+using __range_mapped_type = typename ranges::range_value_t<_Range>::second_type;
+
+template <ranges::input_range _Range>
+using __range_to_alloc_type =
+    pair<add_const_t<typename ranges::range_value_t<_Range>::first_type>,
+         typename ranges::range_value_t<_Range>::second_type>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ITERATOR_RANGES_ITERATOR_TRAITS_H

diff  --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index aac9d548ac935e..6cb794070b7b8b 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -44,6 +44,8 @@ public:
         forward_list(InputIterator first, InputIterator last);
     template <class InputIterator>
         forward_list(InputIterator first, InputIterator last, const allocator_type& a);
+    template<container-compatible-range<T> R>
+        forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
     forward_list(const forward_list& x);
     forward_list(const forward_list& x, const allocator_type& a);
     forward_list(forward_list&& x)
@@ -63,6 +65,8 @@ public:
 
     template <class InputIterator>
         void assign(InputIterator first, InputIterator last);
+    template<container-compatible-range<T> R>
+      void assign_range(R&& rg); // C++23
     void assign(size_type n, const value_type& v);
     void assign(initializer_list<value_type> il);
 
@@ -89,6 +93,8 @@ public:
     template <class... Args> reference emplace_front(Args&&... args);  // reference in C++17
     void push_front(const value_type& v);
     void push_front(value_type&& v);
+    template<container-compatible-range<T> R>
+      void prepend_range(R&& rg); // C++23
 
     void pop_front();
 
@@ -100,6 +106,8 @@ public:
     template <class InputIterator>
         iterator insert_after(const_iterator p,
                               InputIterator first, InputIterator last);
+    template<container-compatible-range<T> R>
+      iterator insert_range_after(const_iterator position, R&& rg); // C++23
     iterator insert_after(const_iterator p, initializer_list<value_type> il);
 
     iterator erase_after(const_iterator p);
@@ -140,6 +148,10 @@ template <class InputIterator, class Allocator = allocator<typename iterator_tra
     forward_list(InputIterator, InputIterator, Allocator = Allocator())
     -> forward_list<typename iterator_traits<InputIterator>::value_type, Allocator>;  // C++17
 
+template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
+  forward_list(from_range_t, R&&, Allocator = Allocator())
+      -> forward_list<ranges::range_value_t<R>, Allocator>; // C++23
+
 template <class T, class Allocator>
     bool operator==(const forward_list<T, Allocator>& x,
                     const forward_list<T, Allocator>& y);
@@ -204,6 +216,10 @@ template <class T, class Allocator, class Predicate>
 #include <__memory/swap_allocator.h>
 #include <__memory/unique_ptr.h>
 #include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_const.h>
@@ -722,6 +738,15 @@ public:
     _LIBCPP_HIDE_FROM_ABI forward_list(_InputIterator __f, _InputIterator __l,
                      const allocator_type& __a,
                      __enable_if_t<__has_input_iterator_category<_InputIterator>::value>* = nullptr);
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI forward_list(from_range_t, _Range&& __range,
+        const allocator_type& __a = allocator_type()) : base(__a) {
+      prepend_range(std::forward<_Range>(__range));
+    }
+#endif
+
     _LIBCPP_HIDE_FROM_ABI forward_list(const forward_list& __x);
     _LIBCPP_HIDE_FROM_ABI forward_list(const forward_list& __x, const __type_identity_t<allocator_type>& __a);
 
@@ -755,6 +780,15 @@ public:
     template <class _InputIterator>
     __enable_if_t<__has_input_iterator_category<_InputIterator>::value, void>
     _LIBCPP_HIDE_FROM_ABI assign(_InputIterator __f, _InputIterator __l);
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void assign_range(_Range&& __range) {
+      __assign_with_sentinel(ranges::begin(__range), ranges::end(__range));
+    }
+#endif
+
     _LIBCPP_HIDE_FROM_ABI void assign(size_type __n, const value_type& __v);
 
     _LIBCPP_INLINE_VISIBILITY
@@ -818,6 +852,14 @@ public:
 #endif // _LIBCPP_CXX03_LANG
     _LIBCPP_HIDE_FROM_ABI void push_front(const value_type& __v);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void prepend_range(_Range&& __range) {
+      insert_range_after(cbefore_begin(), std::forward<_Range>(__range));
+    }
+#endif
+
     _LIBCPP_HIDE_FROM_ABI void pop_front();
 
 #ifndef _LIBCPP_CXX03_LANG
@@ -835,6 +877,18 @@ public:
     __enable_if_t<__has_input_iterator_category<_InputIterator>::value, iterator>
         insert_after(const_iterator __p, _InputIterator __f, _InputIterator __l);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    iterator insert_range_after(const_iterator __position, _Range&& __range) {
+      return __insert_after_with_sentinel(__position, ranges::begin(__range), ranges::end(__range));
+    }
+#endif
+
+    template <class _InputIterator, class _Sentinel>
+    _LIBCPP_HIDE_FROM_ABI
+    iterator __insert_after_with_sentinel(const_iterator __p, _InputIterator __f, _Sentinel __l);
+
     _LIBCPP_HIDE_FROM_ABI iterator erase_after(const_iterator __p);
     _LIBCPP_HIDE_FROM_ABI iterator erase_after(const_iterator __f, const_iterator __l);
 
@@ -896,6 +950,10 @@ private:
     _LIBCPP_HIDE_FROM_ABI void __move_assign(forward_list& __x, false_type);
 #endif // _LIBCPP_CXX03_LANG
 
+    template <class _Iter, class _Sent>
+    _LIBCPP_HIDE_FROM_ABI
+    void __assign_with_sentinel(_Iter __f, _Sent __l);
+
     template <class _Compare>
     static _LIBCPP_HIDE_FROM_ABI
         __node_pointer
@@ -927,6 +985,15 @@ forward_list(_InputIterator, _InputIterator, _Alloc)
   -> forward_list<__iter_value_type<_InputIterator>, _Alloc>;
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+          class _Alloc = allocator<ranges::range_value_t<_Range>>,
+          class = enable_if_t<__is_allocator<_Alloc>::value>
+          >
+forward_list(from_range_t, _Range&&, _Alloc = _Alloc())
+  -> forward_list<ranges::range_value_t<_Range>, _Alloc>;
+#endif
+
 template <class _Tp, class _Alloc>
 inline
 forward_list<_Tp, _Alloc>::forward_list(const allocator_type& __a)
@@ -1107,13 +1174,20 @@ template <class _InputIterator>
 __enable_if_t<__has_input_iterator_category<_InputIterator>::value, void>
 forward_list<_Tp, _Alloc>::assign(_InputIterator __f, _InputIterator __l)
 {
+  __assign_with_sentinel(__f, __l);
+}
+
+template <class _Tp, class _Alloc>
+template <class _Iter, class _Sent>
+_LIBCPP_HIDE_FROM_ABI
+void forward_list<_Tp, _Alloc>::__assign_with_sentinel(_Iter __f, _Sent __l) {
     iterator __i = before_begin();
     iterator __j = _VSTD::next(__i);
     iterator __e = end();
     for (; __j != __e && __f != __l; ++__i, (void) ++__j, ++__f)
         *__j = *__f;
     if (__j == __e)
-        insert_after(__i, __f, __l);
+        __insert_after_with_sentinel(__i, std::move(__f), std::move(__l));
     else
         erase_after(__i, __e);
 }
@@ -1307,9 +1381,17 @@ __enable_if_t<__has_input_iterator_category<_InputIterator>::value, typename for
 forward_list<_Tp, _Alloc>::insert_after(const_iterator __p,
                                         _InputIterator __f, _InputIterator __l)
 {
-    using _Guard = __allocation_guard<__node_allocator>;
+  return __insert_after_with_sentinel(__p, std::move(__f), std::move(__l));
+}
 
+template <class _Tp, class _Alloc>
+template <class _InputIterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI
+typename forward_list<_Tp, _Alloc>::iterator
+forward_list<_Tp, _Alloc>::__insert_after_with_sentinel(const_iterator __p, _InputIterator __f, _Sentinel __l) {
+    using _Guard = __allocation_guard<__node_allocator>;
     __begin_node_pointer __r = __p.__get_begin();
+
     if (__f != __l)
     {
         __node_allocator& __a = base::__alloc();
@@ -1321,6 +1403,7 @@ forward_list<_Tp, _Alloc>::insert_after(const_iterator __p,
           __first = __h.__release_ptr();
         }
         __node_pointer __last = __first;
+
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
         try
         {
@@ -1346,10 +1429,12 @@ forward_list<_Tp, _Alloc>::insert_after(const_iterator __p,
             throw;
         }
 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
+
         __last->__next_ = __r->__next_;
         __r->__next_ = __first;
         __r = static_cast<__begin_node_pointer>(__last);
     }
+
     return iterator(__r);
 }
 

diff  --git a/libcxx/include/list b/libcxx/include/list
index bf613b9f3a3a81..2512259461500b 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -46,6 +46,8 @@ public:
         list(Iter first, Iter last);
     template <class Iter>
         list(Iter first, Iter last, const allocator_type& a);
+    template<container-compatible-range<T> R>
+      list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
     list(const list& x);
     list(const list&, const allocator_type& a);
     list(list&& x)
@@ -64,6 +66,8 @@ public:
     list& operator=(initializer_list<value_type>);
     template <class Iter>
         void assign(Iter first, Iter last);
+    template<container-compatible-range<T> R>
+      void assign_range(R&& rg); // C++23
     void assign(size_type n, const value_type& t);
     void assign(initializer_list<value_type>);
 
@@ -99,8 +103,12 @@ public:
     void pop_back();
     void push_front(const value_type& x);
     void push_front(value_type&& x);
+    template<container-compatible-range<T> R>
+      void prepend_range(R&& rg); // C++23
     void push_back(const value_type& x);
     void push_back(value_type&& x);
+    template<container-compatible-range<T> R>
+      void append_range(R&& rg); // C++23
     template <class... Args>
         iterator emplace(const_iterator position, Args&&... args);
     iterator insert(const_iterator position, const value_type& x);
@@ -108,6 +116,8 @@ public:
     iterator insert(const_iterator position, size_type n, const value_type& x);
     template <class Iter>
         iterator insert(const_iterator position, Iter first, Iter last);
+    template<container-compatible-range<T> R>
+      iterator insert_range(const_iterator position, R&& rg); // C++23
     iterator insert(const_iterator position, initializer_list<value_type> il);
 
     iterator erase(const_iterator position);
@@ -152,6 +162,10 @@ template <class InputIterator, class Allocator = allocator<typename iterator_tra
     list(InputIterator, InputIterator, Allocator = Allocator())
     -> list<typename iterator_traits<InputIterator>::value_type, Allocator>;  // C++17
 
+template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
+  list(from_range_t, R&&, Allocator = Allocator())
+    -> list<ranges::range_value_t<R>, Allocator>; // C++23
+
 template <class T, class Alloc>
     bool operator==(const list<T,Alloc>& x, const list<T,Alloc>& y);
 template <class T, class Alloc>
@@ -207,6 +221,10 @@ template <class T, class Allocator, class Predicate>
 #include <__memory/swap_allocator.h>
 #include <__memory/unique_ptr.h>
 #include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_nothrow_default_constructible.h>
@@ -761,6 +779,14 @@ public:
     _LIBCPP_HIDE_FROM_ABI list(_InpIter __f, _InpIter __l, const allocator_type& __a,
              __enable_if_t<__has_input_iterator_category<_InpIter>::value>* = 0);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI list(from_range_t, _Range&& __range,
+        const allocator_type& __a = allocator_type()) : base(__a) {
+      prepend_range(std::forward<_Range>(__range));
+    }
+#endif
+
     _LIBCPP_HIDE_FROM_ABI list(const list& __c);
     _LIBCPP_HIDE_FROM_ABI list(const list& __c, const __type_identity_t<allocator_type>& __a);
     _LIBCPP_INLINE_VISIBILITY
@@ -792,6 +818,15 @@ public:
     template <class _InpIter>
     _LIBCPP_HIDE_FROM_ABI void assign(_InpIter __f, _InpIter __l,
                     __enable_if_t<__has_input_iterator_category<_InpIter>::value>* = 0);
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void assign_range(_Range&& __range) {
+      __assign_with_sentinel(ranges::begin(__range), ranges::end(__range));
+    }
+#endif
+
     _LIBCPP_HIDE_FROM_ABI void assign(size_type __n, const value_type& __x);
 
     _LIBCPP_INLINE_VISIBILITY
@@ -870,6 +905,20 @@ public:
     _LIBCPP_HIDE_FROM_ABI void push_front(value_type&& __x);
     _LIBCPP_HIDE_FROM_ABI void push_back(value_type&& __x);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void prepend_range(_Range&& __range) {
+      insert_range(begin(), std::forward<_Range>(__range));
+    }
+
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void append_range(_Range&& __range) {
+      insert_range(end(), std::forward<_Range>(__range));
+    }
+#endif
+
     template <class... _Args>
 #if _LIBCPP_STD_VER >= 17
     _LIBCPP_HIDE_FROM_ABI reference emplace_front(_Args&&... __args);
@@ -910,6 +959,14 @@ public:
     _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, _InpIter __f, _InpIter __l,
                         __enable_if_t<__has_input_iterator_category<_InpIter>::value>* = 0);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<_Tp> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    iterator insert_range(const_iterator __position, _Range&& __range) {
+      return __insert_with_sentinel(__position, ranges::begin(__range), ranges::end(__range));
+    }
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     void swap(list& __c)
 #if _LIBCPP_STD_VER >= 14
@@ -986,6 +1043,14 @@ public:
     }
 
 private:
+    template <class _Iterator, class _Sentinel>
+    _LIBCPP_HIDE_FROM_ABI
+    void __assign_with_sentinel(_Iterator __f, _Sentinel __l);
+
+    template <class _Iterator, class _Sentinel>
+    _LIBCPP_HIDE_FROM_ABI
+    iterator __insert_with_sentinel(const_iterator __p, _Iterator __f, _Sentinel __l);
+
     _LIBCPP_INLINE_VISIBILITY
     static void __link_nodes  (__link_pointer __p, __link_pointer __f, __link_pointer __l);
     _LIBCPP_INLINE_VISIBILITY
@@ -1020,6 +1085,15 @@ list(_InputIterator, _InputIterator, _Alloc)
   -> list<__iter_value_type<_InputIterator>, _Alloc>;
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+          class _Alloc = allocator<ranges::range_value_t<_Range>>,
+          class = enable_if_t<__is_allocator<_Alloc>::value>
+          >
+list(from_range_t, _Range&&, _Alloc = _Alloc())
+  -> list<ranges::range_value_t<_Range>, _Alloc>;
+#endif
+
 // Link in nodes [__f, __l] just prior to __p
 template <class _Tp, class _Alloc>
 inline
@@ -1225,12 +1299,19 @@ void
 list<_Tp, _Alloc>::assign(_InpIter __f, _InpIter __l,
                           __enable_if_t<__has_input_iterator_category<_InpIter>::value>*)
 {
+  __assign_with_sentinel(__f, __l);
+}
+
+template <class _Tp, class _Alloc>
+template <class _Iterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI
+void list<_Tp, _Alloc>::__assign_with_sentinel(_Iterator __f, _Sentinel __l) {
     iterator __i = begin();
     iterator __e = end();
     for (; __f != __l && __i != __e; ++__f, (void) ++__i)
         *__i = *__f;
     if (__i == __e)
-        insert(__e, __f, __l);
+        __insert_with_sentinel(__e, std::move(__f), std::move(__l));
     else
         erase(__i, __e);
 }
@@ -1324,6 +1405,14 @@ typename list<_Tp, _Alloc>::iterator
 list<_Tp, _Alloc>::insert(const_iterator __p, _InpIter __f, _InpIter __l,
                           __enable_if_t<__has_input_iterator_category<_InpIter>::value>*)
 {
+    return __insert_with_sentinel(__p, __f, __l);
+}
+
+template <class _Tp, class _Alloc>
+template <class _Iterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI
+typename list<_Tp, _Alloc>::iterator
+list<_Tp, _Alloc>::__insert_with_sentinel(const_iterator __p, _Iterator __f, _Sentinel __l) {
     iterator __r(__p.__ptr_);
     if (__f != __l)
     {

diff  --git a/libcxx/include/map b/libcxx/include/map
index 6cc331c7b05076..d21f6b9bc11054 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -70,6 +70,8 @@ public:
     template <class InputIterator>
         map(InputIterator first, InputIterator last,
             const key_compare& comp, const allocator_type& a);
+    template<container-compatible-range<value_type> R>
+      map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
     map(const map& m);
     map(map&& m)
         noexcept(
@@ -83,6 +85,9 @@ public:
     template <class InputIterator>
         map(InputIterator first, InputIterator last, const allocator_type& a)
             : map(first, last, Compare(), a) {}  // C++14
+    template<container-compatible-range<value_type> R>
+      map(from_range_t, R&& rg, const Allocator& a))
+        : map(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     map(initializer_list<value_type> il, const allocator_type& a)
         : map(il, Compare(), a) {}  // C++14
    ~map();
@@ -138,6 +143,8 @@ public:
         iterator insert(const_iterator position, P&& p);
     template <class InputIterator>
         void insert(InputIterator first, InputIterator last);
+    template<container-compatible-range<value_type> R>
+      void insert_range(R&& rg);                                                      // C++23
     void insert(initializer_list<value_type> il);
 
     node_type extract(const_iterator position);                                       // C++17
@@ -229,6 +236,11 @@ template <class InputIterator,
 map(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator())
   -> map<iter_key_t<InputIterator>, iter_val_t<InputIterator>, Compare, Allocator>; // C++17
 
+template<ranges::input_range R, class Compare = less<range-key-type<R>,
+         class Allocator = allocator<range-to-alloc-type<R>>>
+  map(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+    -> map<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; // C++23
+
 template<class Key, class T, class Compare = less<Key>,
     class Allocator = allocator<pair<const Key, T>>>
 map(initializer_list<pair<const Key, T>>, Compare = Compare(), Allocator = Allocator())
@@ -239,6 +251,10 @@ map(InputIterator, InputIterator, Allocator)
   -> map<iter_key_t<InputIterator>, iter_val_t<InputIterator>, less<iter_key_t<InputIterator>>,
     Allocator>; // C++17
 
+template<ranges::input_range R, class Allocator>
+  map(from_range_t, R&&, Allocator)
+    -> map<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; // C++23
+
 template<class Key, class T, class Allocator>
 map(initializer_list<pair<const Key, T>>, Allocator) -> map<Key, T, less<Key>, Allocator>; // C++17
 
@@ -338,6 +354,9 @@ public:
     template <class InputIterator>
         multimap(InputIterator first, InputIterator last, const key_compare& comp,
                  const allocator_type& a);
+    template<container-compatible-range<value_type> R>
+      multimap(from_range_t, R&& rg,
+               const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
     multimap(const multimap& m);
     multimap(multimap&& m)
         noexcept(
@@ -352,6 +371,9 @@ public:
     template <class InputIterator>
         multimap(InputIterator first, InputIterator last, const allocator_type& a)
             : multimap(first, last, Compare(), a) {} // C++14
+    template<container-compatible-range<value_type> R>
+      multimap(from_range_t, R&& rg, const Allocator& a))
+        : multimap(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     multimap(initializer_list<value_type> il, const allocator_type& a)
         : multimap(il, Compare(), a) {} // C++14
     ~multimap();
@@ -400,6 +422,8 @@ public:
         iterator insert(const_iterator position, P&& p);
     template <class InputIterator>
         void insert(InputIterator first, InputIterator last);
+    template<container-compatible-range<value_type> R>
+      void insert_range(R&& rg);                                                      // C++23
     void insert(initializer_list<value_type> il);
 
     node_type extract(const_iterator position);                                       // C++17
@@ -474,6 +498,11 @@ template <class InputIterator,
 multimap(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator())
   -> multimap<iter_key_t<InputIterator>, iter_val_t<InputIterator>, Compare, Allocator>; // C++17
 
+template<ranges::input_range R, class Compare = less<range-key-type<R>>,
+          class Allocator = allocator<range-to-alloc-type<R>>>
+  multimap(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+    -> multimap<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; // C++23
+
 template<class Key, class T, class Compare = less<Key>,
     class Allocator = allocator<pair<const Key, T>>>
 multimap(initializer_list<pair<const Key, T>>, Compare = Compare(), Allocator = Allocator())
@@ -484,6 +513,10 @@ multimap(InputIterator, InputIterator, Allocator)
   -> multimap<iter_key_t<InputIterator>, iter_val_t<InputIterator>,
     less<iter_key_t<InputIterator>>, Allocator>; // C++17
 
+template<ranges::input_range R, class Allocator>
+  multimap(from_range_t, R&&, Allocator)
+    -> multimap<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; // C++23
+
 template<class Key, class T, class Allocator>
 multimap(initializer_list<pair<const Key, T>>, Allocator)
   -> multimap<Key, T, less<Key>, Allocator>; // C++17
@@ -549,11 +582,15 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__functional/operations.h>
 #include <__iterator/erase_if_container.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/ranges_iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
 #include <__memory/addressof.h>
 #include <__memory/allocator.h>
 #include <__memory_resource/polymorphic_allocator.h>
 #include <__node_handle>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
 #include <__tree>
 #include <__type_traits/is_allocator.h>
 #include <__utility/forward.h>
@@ -1079,6 +1116,16 @@ public:
             insert(__f, __l);
         }
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    map(from_range_t, _Range&& __range, const key_compare& __comp = key_compare(),
+        const allocator_type& __a = allocator_type())
+      : __tree_(__vc(__comp), typename __base::allocator_type(__a)) {
+      insert_range(std::forward<_Range>(__range));
+    }
+#endif
+
 #if _LIBCPP_STD_VER >= 14
     template <class _InputIterator>
     _LIBCPP_INLINE_VISIBILITY
@@ -1086,6 +1133,13 @@ public:
         : map(__f, __l, key_compare(), __a) {}
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    map(from_range_t, _Range&& __range, const allocator_type& __a)
+      : map(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     map(const map& __m)
         : __tree_(__m.__tree_)
@@ -1285,6 +1339,17 @@ public:
                 insert(__e.__i_, *__f);
         }
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void insert_range(_Range&& __range) {
+      const_iterator __end = cend();
+      for (auto&& __element : __range) {
+        insert(__end.__i_, std::forward<decltype(__element)>(__element));
+      }
+    }
+#endif
+
 #if _LIBCPP_STD_VER >= 17
 
     template <class... _Args>
@@ -1570,6 +1635,15 @@ template<class _InputIterator, class _Compare = less<__iter_key_type<_InputItera
 map(_InputIterator, _InputIterator, _Compare = _Compare(), _Allocator = _Allocator())
   -> map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Compare, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Compare = less<__range_key_type<_Range>>,
+          class _Allocator = allocator<__range_to_alloc_type<_Range>>,
+          class = enable_if_t<!__is_allocator<_Compare>::value, void>,
+          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+map(from_range_t, _Range&&, _Compare = _Compare(), _Allocator = _Allocator())
+  -> map<__range_key_type<_Range>, __range_mapped_type<_Range>, _Compare, _Allocator>;
+#endif
+
 template<class _Key, class _Tp, class _Compare = less<remove_const_t<_Key>>,
          class _Allocator = allocator<pair<const _Key, _Tp>>,
          class = enable_if_t<!__is_allocator<_Compare>::value, void>,
@@ -1584,6 +1658,13 @@ map(_InputIterator, _InputIterator, _Allocator)
   -> map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>,
          less<__iter_key_type<_InputIterator>>, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+map(from_range_t, _Range&&, _Allocator)
+  -> map<__range_key_type<_Range>, __range_mapped_type<_Range>, less<__range_key_type<_Range>>, _Allocator>;
+#endif
+
 template<class _Key, class _Tp, class _Allocator,
          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
 map(initializer_list<pair<_Key, _Tp>>, _Allocator)
@@ -1875,6 +1956,16 @@ public:
             insert(__f, __l);
         }
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    multimap(from_range_t, _Range&& __range, const key_compare& __comp = key_compare(),
+        const allocator_type& __a = allocator_type())
+      : __tree_(__vc(__comp), typename __base::allocator_type(__a)) {
+      insert_range(std::forward<_Range>(__range));
+    }
+#endif
+
 #if _LIBCPP_STD_VER >= 14
     template <class _InputIterator>
     _LIBCPP_INLINE_VISIBILITY
@@ -1882,6 +1973,13 @@ public:
         : multimap(__f, __l, key_compare(), __a) {}
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+      : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     multimap(const multimap& __m)
         : __tree_(__m.__tree_.value_comp(),
@@ -2072,6 +2170,17 @@ public:
                 __tree_.__insert_multi(__e.__i_, *__f);
         }
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void insert_range(_Range&& __range) {
+      const_iterator __end = cend();
+      for (auto&& __element : __range) {
+        __tree_.__insert_multi(__end.__i_, std::forward<decltype(__element)>(__element));
+      }
+    }
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     iterator erase(const_iterator __p) {return __tree_.erase(__p.__i_);}
     _LIBCPP_INLINE_VISIBILITY
@@ -2256,6 +2365,15 @@ template<class _InputIterator, class _Compare = less<__iter_key_type<_InputItera
 multimap(_InputIterator, _InputIterator, _Compare = _Compare(), _Allocator = _Allocator())
   -> multimap<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Compare, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Compare = less<__range_key_type<_Range>>,
+          class _Allocator = allocator<__range_to_alloc_type<_Range>>,
+          class = enable_if_t<!__is_allocator<_Compare>::value, void>,
+          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+multimap(from_range_t, _Range&&, _Compare = _Compare(), _Allocator = _Allocator())
+  -> multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, _Compare, _Allocator>;
+#endif
+
 template<class _Key, class _Tp, class _Compare = less<remove_const_t<_Key>>,
          class _Allocator = allocator<pair<const _Key, _Tp>>,
          class = enable_if_t<!__is_allocator<_Compare>::value, void>,
@@ -2270,6 +2388,13 @@ multimap(_InputIterator, _InputIterator, _Allocator)
   -> multimap<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>,
          less<__iter_key_type<_InputIterator>>, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+multimap(from_range_t, _Range&&, _Allocator)
+  -> multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, less<__range_key_type<_Range>>, _Allocator>;
+#endif
+
 template<class _Key, class _Tp, class _Allocator,
          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
 multimap(initializer_list<pair<_Key, _Tp>>, _Allocator)

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 19db0c89e015a2..88de19855451fa 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1146,55 +1146,56 @@ module std [system] {
         export type_traits.is_reference
         export type_traits.remove_cvref
       }
-      module counted_iterator      { private header "__iterator/counted_iterator.h" }
-      module data                  { private header "__iterator/data.h" }
-      module default_sentinel      { private header "__iterator/default_sentinel.h" }
-      module distance              {
+      module counted_iterator       { private header "__iterator/counted_iterator.h" }
+      module data                   { private header "__iterator/data.h" }
+      module default_sentinel       { private header "__iterator/default_sentinel.h" }
+      module distance               {
         private header "__iterator/distance.h"
         export ranges.__ranges.size
       }
-      module empty                 { private header "__iterator/empty.h" }
-      module erase_if_container    { private header "__iterator/erase_if_container.h" }
-      module front_insert_iterator { private header "__iterator/front_insert_iterator.h" }
-      module incrementable_traits  { private header "__iterator/incrementable_traits.h" }
-      module indirectly_comparable { private header "__iterator/indirectly_comparable.h" }
-      module insert_iterator       { private header "__iterator/insert_iterator.h" }
-      module istream_iterator      { private header "__iterator/istream_iterator.h" }
-      module istreambuf_iterator   { private header "__iterator/istreambuf_iterator.h" }
-      module iter_move             { private header "__iterator/iter_move.h" }
-      module iter_swap             { private header "__iterator/iter_swap.h" }
-      module iterator              { private header "__iterator/iterator.h" }
-      module iterator_traits       {
+      module empty                  { private header "__iterator/empty.h" }
+      module erase_if_container     { private header "__iterator/erase_if_container.h" }
+      module front_insert_iterator  { private header "__iterator/front_insert_iterator.h" }
+      module incrementable_traits   { private header "__iterator/incrementable_traits.h" }
+      module indirectly_comparable  { private header "__iterator/indirectly_comparable.h" }
+      module insert_iterator        { private header "__iterator/insert_iterator.h" }
+      module istream_iterator       { private header "__iterator/istream_iterator.h" }
+      module istreambuf_iterator    { private header "__iterator/istreambuf_iterator.h" }
+      module iter_move              { private header "__iterator/iter_move.h" }
+      module iter_swap              { private header "__iterator/iter_swap.h" }
+      module iterator               { private header "__iterator/iterator.h" }
+      module iterator_traits        {
         private header "__iterator/iterator_traits.h"
         export type_traits.is_primary_template
       }
-      module iterator_with_data    { private header "__iterator/iterator_with_data.h" }
+      module iterator_with_data     { private header "__iterator/iterator_with_data.h" }
       module mergeable {
         private header "__iterator/mergeable.h"
         export functional.__functional.ranges_operations
       }
-      module move_iterator         { private header "__iterator/move_iterator.h" }
-      module move_sentinel         { private header "__iterator/move_sentinel.h" }
-      module next                  { private header "__iterator/next.h" }
-      module ostream_iterator      { private header "__iterator/ostream_iterator.h" }
-      module ostreambuf_iterator   {
+      module move_iterator          { private header "__iterator/move_iterator.h" }
+      module move_sentinel          { private header "__iterator/move_sentinel.h" }
+      module next                   { private header "__iterator/next.h" }
+      module ostream_iterator       { private header "__iterator/ostream_iterator.h" }
+      module ostreambuf_iterator    {
         private header "__iterator/ostreambuf_iterator.h"
         export iosfwd
       }
-      module permutable            { private header "__iterator/permutable.h" }
-      module prev                  { private header "__iterator/prev.h" }
-      module projected             { private header "__iterator/projected.h" }
-      module readable_traits       { private header "__iterator/readable_traits.h" }
-      module reverse_access        { private header "__iterator/reverse_access.h" }
-      module reverse_iterator      { private header "__iterator/reverse_iterator.h" }
-      module segmented_iterator    { private header "__iterator/segmented_iterator.h" }
-      module size                  { private header "__iterator/size.h" }
+      module permutable             { private header "__iterator/permutable.h" }
+      module prev                   { private header "__iterator/prev.h" }
+      module projected              { private header "__iterator/projected.h" }
+      module ranges_iterator_traits { private header "__iterator/ranges_iterator_traits.h" }
+      module readable_traits        { private header "__iterator/readable_traits.h" }
+      module reverse_access         { private header "__iterator/reverse_access.h" }
+      module reverse_iterator       { private header "__iterator/reverse_iterator.h" }
+      module segmented_iterator     { private header "__iterator/segmented_iterator.h" }
+      module size                   { private header "__iterator/size.h" }
       module sortable {
         private header "__iterator/sortable.h"
         export functional.__functional.ranges_operations
       }
-      module unreachable_sentinel  { private header "__iterator/unreachable_sentinel.h" }
-      module wrap_iter             { private header "__iterator/wrap_iter.h" }
+      module unreachable_sentinel   { private header "__iterator/unreachable_sentinel.h" }
+      module wrap_iter              { private header "__iterator/wrap_iter.h" }
     }
   }
   module latch {

diff  --git a/libcxx/include/set b/libcxx/include/set
index 76489e896a056b..e44f33e3d47b39 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -56,6 +56,8 @@ public:
     template <class InputIterator>
         set(InputIterator first, InputIterator last, const value_compare& comp,
             const allocator_type& a);
+    template<container-compatible-range<value_type> R>
+      set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
     set(const set& s);
     set(set&& s)
         noexcept(
@@ -70,6 +72,9 @@ public:
     template <class InputIterator>
         set(InputIterator first, InputIterator last, const allocator_type& a)
             : set(first, last, Compare(), a) {}  // C++14
+    template<container-compatible-range<value_type> R>
+      set(from_range_t, R&& rg, const Allocator& a))
+        : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     set(initializer_list<value_type> il, const allocator_type& a)
         : set(il, Compare(), a) {}  // C++14
     ~set();
@@ -114,6 +119,8 @@ public:
     iterator insert(const_iterator position, value_type&& v);
     template <class InputIterator>
         void insert(InputIterator first, InputIterator last);
+    template<container-compatible-range<value_type> R>
+      void insert_range(R&& rg);                                                      // C++23
     void insert(initializer_list<value_type> il);
 
     node_type extract(const_iterator position);                                       // C++17
@@ -190,6 +197,11 @@ set(InputIterator, InputIterator,
     Compare = Compare(), Allocator = Allocator())
   -> set<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>; // C++17
 
+template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>,
+         class Allocator = allocator<ranges::range_value_t<R>>>
+  set(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+    -> set<ranges::range_value_t<R>, Compare, Allocator>; // C++23
+
 template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
 set(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
   -> set<Key, Compare, Allocator>; // C++17
@@ -199,6 +211,10 @@ set(InputIterator, InputIterator, Allocator)
   -> set<typename iterator_traits<InputIterator>::value_type,
           less<typename iterator_traits<InputIterator>::value_type>, Allocator>; // C++17
 
+template<ranges::input_range R, class Allocator>
+  set(from_range_t, R&&, Allocator)
+    -> set<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; // C++23
+
 template<class Key, class Allocator>
 set(initializer_list<Key>, Allocator) -> set<Key, less<Key>, Allocator>; // C++17
 
@@ -284,6 +300,9 @@ public:
     template <class InputIterator>
         multiset(InputIterator first, InputIterator last,
                  const value_compare& comp, const allocator_type& a);
+    template<container-compatible-range<value_type> R>
+      multiset(from_range_t, R&& rg,
+               const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
     multiset(const multiset& s);
     multiset(multiset&& s)
         noexcept(
@@ -298,6 +317,9 @@ public:
     template <class InputIterator>
         multiset(InputIterator first, InputIterator last, const allocator_type& a)
             : set(first, last, Compare(), a) {}  // C++14
+    template<container-compatible-range<value_type> R>
+      multiset(from_range_t, R&& rg, const Allocator& a))
+        : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     multiset(initializer_list<value_type> il, const allocator_type& a)
         : set(il, Compare(), a) {}  // C++14
     ~multiset();
@@ -342,6 +364,8 @@ public:
     iterator insert(const_iterator position, value_type&& v);
     template <class InputIterator>
         void insert(InputIterator first, InputIterator last);
+    template<container-compatible-range<value_type> R>
+      void insert_range(R&& rg);                                                      // C++23
     void insert(initializer_list<value_type> il);
 
     node_type extract(const_iterator position);                                       // C++17
@@ -419,6 +443,11 @@ multiset(InputIterator, InputIterator,
     Compare = Compare(), Allocator = Allocator())
   -> multiset<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>; // C++17
 
+template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>,
+          class Allocator = allocator<ranges::range_value_t<R>>>
+  multiset(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+    -> multiset<ranges::range_value_t<R>, Compare, Allocator>;
+
 template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
 multiset(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
   -> multiset<Key, Compare, Allocator>; // C++17
@@ -428,6 +457,10 @@ multiset(InputIterator, InputIterator, Allocator)
   -> multiset<typename iterator_traits<InputIterator>::value_type,
           less<typename iterator_traits<InputIterator>::value_type>, Allocator>; // C++17
 
+template<ranges::input_range R, class Allocator>
+  multiset(from_range_t, R&&, Allocator)
+    -> multiset<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>;
+
 template<class Key, class Allocator>
 multiset(initializer_list<Key>, Allocator) -> multiset<Key, less<Key>, Allocator>; // C++17
 
@@ -489,10 +522,14 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__functional/operations.h>
 #include <__iterator/erase_if_container.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/ranges_iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
 #include <__memory/allocator.h>
 #include <__memory_resource/polymorphic_allocator.h>
 #include <__node_handle>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
 #include <__tree>
 #include <__type_traits/is_allocator.h>
 #include <__utility/forward.h>
@@ -603,6 +640,16 @@ public:
             insert(__f, __l);
         }
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    set(from_range_t, _Range&& __range, const key_compare& __comp = key_compare(),
+        const allocator_type& __a = allocator_type())
+      : __tree_(__comp, __a) {
+      insert_range(std::forward<_Range>(__range));
+    }
+#endif
+
 #if _LIBCPP_STD_VER >= 14
         template <class _InputIterator>
         _LIBCPP_INLINE_VISIBILITY
@@ -610,6 +657,13 @@ public:
             : set(__f, __l, key_compare(), __a) {}
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    set(from_range_t, _Range&& __range, const allocator_type& __a)
+      : set(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     set(const set& __s)
         : __tree_(__s.__tree_)
@@ -752,6 +806,17 @@ public:
                 __tree_.__insert_unique(__e, *__f);
         }
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void insert_range(_Range&& __range) {
+      const_iterator __end = cend();
+      for (auto&& __element : __range) {
+        __tree_.__insert_unique(__end, std::forward<decltype(__element)>(__element));
+      }
+    }
+#endif
+
 #ifndef _LIBCPP_CXX03_LANG
     _LIBCPP_INLINE_VISIBILITY
     pair<iterator,bool> insert(value_type&& __v)
@@ -947,6 +1012,15 @@ template<class _InputIterator,
 set(_InputIterator, _InputIterator, _Compare = _Compare(), _Allocator = _Allocator())
   -> set<__iter_value_type<_InputIterator>, _Compare, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Compare = less<ranges::range_value_t<_Range>>,
+          class _Allocator = allocator<ranges::range_value_t<_Range>>,
+          class = enable_if_t<__is_allocator<_Allocator>::value, void>,
+          class = enable_if_t<!__is_allocator<_Compare>::value, void>>
+set(from_range_t, _Range&&, _Compare = _Compare(), _Allocator = _Allocator())
+  -> set<ranges::range_value_t<_Range>, _Compare, _Allocator>;
+#endif
+
 template<class _Key, class _Compare = less<_Key>,
          class _Allocator = allocator<_Key>,
          class = enable_if_t<!__is_allocator<_Compare>::value, void>,
@@ -961,6 +1035,13 @@ set(_InputIterator, _InputIterator, _Allocator)
   -> set<__iter_value_type<_InputIterator>,
          less<__iter_value_type<_InputIterator>>, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+set(from_range_t, _Range&&, _Allocator)
+  -> set<ranges::range_value_t<_Range>, less<ranges::range_value_t<_Range>>, _Allocator>;
+#endif
+
 template<class _Key, class _Allocator,
          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
 set(initializer_list<_Key>, _Allocator)
@@ -1160,6 +1241,21 @@ public:
             insert(__f, __l);
         }
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    multiset(from_range_t, _Range&& __range, const key_compare& __comp = key_compare(),
+        const allocator_type& __a = allocator_type())
+      : __tree_(__comp, __a) {
+      insert_range(std::forward<_Range>(__range));
+    }
+
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    multiset(from_range_t, _Range&& __range, const allocator_type& __a)
+      : multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     multiset(const multiset& __s)
         : __tree_(__s.__tree_.value_comp(),
@@ -1301,6 +1397,17 @@ public:
                 __tree_.__insert_multi(__e, *__f);
         }
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void insert_range(_Range&& __range) {
+      const_iterator __end = cend();
+      for (auto&& __element : __range) {
+        __tree_.__insert_multi(__end, std::forward<decltype(__element)>(__element));
+      }
+    }
+#endif
+
 #ifndef _LIBCPP_CXX03_LANG
     _LIBCPP_INLINE_VISIBILITY
     iterator insert(value_type&& __v)
@@ -1496,6 +1603,15 @@ template<class _InputIterator,
 multiset(_InputIterator, _InputIterator, _Compare = _Compare(), _Allocator = _Allocator())
   -> multiset<__iter_value_type<_InputIterator>, _Compare, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Compare = less<ranges::range_value_t<_Range>>,
+          class _Allocator = allocator<ranges::range_value_t<_Range>>,
+          class = enable_if_t<__is_allocator<_Allocator>::value, void>,
+          class = enable_if_t<!__is_allocator<_Compare>::value, void>>
+multiset(from_range_t, _Range&&, _Compare = _Compare(), _Allocator = _Allocator())
+  -> multiset<ranges::range_value_t<_Range>, _Compare, _Allocator>;
+#endif
+
 template<class _Key, class _Compare = less<_Key>,
          class _Allocator = allocator<_Key>,
          class = enable_if_t<__is_allocator<_Allocator>::value, void>,
@@ -1510,6 +1626,13 @@ multiset(_InputIterator, _InputIterator, _Allocator)
   -> multiset<__iter_value_type<_InputIterator>,
          less<__iter_value_type<_InputIterator>>, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
+multiset(from_range_t, _Range&&, _Allocator)
+  -> multiset<ranges::range_value_t<_Range>, less<ranges::range_value_t<_Range>>, _Allocator>;
+#endif
+
 template<class _Key, class _Allocator,
          class = enable_if_t<__is_allocator<_Allocator>::value, void>>
 multiset(initializer_list<_Key>, _Allocator)

diff  --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8052d10d096400..4772667dbbf624 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -59,6 +59,11 @@ public:
                       size_type n = 0, const hasher& hf = hasher(),
                       const key_equal& eql = key_equal(),
                       const allocator_type& a = allocator_type());
+    template<container-compatible-range<value_type> R>
+      unordered_map(from_range_t, R&& rg, size_type n = see below,
+        const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+        const allocator_type& a = allocator_type()); // C++23
+
     explicit unordered_map(const allocator_type&);
     unordered_map(const unordered_map&);
     unordered_map(const unordered_map&, const Allocator&);
@@ -82,6 +87,12 @@ public:
       unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
         const allocator_type& a)
       : unordered_map(f, l, n, hf, key_equal(), a) {}  // C++14
+    template<container-compatible-range<value_type> R>
+      unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
+        : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+    template<container-compatible-range<value_type> R>
+      unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+        : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
     unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
       : unordered_map(il, n, hasher(), key_equal(), a) {}  // C++14
     unordered_map(initializer_list<value_type> il, size_type n, const hasher& hf,
@@ -122,6 +133,8 @@ public:
         iterator insert(const_iterator hint, P&& obj);
     template <class InputIterator>
         void insert(InputIterator first, InputIterator last);
+    template<container-compatible-range<value_type> R>
+      void insert_range(R&& rg);                                                      // C++23
     void insert(initializer_list<value_type>);
 
     node_type extract(const_iterator position);                                       // C++17
@@ -224,6 +237,13 @@ unordered_map(InputIterator, InputIterator, typename see below::size_type = see
   -> unordered_map<iter_key_t<InputIterator>, iter_value_t<InputIterator>, Hash, Pred,
     Allocator>; // C++17
 
+template<ranges::input_range R, class Hash = hash<range-key-type<R>>,
+         class Pred = equal_to<range-key-type<R>>,
+         class Allocator = allocator<range-to-alloc-type<R>>>
+  unordered_map(from_range_t, R&&, typename see below::size_type = see below,
+                Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+    -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; // C++23
+
 template<class Key, class T, class Hash = hash<Key>,
     class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>>
 unordered_map(initializer_list<pair<const Key, T>>, typename see below::size_type = see below,
@@ -245,6 +265,21 @@ unordered_map(InputIterator, InputIterator, typename see below::size_type, Hash,
   -> unordered_map<iter_key_t<InputIterator>, iter_val_t<InputIterator>, Hash,
           equal_to<iter_key_t<InputIterator>>, Allocator>; // C++17
 
+template<ranges::input_range R, class Allocator>
+  unordered_map(from_range_t, R&&, typename see below::size_type, Allocator)
+    -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+                      equal_to<range-key-type<R>>, Allocator>;   // C++23
+
+template<ranges::input_range R, class Allocator>
+  unordered_map(from_range_t, R&&, Allocator)
+    -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+                      equal_to<range-key-type<R>>, Allocator>;   // C++23
+
+template<ranges::input_range R, class Hash, class Allocator>
+  unordered_map(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+    -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash,
+                      equal_to<range-key-type<R>>, Allocator>;   // C++23
+
 template<class Key, class T, typename Allocator>
 unordered_map(initializer_list<pair<const Key, T>>, typename see below::size_type, Allocator)
   -> unordered_map<Key, T, hash<Key>, equal_to<Key>, Allocator>; // C++17
@@ -311,6 +346,10 @@ public:
                       size_type n = 0, const hasher& hf = hasher(),
                       const key_equal& eql = key_equal(),
                       const allocator_type& a = allocator_type());
+    template<container-compatible-range<value_type> R>
+      unordered_multimap(from_range_t, R&& rg, size_type n = see below,
+        const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+        const allocator_type& a = allocator_type()); // C++23
     explicit unordered_multimap(const allocator_type&);
     unordered_multimap(const unordered_multimap&);
     unordered_multimap(const unordered_multimap&, const Allocator&);
@@ -334,6 +373,12 @@ public:
       unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
         const allocator_type& a)
       : unordered_multimap(f, l, n, hf, key_equal(), a) {}  // C++14
+    template<container-compatible-range<value_type> R>
+      unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
+        : unordered_multimap(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+    template<container-compatible-range<value_type> R>
+      unordered_multimap(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+        : unordered_multimap(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
     unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
       : unordered_multimap(il, n, hasher(), key_equal(), a) {}  // C++14
     unordered_multimap(initializer_list<value_type> il, size_type n, const hasher& hf,
@@ -374,6 +419,8 @@ public:
         iterator insert(const_iterator hint, P&& obj);
     template <class InputIterator>
         void insert(InputIterator first, InputIterator last);
+    template<container-compatible-range<value_type> R>
+      void insert_range(R&& rg);                               // C++23
     void insert(initializer_list<value_type>);
 
     node_type extract(const_iterator position);                // C++17
@@ -453,6 +500,13 @@ unordered_multimap(InputIterator, InputIterator, typename see below::size_type =
   -> unordered_multimap<iter_key_t<InputIterator>, iter_value_t<InputIterator>, Hash, Pred,
     Allocator>; // C++17
 
+template<ranges::input_range R, class Hash = hash<range-key-type<R>>,
+         class Pred = equal_to<range-key-type<R>>,
+         class Allocator = allocator<range-to-alloc-type<R>>>
+  unordered_multimap(from_range_t, R&&, typename see below::size_type = see below,
+                Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+    -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; // C++23
+
 template<class Key, class T, class Hash = hash<Key>,
     class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>>
 unordered_multimap(initializer_list<pair<const Key, T>>, typename see below::size_type = see below,
@@ -474,6 +528,21 @@ unordered_multimap(InputIterator, InputIterator, typename see below::size_type,
   -> unordered_multimap<iter_key_t<InputIterator>, iter_val_t<InputIterator>, Hash,
           equal_to<iter_key_t<InputIterator>>, Allocator>; // C++17
 
+template<ranges::input_range R, class Allocator>
+  unordered_multimap(from_range_t, R&&, typename see below::size_type, Allocator)
+    -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+                      equal_to<range-key-type<R>>, Allocator>;   // C++23
+
+template<ranges::input_range R, class Allocator>
+  unordered_multimap(from_range_t, R&&, Allocator)
+    -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+                      equal_to<range-key-type<R>>, Allocator>;   // C++23
+
+template<ranges::input_range R, class Hash, class Allocator>
+  unordered_multimap(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+    -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, Hash,
+                      equal_to<range-key-type<R>>, Allocator>;   // C++23
+
 template<class Key, class T, typename Allocator>
 unordered_multimap(initializer_list<pair<const Key, T>>, typename see below::size_type, Allocator)
   -> unordered_multimap<Key, T, hash<Key>, equal_to<Key>, Allocator>; // C++17
@@ -524,10 +593,14 @@ template <class Key, class T, class Hash, class Pred, class Alloc>
 #include <__iterator/distance.h>
 #include <__iterator/erase_if_container.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/ranges_iterator_traits.h>
 #include <__memory/addressof.h>
 #include <__memory/allocator.h>
 #include <__memory_resource/polymorphic_allocator.h>
 #include <__node_handle>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/forward.h>
@@ -1111,6 +1184,21 @@ public:
                       size_type __n, const hasher& __hf,
                       const key_equal& __eql,
                       const allocator_type& __a);
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_map(from_range_t, _Range&& __range, size_type __n = /*implementation-defined*/0,
+                  const hasher& __hf = hasher(), const key_equal& __eql = key_equal(),
+                  const allocator_type& __a = allocator_type())
+        : __table_(__hf, __eql, typename __table::allocator_type(__a)) {
+      if (__n > 0) {
+        __table_.__rehash_unique(__n);
+      }
+      insert_range(std::forward<_Range>(__range));
+    }
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     explicit unordered_map(const allocator_type& __a);
     _LIBCPP_HIDE_FROM_ABI unordered_map(const unordered_map& __u);
@@ -1143,6 +1231,19 @@ public:
       unordered_map(_InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf,
         const allocator_type& __a)
       : unordered_map(__first, __last, __n, __hf, key_equal(), __a) {}
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_map(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
+        : unordered_map(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
+
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_map(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
+        : unordered_map(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     unordered_map(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_map(__il, __n, hasher(), key_equal(), __a) {}
@@ -1217,6 +1318,16 @@ public:
         _LIBCPP_INLINE_VISIBILITY
         void insert(_InputIterator __first, _InputIterator __last);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void insert_range(_Range&& __range) {
+      for (auto&& __element : __range) {
+        __table_.__insert_unique(std::forward<decltype(__element)>(__element));
+      }
+    }
+#endif
+
 #ifndef _LIBCPP_CXX03_LANG
     _LIBCPP_INLINE_VISIBILITY
     void insert(initializer_list<value_type> __il)
@@ -1528,6 +1639,20 @@ unordered_map(_InputIterator, _InputIterator, typename allocator_traits<_Allocat
               _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
   -> unordered_map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Hash, _Pred, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+          class _Hash = hash<__range_key_type<_Range>>,
+          class _Pred = equal_to<__range_key_type<_Range>>,
+          class _Allocator = allocator<__range_to_alloc_type<_Range>>,
+          class = enable_if_t<!__is_allocator<_Hash>::value>,
+          class = enable_if_t<!is_integral<_Hash>::value>,
+          class = enable_if_t<!__is_allocator<_Pred>::value>,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_map(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type = 0,
+              _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
+  -> unordered_map<__range_key_type<_Range>, __range_mapped_type<_Range>, _Hash, _Pred, _Allocator>; // C++23
+#endif
+
 template<class _Key, class _Tp, class _Hash = hash<remove_const_t<_Key>>,
          class _Pred = equal_to<remove_const_t<_Key>>,
          class _Allocator = allocator<pair<const _Key, _Tp>>,
@@ -1562,6 +1687,30 @@ unordered_map(_InputIterator, _InputIterator, typename allocator_traits<_Allocat
   -> unordered_map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>,
                    _Hash, equal_to<__iter_key_type<_InputIterator>>, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_map(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Allocator)
+  -> unordered_map<__range_key_type<_Range>, __range_mapped_type<_Range>, hash<__range_key_type<_Range>>,
+                   equal_to<__range_key_type<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_map(from_range_t, _Range&&, _Allocator)
+  -> unordered_map<__range_key_type<_Range>, __range_mapped_type<_Range>, hash<__range_key_type<_Range>>,
+                   equal_to<__range_key_type<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Hash, class _Allocator,
+          class = enable_if_t<!__is_allocator<_Hash>::value>,
+          class = enable_if_t<!is_integral<_Hash>::value>,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_map(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Hash, _Allocator)
+  -> unordered_map<__range_key_type<_Range>, __range_mapped_type<_Range>, _Hash,
+                   equal_to<__range_key_type<_Range>>, _Allocator>;
+
+#endif
+
 template<class _Key, class _Tp, class _Allocator,
          class = enable_if_t<__is_allocator<_Allocator>::value>>
 unordered_map(initializer_list<pair<_Key, _Tp>>, typename allocator_traits<_Allocator>::size_type, _Allocator)
@@ -1950,6 +2099,21 @@ private:
                       size_type __n, const hasher& __hf,
                       const key_equal& __eql,
                       const allocator_type& __a);
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_multimap(from_range_t, _Range&& __range, size_type __n = /*implementation-defined*/0,
+                       const hasher& __hf = hasher(), const key_equal& __eql = key_equal(),
+                       const allocator_type& __a = allocator_type())
+        : __table_(__hf, __eql, typename __table::allocator_type(__a)) {
+      if (__n > 0) {
+        __table_.__rehash_multi(__n);
+      }
+      insert_range(std::forward<_Range>(__range));
+    }
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     explicit unordered_multimap(const allocator_type& __a);
     _LIBCPP_HIDE_FROM_ABI unordered_multimap(const unordered_multimap& __u);
@@ -1983,6 +2147,19 @@ private:
       unordered_multimap(_InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf,
         const allocator_type& __a)
       : unordered_multimap(__first, __last, __n, __hf, key_equal(), __a) {}
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_multimap(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
+        : unordered_multimap(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
+
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_multimap(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
+        : unordered_multimap(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     unordered_multimap(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_multimap(__il, __n, hasher(), key_equal(), __a) {}
@@ -2056,6 +2233,16 @@ private:
     _LIBCPP_INLINE_VISIBILITY
     void insert(_InputIterator __first, _InputIterator __last);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void insert_range(_Range&& __range) {
+      for (auto&& __element : __range) {
+        __table_.__insert_multi(std::forward<decltype(__element)>(__element));
+      }
+    }
+#endif
+
 #ifndef _LIBCPP_CXX03_LANG
     _LIBCPP_INLINE_VISIBILITY
     void insert(initializer_list<value_type> __il)
@@ -2276,6 +2463,20 @@ unordered_multimap(_InputIterator, _InputIterator, typename allocator_traits<_Al
                    _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
   -> unordered_multimap<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Hash, _Pred, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+          class _Hash = hash<__range_key_type<_Range>>,
+          class _Pred = equal_to<__range_key_type<_Range>>,
+          class _Allocator = allocator<__range_to_alloc_type<_Range>>,
+          class = enable_if_t<!__is_allocator<_Hash>::value>,
+          class = enable_if_t<!is_integral<_Hash>::value>,
+          class = enable_if_t<!__is_allocator<_Pred>::value>,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multimap(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type = 0,
+              _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
+  -> unordered_multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, _Hash, _Pred, _Allocator>;
+#endif
+
 template<class _Key, class _Tp, class _Hash = hash<remove_const_t<_Key>>,
          class _Pred = equal_to<remove_const_t<_Key>>,
          class _Allocator = allocator<pair<const _Key, _Tp>>,
@@ -2310,6 +2511,30 @@ unordered_multimap(_InputIterator, _InputIterator, typename allocator_traits<_Al
   -> unordered_multimap<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>,
                         _Hash, equal_to<__iter_key_type<_InputIterator>>, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multimap(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Allocator)
+  -> unordered_multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, hash<__range_key_type<_Range>>,
+                   equal_to<__range_key_type<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multimap(from_range_t, _Range&&, _Allocator)
+  -> unordered_multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, hash<__range_key_type<_Range>>,
+                   equal_to<__range_key_type<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Hash, class _Allocator,
+          class = enable_if_t<!__is_allocator<_Hash>::value>,
+          class = enable_if_t<!is_integral<_Hash>::value>,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multimap(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Hash, _Allocator)
+  -> unordered_multimap<__range_key_type<_Range>, __range_mapped_type<_Range>, _Hash,
+                   equal_to<__range_key_type<_Range>>, _Allocator>;
+
+#endif
+
 template<class _Key, class _Tp, class _Allocator,
          class = enable_if_t<__is_allocator<_Allocator>::value>>
 unordered_multimap(initializer_list<pair<_Key, _Tp>>, typename allocator_traits<_Allocator>::size_type, _Allocator)

diff  --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 1bb88fce02b6e2..2e2f4caddb9938 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -58,6 +58,10 @@ public:
                       size_type n = 0, const hasher& hf = hasher(),
                       const key_equal& eql = key_equal(),
                       const allocator_type& a = allocator_type());
+    template<container-compatible-range<value_type> R>
+      unordered_set(from_range_t, R&& rg, size_type n = see below,
+        const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+        const allocator_type& a = allocator_type()); // C++23
     explicit unordered_set(const allocator_type&);
     unordered_set(const unordered_set&);
     unordered_set(const unordered_set&, const Allocator&);
@@ -77,6 +81,12 @@ public:
     template <class InputIterator>
       unordered_set(InputIterator f, InputIterator l, size_type n,
                     const hasher& hf,  const allocator_type& a); // C++14
+    template<container-compatible-range<value_type> R>
+      unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
+        : unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+    template<container-compatible-range<value_type> R>
+      unordered_set(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+        : unordered_set(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
     unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a); // C++14
     unordered_set(initializer_list<value_type> il, size_type n,
                   const hasher& hf,  const allocator_type& a); // C++14
@@ -113,6 +123,8 @@ public:
     iterator insert(const_iterator hint, value_type&& obj);
     template <class InputIterator>
         void insert(InputIterator first, InputIterator last);
+    template<container-compatible-range<value_type> R>
+      void insert_range(R&& rg);                                      // C++23
     void insert(initializer_list<value_type>);
 
     node_type extract(const_iterator position);                       // C++17
@@ -191,6 +203,13 @@ unordered_set(InputIterator, InputIterator, typename see below::size_type = see
   -> unordered_set<typename iterator_traits<InputIterator>::value_type,
         Hash, Pred, Allocator>; // C++17
 
+template<ranges::input_range R,
+         class Hash = hash<ranges::range_value_t<R>>,
+         class Pred = equal_to<ranges::range_value_t<R>>,
+         class Allocator = allocator<ranges::range_value_t<R>>>
+  unordered_set(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+    -> unordered_set<ranges::range_value_t<R>, Hash, Pred, Allocator>; // C++23
+
 template<class T, class Hash = hash<T>,
           class Pred = equal_to<T>, class Allocator = allocator<T>>
 unordered_set(initializer_list<T>, typename see below::size_type = see below,
@@ -211,6 +230,21 @@ unordered_set(InputIterator, InputIterator, typename see below::size_type,
         equal_to<typename iterator_traits<InputIterator>::value_type>,
         Allocator>; // C++17
 
+template<ranges::input_range R, class Allocator>
+  unordered_set(from_range_t, R&&, typename see below::size_type, Allocator)
+    -> unordered_set<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+                      equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Allocator>
+  unordered_set(from_range_t, R&&, Allocator)
+    -> unordered_set<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+                      equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Hash, class Allocator>
+  unordered_set(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+    -> unordered_set<ranges::range_value_t<R>, Hash,
+                      equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
 template<class T, class Allocator>
 unordered_set(initializer_list<T>, typename see below::size_type, Allocator)
   -> unordered_set<T, hash<T>, equal_to<T>, Allocator>; // C++17
@@ -272,6 +306,10 @@ public:
                       size_type n = 0, const hasher& hf = hasher(),
                       const key_equal& eql = key_equal(),
                       const allocator_type& a = allocator_type());
+    template<container-compatible-range<value_type> R>
+      unordered_multiset(from_range_t, R&& rg, size_type n = see below,
+        const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+        const allocator_type& a = allocator_type()); // C++23
     explicit unordered_multiset(const allocator_type&);
     unordered_multiset(const unordered_multiset&);
     unordered_multiset(const unordered_multiset&, const Allocator&);
@@ -291,6 +329,12 @@ public:
     template <class InputIterator>
       unordered_multiset(InputIterator f, InputIterator l, size_type n,
                          const hasher& hf, const allocator_type& a); // C++14
+    template<container-compatible-range<value_type> R>
+      unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
+        : unordered_multiset(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+    template<container-compatible-range<value_type> R>
+      unordered_multiset(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+        : unordered_multiset(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
     unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a); // C++14
     unordered_multiset(initializer_list<value_type> il, size_type n,
                        const hasher& hf,  const allocator_type& a); // C++14
@@ -327,6 +371,8 @@ public:
     iterator insert(const_iterator hint, value_type&& obj);
     template <class InputIterator>
         void insert(InputIterator first, InputIterator last);
+    template<container-compatible-range<value_type> R>
+      void insert_range(R&& rg);                            // C++23
     void insert(initializer_list<value_type>);
 
     node_type extract(const_iterator position);             // C++17
@@ -405,6 +451,13 @@ unordered_multiset(InputIterator, InputIterator, see below::size_type = see belo
   -> unordered_multiset<typename iterator_traits<InputIterator>::value_type,
         Hash, Pred, Allocator>; // C++17
 
+template<ranges::input_range R,
+         class Hash = hash<ranges::range_value_t<R>>,
+         class Pred = equal_to<ranges::range_value_t<R>>,
+         class Allocator = allocator<ranges::range_value_t<R>>>
+  unordered_multiset(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+    -> unordered_multiset<ranges::range_value_t<R>, Hash, Pred, Allocator>; // C++23
+
 template<class T, class Hash = hash<T>,
           class Pred = equal_to<T>, class Allocator = allocator<T>>
 unordered_multiset(initializer_list<T>, typename see below::size_type = see below,
@@ -424,6 +477,21 @@ unordered_multiset(InputIterator, InputIterator, typename see below::size_type,
   -> unordered_multiset<typename iterator_traits<InputIterator>::value_type, Hash,
         equal_to<typename iterator_traits<InputIterator>::value_type>, Allocator>; // C++17
 
+template<ranges::input_range R, class Allocator>
+  unordered_multiset(from_range_t, R&&, typename see below::size_type, Allocator)
+    -> unordered_multiset<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+                      equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Allocator>
+  unordered_multiset(from_range_t, R&&, Allocator)
+    -> unordered_multiset<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+                      equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
+template<ranges::input_range R, class Hash, class Allocator>
+  unordered_multiset(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+    -> unordered_multiset<ranges::range_value_t<R>, Hash,
+                      equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+
 template<class T, class Allocator>
 unordered_multiset(initializer_list<T>, typename see below::size_type, Allocator)
   -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>; // C++17
@@ -469,10 +537,14 @@ template <class Value, class Hash, class Pred, class Alloc>
 #include <__iterator/distance.h>
 #include <__iterator/erase_if_container.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/ranges_iterator_traits.h>
 #include <__memory/addressof.h>
 #include <__memory/allocator.h>
 #include <__memory_resource/polymorphic_allocator.h>
 #include <__node_handle>
+#include <__ranges/concepts.h>
+#include <__ranges/container_compatible_range.h>
+#include <__ranges/from_range.h>
 #include <__type_traits/is_allocator.h>
 #include <__utility/forward.h>
 #include <version>
@@ -572,6 +644,21 @@ public:
     _LIBCPP_HIDE_FROM_ABI unordered_set(_InputIterator __first, _InputIterator __last,
                       size_type __n, const hasher& __hf, const key_equal& __eql,
                       const allocator_type& __a);
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_set(from_range_t, _Range&& __range, size_type __n = /*implementation-defined*/0,
+                  const hasher& __hf = hasher(), const key_equal& __eql = key_equal(),
+                  const allocator_type& __a = allocator_type())
+    : __table_(__hf, __eql, __a) {
+      if (__n > 0) {
+        __table_.__rehash_unique(__n);
+      }
+      insert_range(std::forward<_Range>(__range));
+    }
+#endif
+
 #if _LIBCPP_STD_VER >= 14
     template <class _InputIterator>
     inline _LIBCPP_INLINE_VISIBILITY
@@ -583,6 +670,19 @@ public:
                       size_type __n, const hasher& __hf, const allocator_type& __a)
             : unordered_set(__first, __last, __n, __hf, key_equal(), __a) {}
 #endif
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_set(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
+        : unordered_set(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
+
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_set(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
+        : unordered_set(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     explicit unordered_set(const allocator_type& __a);
     _LIBCPP_HIDE_FROM_ABI unordered_set(const unordered_set& __u);
@@ -688,6 +788,16 @@ public:
         _LIBCPP_INLINE_VISIBILITY
         void insert(_InputIterator __first, _InputIterator __last);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void insert_range(_Range&& __range) {
+      for (auto&& __element : __range) {
+        __table_.__insert_unique(std::forward<decltype(__element)>(__element));
+      }
+    }
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     iterator erase(const_iterator __p) {return __table_.erase(__p);}
     _LIBCPP_INLINE_VISIBILITY
@@ -866,6 +976,20 @@ unordered_set(_InputIterator, _InputIterator, typename allocator_traits<_Allocat
               _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
   -> unordered_set<__iter_value_type<_InputIterator>, _Hash, _Pred, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+          class _Hash = hash<ranges::range_value_t<_Range>>,
+          class _Pred = equal_to<ranges::range_value_t<_Range>>,
+          class _Allocator = allocator<ranges::range_value_t<_Range>>,
+          class = enable_if_t<!__is_allocator<_Hash>::value>,
+          class = enable_if_t<!is_integral<_Hash>::value>,
+          class = enable_if_t<!__is_allocator<_Pred>::value>,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_set(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type = 0,
+              _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
+  -> unordered_set<ranges::range_value_t<_Range>, _Hash, _Pred, _Allocator>; // C++23
+#endif
+
 template<class _Tp, class _Hash = hash<_Tp>,
          class _Pred = equal_to<_Tp>,
          class _Allocator = allocator<_Tp>,
@@ -898,6 +1022,29 @@ unordered_set(_InputIterator, _InputIterator,
                    equal_to<__iter_value_type<_InputIterator>>,
                    _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_set(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Allocator)
+  -> unordered_set<ranges::range_value_t<_Range>, hash<ranges::range_value_t<_Range>>,
+                   equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_set(from_range_t, _Range&&, _Allocator)
+  -> unordered_set<ranges::range_value_t<_Range>, hash<ranges::range_value_t<_Range>>,
+                   equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Hash, class _Allocator,
+          class = enable_if_t<!__is_allocator<_Hash>::value>,
+          class = enable_if_t<!is_integral<_Hash>::value>,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_set(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Hash, _Allocator)
+  -> unordered_set<ranges::range_value_t<_Range>, _Hash, equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+#endif
+
 template<class _Tp, class _Allocator,
          class = enable_if_t<__is_allocator<_Allocator>::value>>
 unordered_set(initializer_list<_Tp>, typename allocator_traits<_Allocator>::size_type, _Allocator)
@@ -1188,6 +1335,21 @@ public:
     _LIBCPP_HIDE_FROM_ABI unordered_multiset(_InputIterator __first, _InputIterator __last,
                       size_type __n , const hasher& __hf,
                       const key_equal& __eql, const allocator_type& __a);
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_multiset(from_range_t, _Range&& __range, size_type __n = /*implementation-defined*/0,
+                  const hasher& __hf = hasher(), const key_equal& __eql = key_equal(),
+                  const allocator_type& __a = allocator_type())
+    : __table_(__hf, __eql, __a) {
+      if (__n > 0) {
+        __table_.__rehash_multi(__n);
+      }
+      insert_range(std::forward<_Range>(__range));
+    }
+#endif
+
 #if _LIBCPP_STD_VER >= 14
     template <class _InputIterator>
     inline _LIBCPP_INLINE_VISIBILITY
@@ -1200,6 +1362,19 @@ public:
                        size_type __n, const hasher& __hf, const allocator_type& __a)
         : unordered_multiset(__first, __last, __n, __hf, key_equal(), __a) {}
 #endif
+
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_multiset(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
+        : unordered_multiset(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
+
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    unordered_multiset(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
+        : unordered_multiset(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
+#endif
+
     _LIBCPP_INLINE_VISIBILITY
     explicit unordered_multiset(const allocator_type& __a);
     _LIBCPP_HIDE_FROM_ABI unordered_multiset(const unordered_multiset& __u);
@@ -1298,6 +1473,16 @@ public:
         _LIBCPP_INLINE_VISIBILITY
         void insert(_InputIterator __first, _InputIterator __last);
 
+#if _LIBCPP_STD_VER >= 23
+    template <_ContainerCompatibleRange<value_type> _Range>
+    _LIBCPP_HIDE_FROM_ABI
+    void insert_range(_Range&& __range) {
+      for (auto&& __element : __range) {
+        __table_.__insert_multi(std::forward<decltype(__element)>(__element));
+      }
+    }
+#endif
+
 #if _LIBCPP_STD_VER >= 17
     _LIBCPP_INLINE_VISIBILITY
     iterator insert(node_type&& __nh)
@@ -1477,6 +1662,20 @@ unordered_multiset(_InputIterator, _InputIterator, typename allocator_traits<_Al
               _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
   -> unordered_multiset<__iter_value_type<_InputIterator>, _Hash, _Pred, _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+template <ranges::input_range _Range,
+          class _Hash = hash<ranges::range_value_t<_Range>>,
+          class _Pred = equal_to<ranges::range_value_t<_Range>>,
+          class _Allocator = allocator<ranges::range_value_t<_Range>>,
+          class = enable_if_t<!__is_allocator<_Hash>::value>,
+          class = enable_if_t<!is_integral<_Hash>::value>,
+          class = enable_if_t<!__is_allocator<_Pred>::value>,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multiset(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type = 0,
+              _Hash = _Hash(), _Pred = _Pred(), _Allocator = _Allocator())
+  -> unordered_multiset<ranges::range_value_t<_Range>, _Hash, _Pred, _Allocator>; // C++23
+#endif
+
 template<class _Tp, class _Hash = hash<_Tp>,
          class _Pred = equal_to<_Tp>, class _Allocator = allocator<_Tp>,
          class = enable_if_t<!__is_allocator<_Hash>::value>,
@@ -1507,6 +1706,29 @@ unordered_multiset(_InputIterator, _InputIterator, typename allocator_traits<_Al
                    equal_to<__iter_value_type<_InputIterator>>,
                    _Allocator>;
 
+#if _LIBCPP_STD_VER >= 23
+
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multiset(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Allocator)
+  -> unordered_multiset<ranges::range_value_t<_Range>, hash<ranges::range_value_t<_Range>>,
+                   equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Allocator,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multiset(from_range_t, _Range&&, _Allocator)
+  -> unordered_multiset<ranges::range_value_t<_Range>, hash<ranges::range_value_t<_Range>>,
+                   equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+template <ranges::input_range _Range, class _Hash, class _Allocator,
+          class = enable_if_t<!__is_allocator<_Hash>::value>,
+          class = enable_if_t<!is_integral<_Hash>::value>,
+          class = enable_if_t<__is_allocator<_Allocator>::value>>
+unordered_multiset(from_range_t, _Range&&, typename allocator_traits<_Allocator>::size_type, _Hash, _Allocator)
+  -> unordered_multiset<ranges::range_value_t<_Range>, _Hash, equal_to<ranges::range_value_t<_Range>>, _Allocator>;
+
+#endif
+
 template<class _Tp, class _Allocator,
          class = enable_if_t<__is_allocator<_Allocator>::value>>
 unordered_multiset(initializer_list<_Tp>, typename allocator_traits<_Allocator>::size_type, _Allocator)

diff  --git a/libcxx/test/std/containers/associative/from_range_associative_containers.h b/libcxx/test/std/containers/associative/from_range_associative_containers.h
new file mode 100644
index 00000000000000..6d2d6e6874aaa2
--- /dev/null
+++ b/libcxx/test/std/containers/associative/from_range_associative_containers.h
@@ -0,0 +1,304 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_ASSOCIATIVE_CONTAINERS_H
+#define SUPPORT_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <ranges>
+#include <vector>
+#include <utility>
+
+#include "../exception_safety_helpers.h"
+#include "../from_range_helpers.h"
+#include "../test_compare.h"
+#include "MoveOnly.h"
+#include "almost_satisfies_types.h"
+#include "count_new.h"
+#include "test_macros.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::less<typename Container::key_type>());
+  Container(std::from_range, std::forward<Range>(range), std::less<typename Container::key_type>(),
+      std::allocator<typename Container::value_type>());
+  Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());
+};
+
+template <template <class...> class Container, class K, class V, class K2, class V2>
+constexpr bool test_map_constraints() {
+  using ValueType = std::pair<const K, V>;
+
+  // Input range with the same value type.
+  static_assert(HasFromRangeCtr<Container<K, V>, InputRange<ValueType>>);
+  // Input range with a convertible value type.
+  static_assert(HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const K2, V2>>>);
+  // Input range with a non-convertible value type.
+  static_assert(!HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const Empty, V>>>);
+  static_assert(!HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const K, Empty>>>);
+  // Not an input range.
+  static_assert(!HasFromRangeCtr<Container<K, V>, InputRangeNotDerivedFromGeneric<ValueType>>);
+
+  return true;
+}
+
+template <template <class ...> class Container,
+          class K,
+          class V,
+          class Iter,
+          class Sent,
+          class Comp,
+          class Alloc,
+          class ValueType = std::pair<const K, V>>
+void test_associative_map_with_input(std::vector<ValueType>&& input) {
+  auto in = wrap_input<Iter, Sent>(input);
+
+  { // (range)
+    Container<K, V> c(std::from_range, in);
+
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+  }
+
+  { // (range, comp)
+    Comp comp;
+    Container<K, V, Comp> c(std::from_range, in, comp);
+
+    assert(c.key_comp() == comp);
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+  }
+
+  { // (range, allocator)
+    Alloc alloc;
+    Container<K, V, std::less<K>, Alloc> c(std::from_range, in, alloc);
+
+    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));
+  }
+
+  { // (range, comp, allocator)
+    Comp comp;
+    Alloc alloc;
+    Container<K, V, Comp, Alloc> c(std::from_range, in, comp, alloc);
+
+    assert(c.key_comp() == comp);
+    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));
+  }
+}
+
+template <template <class ...> class Container,
+          class K,
+          class V,
+          class Iter,
+          class Sent,
+          class Comp,
+          class Alloc>
+void test_associative_map() {
+  auto test_with_input = &test_associative_map_with_input<Container, K, V, Iter, Sent, Comp, Alloc>;
+
+  // Normal input.
+  test_with_input({{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}});
+  // Empty input.
+  test_with_input({});
+  // Single-element input.
+  test_with_input({{1, 2}});
+}
+
+template <template <class ...> class Container>
+void test_associative_map_move_only() {
+  std::pair<const int, MoveOnly> input[5];
+  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+  [[maybe_unused]] Container<int, MoveOnly> c(std::from_range, in);
+}
+
+template <template <class ...> class Container>
+void test_map_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using K = int;
+  using V = ThrowingCopy<3>;
+
+  V::throwing_enabled = false;
+  std::pair<const K, V> in[5] = {
+    {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
+  };
+  V::throwing_enabled = true;
+  V::reset();
+
+  try {
+    Container<K, V> c(std::from_range, in);
+    assert(false); // The constructor call above should throw.
+
+  } catch (int) {
+    assert(V::created_by_copying == 3);
+    assert(V::destroyed == 2); // No destructor call for the partially-constructed element.
+  }
+#endif
+}
+
+template <template <class ...> class Container, class K, class V>
+void test_map_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using ValueType = std::pair<const K, V>;
+  ValueType in[] = {
+    ValueType{K{1}, V{1}}
+  };
+
+  try {
+    ThrowingAllocator<ValueType> alloc;
+
+    globalMemCounter.reset();
+    Container<K, V, test_less<K>, ThrowingAllocator<ValueType>> c(std::from_range, in, alloc);
+    assert(false); // The constructor call above should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+template <class Container, class Range>
+concept SetHasFromRangeCtr = requires (Range&& range) {
+  Container(std::from_range, std::forward<Range>(range));
+  Container(std::from_range, std::forward<Range>(range), std::less<typename Container::value_type>());
+  Container(std::from_range, std::forward<Range>(range), std::less<typename Container::value_type>(),
+      std::allocator<typename Container::value_type>());
+  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_set_constraints() {
+  // Input range with the same value type.
+  static_assert(SetHasFromRangeCtr<Container<T>, InputRange<T>>);
+  // Input range with a convertible value type.
+  static_assert(SetHasFromRangeCtr<Container<T>, InputRange<U>>);
+  // Input range with a non-convertible value type.
+  static_assert(!SetHasFromRangeCtr<Container<T>, InputRange<Empty>>);
+  // Not an input range.
+  static_assert(!SetHasFromRangeCtr<Container<T>, InputRangeNotDerivedFromGeneric<T>>);
+
+  return true;
+}
+
+template <template <class ...> class Container,
+          class T,
+          class Iter,
+          class Sent,
+          class Comp,
+          class Alloc>
+void test_associative_set_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)
+    Container<T> c(std::from_range, in);
+
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+  }
+
+  { // (range, comp)
+    Comp comp;
+    Container<T, Comp> c(std::from_range, in, comp);
+
+    assert(c.key_comp() == comp);
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+  }
+
+  { // (range, allocator)
+    Alloc alloc;
+    Container<T, std::less<T>, Alloc> c(std::from_range, in, alloc);
+
+    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));
+  }
+
+  { // (range, comp, allocator)
+    Comp comp;
+    Alloc alloc;
+    Container<T, Comp, Alloc> c(std::from_range, in, comp, alloc);
+
+    assert(c.key_comp() == comp);
+    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));
+  }
+}
+
+template <template <class ...> class Container,
+          class T,
+          class Iter,
+          class Sent,
+          class Comp,
+          class Alloc>
+void test_associative_set() {
+  auto test_with_input = &test_associative_set_with_input<Container, 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>
+void test_associative_set_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 Container>
+void test_set_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using T = ThrowingCopy<3>;
+  T::reset();
+  T in[5] = {{1}, {2}, {3}, {4}, {5}};
+
+  try {
+    Container<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 Container, class T>
+void test_set_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  T in[] = {1, 2};
+
+  try {
+    ThrowingAllocator<T> alloc;
+
+    globalMemCounter.reset();
+    Container<T, test_less<T>, ThrowingAllocator<T>> c(std::from_range, in, alloc);
+    assert(false); // The constructor call above should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+#endif // SUPPORT_FROM_RANGE_ASSOCIATIVE_CONTAINERS_H

diff  --git a/libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp
index f104397955c09b..d68e6af2681a54 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/deduct.pass.cpp
@@ -24,8 +24,18 @@
 // template<class Key, class Allocator>
 // map(initializer_list<Key>, Allocator)
 //   -> map<Key, less<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Compare = less<range-key-type<R>,
+//          class Allocator = allocator<range-to-alloc-type<R>>>
+//   map(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+//     -> map<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   map(from_range_t, R&&, Allocator)
+//     -> map<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; // C++23
 
 #include <algorithm> // std::equal
+#include <array>
 #include <cassert>
 #include <climits> // INT_MAX
 #include <functional>
@@ -152,6 +162,35 @@ int main(int, char**)
     ASSERT_SAME_TYPE(decltype(m2), std::map<int, int>);
     }
 
+#if TEST_STD_VER >= 23
+    {
+      using Range = std::array<P, 0>;
+      using Comp = std::greater<int>;
+      using DefaultComp = std::less<int>;
+      using Alloc = test_allocator<PC>;
+
+      { // (from_range, range)
+        std::map c(std::from_range, Range());
+        static_assert(std::is_same_v<decltype(c), std::map<int, long>>);
+      }
+
+      { // (from_range, range, comp)
+        std::map c(std::from_range, Range(), Comp());
+        static_assert(std::is_same_v<decltype(c), std::map<int, long, Comp>>);
+      }
+
+      { // (from_range, range, comp, alloc)
+        std::map c(std::from_range, Range(), Comp(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::map<int, long, Comp, Alloc>>);
+      }
+
+      { // (from_range, range, alloc)
+        std::map c(std::from_range, Range(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::map<int, long, DefaultComp, Alloc>>);
+      }
+    }
+#endif
+
     AssociativeContainerDeductionGuidesSfinaeAway<std::map, std::map<int, long>>();
 
     return 0;

diff  --git a/libcxx/test/std/containers/associative/map/map.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/from_range.pass.cpp
new file mode 100644
index 00000000000000..f375dbfcf47b2d
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.cons/from_range.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<value_type> R>
+//   map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   map(from_range_t, R&& rg, const Allocator& a))
+//     : map(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+
+#include <array>
+#include <map>
+
+#include "../../from_range_associative_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+  using T = std::pair<const int, char>;
+
+  std::array input = {
+    T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+  };
+  std::array expected = {
+    T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{4, 'a'}
+  };
+  auto c = std::map<int, char>(std::from_range, input);
+  assert(std::ranges::is_permutation(expected, c));
+}
+
+int main(int, char**) {
+  using T = std::pair<const int, int>;
+  for_all_iterators_and_allocators<T>([]<class Iter, class Sent, class Alloc>() {
+    test_associative_map<std::map, int, int, Iter, Sent, test_less<int>, Alloc>();
+  });
+  test_associative_map_move_only<std::map>();
+  test_duplicates();
+
+  static_assert(test_map_constraints<std::map, int, int, double, double>());
+
+  test_map_exception_safety_throwing_copy<std::map>();
+  test_map_exception_safety_throwing_allocator<std::map, int, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/associative/map/map.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/insert_range.pass.cpp
new file mode 100644
index 00000000000000..1d7fe7193facf4
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/insert_range.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
+
+// <map>
+
+// template<container-compatible-range<value_type> R>
+//   void insert_range(R&& rg); // C++23
+
+#include <map>
+
+#include "../../../insert_range_maps_sets.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+  // Note: we want to use a pair with non-const elements for input (an assignable type is a lot more convenient) but
+  // have to use the exact `value_type` of the map (that is, `pair<const K, V>`) for the allocator.
+  using Pair = std::pair<int, char>;
+  using ConstPair = std::pair<const int, char>;
+  for_all_iterators_and_allocators<ConstPair, const Pair*>([]<class Iter, class Sent, class Alloc>() {
+    test_map_set_insert_range<std::map<int, char, test_less<int>, Alloc>, Pair, Iter, Sent>();
+  });
+
+  static_assert(test_map_constraints_insert_range<std::map, int, int, char, double>());
+
+  test_map_insert_range_move_only<std::map>();
+
+  test_map_insert_range_exception_safety_throwing_copy<std::map>();
+  test_assoc_map_insert_range_exception_safety_throwing_allocator<std::map, int, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp
index c71a739036fab3..56bc5400b65ab3 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp
@@ -24,8 +24,18 @@
 // template<class Key, class Allocator>
 // multimap(initializer_list<Key>, Allocator)
 //   -> multimap<Key, less<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Compare = less<range-key-type<R>>,
+//           class Allocator = allocator<range-to-alloc-type<R>>>
+//   multimap(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+//     -> multimap<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   multimap(from_range_t, R&&, Allocator)
+//     -> multimap<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; // C++23
 
 #include <algorithm> // std::equal
+#include <array>
 #include <cassert>
 #include <climits> // INT_MAX
 #include <functional>
@@ -152,6 +162,35 @@ int main(int, char**)
     ASSERT_SAME_TYPE(decltype(m2), std::multimap<int, int>);
     }
 
+#if TEST_STD_VER >= 23
+    {
+      using Range = std::array<P, 0>;
+      using Comp = std::greater<int>;
+      using DefaultComp = std::less<int>;
+      using Alloc = test_allocator<PC>;
+
+      { // (from_range, range)
+        std::multimap c(std::from_range, Range());
+        static_assert(std::is_same_v<decltype(c), std::multimap<int, long>>);
+      }
+
+      { // (from_range, range, comp)
+        std::multimap c(std::from_range, Range(), Comp());
+        static_assert(std::is_same_v<decltype(c), std::multimap<int, long, Comp>>);
+      }
+
+      { // (from_range, range, comp, alloc)
+        std::multimap c(std::from_range, Range(), Comp(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::multimap<int, long, Comp, Alloc>>);
+      }
+
+      { // (from_range, range, alloc)
+        std::multimap c(std::from_range, Range(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::multimap<int, long, DefaultComp, Alloc>>);
+      }
+    }
+#endif
+
     AssociativeContainerDeductionGuidesSfinaeAway<std::multimap, std::multimap<int, long>>();
 
     return 0;

diff  --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/from_range.pass.cpp
new file mode 100644
index 00000000000000..b38078c7e82f29
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/from_range.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<value_type> R>
+//   multimap(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   multimap(from_range_t, R&& rg, const Allocator& a))
+//     : multimap(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+
+#include <array>
+#include <map>
+
+#include "../../from_range_associative_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+  using T = std::pair<const int, char>;
+  std::array input = {
+    T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+  };
+  auto c = std::multimap<int, char>(std::from_range, input);
+  assert(std::ranges::is_permutation(input, c));
+}
+
+int main(int, char**) {
+  using T = std::pair<const int, int>;
+  for_all_iterators_and_allocators<T>([]<class Iter, class Sent, class Alloc>() {
+    test_associative_map<std::multimap, int, int, Iter, Sent, test_less<int>, Alloc>();
+  });
+  test_associative_map_move_only<std::multimap>();
+  test_duplicates();
+
+  static_assert(test_map_constraints<std::multimap, int, int, double, double>());
+
+  test_map_exception_safety_throwing_copy<std::multimap>();
+  test_map_exception_safety_throwing_allocator<std::multimap, int, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/insert_range.pass.cpp
new file mode 100644
index 00000000000000..c7c05a896bc9f9
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/insert_range.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
+
+// <map>
+
+// template<container-compatible-range<value_type> R>
+//   void insert_range(R&& rg); // C++23
+
+#include <map>
+
+#include "../../../insert_range_maps_sets.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+  // Note: we want to use a pair with non-const elements for input (an assignable type is a lot more convenient) but
+  // have to use the exact `value_type` of the map (that is, `pair<const K, V>`) for the allocator.
+  using Pair = std::pair<int, char>;
+  using ConstPair = std::pair<const int, char>;
+  for_all_iterators_and_allocators<ConstPair, const Pair*>([]<class Iter, class Sent, class Alloc>() {
+    test_map_set_insert_range<std::multimap<int, char, test_less<int>, Alloc>, Pair, Iter, Sent>(/*allow_duplicates=*/true);
+  });
+
+  static_assert(test_map_constraints_insert_range<std::multimap, int, int, char, double>());
+
+  test_map_insert_range_move_only<std::multimap>();
+
+  test_map_insert_range_exception_safety_throwing_copy<std::multimap>();
+  test_assoc_map_insert_range_exception_safety_throwing_allocator<std::multimap, int, int>();
+
+  return 0;
+}
+

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
new file mode 100644
index 00000000000000..9dd85eea47c290
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
+
+// <set>
+
+// template<container-compatible-range<value_type> R>
+//   void insert_range(R&& rg); // C++23
+
+#include <set>
+
+#include "../../insert_range_maps_sets.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_map_set_insert_range<std::multiset<int, test_less<int>, Alloc>, int, Iter, Sent>(/*allow_duplicates=*/true);
+  });
+
+  static_assert(test_set_constraints_insert_range<std::multiset, int, double>());
+
+  test_set_insert_range_move_only<std::multiset>();
+
+  test_set_insert_range_exception_safety_throwing_copy<std::multiset>();
+  test_assoc_set_insert_range_exception_safety_throwing_allocator<std::multiset, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index 233ddd2397921f..93bff1a41c6674 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -27,8 +27,18 @@
 // template<class Key, class Allocator>
 // multiset(initializer_list<Key>, Allocator)
 //   -> multiset<Key, less<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>,
+//          class Allocator = allocator<ranges::range_value_t<R>>>
+//   multiset(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+//     -> multiset<ranges::range_value_t<R>, Compare, Allocator>;
+//
+// template<ranges::input_range R, class Allocator>
+//   multiset(from_range_t, R&&, Allocator)
+//     -> multiset<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>;
 
 #include <algorithm> // std::equal
+#include <array>
 #include <cassert>
 #include <climits> // INT_MAX
 #include <functional>
@@ -187,6 +197,35 @@ int main(int, char **) {
     assert(s.size() == 2);
   }
 
+#if TEST_STD_VER >= 23
+    {
+      using Range = std::array<int, 0>;
+      using Comp = std::greater<int>;
+      using DefaultComp = std::less<int>;
+      using Alloc = test_allocator<int>;
+
+      { // (from_range, range)
+        std::multiset c(std::from_range, Range());
+        static_assert(std::is_same_v<decltype(c), std::multiset<int>>);
+      }
+
+      { // (from_range, range, comp)
+        std::multiset c(std::from_range, Range(), Comp());
+        static_assert(std::is_same_v<decltype(c), std::multiset<int, Comp>>);
+      }
+
+      { // (from_range, range, comp, alloc)
+        std::multiset c(std::from_range, Range(), Comp(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::multiset<int, Comp, Alloc>>);
+      }
+
+      { // (from_range, range, alloc)
+        std::multiset c(std::from_range, Range(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::multiset<int, DefaultComp, Alloc>>);
+      }
+    }
+#endif
+
   AssociativeContainerDeductionGuidesSfinaeAway<std::multiset, std::multiset<int>>();
 
   return 0;

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
new file mode 100644
index 00000000000000..74533d7e41abff
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<value_type> R>
+//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   multiset(from_range_t, R&& rg, const Allocator& a))
+//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+
+#include <algorithm>
+#include <array>
+#include <set>
+
+#include "../../from_range_associative_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+  std::array input = {1, 2, 3, 3, 3, 4, 2, 1, 2};
+  auto c = std::multiset<int>(std::from_range, input);
+  assert(std::ranges::is_permutation(input, c));
+}
+
+int main(int, char**) {
+  for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+    test_associative_set<std::multiset, int, Iter, Sent, test_less<int>, Alloc>();
+  });
+  test_associative_set_move_only<std::multiset>();
+  test_duplicates();
+
+  static_assert(test_set_constraints<std::multiset, int, double>());
+
+  test_set_exception_safety_throwing_copy<std::multiset>();
+  test_set_exception_safety_throwing_allocator<std::multiset, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
new file mode 100644
index 00000000000000..1956fc6bd7f3ea
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
+
+// <set>
+
+// template<container-compatible-range<value_type> R>
+//   void insert_range(R&& rg); // C++23
+
+#include <set>
+
+#include "../../insert_range_maps_sets.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_map_set_insert_range<std::set<int, test_less<int>, Alloc>, int, Iter, Sent>();
+  });
+
+  static_assert(test_set_constraints_insert_range<std::set, int, double>());
+
+  test_set_insert_range_move_only<std::set>();
+
+  test_set_insert_range_exception_safety_throwing_copy<std::set>();
+  test_assoc_set_insert_range_exception_safety_throwing_allocator<std::set, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
index fdc725504b065c..74e72b7b71de50 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
@@ -27,8 +27,18 @@
 // template<class Key, class Allocator>
 // set(initializer_list<Key>, Allocator)
 //   -> set<Key, less<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>,
+//          class Allocator = allocator<ranges::range_value_t<R>>>
+//   set(from_range_t, R&&, Compare = Compare(), Allocator = Allocator())
+//     -> set<ranges::range_value_t<R>, Compare, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   set(from_range_t, R&&, Allocator)
+//     -> set<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; // C++23
 
 #include <algorithm> // std::equal
+#include <array>
 #include <cassert>
 #include <climits> // INT_MAX
 #include <functional>
@@ -185,6 +195,35 @@ int main(int, char **) {
     assert(s.size() == 2);
   }
 
+#if TEST_STD_VER >= 23
+    {
+      using Range = std::array<int, 0>;
+      using Comp = std::greater<int>;
+      using DefaultComp = std::less<int>;
+      using Alloc = test_allocator<int>;
+
+      { // (from_range, range)
+        std::set c(std::from_range, Range());
+        static_assert(std::is_same_v<decltype(c), std::set<int>>);
+      }
+
+      { // (from_range, range, comp)
+        std::set c(std::from_range, Range(), Comp());
+        static_assert(std::is_same_v<decltype(c), std::set<int, Comp>>);
+      }
+
+      { // (from_range, range, comp, alloc)
+        std::set c(std::from_range, Range(), Comp(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::set<int, Comp, Alloc>>);
+      }
+
+      { // (from_range, range, alloc)
+        std::set c(std::from_range, Range(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::set<int, DefaultComp, Alloc>>);
+      }
+    }
+#endif
+
   AssociativeContainerDeductionGuidesSfinaeAway<std::set, std::set<int>>();
 
   return 0;

diff  --git a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
new file mode 100644
index 00000000000000..4c9b4aaf262912
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<value_type> R>
+//   set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   set(from_range_t, R&& rg, const Allocator& a))
+//     : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+
+#include <array>
+#include <set>
+
+#include "../../from_range_associative_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+  using T = KeyValue;
+
+  std::array input = {
+    T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+  };
+  std::array expected = {
+    T{1, 'a'}, T{2, 'b'}, T{3, 'c'}, T{4, 'a'}
+  };
+  auto c = std::set<T>(std::from_range, input);
+  assert(std::ranges::is_permutation(expected, c));
+}
+
+int main(int, char**) {
+  for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+    test_associative_set<std::set, int, Iter, Sent, test_less<int>, Alloc>();
+  });
+  test_associative_set_move_only<std::set>();
+  test_duplicates();
+
+  static_assert(test_set_constraints<std::set, int, double>());
+
+  test_set_exception_safety_throwing_copy<std::set>();
+  test_set_exception_safety_throwing_allocator<std::set, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/insert_range_helpers.h b/libcxx/test/std/containers/insert_range_helpers.h
index 2c4e14ce8b737c..531a7df10c7f9f 100644
--- a/libcxx/test/std/containers/insert_range_helpers.h
+++ b/libcxx/test/std/containers/insert_range_helpers.h
@@ -21,6 +21,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include "exception_safety_helpers.h"
 #include "from_range_helpers.h"
 #include "min_allocator.h"
 #include "test_allocator.h"

diff  --git a/libcxx/test/std/containers/insert_range_maps_sets.h b/libcxx/test/std/containers/insert_range_maps_sets.h
new file mode 100644
index 00000000000000..82fea93b68fe37
--- /dev/null
+++ b/libcxx/test/std/containers/insert_range_maps_sets.h
@@ -0,0 +1,415 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_INSERT_RANGE_MAPS_SETS_H
+#define SUPPORT_INSERT_RANGE_MAPS_SETS_H
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <type_traits>
+#include <vector>
+
+#include "MoveOnly.h"
+#include "almost_satisfies_types.h"
+#include "count_new.h"
+#include "exception_safety_helpers.h"
+#include "insert_range_helpers.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_compare.h"
+#include "test_hash.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class Container, class Range>
+concept HasInsertRange = requires (Container& c, Range&& range) {
+  c.insert_range(range);
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_set_constraints_insert_range() {
+  // Input range with the same value type.
+  static_assert(HasInsertRange<Container<T>, InputRange<T>>);
+  // Input range with a convertible value type.
+  static_assert(HasInsertRange<Container<T>, InputRange<U>>);
+  // Input range with a non-convertible value type.
+  static_assert(!HasInsertRange<Container<T>, InputRange<Empty>>);
+  // Not an input range.
+  static_assert(!HasInsertRange<Container<T>, InputRangeNotDerivedFrom>);
+  static_assert(!HasInsertRange<Container<T>, InputRangeNotIndirectlyReadable>);
+  static_assert(!HasInsertRange<Container<T>, InputRangeNotInputOrOutputIterator>);
+
+  return true;
+}
+
+template <template <class...> class Container, class K, class V, class K2, class V2>
+constexpr bool test_map_constraints_insert_range() {
+  using ValueType = std::pair<const K, V>;
+
+  // Input range with the same value type.
+  static_assert(HasInsertRange<Container<K, V>, InputRange<ValueType>>);
+  // Input range with a convertible value type.
+  static_assert(HasInsertRange<Container<K, V>, InputRange<std::pair<const K2, V2>>>);
+  // Input range with a non-convertible value type.
+  static_assert(!HasInsertRange<Container<K, V>, InputRange<std::pair<const K, Empty>>>);
+  static_assert(!HasInsertRange<Container<K, V>, InputRange<std::pair<const Empty, V>>>);
+  // Not an input range.
+  static_assert(!HasInsertRange<Container<K, V>, InputRangeNotDerivedFromGeneric<ValueType>>);
+
+  return true;
+}
+
+template <class T>
+struct TestCaseMapSet {
+  Buffer<T> initial;
+  Buffer<T> input;
+  Buffer<T> expected;
+  Buffer<T> expected_multi;
+};
+
+// Empty container.
+
+template <class T>
+TestCaseMapSet<T> constexpr EmptyContainer_EmptyRange {
+  .initial = {}, .input = {}, .expected = {}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr EmptyContainer_OneElementRange {
+  .initial = {}, .input = {1}, .expected = {1}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr EmptyContainer_OneElementRange<std::pair<K, V>> {
+  .initial = {}, .input = {{1, 'a'}}, .expected = {{1, 'a'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr EmptyContainer_RangeNoDuplicates {
+  .initial = {}, .input = {5, 1, 3, 8, 6}, .expected = {5, 1, 3, 8, 6}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr EmptyContainer_RangeNoDuplicates<std::pair<K, V>> {
+  .initial = {}, .input = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
+  .expected = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr EmptyContainer_RangeWithDuplicates {
+  .initial = {},
+  .input = {5, 1, 1, 3, 5, 8, 5, 6, 10},
+  .expected = {5, 1, 3, 8, 6, 10},
+  .expected_multi = {5, 1, 1, 3, 5, 8, 5, 6, 10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr EmptyContainer_RangeWithDuplicates<std::pair<K, V>> {
+  .initial = {},
+  .input = {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
+  .expected = {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'b'}},
+  .expected_multi = {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}}
+};
+
+// One-element container.
+
+template <class T>
+TestCaseMapSet<T> constexpr OneElementContainer_EmptyRange {
+  .initial = {10}, .input = {}, .expected = {10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr OneElementContainer_EmptyRange<std::pair<K, V>> {
+  .initial = {{10, 'A'}}, .input = {}, .expected = {{10, 'A'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr OneElementContainer_OneElementRange {
+  .initial = {10}, .input = {1}, .expected = {1, 10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr OneElementContainer_OneElementRange<std::pair<K, V>> {
+  .initial = {{10, 'A'}}, .input = {{1, 'a'}}, .expected = {{1, 'a'}, {10, 'A'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr OneElementContainer_RangeNoDuplicates {
+  .initial = {10}, .input = {5, 1, 3, 8, 6}, .expected = {5, 1, 3, 8, 6, 10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr OneElementContainer_RangeNoDuplicates<std::pair<K, V>> {
+  .initial = {{10, 'A'}}, .input = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
+  .expected = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}, {10, 'A'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr OneElementContainer_RangeWithDuplicates {
+  .initial = {10},
+  .input = {5, 1, 1, 3, 5, 8, 5, 6, 10},
+  .expected = {5, 1, 3, 8, 6, 10},
+  .expected_multi = {5, 1, 1, 3, 5, 8, 5, 6, 10, 10}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr OneElementContainer_RangeWithDuplicates<std::pair<K, V>> {
+  .initial = {{10, 'A'}},
+  .input = {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
+  .expected = {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'A'}},
+  .expected_multi = {
+    {5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'A'}, {10, 'b'}
+  }
+};
+
+// N-elements container.
+
+template <class T>
+TestCaseMapSet<T> constexpr NElementsContainer_EmptyRange {
+  .initial = {10, 15, 19, 16}, .input = {}, .expected = {10, 15, 19, 16}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr NElementsContainer_EmptyRange<std::pair<K, V>> {
+  .initial = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}, .input = {},
+  .expected = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr NElementsContainer_OneElementRange {
+  .initial = {10, 15, 19, 16}, .input = {1}, .expected = {1, 10, 15, 19, 16}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr NElementsContainer_OneElementRange<std::pair<K, V>> {
+  .initial = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}, .input = {{1, 'a'}},
+  .expected = {{1, 'a'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr NElementsContainer_RangeNoDuplicates {
+  .initial = {10, 15, 19, 16}, .input = {5, 1, 3, 8, 6}, .expected = {5, 1, 3, 8, 6, 10, 15, 19, 16}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr NElementsContainer_RangeNoDuplicates<std::pair<K, V>> {
+  .initial = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
+  .input = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}},
+  .expected = {{5, 'a'}, {1, 'e'}, {3, 'i'}, {8, 'o'}, {6, 'u'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}}
+};
+
+template <class T>
+TestCaseMapSet<T> constexpr NElementsContainer_RangeWithDuplicates {
+  .initial = {10, 15, 19, 16},
+  .input = {5, 1, 1, 3, 5, 8, 5, 6, 10},
+  .expected = {5, 1, 3, 8, 6, 10, 15, 19, 16},
+  .expected_multi = {5, 1, 1, 3, 5, 8, 5, 6, 10, 10, 15, 19, 16}
+};
+template <class K, class V> TestCaseMapSet<std::pair<K, V>>
+constexpr NElementsContainer_RangeWithDuplicates<std::pair<K, V>> {
+  .initial = {{10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
+  .input = {{5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'}},
+  .expected = {{5, 'a'}, {1, 'a'}, {3, 'a'}, {8, 'a'}, {6, 'a'}, {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}},
+  .expected_multi = {
+    {5, 'a'}, {1, 'a'}, {1, 'b'}, {3, 'a'}, {5, 'b'}, {8, 'a'}, {5, 'c'}, {6, 'a'}, {10, 'b'},
+    {10, 'A'}, {15, 'B'}, {19, 'C'}, {16, 'D'}
+  }
+};
+
+template <class Container, class T, class Iter, class Sent>
+void test_map_set_insert_range(bool allow_duplicates = false) {
+  auto test = [&](const TestCaseMapSet<T>& test_case, bool check_multi = false) {
+    Container c(test_case.initial.begin(), test_case.initial.end());
+    auto in = wrap_input<Iter, Sent>(test_case.input);
+
+    c.insert_range(in);
+    if (check_multi) {
+      return std::ranges::is_permutation(c, test_case.expected_multi);
+    } else {
+      return std::ranges::is_permutation(c, test_case.expected);
+    }
+  };
+
+  { // Empty container.
+    // empty_c.insert_range(empty_range)
+    assert(test(EmptyContainer_EmptyRange<T>));
+    // empty_c.insert_range(one_element_range)
+    assert(test(EmptyContainer_OneElementRange<T>));
+    // empty_c.insert_range(range_no_duplicates)
+    assert(test(EmptyContainer_RangeNoDuplicates<T>));
+    // empty_c.insert_range(range_with_duplicates)
+    assert(test(EmptyContainer_RangeWithDuplicates<T>, allow_duplicates));
+  }
+
+  { // One-element container.
+    // one_element_c.insert_range(empty_range)
+    assert(test(OneElementContainer_EmptyRange<T>));
+    // one_element_c.insert_range(one_element_range)
+    assert(test(OneElementContainer_OneElementRange<T>));
+    // one_element_c.insert_range(range_no_duplicates)
+    assert(test(OneElementContainer_RangeNoDuplicates<T>));
+    // one_element_c.insert_range(range_with_duplicates)
+    assert(test(OneElementContainer_RangeWithDuplicates<T>, allow_duplicates));
+  }
+
+  { // N-elements container.
+    // n_elements_c.insert_range(empty_range)
+    assert(test(NElementsContainer_EmptyRange<T>));
+    // n_elements_c.insert_range(one_element_range)
+    assert(test(NElementsContainer_OneElementRange<T>));
+    // n_elements_c.insert_range(range_no_duplicates)
+    assert(test(NElementsContainer_RangeNoDuplicates<T>));
+    // n_elements_c.insert_range(range_with_duplicates)
+    assert(test(NElementsContainer_RangeWithDuplicates<T>, allow_duplicates));
+  }
+}
+
+// Move-only types.
+
+template <template <class ...> class Container>
+void test_set_insert_range_move_only() {
+  MoveOnly input[5];
+  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+  Container<MoveOnly> c;
+  c.insert_range(in);
+}
+
+template <template <class ...> class Container>
+void test_map_insert_range_move_only() {
+  using Value = std::pair<const int, MoveOnly>;
+  Value input[5];
+  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+  Container<int, MoveOnly> c;
+  c.insert_range(in);
+}
+
+// Exception safety.
+
+template <template <class ...> class Container>
+void test_set_insert_range_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using T = ThrowingCopy<3>;
+  T::reset();
+  T in[5] = {{1}, {2}, {3}, {4}, {5}};
+
+  try {
+    Container<T> c;
+    c.insert_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 Container>
+void test_map_insert_range_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using K = int;
+  using V = ThrowingCopy<3>;
+
+  V::throwing_enabled = false;
+  std::pair<const K, V> in[5] = {
+    {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
+  };
+  V::throwing_enabled = true;
+  V::reset();
+
+  try {
+    Container<K, V> c;
+    c.insert_range(in);
+    assert(false); // The function call above should throw.
+
+  } catch (int) {
+    assert(V::created_by_copying == 3);
+    assert(V::destroyed == 2); // No destructor call for the partially-constructed element.
+  }
+#endif
+}
+
+template <template <class ...> class Container, class T>
+void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  T in[] = {1, 2};
+
+  try {
+    ThrowingAllocator<T> alloc;
+
+    globalMemCounter.reset();
+    Container<T, test_less<T>, ThrowingAllocator<T>> c(alloc);
+    c.insert_range(in);
+    assert(false); // The function call above should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+template <template <class ...> class Container, class T>
+void test_unord_set_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  T in[] = {1, 2};
+
+  try {
+    ThrowingAllocator<T> alloc;
+
+    globalMemCounter.reset();
+    Container<T, test_hash<T>, test_equal_to<T>, ThrowingAllocator<T>> c(alloc);
+    c.insert_range(in);
+    assert(false); // The function call above should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+template <template <class ...> class Container, class K, class V>
+void test_assoc_map_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using ValueType = std::pair<const K, V>;
+  ValueType in[] = {
+    ValueType{K{1}, V{1}}
+  };
+
+  try {
+    ThrowingAllocator<ValueType> alloc;
+
+    globalMemCounter.reset();
+    Container<K, V, test_less<K>, ThrowingAllocator<ValueType>> c(alloc);
+    c.insert_range(in);
+    assert(false); // The function call above should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+template <template <class ...> class Container, class K, class V>
+void test_unord_map_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using ValueType = std::pair<const K, V>;
+  ValueType in[] = {
+    ValueType{K{1}, V{1}}
+  };
+
+  try {
+    ThrowingAllocator<ValueType> alloc;
+
+    globalMemCounter.reset();
+    Container<K, V, test_hash<K>, test_equal_to<K>, ThrowingAllocator<ValueType>> c(alloc);
+    c.insert_range(in);
+    assert(false); // The function call above should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+#endif // SUPPORT_INSERT_RANGE_MAPS_SETS_H

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp
index 2fe35df2cd412a..a6f1a15bdd85c3 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/exception_safety.pass.cpp
@@ -23,20 +23,28 @@
 //     forward_list(InputIterator first, InputIterator last, const allocator_type& a);
 // forward_list(const forward_list& x);
 // forward_list(const forward_list& x, const allocator_type& a);
+// template<container-compatible-range<T> R>
+//     forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
 //
 // forward_list& operator=(const forward_list& x);
 //
 // template <class InputIterator>
 //     void assign(InputIterator first, InputIterator last);
 // void assign(size_type n, const value_type& v);
+// template<container-compatible-range<T> R>
+//     void assign_range(R&& rg); // C++23
 //
 // void push_front(const value_type& v);
+//  template<container-compatible-range<T> R>
+//    void prepend_range(R&& rg); // C++23
 //
 // iterator insert_after(const_iterator p, const value_type& v);
 // iterator insert_after(const_iterator p, size_type n, const value_type& v);
 // template <class InputIterator>
 //     iterator insert_after(const_iterator p,
 //                           InputIterator first, InputIterator last);
+//  template<container-compatible-range<T> R>
+//     iterator insert_range_after(const_iterator position, R&& rg); // C++23
 //
 // void resize(size_type n, const value_type& v);
 
@@ -44,6 +52,11 @@
 
 #include <cassert>
 #include "../../exception_safety_helpers.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 23
+#include <ranges>
+#endif
 
 int main(int, char**) {
   {
@@ -90,6 +103,22 @@ int main(int, char**) {
       (void)c;
     });
 
+#if TEST_STD_VER >= 23
+    // template<container-compatible-range<T> R>
+    //     forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
+      {
+        std::forward_list<T> c(std::from_range, std::ranges::subrange(from, to));
+        (void)c;
+      }
+
+      {
+        std::forward_list<T> c(std::from_range, std::ranges::subrange(from, to), Alloc());
+        (void)c;
+      }
+    });
+#endif
+
     // template <class InputIterator>
     //     forward_list(InputIterator first, InputIterator last, const allocator_type& a);
     test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
@@ -122,12 +151,30 @@ int main(int, char**) {
       c.assign(from, to);
     });
 
+#if TEST_STD_VER >= 23
+    // template<container-compatible-range<T> R>
+    //     void assign_range(R&& rg); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::forward_list<T> c;
+      c.assign_range(std::ranges::subrange(from, to));
+    });
+#endif
+
     // void assign(size_type n, const value_type& v);
     test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
       std::forward_list<T> c;
       c.assign(Size, *from);
     });
 
+#if TEST_STD_VER >= 23
+    // template<container-compatible-range<T> R>
+    //   void prepend_range(R&& rg); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::forward_list<T> c;
+      c.prepend_range(std::ranges::subrange(from, to));
+    });
+#endif
+
     // iterator insert_after(const_iterator p, size_type n, const value_type& v);
     test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
       std::forward_list<T> c;
@@ -142,6 +189,15 @@ int main(int, char**) {
       c.insert_after(c.before_begin(), from, to);
     });
 
+#if TEST_STD_VER >= 23
+    // template<container-compatible-range<T> R>
+    //     iterator insert_range_after(const_iterator position, R&& rg); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::forward_list<T> c;
+      c.insert_range_after(c.before_begin(), std::ranges::subrange(from, to));
+    });
+#endif
+
     // void resize(size_type n, const value_type& v);
     test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
       std::forward_list<T> c;

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp
index 74eb69bb08e42e..81b70a95df43c6 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/deduct.pass.cpp
@@ -13,8 +13,12 @@
 //    forward_list(InputIterator, InputIterator, Allocator = Allocator())
 //    -> forward_list<typename iterator_traits<InputIterator>::value_type, Allocator>;
 //
+// template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
+//   forward_list(from_range_t, R&&, Allocator = Allocator())
+//       -> forward_list<ranges::range_value_t<R>, Allocator>; // C++23
 
 #include <algorithm>
+#include <array>
 #include <forward_list>
 #include <iterator>
 #include <cassert>
@@ -128,6 +132,21 @@ int main(int, char**)
         }
     }
 
+#if TEST_STD_VER >= 23
+    {
+      {
+        std::forward_list c(std::from_range, std::array<int, 0>());
+        static_assert(std::is_same_v<decltype(c), std::forward_list<int>>);
+      }
+
+      {
+        using Alloc = test_allocator<int>;
+        std::forward_list c(std::from_range, std::array<int, 0>(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::forward_list<int, Alloc>>);
+      }
+    }
+#endif
+
     SequenceContainerDeductionGuidesSfinaeAway<std::forward_list, std::forward_list<int>>();
 
     return 0;

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_range.pass.cpp
new file mode 100644
index 00000000000000..312f6dbad3550b
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/from_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>
+//   forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+
+#include <forward_list>
+
+#include "../../from_range_sequence_containers.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+  for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_container<std::forward_list, int, Iter, Sent, Alloc>([](const auto&) {
+      // No additional validation to do.
+    });
+  });
+  test_sequence_container_move_only<std::forward_list>();
+
+  static_assert(test_constraints<std::forward_list, int, double>());
+
+  test_exception_safety_throwing_copy<std::forward_list>();
+  test_exception_safety_throwing_allocator<std::forward_list, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
new file mode 100644
index 00000000000000..a27cc757025b52
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   constexpr void prepend_range(R&& rg); // C++23
+
+#include <forward_list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - 
diff erent kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
+//   {empty/one-element/full} container);
+// - prepending move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+  static_assert(test_constraints_assign_range<std::forward_list, int, double>());
+
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_prepend_range<std::forward_list<int, Alloc>, Iter, Sent>([](auto&&) {
+      // No additional validation to do.
+    });
+  });
+  test_sequence_prepend_range_move_only<std::forward_list>();
+
+  test_prepend_range_exception_safety_throwing_copy<std::forward_list>();
+  test_prepend_range_exception_safety_throwing_allocator<std::forward_list, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp
new file mode 100644
index 00000000000000..1b9133e7c3a7a6
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_range_after.pass.cpp
@@ -0,0 +1,383 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   constexpr iterator insert_range_after(const_iterator position, R&& rg); // C++23
+
+#include <forward_list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+template <class Container, class Range>
+concept HasInsertRangeAfter = requires (Container& c, Range&& range) {
+  c.insert_range_after(c.begin(), range);
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_constraints_insert_range_after() {
+  // Input range with the same value type.
+  static_assert(HasInsertRangeAfter<Container<T>, InputRange<T>>);
+  // Input range with a convertible value type.
+  static_assert(HasInsertRangeAfter<Container<T>, InputRange<U>>);
+  // Input range with a non-convertible value type.
+  static_assert(!HasInsertRangeAfter<Container<T>, InputRange<Empty>>);
+  // Not an input range.
+  static_assert(!HasInsertRangeAfter<Container<T>, InputRangeNotDerivedFrom>);
+  static_assert(!HasInsertRangeAfter<Container<T>, InputRangeNotIndirectlyReadable>);
+  static_assert(!HasInsertRangeAfter<Container<T>, InputRangeNotInputOrOutputIterator>);
+
+  return true;
+}
+
+// Tested cases:
+// - 
diff erent kinds of insertions (inserting an {empty/one-element/mid-sized/long range} into an
+//   {empty/one-element/full} container at the {beginning/middle/end});
+// - inserting move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+
+template <class T, class Iter, class Sent, class Alloc>
+constexpr void test_sequence_insert_range_after() {
+  using Container = std::forward_list<T, Alloc>;
+  // Index `0` translates to `before_begin()` and the last index translates to the index before `end()`.
+  auto get_insert_pos = [](auto& c, auto& test_case) { return std::ranges::next(c.before_begin(), test_case.index); };
+  // Unlike `insert_range` in other containers, `insert_range_after` returns the iterator to the last inserted element.
+  auto get_return_pos = [](auto& c, auto& test_case) {
+    return std::ranges::next(c.before_begin(), test_case.index + test_case.input.size());
+  };
+
+  { // Empty container.
+    { // empty_c.insert_range_after(end, empty_range)
+      auto& test_case = EmptyContainer_EmptyRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // empty_c.insert_range_after(end, one_element_range)
+      auto& test_case = EmptyContainer_OneElementRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // empty_c.insert_range_after(end, mid_range)
+      auto& test_case = EmptyContainer_MidRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+  }
+
+  { // One-element container.
+    { // one_element_c.insert_range_after(begin, empty_range)
+      auto& test_case = OneElementContainer_Begin_EmptyRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // one_element_c.insert_range_after(end, empty_range)
+      auto& test_case = OneElementContainer_End_EmptyRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // one_element_c.insert_range_after(begin, one_element_range)
+      auto& test_case = OneElementContainer_Begin_OneElementRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // one_element_c.insert_range_after(end, one_element_range)
+      auto& test_case = OneElementContainer_End_OneElementRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // one_element_c.insert_range_after(begin, mid_range)
+      auto& test_case = OneElementContainer_Begin_MidRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // one_element_c.insert_range_after(end, mid_range)
+      auto& test_case = OneElementContainer_End_MidRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+  }
+
+  { // Full container.
+    { // full_container.insert_range_after(begin, empty_range)
+      auto& test_case = FullContainer_Begin_EmptyRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(mid, empty_range)
+      auto& test_case = FullContainer_Mid_EmptyRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(end, empty_range)
+      auto& test_case = FullContainer_End_EmptyRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(begin, one_element_range)
+      auto& test_case = FullContainer_Begin_OneElementRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(end, one_element_range)
+      auto& test_case = FullContainer_Mid_OneElementRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(end, one_element_range)
+      auto& test_case = FullContainer_End_OneElementRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(begin, mid_range)
+      auto& test_case = FullContainer_Begin_MidRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(mid, mid_range)
+      auto& test_case = FullContainer_Mid_MidRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(end, mid_range)
+      auto& test_case = FullContainer_End_MidRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(begin, long_range)
+      auto& test_case = FullContainer_Begin_LongRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(mid, long_range)
+      auto& test_case = FullContainer_Mid_LongRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+
+    { // full_container.insert_range_after(end, long_range)
+      auto& test_case = FullContainer_End_LongRange<T>;
+
+      Container c(test_case.initial.begin(), test_case.initial.end());
+      auto in = wrap_input<Iter, Sent>(test_case.input);
+      auto pos = get_insert_pos(c, test_case);
+
+      auto result = c.insert_range_after(pos, in);
+      assert(std::ranges::equal(c, test_case.expected));
+      assert(result == get_return_pos(c, test_case));
+    }
+  }
+
+  // Also check inserting after `begin()` (the tests above only use `before_begin()`).
+  {
+    Container c{5, 1, 3, 4, 9};
+    Buffer<T> input{-18, -15, -11};
+    auto in = wrap_input<Iter, Sent>(input);
+
+    auto result = c.insert_range_after(c.begin(), in);
+    assert(std::ranges::equal(c, Buffer<int>{5, -18, -15, -11, 1, 3, 4, 9}));
+    assert(result == std::ranges::next(c.begin(), 3));
+  }
+}
+
+void test_sequence_insert_range_after_move_only() {
+  MoveOnly input[5];
+  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+  std::forward_list<MoveOnly> c;
+  c.insert_range_after(c.before_begin(), in);
+}
+
+void test_insert_range_after_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using T = ThrowingCopy<3>;
+  T::reset();
+  T in[5];
+
+  try {
+    std::forward_list<T> c;
+    c.insert_range_after(c.before_begin(), 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 <class T>
+void test_insert_range_after_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  T in[] = {0, 1};
+
+  try {
+    ThrowingAllocator<T> alloc;
+
+    globalMemCounter.reset();
+    std::forward_list<T, ThrowingAllocator<T>> c(alloc);
+    c.insert_range_after(c.before_begin(), in);
+    assert(false); // The function call above should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+int main(int, char**) {
+  static_assert(test_constraints_insert_range_after<std::forward_list, int, double>());
+
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_insert_range_after<int, Iter, Sent, Alloc>();
+  });
+  test_sequence_insert_range_after_move_only();
+
+  test_insert_range_after_exception_safety_throwing_copy();
+  test_insert_range_after_exception_safety_throwing_allocator<int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp
new file mode 100644
index 00000000000000..418aa72052ba91
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   constexpr void prepend_range(R&& rg); // C++23
+
+#include <forward_list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - 
diff erent kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
+//   {empty/one-element/full} container);
+// - prepending move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+  static_assert(test_constraints_prepend_range<std::forward_list, int, double>());
+
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_prepend_range<std::forward_list<int, Alloc>, Iter, Sent>([](auto&&) {
+      // No additional validation to do.
+    });
+  });
+  test_sequence_prepend_range_move_only<std::forward_list>();
+
+  test_prepend_range_exception_safety_throwing_copy<std::forward_list>();
+  test_prepend_range_exception_safety_throwing_allocator<std::forward_list, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/sequences/list/exception_safety.pass.cpp b/libcxx/test/std/containers/sequences/list/exception_safety.pass.cpp
new file mode 100644
index 00000000000000..c734d18f2bb2fc
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/exception_safety.pass.cpp
@@ -0,0 +1,225 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <list>
+
+// UNSUPPORTED: c++03, no-exceptions
+
+// TODO:
+// - throwing upon moving;
+// - initializer lists;
+// - throwing when constructing the element in place.
+
+// list(size_type n, const value_type& v);
+// list(size_type n, const value_type& v, const allocator_type& a);
+// template <class InputIterator>
+//     list(InputIterator first, InputIterator last);
+// template <class InputIterator>
+//     list(InputIterator first, InputIterator last, const allocator_type& a);
+// template<container-compatible-range<T> R>
+//     list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+// list(const list& x);
+// list(const list& x, const allocator_type& a);
+//
+// list& operator=(const list& x);
+//
+// template <class InputIterator>
+//     void assign(InputIterator first, InputIterator last);
+// void assign(size_type n, const value_type& v);
+// template<container-compatible-range<T> R>
+//     void assign_range(R&& rg); // C++23
+//
+// template<container-compatible-range<T> R>
+//   void prepend_range(R&& rg); // C++23
+// void push_back(const value_type& x);
+// template<container-compatible-range<T> R>
+//   void append_range(R&& rg); // C++23
+// void push_front(const value_type& v);
+//
+// iterator insert(const_iterator p, const value_type& v);
+// iterator insert(const_iterator p, size_type n, const value_type& v);
+// template <class InputIterator>
+//     iterator insert(const_iterator p,
+//                     InputIterator first, InputIterator last);
+// template<container-compatible-range<T> R>
+//     iterator insert_range(const_iterator position, R&& rg); // C++23
+//
+// void resize(size_type n, const value_type& v);
+
+#include <list>
+
+#include <cassert>
+#include "../../exception_safety_helpers.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 23
+#include <ranges>
+#endif
+
+int main(int, char**) {
+  {
+    constexpr int ThrowOn = 1;
+    constexpr int Size = 1;
+    using T = ThrowingCopy<ThrowOn>;
+
+    // void push_front(const value_type& v);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*){
+      std::list<T> c;
+      c.push_front(*from);
+    });
+
+    // void push_back(const value_type& v);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*){
+      std::list<T> c;
+      c.push_back(*from);
+    });
+
+    // iterator insert(const_iterator p, const value_type& v);
+    test_exception_safety_throwing_copy</*ThrowOn=*/1, Size>([](T* from, T*){
+      std::list<T> c;
+      c.insert(c.end(), *from);
+    });
+  }
+
+  {
+    constexpr int ThrowOn = 3;
+    constexpr int Size = 5;
+    using T = ThrowingCopy<ThrowOn>;
+    using C = std::list<T>;
+    using Alloc = std::allocator<T>;
+
+    // list(size_type n, const value_type& v);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*){
+      std::list<T> c(Size, *from);
+      (void)c;
+    });
+
+    // list(size_type n, const value_type& v, const allocator_type& a);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*){
+      std::list<T> c(Size, *from, Alloc());
+      (void)c;
+    });
+
+    // template <class InputIterator>
+    //     list(InputIterator first, InputIterator last);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
+      std::list<T> c(from, to);
+      (void)c;
+    });
+
+    // template <class InputIterator>
+    //     list(InputIterator first, InputIterator last, const allocator_type& a);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
+      std::list<T> c(from, to, Alloc());
+      (void)c;
+    });
+
+#if TEST_STD_VER >= 23
+    // template<container-compatible-range<T> R>
+    //     list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to){
+      {
+        std::list<T> c(std::from_range, std::ranges::subrange(from, to));
+        (void)c;
+      }
+
+      {
+        std::list<T> c(std::from_range, std::ranges::subrange(from, to), Alloc());
+        (void)c;
+      }
+    });
+#endif
+
+    // list(const list& x);
+    test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+      std::list<T> c(in);
+      (void)c;
+    });
+
+    // list(const list& x, const allocator_type& a);
+    test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+      std::list<T> c(in, Alloc());
+      (void)c;
+    });
+
+    // list& operator=(const list& x);
+    test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+      std::list<T> c;
+      c = in;
+    });
+
+    // template <class InputIterator>
+    //     void assign(InputIterator first, InputIterator last);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::list<T> c;
+      c.assign(from, to);
+    });
+
+#if TEST_STD_VER >= 23
+    // template<container-compatible-range<T> R>
+    //     void assign_range(R&& rg); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::list<T> c;
+      c.assign_range(std::ranges::subrange(from, to));
+    });
+#endif
+
+    // void assign(size_type n, const value_type& v);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+      std::list<T> c;
+      c.assign(Size, *from);
+    });
+
+#if TEST_STD_VER >= 23
+    // template<container-compatible-range<T> R>
+    //   void prepend_range(R&& rg); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::list<T> c;
+      c.prepend_range(std::ranges::subrange(from, to));
+    });
+
+    // template<container-compatible-range<T> R>
+    //   void append_range(R&& rg); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::list<T> c;
+      c.append_range(std::ranges::subrange(from, to));
+    });
+#endif
+
+    // iterator insert(const_iterator p, size_type n, const value_type& v);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+      std::list<T> c;
+      c.insert(c.end(), Size, *from);
+    });
+
+    // template <class InputIterator>
+    //     iterator insert(const_iterator p,
+    //                     InputIterator first, InputIterator last);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::list<T> c;
+      c.insert(c.end(), from, to);
+    });
+
+#if TEST_STD_VER >= 23
+    // template<container-compatible-range<T> R>
+    //     iterator insert_range(const_iterator position, R&& rg); // C++23
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+      std::list<T> c;
+      c.insert_range(c.end(), std::ranges::subrange(from, to));
+    });
+#endif
+
+    // void resize(size_type n, const value_type& v);
+    test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+      std::list<T> c;
+      c.resize(Size, *from);
+    });
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp b/libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp
index a82dac71f1bae8..2210a9ea6e699b 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.cons/deduct.pass.cpp
@@ -13,12 +13,16 @@
 //    list(InputIterator, InputIterator, Allocator = Allocator())
 //    -> list<typename iterator_traits<InputIterator>::value_type, Allocator>;
 //
+// template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
+//   list(from_range_t, R&&, Allocator = Allocator())
+//     -> list<ranges::range_value_t<R>, Allocator>; // C++23
 
-#include <list>
-#include <iterator>
+#include <array>
 #include <cassert>
-#include <cstddef>
 #include <climits> // INT_MAX
+#include <cstddef>
+#include <iterator>
+#include <list>
 
 #include "deduction_guides_sfinae_checks.h"
 #include "test_macros.h"
@@ -127,6 +131,21 @@ int main(int, char**)
         }
     }
 
+#if TEST_STD_VER >= 23
+    {
+      {
+        std::list c(std::from_range, std::array<int, 0>());
+        static_assert(std::is_same_v<decltype(c), std::list<int>>);
+      }
+
+      {
+        using Alloc = test_allocator<int>;
+        std::list c(std::from_range, std::array<int, 0>(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::list<int, Alloc>>);
+      }
+    }
+#endif
+
     SequenceContainerDeductionGuidesSfinaeAway<std::list, std::list<int>>();
 
     return 0;

diff  --git a/libcxx/test/std/containers/sequences/list/list.cons/from_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.cons/from_range.pass.cpp
new file mode 100644
index 00000000000000..42b7475c00784b
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.cons/from_range.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   list(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+
+#include <list>
+
+#include "../../from_range_sequence_containers.h"
+#include "test_macros.h"
+
+int main(int, char**) {
+  for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_container<std::list, int, Iter, Sent, Alloc>([](const auto&) {
+      // No additional validation to do.
+    });
+  });
+  test_sequence_container_move_only<std::list>();
+
+  static_assert(test_constraints<std::list, int, double>());
+
+  test_exception_safety_throwing_copy<std::list>();
+  test_exception_safety_throwing_allocator<std::list, int>();
+
+  return 0;
+}
+

diff  --git a/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp
new file mode 100644
index 00000000000000..46a99cb5484478
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   constexpr void append_range(R&& rg); // C++23
+
+#include <list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - 
diff erent kinds of insertions (appending an {empty/one-element/mid-sized/long range} into an
+//   {empty/one-element/full} container);
+// - appending move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+  static_assert(test_constraints_append_range<std::list, int, double>());
+
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_append_range<std::list<int, Alloc>, Iter, Sent>([](auto&&) {
+      // No additional validation to do.
+    });
+  });
+  test_sequence_append_range_move_only<std::list>();
+
+  test_append_range_exception_safety_throwing_copy<std::list>();
+  test_append_range_exception_safety_throwing_allocator<std::list, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/sequences/list/list.modifiers/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/assign_range.pass.cpp
new file mode 100644
index 00000000000000..d745786b6815d0
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/assign_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   constexpr void assign_range(R&& rg); // C++23
+
+#include <list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - 
diff erent kinds of assignments (assigning an {empty/one-element/mid-sized/long range} to an
+//   {empty/one-element/full} container);
+// - assigning move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+  static_assert(test_constraints_assign_range<std::list, int, double>());
+
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_assign_range<std::list<int, Alloc>, Iter, Sent>([](auto&&) {
+      // No additional validation to do.
+    });
+  });
+  test_sequence_assign_range_move_only<std::list>();
+
+  test_assign_range_exception_safety_throwing_copy<std::list>();
+  test_assign_range_exception_safety_throwing_allocator<std::list, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/sequences/list/list.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/insert_range.pass.cpp
new file mode 100644
index 00000000000000..eb3937eb8f9e7a
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/insert_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   constexpr iterator insert_range(const_iterator position, R&& rg); // C++23
+
+#include <list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - 
diff erent kinds of insertions (inserting an {empty/one-element/mid-sized/long range} into an
+//   {empty/one-element/full} container at the {beginning/middle/end});
+// - inserting move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+  static_assert(test_constraints_insert_range<std::list, int, double>());
+
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_insert_range<std::list<int, Alloc>, Iter, Sent>([](auto&&) {
+      // No additional validation to do.
+    });
+  });
+  test_sequence_insert_range_move_only<std::list>();
+
+  test_insert_range_exception_safety_throwing_copy<std::list>();
+  test_insert_range_exception_safety_throwing_allocator<std::list, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp
new file mode 100644
index 00000000000000..d5e4d4fabb761f
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   constexpr void prepend_range(R&& rg); // C++23
+
+#include <list>
+
+#include "../../insert_range_sequence_containers.h"
+#include "test_macros.h"
+
+// Tested cases:
+// - 
diff erent kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
+//   {empty/one-element/full} container);
+// - prepending move-only elements;
+// - an exception is thrown when copying the elements or when allocating new elements.
+int main(int, char**) {
+  static_assert(test_constraints_prepend_range<std::list, int, double>());
+
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_sequence_prepend_range<std::list<int, Alloc>, Iter, Sent>([](auto&&) {
+      // No additional validation to do.
+    });
+  });
+  test_sequence_prepend_range_move_only<std::list>();
+
+  test_prepend_range_exception_safety_throwing_copy<std::list>();
+  test_prepend_range_exception_safety_throwing_allocator<std::list, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/unord/from_range_unordered_containers.h b/libcxx/test/std/containers/unord/from_range_unordered_containers.h
new file mode 100644
index 00000000000000..763dd3e423f320
--- /dev/null
+++ b/libcxx/test/std/containers/unord/from_range_unordered_containers.h
@@ -0,0 +1,435 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_UNORDERED_CONTAINERS_H
+#define SUPPORT_FROM_RANGE_UNORDERED_CONTAINERS_H
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <limits>
+#include <ranges>
+#include <vector>
+#include <utility>
+
+#include "../exception_safety_helpers.h"
+#include "../from_range_helpers.h"
+#include "../test_compare.h"
+#include "../test_hash.h"
+#include "MoveOnly.h"
+#include "almost_satisfies_types.h"
+#include "count_new.h"
+#include "test_macros.h"
+
+// template<container-compatible-range<value_type> R>
+//   unordered-container(from_range_t, R&& rg, size_type n = see below,
+//     const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+//     const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered-container(from_range_t, R&& rg, size_type n, const allocator_type& a)
+//     : unordered-container(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered-container(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+//     : unordered-container(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
+
+template <class Container, class Range>
+concept HasFromRangeCtr = requires (Range&& range) {
+  // (from_range, range)
+  Container(std::from_range, std::forward<Range>(range));
+  // (from_range, range, n)
+  Container(std::from_range, std::forward<Range>(range), 0);
+  // (from_range, range, n, hash)
+  Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::key_type>());
+  // (from_range, range, n, hash, equal)
+  Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::key_type>(),
+            std::equal_to<typename Container::key_type>());
+  // (from_range, range, n, hash, equal, alloc)
+  Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::key_type>(),
+            std::equal_to<typename Container::key_type>(), std::allocator<typename Container::value_type>());
+  // (from_range, range, n, alloc)
+  Container(std::from_range, std::forward<Range>(range), 0, std::allocator<typename Container::value_type>());
+  // (from_range, range, n, hash, alloc)
+  Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::key_type>(),
+            std::allocator<typename Container::value_type>());
+};
+
+template <template <class...> class Container, class K, class V, class K2, class V2>
+constexpr bool test_map_constraints() {
+  using ValueType = std::pair<const K, V>;
+
+  // Input range with the same value type.
+  static_assert(HasFromRangeCtr<Container<K, V>, InputRange<ValueType>>);
+  // Input range with a convertible value type.
+  static_assert(HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const K2, V2>>>);
+  // Input range with a non-convertible value type.
+  static_assert(!HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const Empty, V>>>);
+  static_assert(!HasFromRangeCtr<Container<K, V>, InputRange<std::pair<const K, Empty>>>);
+  // Not an input range.
+  static_assert(!HasFromRangeCtr<Container<K, V>, InputRangeNotDerivedFromGeneric<ValueType>>);
+
+  return true;
+}
+
+template <template <class ...> class Container,
+          class K,
+          class V,
+          class Iter,
+          class Sent,
+          class Hash,
+          class Equal,
+          class Alloc,
+          class ValueType = std::pair<const K, V>>
+void test_unordered_map_with_input(std::vector<ValueType>&& input) {
+  using DefaultHash = std::hash<int>;
+  using DefaultEqual = std::equal_to<int>;
+
+  auto validate = [](auto&& c) {
+    if (!c.empty()) {
+      auto 
diff  = c.load_factor() - (static_cast<float>(c.size()) / c.bucket_count());
+      assert(std::fabs(
diff ) < std::numeric_limits<float>::epsilon());
+    }
+    assert(c.max_load_factor() == 1);
+  };
+
+  auto in = wrap_input<Iter, Sent>(input);
+
+  { // (range)
+    Container<K, V> c(std::from_range, in);
+
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+    validate(c);
+  }
+
+  { // (range, n)
+    Container<K, V> c(std::from_range, in, 123);
+
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+    validate(c);
+  }
+
+  { // (range, n, hasher)
+    Container<K, V, Hash> c(std::from_range, in, 123, Hash());
+
+    assert(c.hash_function() == Hash());
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+    validate(c);
+  }
+
+  { // (range, n, hasher, key_equal)
+    Container<K, V, Hash, Equal> c(std::from_range, in, 123, Hash(), Equal());
+
+    assert(c.hash_function() == Hash());
+    assert(c.key_eq() == Equal());
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+    validate(c);
+  }
+
+  { // (range, n, hasher, key_equal, allocator)
+    Alloc alloc;
+    Container<K, V, Hash, Equal, Alloc> c(std::from_range, in, 123, Hash(), Equal(), alloc);
+
+    assert(c.hash_function() == Hash());
+    assert(c.key_eq() == Equal());
+    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));
+    validate(c);
+  }
+
+  { // (range, n, allocator)
+    Alloc alloc;
+    Container<K, V, DefaultHash, DefaultEqual, Alloc> c(std::from_range, in, 123, alloc);
+
+    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));
+    validate(c);
+  }
+
+  { // (range, n, hasher, allocator)
+    Alloc alloc;
+    Container<K, V, Hash, DefaultEqual, Alloc> c(std::from_range, in, 123, Hash(), alloc);
+
+    assert(c.hash_function() == Hash());
+    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));
+    validate(c);
+  }
+}
+
+template <template <class ...> class Container,
+          class K,
+          class V,
+          class Iter,
+          class Sent,
+          class Hash,
+          class Equal,
+          class Alloc>
+void test_unordered_map() {
+  auto test_with_input = &test_unordered_map_with_input<Container, K, V, Iter, Sent, Hash, Equal, Alloc>;
+
+  // Normal input.
+  test_with_input({{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}});
+  // Empty input.
+  test_with_input({});
+  // Single-element input.
+  test_with_input({{1, 2}});
+}
+
+template <template <class ...> class Container>
+void test_unordered_map_move_only() {
+  std::pair<const int, MoveOnly> input[5];
+  std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
+
+  [[maybe_unused]] Container<int, MoveOnly> c(std::from_range, in);
+}
+
+template <template <class ...> class Container>
+void test_map_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using K = int;
+  using V = ThrowingCopy<3>;
+
+  V::throwing_enabled = false;
+  std::pair<const K, V> in[5] = {
+    {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}
+  };
+  V::throwing_enabled = true;
+  V::reset();
+
+  try {
+    Container<K, V> c(std::from_range, in);
+    assert(false); // The constructor call above should throw.
+
+  } catch (int) {
+    assert(V::created_by_copying == 3);
+    assert(V::destroyed == 2); // No destructor call for the partially-constructed element.
+  }
+#endif
+}
+
+template <template <class ...> class Container, class K, class V>
+void test_map_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using ValueType = std::pair<const K, V>;
+  ValueType in[] = {
+    ValueType{K{1}, V{1}}
+  };
+
+  try {
+    ThrowingAllocator<ValueType> alloc;
+
+    globalMemCounter.reset();
+    Container<K, V, test_hash<K>, test_equal_to<K>, ThrowingAllocator<ValueType>>
+        c(std::from_range, in, /*n=*/0, alloc);
+    assert(false); // The constructor call should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+template <class Container, class Range>
+concept SetHasFromRangeCtr = requires (Range&& range) {
+  // (from_range, range)
+  Container(std::from_range, std::forward<Range>(range));
+  // (from_range, range, n)
+  Container(std::from_range, std::forward<Range>(range), 0);
+  // (from_range, range, n, hash)
+  Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::value_type>());
+  // (from_range, range, n, hash, equal)
+  Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::value_type>(),
+            std::equal_to<typename Container::value_type>());
+  // (from_range, range, n, hash, equal, alloc)
+  Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::value_type>(),
+            std::equal_to<typename Container::value_type>(), std::allocator<typename Container::value_type>());
+  // (from_range, range, n, alloc)
+  Container(std::from_range, std::forward<Range>(range), 0, std::allocator<typename Container::value_type>());
+  // (from_range, range, n, hash, alloc)
+  Container(std::from_range, std::forward<Range>(range), 0, std::hash<typename Container::value_type>(),
+            std::allocator<typename Container::value_type>());
+};
+
+template <template <class...> class Container, class T, class U>
+constexpr bool test_set_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>, InputRangeNotDerivedFromGeneric<T>>);
+
+  return true;
+}
+
+template <template <class ...> class Container,
+          class T,
+          class Iter,
+          class Sent,
+          class Hash,
+          class Equal,
+          class Alloc>
+void test_unordered_set_with_input(std::vector<T>&& input) {
+  using DefaultHash = std::hash<int>;
+  using DefaultEqual = std::equal_to<int>;
+
+  auto validate = [](auto&& c) {
+    if (!c.empty()) {
+      auto 
diff  = c.load_factor() - (static_cast<float>(c.size()) / c.bucket_count());
+      assert(std::fabs(
diff ) < std::numeric_limits<float>::epsilon());
+    }
+    assert(c.max_load_factor() == 1);
+  };
+
+  auto b = Iter(input.data());
+  auto e = Iter(input.data() + input.size());
+  std::ranges::subrange in(std::move(b), Sent(std::move(e)));
+
+  { // (range)
+    Container<T> c(std::from_range, in);
+
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+    validate(c);
+  }
+
+  { // (range, n)
+    Container<T> c(std::from_range, in, 123);
+
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+    validate(c);
+  }
+
+  { // (range, n, hasher)
+    Container<T, Hash> c(std::from_range, in, 123, Hash());
+
+    assert(c.hash_function() == Hash());
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+    validate(c);
+  }
+
+  { // (range, n, hasher, key_equal)
+    Container<T, Hash, Equal> c(std::from_range, in, 123, Hash(), Equal());
+
+    assert(c.hash_function() == Hash());
+    assert(c.key_eq() == Equal());
+    assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
+    assert(std::ranges::is_permutation(input, c));
+    validate(c);
+  }
+
+  { // (range, n, hasher, key_equal, allocator)
+    Alloc alloc;
+    Container<T, Hash, Equal, Alloc> c(std::from_range, in, 123, Hash(), Equal(), alloc);
+
+    assert(c.hash_function() == Hash());
+    assert(c.key_eq() == Equal());
+    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));
+    validate(c);
+  }
+
+  { // (range, n, allocator)
+    Alloc alloc;
+    Container<T, DefaultHash, DefaultEqual, Alloc> c(std::from_range, in, 123, alloc);
+
+    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));
+    validate(c);
+  }
+
+  { // (range, n, hasher, allocator)
+    Alloc alloc;
+    Container<T, Hash, DefaultEqual, Alloc> c(std::from_range, in, 123, Hash(), alloc);
+
+    assert(c.hash_function() == Hash());
+    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));
+    validate(c);
+  }
+}
+
+template <template <class ...> class Container,
+          class T,
+          class Iter,
+          class Sent,
+          class Hash,
+          class Equal,
+          class Alloc>
+void test_unordered_set() {
+  auto test_with_input = &test_unordered_set_with_input<Container, T, Iter, Sent, Hash, Equal, 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>
+void test_unordered_set_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 Container>
+void test_set_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using T = ThrowingCopy<3>;
+  T::reset();
+  T in[5] = {{1}, {2}, {3}, {4}, {5}};
+
+  try {
+    Container<T, test_hash<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 Container, class T>
+void test_set_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  T in[] = {1, 2, 3};
+
+  try {
+    ThrowingAllocator<T> alloc;
+
+    globalMemCounter.reset();
+    Container<T, test_hash<T>, test_equal_to<T>, ThrowingAllocator<T>> c(std::from_range, in, /*n=*/0, alloc);
+    assert(false); // The constructor call should throw.
+
+  } catch (int) {
+    assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  }
+#endif
+}
+
+#endif // SUPPORT_FROM_RANGE_UNORDERED_CONTAINERS_H

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp
index 9a36348c684d58..873b77e06a8d85 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/deduct.pass.cpp
@@ -54,14 +54,39 @@
 // unordered_map(initializer_list<pair<Key, T>>, typename see below::size_type, Hash,
 //               Allocator)
 //   -> unordered_map<Key, T, Hash, equal_to<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Hash = hash<range-key-type<R>>,
+//           class Pred = equal_to<range-key-type<R>>,
+//           class Allocator = allocator<range-to-alloc-type<R>>>
+//   unordered_map(from_range_t, R&&, typename see below::size_type = see below,
+//                 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+//     -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   unordered_map(from_range_t, R&&, typename see below::size_type, Allocator)
+//     -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+//                       equal_to<range-key-type<R>>, Allocator>;   // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   unordered_map(from_range_t, R&&, Allocator)
+//     -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+//                       equal_to<range-key-type<R>>, Allocator>;   // C++23
+//
+// template<ranges::input_range R, class Hash, class Allocator>
+//   unordered_map(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+//     -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash,
+//                       equal_to<range-key-type<R>>, Allocator>;   // C++23
 
 #include <algorithm> // is_permutation
+#include <array>
 #include <cassert>
 #include <climits> // INT_MAX
 #include <iterator>
 #include <type_traits>
 #include <unordered_map>
 
+#include "../../../test_compare.h"
+#include "../../../test_hash.h"
 #include "deduction_guides_sfinae_checks.h"
 #include "test_allocator.h"
 
@@ -220,6 +245,58 @@ int main(int, char**)
     ASSERT_SAME_TYPE(decltype(m2), std::unordered_map<int, int>);
     }
 
+#if TEST_STD_VER >= 23
+    {
+      using Range = std::array<P, 0>;
+      using Pred = test_equal_to<int>;
+      using DefaultPred = std::equal_to<int>;
+      using Hash = test_hash<int>;
+      using DefaultHash = std::hash<int>;
+      using Alloc = test_allocator<PC>;
+
+      { // (from_range, range)
+        std::unordered_map c(std::from_range, Range());
+        static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long>>);
+      }
+
+      { // (from_range, range, n)
+        std::unordered_map c(std::from_range, Range(), std::size_t());
+        static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long>>);
+      }
+
+      { // (from_range, range, n, hash)
+        std::unordered_map c(std::from_range, Range(), std::size_t(), Hash());
+        static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash>>);
+      }
+
+      { // (from_range, range, n, hash, pred)
+        std::unordered_map c(std::from_range, Range(), std::size_t(), Hash(), Pred());
+        static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash, Pred>>);
+      }
+
+      { // (from_range, range, n, hash, pred, alloc)
+        std::unordered_map c(std::from_range, Range(), std::size_t(), Hash(), Pred(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash, Pred, Alloc>>);
+      }
+
+      { // (from_range, range, n, alloc)
+        std::unordered_map c(std::from_range, Range(), std::size_t(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, DefaultHash, DefaultPred, Alloc>>);
+      }
+
+      // TODO(LWG 2713): uncomment this test once the constructor is added.
+      { // (from_range, range, alloc)
+        //std::unordered_map c(std::from_range, Range(), Alloc());
+        //static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, DefaultHash, DefaultPred, Alloc>>);
+      }
+
+      { // (from_range, range, n, hash, alloc)
+        std::unordered_map c(std::from_range, Range(), std::size_t(), Hash(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_map<int, long, Hash, DefaultPred, Alloc>>);
+      }
+    }
+#endif
+
     UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_map, std::unordered_map<int, long>>();
 
     return 0;

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/from_range.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/from_range.pass.cpp
new file mode 100644
index 00000000000000..f48057c12f31c1
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/from_range.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<value_type> R>
+//   unordered_map(from_range_t, R&& rg, size_type n = see below,
+//     const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+//     const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
+//     : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+//     : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
+
+#include <array>
+#include <unordered_map>
+
+#include "../../from_range_unordered_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+  using T = std::pair<const int, char>;
+
+  std::array input = {
+    T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+  };
+  std::array expected = {
+    T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{4, 'a'}
+  };
+  auto c = std::unordered_map<int, char>(std::from_range, input);
+  assert(std::ranges::is_permutation(expected, c));
+}
+
+int main(int, char**) {
+  using T = std::pair<const int, int>;
+  for_all_iterators_and_allocators<T>([]<class Iter, class Sent, class Alloc>() {
+    test_unordered_map<std::unordered_map, int, int, Iter, Sent, test_hash<int>, test_equal_to<int>, Alloc>();
+  });
+  test_unordered_map_move_only<std::unordered_map>();
+  test_duplicates();
+
+  static_assert(test_map_constraints<std::unordered_map, int, int, double, double>());
+
+  test_map_exception_safety_throwing_copy<std::unordered_map>();
+  test_map_exception_safety_throwing_allocator<std::unordered_map, int, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_iter_iter.pass.cpp
new file mode 100644
index 00000000000000..342ff584f64500
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_iter_iter.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <unordered_map>
+
+// template <class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>,
+//           class Alloc = allocator<pair<const Key, T>>>
+// class unordered_map
+
+// template <class InputIterator>
+//     void insert(InputIterator first, InputIterator last);
+
+#include <unordered_map>
+#include <string>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "min_allocator.h"
+
+int main(int, char**)
+{
+    {
+        typedef std::unordered_map<int, std::string> C;
+        typedef std::pair<int, std::string> P;
+        P a[] =
+        {
+            P(1, "one"),
+            P(2, "two"),
+            P(3, "three"),
+            P(4, "four"),
+            P(1, "four"),
+            P(2, "four"),
+        };
+        C c;
+        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+        assert(c.size() == 4);
+        assert(c.at(1) == "one");
+        assert(c.at(2) == "two");
+        assert(c.at(3) == "three");
+        assert(c.at(4) == "four");
+    }
+#if TEST_STD_VER >= 11
+    {
+        typedef std::unordered_map<int, std::string, std::hash<int>, std::equal_to<int>,
+                            min_allocator<std::pair<const int, std::string>>> C;
+        typedef std::pair<int, std::string> P;
+        P a[] =
+        {
+            P(1, "one"),
+            P(2, "two"),
+            P(3, "three"),
+            P(4, "four"),
+            P(1, "four"),
+            P(2, "four"),
+        };
+        C c;
+        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+        assert(c.size() == 4);
+        assert(c.at(1) == "one");
+        assert(c.at(2) == "two");
+        assert(c.at(3) == "three");
+        assert(c.at(4) == "four");
+    }
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp
index 342ff584f64500..8b004336f68cde 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/insert_range.pass.cpp
@@ -6,68 +6,35 @@
 //
 //===----------------------------------------------------------------------===//
 
-// <unordered_map>
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
 
-// template <class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>,
-//           class Alloc = allocator<pair<const Key, T>>>
-// class unordered_map
+// <unordered_map>
 
-// template <class InputIterator>
-//     void insert(InputIterator first, InputIterator last);
+// template<container-compatible-range<value_type> R>
+//   void insert_range(R&& rg); // C++23
 
 #include <unordered_map>
-#include <string>
-#include <cassert>
 
+#include "../../../insert_range_maps_sets.h"
 #include "test_macros.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
 
-int main(int, char**)
-{
-    {
-        typedef std::unordered_map<int, std::string> C;
-        typedef std::pair<int, std::string> P;
-        P a[] =
-        {
-            P(1, "one"),
-            P(2, "two"),
-            P(3, "three"),
-            P(4, "four"),
-            P(1, "four"),
-            P(2, "four"),
-        };
-        C c;
-        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
-        assert(c.size() == 4);
-        assert(c.at(1) == "one");
-        assert(c.at(2) == "two");
-        assert(c.at(3) == "three");
-        assert(c.at(4) == "four");
-    }
-#if TEST_STD_VER >= 11
-    {
-        typedef std::unordered_map<int, std::string, std::hash<int>, std::equal_to<int>,
-                            min_allocator<std::pair<const int, std::string>>> C;
-        typedef std::pair<int, std::string> P;
-        P a[] =
-        {
-            P(1, "one"),
-            P(2, "two"),
-            P(3, "three"),
-            P(4, "four"),
-            P(1, "four"),
-            P(2, "four"),
-        };
-        C c;
-        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
-        assert(c.size() == 4);
-        assert(c.at(1) == "one");
-        assert(c.at(2) == "two");
-        assert(c.at(3) == "three");
-        assert(c.at(4) == "four");
-    }
-#endif
+int main(int, char**) {
+  // Note: we want to use a pair with non-const elements for input (an assignable type is a lot more convenient) but
+  // have to use the exact `value_type` of the map (that is, `pair<const K, V>`) for the allocator.
+  using Pair = std::pair<int, char>;
+  using ConstPair = std::pair<const int, char>;
+  for_all_iterators_and_allocators<ConstPair, const Pair*>([]<class Iter, class Sent, class Alloc>() {
+    test_map_set_insert_range<std::unordered_map<int, char, test_hash<int>, test_equal_to<int>, Alloc>, Pair, Iter, Sent>();
+  });
+
+  static_assert(test_map_constraints_insert_range<std::unordered_map, int, int, char, double>());
+
+  test_map_insert_range_move_only<std::unordered_map>();
+
+  test_map_insert_range_exception_safety_throwing_copy<std::unordered_map>();
+  test_unord_map_insert_range_exception_safety_throwing_allocator<std::unordered_map, int, int>();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp
index 527cc4c247e8ff..7e93032f0175b6 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/deduct.pass.cpp
@@ -54,13 +54,38 @@
 // unordered_multimap(initializer_list<pair<Key, T>>, typename see below::size_type, Hash,
 //                    Allocator)
 //   -> unordered_multimap<Key, T, Hash, equal_to<Key>, Allocator>;
+//
+// template<ranges::input_range R, class Hash = hash<range-key-type<R>>,
+//           class Pred = equal_to<range-key-type<R>>,
+//           class Allocator = allocator<range-to-alloc-type<R>>>
+//   unordered_multimap(from_range_t, R&&, typename see below::size_type = see below,
+//                 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+//     -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   unordered_multimap(from_range_t, R&&, typename see below::size_type, Allocator)
+//     -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+//                       equal_to<range-key-type<R>>, Allocator>;   // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   unordered_multimap(from_range_t, R&&, Allocator)
+//     -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>,
+//                       equal_to<range-key-type<R>>, Allocator>;   // C++23
+//
+// template<ranges::input_range R, class Hash, class Allocator>
+//   unordered_multimap(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+//     -> unordered_multimap<range-key-type<R>, range-mapped-type<R>, Hash,
+//                       equal_to<range-key-type<R>>, Allocator>;   // C++23
 
 #include <algorithm> // is_permutation
+#include <array>
 #include <cassert>
 #include <climits> // INT_MAX
 #include <type_traits>
 #include <unordered_map>
 
+#include "../../../test_compare.h"
+#include "../../../test_hash.h"
 #include "deduction_guides_sfinae_checks.h"
 #include "test_allocator.h"
 
@@ -219,6 +244,58 @@ int main(int, char**)
     ASSERT_SAME_TYPE(decltype(m2), std::unordered_multimap<int, int>);
     }
 
+#if TEST_STD_VER >= 23
+    {
+      using Range = std::array<P, 0>;
+      using Pred = test_equal_to<int>;
+      using DefaultPred = std::equal_to<int>;
+      using Hash = test_hash<int>;
+      using DefaultHash = std::hash<int>;
+      using Alloc = test_allocator<PC>;
+
+      { // (from_range, range)
+        std::unordered_multimap c(std::from_range, Range());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long>>);
+      }
+
+      { // (from_range, range, n)
+        std::unordered_multimap c(std::from_range, Range(), std::size_t());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long>>);
+      }
+
+      { // (from_range, range, n, hash)
+        std::unordered_multimap c(std::from_range, Range(), std::size_t(), Hash());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, Hash>>);
+      }
+
+      { // (from_range, range, n, hash, pred)
+        std::unordered_multimap c(std::from_range, Range(), std::size_t(), Hash(), Pred());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, Hash, Pred>>);
+      }
+
+      { // (from_range, range, n, hash, pred, alloc)
+        std::unordered_multimap c(std::from_range, Range(), std::size_t(), Hash(), Pred(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, Hash, Pred, Alloc>>);
+      }
+
+      { // (from_range, range, n, alloc)
+        std::unordered_multimap c(std::from_range, Range(), std::size_t(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, DefaultHash, DefaultPred, Alloc>>);
+      }
+
+      // TODO(LWG 2713): uncomment this test once the constructor is added.
+      { // (from_range, range, alloc)
+        //std::unordered_multimap c(std::from_range, Range(), Alloc());
+        //static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, DefaultHash, DefaultPred, Alloc>>);
+      }
+
+      { // (from_range, range, n, hash, alloc)
+        std::unordered_multimap c(std::from_range, Range(), std::size_t(), Hash(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multimap<int, long, Hash, DefaultPred, Alloc>>);
+      }
+    }
+#endif
+
     UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_multimap, std::unordered_multimap<int, long>>();
 
     return 0;

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/from_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/from_range.pass.cpp
new file mode 100644
index 00000000000000..4fe95da0103945
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/from_range.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<value_type> R>
+//   unordered_multimap(from_range_t, R&& rg, size_type n = see below,
+//     const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+//     const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
+//     : unordered_multimap(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered_multimap(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+//     : unordered_multimap(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
+
+#include <array>
+#include <unordered_map>
+
+#include "../../from_range_unordered_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+  using T = std::pair<const int, char>;
+  std::array input = {
+    T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+  };
+  auto c = std::unordered_multimap<int, char>(std::from_range, input);
+  assert(std::ranges::is_permutation(input, c));
+}
+
+int main(int, char**) {
+  using T = std::pair<const int, int>;
+  for_all_iterators_and_allocators<T>([]<class Iter, class Sent, class Alloc>() {
+    test_unordered_map<std::unordered_multimap, int, int, Iter, Sent, test_hash<int>, test_equal_to<int>, Alloc>();
+  });
+  test_unordered_map_move_only<std::unordered_multimap>();
+  test_duplicates();
+
+  static_assert(test_map_constraints<std::unordered_multimap, int, int, double, double>());
+
+  test_map_exception_safety_throwing_copy<std::unordered_multimap>();
+  test_map_exception_safety_throwing_allocator<std::unordered_multimap, int, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_iter_iter.pass.cpp
new file mode 100644
index 00000000000000..519ed7a7bf20ec
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_iter_iter.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <unordered_map>
+
+// template <class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>,
+//           class Alloc = allocator<pair<const Key, T>>>
+// class unordered_multimap
+
+// template <class InputIterator>
+//     void insert(InputIterator first, InputIterator last);
+
+#include <unordered_map>
+#include <string>
+#include <set>
+#include <cassert>
+#include <cstddef>
+
+#include "test_macros.h"
+#include "../../../check_consecutive.h"
+#include "test_iterators.h"
+#include "min_allocator.h"
+
+int main(int, char**)
+{
+    {
+        typedef std::unordered_multimap<int, std::string> C;
+        typedef std::pair<int, std::string> P;
+        P a[] =
+        {
+            P(1, "one"),
+            P(2, "two"),
+            P(3, "three"),
+            P(4, "four"),
+            P(1, "four"),
+            P(2, "four"),
+        };
+        C c;
+        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+        assert(c.size() == 6);
+        typedef std::pair<C::iterator, C::iterator> Eq;
+        Eq eq = c.equal_range(1);
+        assert(std::distance(eq.first, eq.second) == 2);
+        std::multiset<std::string> s;
+        s.insert("one");
+        s.insert("four");
+        CheckConsecutiveKeys<C::const_iterator>(c.find(1), c.end(), 1, s);
+        eq = c.equal_range(2);
+        assert(std::distance(eq.first, eq.second) == 2);
+        s.insert("two");
+        s.insert("four");
+        CheckConsecutiveKeys<C::const_iterator>(c.find(2), c.end(), 2, s);
+        eq = c.equal_range(3);
+        assert(std::distance(eq.first, eq.second) == 1);
+        C::iterator k = eq.first;
+        assert(k->first == 3);
+        assert(k->second == "three");
+        eq = c.equal_range(4);
+        assert(std::distance(eq.first, eq.second) == 1);
+        k = eq.first;
+        assert(k->first == 4);
+        assert(k->second == "four");
+        assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
+        assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
+    }
+#if TEST_STD_VER >= 11
+    {
+        typedef std::unordered_multimap<int, std::string, std::hash<int>, std::equal_to<int>,
+                            min_allocator<std::pair<const int, std::string>>> C;
+        typedef std::pair<int, std::string> P;
+        P a[] =
+        {
+            P(1, "one"),
+            P(2, "two"),
+            P(3, "three"),
+            P(4, "four"),
+            P(1, "four"),
+            P(2, "four"),
+        };
+        C c;
+        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+        assert(c.size() == 6);
+        typedef std::pair<C::iterator, C::iterator> Eq;
+        Eq eq = c.equal_range(1);
+        assert(std::distance(eq.first, eq.second) == 2);
+        std::multiset<std::string> s;
+        s.insert("one");
+        s.insert("four");
+        CheckConsecutiveKeys<C::const_iterator>(c.find(1), c.end(), 1, s);
+        eq = c.equal_range(2);
+        assert(std::distance(eq.first, eq.second) == 2);
+        s.insert("two");
+        s.insert("four");
+        CheckConsecutiveKeys<C::const_iterator>(c.find(2), c.end(), 2, s);
+        eq = c.equal_range(3);
+        assert(std::distance(eq.first, eq.second) == 1);
+        C::iterator k = eq.first;
+        assert(k->first == 3);
+        assert(k->second == "three");
+        eq = c.equal_range(4);
+        assert(std::distance(eq.first, eq.second) == 1);
+        k = eq.first;
+        assert(k->first == 4);
+        assert(k->second == "four");
+        assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
+        assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
+    }
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp
index 519ed7a7bf20ec..fcde119f487030 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/insert_range.pass.cpp
@@ -6,111 +6,36 @@
 //
 //===----------------------------------------------------------------------===//
 
-// <unordered_map>
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
 
-// template <class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>,
-//           class Alloc = allocator<pair<const Key, T>>>
-// class unordered_multimap
+// <unordered_map>
 
-// template <class InputIterator>
-//     void insert(InputIterator first, InputIterator last);
+// template<container-compatible-range<value_type> R>
+//   void insert_range(R&& rg); // C++23
 
 #include <unordered_map>
-#include <string>
-#include <set>
-#include <cassert>
-#include <cstddef>
 
+#include "../../../insert_range_maps_sets.h"
 #include "test_macros.h"
-#include "../../../check_consecutive.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
 
-int main(int, char**)
-{
-    {
-        typedef std::unordered_multimap<int, std::string> C;
-        typedef std::pair<int, std::string> P;
-        P a[] =
-        {
-            P(1, "one"),
-            P(2, "two"),
-            P(3, "three"),
-            P(4, "four"),
-            P(1, "four"),
-            P(2, "four"),
-        };
-        C c;
-        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
-        assert(c.size() == 6);
-        typedef std::pair<C::iterator, C::iterator> Eq;
-        Eq eq = c.equal_range(1);
-        assert(std::distance(eq.first, eq.second) == 2);
-        std::multiset<std::string> s;
-        s.insert("one");
-        s.insert("four");
-        CheckConsecutiveKeys<C::const_iterator>(c.find(1), c.end(), 1, s);
-        eq = c.equal_range(2);
-        assert(std::distance(eq.first, eq.second) == 2);
-        s.insert("two");
-        s.insert("four");
-        CheckConsecutiveKeys<C::const_iterator>(c.find(2), c.end(), 2, s);
-        eq = c.equal_range(3);
-        assert(std::distance(eq.first, eq.second) == 1);
-        C::iterator k = eq.first;
-        assert(k->first == 3);
-        assert(k->second == "three");
-        eq = c.equal_range(4);
-        assert(std::distance(eq.first, eq.second) == 1);
-        k = eq.first;
-        assert(k->first == 4);
-        assert(k->second == "four");
-        assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
-        assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
-    }
-#if TEST_STD_VER >= 11
-    {
-        typedef std::unordered_multimap<int, std::string, std::hash<int>, std::equal_to<int>,
-                            min_allocator<std::pair<const int, std::string>>> C;
-        typedef std::pair<int, std::string> P;
-        P a[] =
-        {
-            P(1, "one"),
-            P(2, "two"),
-            P(3, "three"),
-            P(4, "four"),
-            P(1, "four"),
-            P(2, "four"),
-        };
-        C c;
-        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
-        assert(c.size() == 6);
-        typedef std::pair<C::iterator, C::iterator> Eq;
-        Eq eq = c.equal_range(1);
-        assert(std::distance(eq.first, eq.second) == 2);
-        std::multiset<std::string> s;
-        s.insert("one");
-        s.insert("four");
-        CheckConsecutiveKeys<C::const_iterator>(c.find(1), c.end(), 1, s);
-        eq = c.equal_range(2);
-        assert(std::distance(eq.first, eq.second) == 2);
-        s.insert("two");
-        s.insert("four");
-        CheckConsecutiveKeys<C::const_iterator>(c.find(2), c.end(), 2, s);
-        eq = c.equal_range(3);
-        assert(std::distance(eq.first, eq.second) == 1);
-        C::iterator k = eq.first;
-        assert(k->first == 3);
-        assert(k->second == "three");
-        eq = c.equal_range(4);
-        assert(std::distance(eq.first, eq.second) == 1);
-        k = eq.first;
-        assert(k->first == 4);
-        assert(k->second == "four");
-        assert(static_cast<std::size_t>(std::distance(c.begin(), c.end())) == c.size());
-        assert(static_cast<std::size_t>(std::distance(c.cbegin(), c.cend())) == c.size());
-    }
-#endif
+int main(int, char**) {
+  // Note: we want to use a pair with non-const elements for input (an assignable type is a lot more convenient) but
+  // have to use the exact `value_type` of the map (that is, `pair<const K, V>`) for the allocator.
+  using Pair = std::pair<int, char>;
+  using ConstPair = std::pair<const int, char>;
+  for_all_iterators_and_allocators<ConstPair, const Pair*>([]<class Iter, class Sent, class Alloc>() {
+    test_map_set_insert_range<std::unordered_multimap<int, char, test_hash<int>, test_equal_to<int>, Alloc>, Pair, Iter, Sent>(/*allow_duplicates=*/true);
+  });
+
+  static_assert(test_map_constraints_insert_range<std::unordered_multimap, int, int, char, double>());
+
+  test_map_insert_range_move_only<std::unordered_multimap>();
+
+  test_map_insert_range_exception_safety_throwing_copy<std::unordered_multimap>();
+  test_unord_map_insert_range_exception_safety_throwing_allocator<std::unordered_multimap, int, int>();
 
   return 0;
 }
+

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/insert_iter_iter.pass.cpp
new file mode 100644
index 00000000000000..f893a9dfd0912b
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/insert_iter_iter.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <unordered_set>
+
+// template <class Value, class Hash = hash<Value>, class Pred = equal_to<Value>,
+//           class Alloc = allocator<Value>>
+// class unordered_multiset
+
+// template <class InputIterator>
+//     void insert(InputIterator first, InputIterator last);
+
+#include <unordered_set>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "min_allocator.h"
+
+int main(int, char**)
+{
+    {
+        typedef std::unordered_multiset<int> C;
+        typedef int P;
+        P a[] =
+        {
+            P(1),
+            P(2),
+            P(3),
+            P(4),
+            P(1),
+            P(2)
+        };
+        C c;
+        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+        assert(c.size() == 6);
+        assert(c.count(1) == 2);
+        assert(c.count(2) == 2);
+        assert(c.count(3) == 1);
+        assert(c.count(4) == 1);
+    }
+#if TEST_STD_VER >= 11
+    {
+        typedef std::unordered_multiset<int, std::hash<int>,
+                                      std::equal_to<int>, min_allocator<int>> C;
+        typedef int P;
+        P a[] =
+        {
+            P(1),
+            P(2),
+            P(3),
+            P(4),
+            P(1),
+            P(2)
+        };
+        C c;
+        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+        assert(c.size() == 6);
+        assert(c.count(1) == 2);
+        assert(c.count(2) == 2);
+        assert(c.count(3) == 1);
+        assert(c.count(4) == 1);
+    }
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp
index f893a9dfd0912b..73ac4cf071e1bb 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/insert_range.pass.cpp
@@ -6,67 +6,32 @@
 //
 //===----------------------------------------------------------------------===//
 
-// <unordered_set>
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
 
-// template <class Value, class Hash = hash<Value>, class Pred = equal_to<Value>,
-//           class Alloc = allocator<Value>>
-// class unordered_multiset
+// <set>
 
-// template <class InputIterator>
-//     void insert(InputIterator first, InputIterator last);
+// template<container-compatible-range<value_type> R>
+//   void insert_range(R&& rg); // C++23
 
 #include <unordered_set>
-#include <cassert>
 
+#include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
 
-int main(int, char**)
-{
-    {
-        typedef std::unordered_multiset<int> C;
-        typedef int P;
-        P a[] =
-        {
-            P(1),
-            P(2),
-            P(3),
-            P(4),
-            P(1),
-            P(2)
-        };
-        C c;
-        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
-        assert(c.size() == 6);
-        assert(c.count(1) == 2);
-        assert(c.count(2) == 2);
-        assert(c.count(3) == 1);
-        assert(c.count(4) == 1);
-    }
-#if TEST_STD_VER >= 11
-    {
-        typedef std::unordered_multiset<int, std::hash<int>,
-                                      std::equal_to<int>, min_allocator<int>> C;
-        typedef int P;
-        P a[] =
-        {
-            P(1),
-            P(2),
-            P(3),
-            P(4),
-            P(1),
-            P(2)
-        };
-        C c;
-        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
-        assert(c.size() == 6);
-        assert(c.count(1) == 2);
-        assert(c.count(2) == 2);
-        assert(c.count(3) == 1);
-        assert(c.count(4) == 1);
-    }
-#endif
+int main(int, char**) {
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_map_set_insert_range<std::unordered_multiset<int, test_hash<int>, test_equal_to<int>, Alloc>, int, Iter, Sent>(/*allow_duplicates=*/true);
+  });
+
+  static_assert(test_set_constraints_insert_range<std::unordered_multiset, int, double>());
+
+  test_set_insert_range_move_only<std::unordered_multiset>();
+
+  test_set_insert_range_exception_safety_throwing_copy<std::unordered_multiset>();
+  test_unord_set_insert_range_exception_safety_throwing_allocator<std::unordered_multiset, int>();
 
   return 0;
 }
+

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp
index 912a6d8653259e..72448875cda758 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/deduct.pass.cpp
@@ -46,13 +46,38 @@
 // template<class T, class Hash, class Allocator>
 // unordered_multiset(initializer_list<T>, typename see below::size_type, Hash, Allocator)
 //   -> unordered_multiset<T, Hash, equal_to<T>, Allocator>;
+//
+// template<ranges::input_range R,
+//          class Hash = hash<ranges::range_value_t<R>>,
+//          class Pred = equal_to<ranges::range_value_t<R>>,
+//          class Allocator = allocator<ranges::range_value_t<R>>>
+//   unordered_multiset(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+//     -> unordered_multiset<ranges::range_value_t<R>, Hash, Pred, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   unordered_multiset(from_range_t, R&&, typename see below::size_type, Allocator)
+//     -> unordered_multiset<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+//                       equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   unordered_multiset(from_range_t, R&&, Allocator)
+//     -> unordered_multiset<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+//                       equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Hash, class Allocator>
+//   unordered_multiset(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+//     -> unordered_multiset<ranges::range_value_t<R>, Hash,
+//                       equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
 
 #include <algorithm> // is_permutation
+#include <array>
 #include <cassert>
 #include <climits> // INT_MAX
 #include <type_traits>
 #include <unordered_set>
 
+#include "../../../test_compare.h"
+#include "../../../test_hash.h"
 #include "deduction_guides_sfinae_checks.h"
 #include "test_allocator.h"
 
@@ -193,6 +218,57 @@ int main(int, char**)
     assert(s.get_allocator().get_id() == 42);
     }
 
+#if TEST_STD_VER >= 23
+    {
+      using Range = std::array<int, 0>;
+      using Pred = test_equal_to<int>;
+      using DefaultPred = std::equal_to<int>;
+      using Hash = test_hash<int>;
+      using DefaultHash = std::hash<int>;
+      using Alloc = test_allocator<int>;
+
+      { // (from_range, range)
+        std::unordered_multiset c(std::from_range, Range());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int>>);
+      }
+
+      { // (from_range, range, n)
+        std::unordered_multiset c(std::from_range, Range(), std::size_t());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int>>);
+      }
+
+      { // (from_range, range, n, hash)
+        std::unordered_multiset c(std::from_range, Range(), std::size_t(), Hash());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, Hash>>);
+      }
+
+      { // (from_range, range, n, hash, pred)
+        std::unordered_multiset c(std::from_range, Range(), std::size_t(), Hash(), Pred());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, Hash, Pred>>);
+      }
+
+      { // (from_range, range, n, hash, pred, alloc)
+        std::unordered_multiset c(std::from_range, Range(), std::size_t(), Hash(), Pred(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, Hash, Pred, Alloc>>);
+      }
+
+      { // (from_range, range, n, alloc)
+        std::unordered_multiset c(std::from_range, Range(), std::size_t(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, DefaultHash, DefaultPred, Alloc>>);
+      }
+
+      // TODO(LWG 2713): uncomment this test once the constructor is added.
+      { // (from_range, range, alloc)
+        //std::unordered_multiset c(std::from_range, Range(), Alloc());
+        //static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, DefaultHash, DefaultPred, Alloc>>);
+      }
+
+      { // (from_range, range, n, hash, alloc)
+        std::unordered_multiset c(std::from_range, Range(), std::size_t(), Hash(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_multiset<int, Hash, DefaultPred, Alloc>>);
+      }
+    }
+#endif
     UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_multiset, std::unordered_multiset<int>>();
 
     return 0;

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/from_range.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/from_range.pass.cpp
new file mode 100644
index 00000000000000..918e7d85e99eb0
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/from_range.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<value_type> R>
+//   unordered_multiset(from_range_t, R&& rg, size_type n = see below,
+//     const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+//     const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
+//     : unordered_multiset(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered_multiset(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+//     : unordered_multiset(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
+
+#include <array>
+#include <unordered_set>
+
+#include "../../from_range_unordered_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+  std::array input = {1, 2, 3, 3, 3, 4, 2, 1, 2};
+  auto c = std::unordered_multiset<int>(std::from_range, input);
+  assert(std::ranges::is_permutation(input, c));
+}
+
+int main(int, char**) {
+  for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+    test_unordered_set<std::unordered_multiset, int, Iter, Sent, test_hash<int>, test_equal_to<int>, Alloc>();
+  });
+  test_unordered_set_move_only<std::unordered_multiset>();
+
+  static_assert(test_set_constraints<std::unordered_set, int, double>());
+
+  test_set_exception_safety_throwing_copy<std::unordered_multiset>();
+  test_set_exception_safety_throwing_allocator<std::unordered_multiset, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/unord/unord.set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/unord/unord.set/insert_iter_iter.pass.cpp
new file mode 100644
index 00000000000000..451ee6767100a5
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/insert_iter_iter.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <unordered_set>
+
+// template <class Value, class Hash = hash<Value>, class Pred = equal_to<Value>,
+//           class Alloc = allocator<Value>>
+// class unordered_set
+
+// template <class InputIterator>
+//     void insert(InputIterator first, InputIterator last);
+
+#include <unordered_set>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "min_allocator.h"
+
+int main(int, char**)
+{
+    {
+        typedef std::unordered_set<int> C;
+        typedef int P;
+        P a[] =
+        {
+            P(1),
+            P(2),
+            P(3),
+            P(4),
+            P(1),
+            P(2)
+        };
+        C c;
+        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+        assert(c.size() == 4);
+        assert(c.count(1) == 1);
+        assert(c.count(2) == 1);
+        assert(c.count(3) == 1);
+        assert(c.count(4) == 1);
+    }
+#if TEST_STD_VER >= 11
+    {
+        typedef std::unordered_set<int, std::hash<int>,
+                                      std::equal_to<int>, min_allocator<int>> C;
+        typedef int P;
+        P a[] =
+        {
+            P(1),
+            P(2),
+            P(3),
+            P(4),
+            P(1),
+            P(2)
+        };
+        C c;
+        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
+        assert(c.size() == 4);
+        assert(c.count(1) == 1);
+        assert(c.count(2) == 1);
+        assert(c.count(3) == 1);
+        assert(c.count(4) == 1);
+    }
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp b/libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp
index 451ee6767100a5..c6306a28b7adb5 100644
--- a/libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/insert_range.pass.cpp
@@ -6,67 +6,31 @@
 //
 //===----------------------------------------------------------------------===//
 
-// <unordered_set>
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
+// ADDITIONAL_COMPILE_FLAGS: -Wno-missing-field-initializers
 
-// template <class Value, class Hash = hash<Value>, class Pred = equal_to<Value>,
-//           class Alloc = allocator<Value>>
-// class unordered_set
+// <set>
 
-// template <class InputIterator>
-//     void insert(InputIterator first, InputIterator last);
+// template<container-compatible-range<value_type> R>
+//   void insert_range(R&& rg); // C++23
 
 #include <unordered_set>
-#include <cassert>
 
+#include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
 
-int main(int, char**)
-{
-    {
-        typedef std::unordered_set<int> C;
-        typedef int P;
-        P a[] =
-        {
-            P(1),
-            P(2),
-            P(3),
-            P(4),
-            P(1),
-            P(2)
-        };
-        C c;
-        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
-        assert(c.size() == 4);
-        assert(c.count(1) == 1);
-        assert(c.count(2) == 1);
-        assert(c.count(3) == 1);
-        assert(c.count(4) == 1);
-    }
-#if TEST_STD_VER >= 11
-    {
-        typedef std::unordered_set<int, std::hash<int>,
-                                      std::equal_to<int>, min_allocator<int>> C;
-        typedef int P;
-        P a[] =
-        {
-            P(1),
-            P(2),
-            P(3),
-            P(4),
-            P(1),
-            P(2)
-        };
-        C c;
-        c.insert(cpp17_input_iterator<P*>(a), cpp17_input_iterator<P*>(a + sizeof(a)/sizeof(a[0])));
-        assert(c.size() == 4);
-        assert(c.count(1) == 1);
-        assert(c.count(2) == 1);
-        assert(c.count(3) == 1);
-        assert(c.count(4) == 1);
-    }
-#endif
+int main(int, char**) {
+  for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
+    test_map_set_insert_range<std::unordered_set<int, test_hash<int>, test_equal_to<int>, Alloc>, int, Iter, Sent>();
+  });
+
+  static_assert(test_set_constraints_insert_range<std::unordered_set, int, double>());
+
+  test_set_insert_range_move_only<std::unordered_set>();
+
+  test_set_insert_range_exception_safety_throwing_copy<std::unordered_set>();
+  test_unord_set_insert_range_exception_safety_throwing_allocator<std::unordered_set, int>();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp
index 13c541692d1ba1..13e42f37fd21f2 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/deduct.pass.cpp
@@ -46,13 +46,38 @@
 // template<class T, class Hash, class Allocator>
 // unordered_set(initializer_list<T>, typename see below::size_type, Hash, Allocator)
 //   -> unordered_set<T, Hash, equal_to<T>, Allocator>;
+//
+// template<ranges::input_range R,
+//          class Hash = hash<ranges::range_value_t<R>>,
+//          class Pred = equal_to<ranges::range_value_t<R>>,
+//          class Allocator = allocator<ranges::range_value_t<R>>>
+//   unordered_set(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator())
+//     -> unordered_set<ranges::range_value_t<R>, Hash, Pred, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   unordered_set(from_range_t, R&&, typename see below::size_type, Allocator)
+//     -> unordered_set<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+//                       equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Allocator>
+//   unordered_set(from_range_t, R&&, Allocator)
+//     -> unordered_set<ranges::range_value_t<R>, hash<ranges::range_value_t<R>>,
+//                       equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
+//
+// template<ranges::input_range R, class Hash, class Allocator>
+//   unordered_set(from_range_t, R&&, typename see below::size_type, Hash, Allocator)
+//     -> unordered_set<ranges::range_value_t<R>, Hash,
+//                       equal_to<ranges::range_value_t<R>>, Allocator>; // C++23
 
 #include <algorithm> // is_permutation
+#include <array>
 #include <cassert>
 #include <climits> // INT_MAX
 #include <type_traits>
 #include <unordered_set>
 
+#include "../../../test_compare.h"
+#include "../../../test_hash.h"
 #include "deduction_guides_sfinae_checks.h"
 #include "test_allocator.h"
 
@@ -193,6 +218,58 @@ int main(int, char**)
     assert(s.get_allocator().get_id() == 42);
     }
 
+#if TEST_STD_VER >= 23
+    {
+      using Range = std::array<int, 0>;
+      using Pred = test_equal_to<int>;
+      using DefaultPred = std::equal_to<int>;
+      using Hash = test_hash<int>;
+      using DefaultHash = std::hash<int>;
+      using Alloc = test_allocator<int>;
+
+      { // (from_range, range)
+        std::unordered_set c(std::from_range, Range());
+        static_assert(std::is_same_v<decltype(c), std::unordered_set<int>>);
+      }
+
+      { // (from_range, range, n)
+        std::unordered_set c(std::from_range, Range(), std::size_t());
+        static_assert(std::is_same_v<decltype(c), std::unordered_set<int>>);
+      }
+
+      { // (from_range, range, n, hash)
+        std::unordered_set c(std::from_range, Range(), std::size_t(), Hash());
+        static_assert(std::is_same_v<decltype(c), std::unordered_set<int, Hash>>);
+      }
+
+      { // (from_range, range, n, hash, pred)
+        std::unordered_set c(std::from_range, Range(), std::size_t(), Hash(), Pred());
+        static_assert(std::is_same_v<decltype(c), std::unordered_set<int, Hash, Pred>>);
+      }
+
+      { // (from_range, range, n, hash, pred, alloc)
+        std::unordered_set c(std::from_range, Range(), std::size_t(), Hash(), Pred(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_set<int, Hash, Pred, Alloc>>);
+      }
+
+      { // (from_range, range, n, alloc)
+        std::unordered_set c(std::from_range, Range(), std::size_t(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_set<int, DefaultHash, DefaultPred, Alloc>>);
+      }
+
+      // TODO(LWG 2713): uncomment this test once the constructor is added.
+      { // (from_range, range, alloc)
+        //std::unordered_set c(std::from_range, Range(), Alloc());
+        //static_assert(std::is_same_v<decltype(c), std::unordered_set<int, DefaultHash, DefaultPred, Alloc>>);
+      }
+
+      { // (from_range, range, n, hash, alloc)
+        std::unordered_set c(std::from_range, Range(), std::size_t(), Hash(), Alloc());
+        static_assert(std::is_same_v<decltype(c), std::unordered_set<int, Hash, DefaultPred, Alloc>>);
+      }
+    }
+#endif
+
     UnorderedContainerDeductionGuidesSfinaeAway<std::unordered_set, std::unordered_set<int>>();
 
     return 0;

diff  --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/from_range.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/from_range.pass.cpp
new file mode 100644
index 00000000000000..581781949365e8
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/from_range.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<value_type> R>
+//   unordered_set(from_range_t, R&& rg, size_type n = see below,
+//     const hasher& hf = hasher(), const key_equal& eql = key_equal(),
+//     const allocator_type& a = allocator_type()); // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
+//     : unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
+//
+// template<container-compatible-range<value_type> R>
+//   unordered_set(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
+//     : unordered_set(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
+
+#include <array>
+#include <unordered_set>
+
+#include "../../from_range_unordered_containers.h"
+#include "test_macros.h"
+
+void test_duplicates() {
+  using T = KeyValue;
+
+  std::array input = {
+    T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}
+  };
+  std::array expected = {
+    T{1, 'a'}, T{2, 'b'}, T{3, 'c'}, T{4, 'a'}
+  };
+  auto c = std::unordered_set<T>(std::from_range, input);
+  assert(std::ranges::is_permutation(expected, c));
+}
+
+int main(int, char**) {
+  for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
+    test_unordered_set<std::unordered_set, int, Iter, Sent, test_hash<int>, test_equal_to<int>, Alloc>();
+  });
+  test_unordered_set_move_only<std::unordered_set>();
+  test_duplicates();
+
+  static_assert(test_set_constraints<std::unordered_set, int, double>());
+
+  test_set_exception_safety_throwing_copy<std::unordered_set>();
+  test_set_exception_safety_throwing_allocator<std::unordered_set, int>();
+
+  return 0;
+}

diff  --git a/libcxx/test/support/deduction_guides_sfinae_checks.h b/libcxx/test/support/deduction_guides_sfinae_checks.h
index dcc41aa1b4ba7b..8d1365848eafc4 100644
--- a/libcxx/test/support/deduction_guides_sfinae_checks.h
+++ b/libcxx/test/support/deduction_guides_sfinae_checks.h
@@ -161,8 +161,8 @@ constexpr void ContainerAdaptorDeductionGuidesSfinaeAway() {
 // - "bad" input iterators (that is, a type not qualifying as an input
 //   iterator);
 // - a bad allocator;
-// - an allocator in place of a comparator.
-
+// - an allocator in place of a comparator;
+// - a range not satisfying the `input_range` concept.
 template<template<typename ...> class Container, typename InstantiatedContainer>
 constexpr void AssociativeContainerDeductionGuidesSfinaeAway() {
   using ValueType = typename InstantiatedContainer::value_type;
@@ -224,6 +224,42 @@ constexpr void AssociativeContainerDeductionGuidesSfinaeAway() {
   // Note: (init_list, BAD_alloc) is interpreted as (init_list, comp) instead
   // and fails upon instantiation. There is no requirement to SFINAE away bad
   // comparators.
+
+#if TEST_STD_VER >= 23
+  using Range = RangeT<ValueType>;
+  using BadRange = BadRangeT<ValueType>;
+
+  // (from_range, range)
+  //
+  // Can deduce from (from_range, range)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range>);
+  // Cannot deduce from (from_range, BAD_range)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange>);
+
+  // (from_range, range, comp)
+  //
+  // Can deduce from (from_range, _range, comp)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, Comp>);
+  // Cannot deduce from (from_range, BAD_range, comp)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, Comp>);
+
+  // (from_range, range, comp, alloc)
+  //
+  // Can deduce from (from_range, range, comp, alloc)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, Comp, Alloc>);
+  // Cannot deduce from (from_range, BAD_range, comp, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, Comp, Alloc>);
+  // Cannot deduce from (from_range, range, comp, BAD_alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, Range, Comp, BadAlloc>);
+
+  // (from_range, range, alloc)
+  //
+  // Can deduce from (from_range, range, alloc)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, Alloc>);
+  // Cannot deduce from (from_range, BAD_range, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, Alloc>);
+  // Note: (from_range, range, BAD_alloc) is interpreted as (from_range, range, comp) instead.
+#endif
 }
 
 // For unordered containers the deduction guides should be SFINAE'd away when
@@ -233,7 +269,8 @@ constexpr void AssociativeContainerDeductionGuidesSfinaeAway() {
 // - a bad allocator;
 // - a bad hash functor (an integral type in place of a hash);
 // - an allocator in place of a hash functor;
-// - an allocator in place of a predicate.
+// - an allocator in place of a predicate;
+// - a range not satisfying the `input_range` concept.
 template<template<typename ...> class Container, typename InstantiatedContainer>
 constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
   using ValueType = typename InstantiatedContainer::value_type;
@@ -243,7 +280,7 @@ constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
   using Iter = ValueType*;
   using InitList = std::initializer_list<ValueType>;
 
-  using BadHash = int;
+  using BadHash = short;
   struct BadAlloc {};
   // The only requirement in the Standard is that integral types cannot be
   // considered input iterators, beyond that it is unspecified.
@@ -298,8 +335,7 @@ constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
   LIBCPP_STATIC_ASSERT(SFINAEs_away<Container, OutputIter, OutputIter,
       std::size_t, Hash, Pred, Alloc>);
   // Cannot deduce from (iter, iter, buckets, BAD_hash, pred, alloc)
-  static_assert(
-      SFINAEs_away<Container, Iter, Iter, std::size_t, BadHash, Pred, Alloc>);
+  static_assert(SFINAEs_away<Container, Iter, Iter, std::size_t, BadHash, Pred, Alloc>);
   // Cannot deduce from (iter, iter, buckets, ALLOC_as_hash, pred, alloc)
   static_assert(
       SFINAEs_away<Container, Iter, Iter, std::size_t, AllocAsHash, Pred, Alloc>);
@@ -337,8 +373,7 @@ constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
   // Cannot deduce from (iter, iter, buckets, BAD_hash, alloc)
   static_assert(SFINAEs_away<Container, Iter, Iter, std::size_t, BadHash, Alloc>);
   // Cannot deduce from (iter, iter, buckets, ALLOC_as_hash, alloc)
-  static_assert(
-      SFINAEs_away<Container, Iter, Iter, std::size_t, AllocAsHash, Alloc>);
+  static_assert(SFINAEs_away<Container, Iter, Iter, std::size_t, AllocAsHash, Alloc>);
   // Note: (iter, iter, buckets, hash, BAD_alloc) is interpreted as (iter, iter,
   // buckets, hash, pred), which is valid because there are no requirements for
   // the predicate.
@@ -391,6 +426,87 @@ constexpr void UnorderedContainerDeductionGuidesSfinaeAway() {
   //
   // Cannot deduce from (init_list, BAD_alloc)
   static_assert(SFINAEs_away<Container, InitList, BadAlloc>);
+
+#if TEST_STD_VER >= 23
+  using Range = RangeT<ValueType>;
+  using BadRange = BadRangeT<ValueType>;
+
+  // (from_range, range)
+  //
+  // Can deduce from (from_range, range)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range>);
+  // Cannot deduce from (from_range, BAD_range)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange>);
+
+  // (from_range, range, buckets)
+  //
+  // Can deduce from (from_range, range, buckets)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t>);
+  // Cannot deduce from (from_range, BAD_range, buckets)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t>);
+
+  // (from_range, range, buckets, hash)
+  //
+  // Can deduce from (from_range, range, buckets, hash)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash>);
+  // Cannot deduce from (from_range, BAD_range, buckets, hash)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Hash>);
+  // Cannot deduce from (from_range, range, buckets, BAD_hash)
+  static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, BadHash>);
+
+  // (from_range, range, buckets, hash, pred)
+  //
+  // Can deduce from (from_range, range, buckets, hash, pred)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash, Pred>);
+  // Cannot deduce from (from_range, BAD_range, buckets, hash, pred)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Hash, Pred>);
+  // Cannot deduce from (from_range, range, buckets, BAD_hash, pred)
+  static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, BadHash, Pred>);
+
+  // (from_range, range, buckets, hash, pred, alloc)
+  //
+  // Can deduce from (from_range, range, buckets, hash, pred, alloc)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash, Pred, Alloc>);
+  // Cannot deduce from (from_range, BAD_range, buckets, hash, pred, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Hash, Pred, Alloc>);
+  // Cannot deduce from (from_range, range, buckets, BAD_hash, pred, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, BadHash, Pred, Alloc>);
+  // Cannot deduce from (from_range, range, buckets, hash, pred, BAD_alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash, Pred, BadAlloc>);
+
+  // (from_range, range, buckets, alloc)
+  //
+  // Can deduce from (from_range, range, buckets, alloc)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Alloc>);
+  // Cannot deduce from (from_range, BAD_range, buckets, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Alloc>);
+  // Note: (from_range, range, buckets, BAD_alloc) is interpreted as (from_range, range, buckets, hash), which is valid
+  // because the only requirement for the hash parameter is that it's not integral.
+
+  // (from_range, range, alloc)
+  //
+  // Can deduce from (from_range, range, alloc)
+  // TODO(LWG 2713): uncomment this test once the constructor is added.
+  // static_assert(!SFINAEs_away<Container, std::from_range_t, Range, Alloc>);
+  // Cannot deduce from (from_range, BAD_range, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, Alloc>);
+  // Cannot deduce from (from_range, range, BAD_alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, Range, BadAlloc>);
+
+  // (from_range, range, buckets, hash, alloc)
+  //
+  // Can deduce from (from_range, range, buckets, hash, alloc)
+  static_assert(!SFINAEs_away<Container, std::from_range_t, Range, std::size_t, Hash, Alloc>);
+  // Cannot deduce from (from_range, BAD_range, buckets, hash, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, BadRange, std::size_t, Hash, Alloc>);
+  // Cannot deduce from (from_range, range, buckets, BAD_hash, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, BadHash, Alloc>);
+  // Cannot deduce from (from_range, range, buckets, ALLOC_as_hash, alloc)
+  static_assert(SFINAEs_away<Container, std::from_range_t, Range, std::size_t, AllocAsHash, Alloc>);
+  // Cannot deduce from (from_range, range, buckets, hash, BAD_alloc)
+  // Note: (from_range, range, buckets, hash, BAD_alloc) is interpreted as (from_range, range, buckets, hash, pred),
+  // which is valid because the only requirement for the predicate parameter is that it does not resemble an allocator.
+#endif
 }
 
 #endif // TEST_SUPPORT_DEDUCTION_GUIDES_SFINAE_CHECKS_H


        


More information about the libcxx-commits mailing list