https://gcc.gnu.org/g:f46eccc9409136831225e30017a1d8ba6654a9b9

commit r17-908-gf46eccc9409136831225e30017a1d8ba6654a9b9
Author: Patrick Palka <[email protected]>
Date:   Thu May 28 10:39:29 2026 -0400

    libstdc++: Implement P3567R2 flat_meow fixes
    
    This implements the changes in sections 5, 6 and 8 of P3567R2; the other
    changes (in section 4 and 7) are effectively already implemented.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/version.def (flat_map): Bump to 202511.
            (flat_set): Likewise.
            * include/bits/version.h: Regenerate.
            * include/std/flat_map (_Flat_map_impl): Remove
            is_nothrow_swappable_v assertions.
            (_Flat_map_impl::_Flat_map_impl): Explicitly default copy ctor.
            Define move ctor with corrected exception handling as per
            P3567R2.
            (_Flat_map_impl::operator=): Likewise.
            (_Flat_map_impl::insert_range): Define new __sorted_t overload
            as per P3567R2.
            (_Flat_map_impl::swap): Make conditionally noexcept as per
            P3567R2.
            * include/std/flat_set (_Flat_set_impl): Remove
            is_nothrow_swappable_v assertion.
            (_Flat_set_impl::_Flat_set_impl): Explicitly default copy ctor.
            Define move ctor with correct invariant preserving behavior as
            per P3567R2.
            (_Flat_set_impl::operator=): Likewise.
            (_Flat_set_impl::_M_insert_range): Factored out from
            insert_range.  Add bool parameter __is_sorted defaulted to
            false.
            (_Flat_set_impl::insert_range): Define new __sorted_t overload
            as per P3567R2.
            (_Flat_set_impl::swap): Make conditionally noexcept as per
            P3567R2.  Correct to use ranges::swap instead of ADL swap.
            * testsuite/23_containers/flat_map/1.cc (test11, test12):
            New tests.
            * testsuite/23_containers/flat_multimap/1.cc (test10, test11):
            New tests.
            * testsuite/23_containers/flat_multiset/1.cc (test10, test11):
            New tests.
            * testsuite/23_containers/flat_set/1.cc (test10, test11):
            New tests.
    
    Reviewed-by: Tomasz KamiƄski <[email protected]>
    Reviewed-by: Jonathan Wakely <[email protected]>

Diff:
---
 libstdc++-v3/include/bits/version.def              |   9 +-
 libstdc++-v3/include/bits/version.h                |   8 +-
 libstdc++-v3/include/std/flat_map                  |  54 +++++++++--
 libstdc++-v3/include/std/flat_set                  |  69 ++++++++++++--
 libstdc++-v3/testsuite/23_containers/flat_map/1.cc | 104 ++++++++++++++++++++-
 .../testsuite/23_containers/flat_multimap/1.cc     | 102 ++++++++++++++++++++
 .../testsuite/23_containers/flat_multiset/1.cc     |  94 +++++++++++++++++++
 libstdc++-v3/testsuite/23_containers/flat_set/1.cc |  96 ++++++++++++++++++-
 8 files changed, 513 insertions(+), 23 deletions(-)

diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 2f32a8bda98f..97f7d6865190 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1898,16 +1898,21 @@ ftms = {
 
 ftms = {
   name = flat_map;
+  // 202207 P0429R9 A Standard flat_map
+  // 202511 P3567R2 flat_meow Fixes
   values = {
-    v = 202207;
+    v = 202511; // P3567R2 applied as DR for C++23
     cxxmin = 23;
   };
 };
 
 ftms = {
   name = flat_set;
+  // 202207 P1222R4 A Standard flat_set
+  //        LWG3751 Missing feature macro for flat_set
+  // 202511 P3567R2 flat_meow Fixes
   values = {
-    v = 202207;
+    v = 202511; // P3567R2 applied as DR for C++23
     cxxmin = 23;
   };
 };
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 517b9020ec65..c31893e34c4e 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2108,9 +2108,9 @@
 
 #if !defined(__cpp_lib_flat_map)
 # if (__cplusplus >= 202100L)
-#  define __glibcxx_flat_map 202207L
+#  define __glibcxx_flat_map 202511L
 #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_flat_map)
-#   define __cpp_lib_flat_map 202207L
+#   define __cpp_lib_flat_map 202511L
 #  endif
 # endif
 #endif /* !defined(__cpp_lib_flat_map) */
@@ -2118,9 +2118,9 @@
 
 #if !defined(__cpp_lib_flat_set)
 # if (__cplusplus >= 202100L)
-#  define __glibcxx_flat_set 202207L
+#  define __glibcxx_flat_set 202511L
 #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_flat_set)
-#   define __cpp_lib_flat_set 202207L
+#   define __cpp_lib_flat_set 202511L
 #  endif
 # endif
 #endif /* !defined(__cpp_lib_flat_set) */
diff --git a/libstdc++-v3/include/std/flat_map 
b/libstdc++-v3/include/std/flat_map
index a51c6625d54a..f06c75a16f65 100644
--- a/libstdc++-v3/include/std/flat_map
+++ b/libstdc++-v3/include/std/flat_map
@@ -74,9 +74,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static_assert(is_same_v<_Key, typename _KeyContainer::value_type>);
       static_assert(is_same_v<_Tp, typename _MappedContainer::value_type>);
 
-      static_assert(is_nothrow_swappable_v<_KeyContainer>);
-      static_assert(is_nothrow_swappable_v<_MappedContainer>);
-
       using _Derived = __conditional_t<_Multi,
                                       flat_multimap<_Key, _Tp, _Compare,
                                                     _KeyContainer, 
_MappedContainer>,
@@ -395,6 +392,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        : _Flat_map_impl(__s, __il.begin(), __il.end(), __comp, __a)
        { }
 
+      _Flat_map_impl(const _Flat_map_impl&) = default;
+      _Flat_map_impl& operator=(const _Flat_map_impl&) = default;
+
+      _GLIBCXX26_CONSTEXPR
+      _Flat_map_impl(_Flat_map_impl&& __other)
+      noexcept(is_nothrow_move_constructible_v<containers>
+              && is_nothrow_move_constructible_v<key_compare>)
+#if __cpp_exceptions
+      try
+#endif
+      : _M_cont(std::move(__other._M_cont)), 
_M_comp(std::move(__other._M_comp))
+      { __other.clear(); }
+#if __cpp_exceptions
+      catch (...)
+      { __other.clear(); }
+#endif
+
+      _GLIBCXX26_CONSTEXPR
+      _Flat_map_impl&
+      operator=(_Flat_map_impl&& __other)
+      noexcept(is_nothrow_move_assignable_v<containers>
+              && is_nothrow_move_assignable_v<key_compare>)
+      {
+       auto __guard = _M_make_clear_guard();
+       auto __guard_other = _ClearGuard{__other._M_cont};
+       _M_cont = std::move(__other._M_cont);
+       _M_comp = std::move(__other._M_comp);
+       __guard._M_disable();
+       // __guard_other._M_disable is deliberately not called.
+       return *this;
+      }
+
       _GLIBCXX26_CONSTEXPR
       _Derived&
       operator=(initializer_list<value_type> __il)
@@ -644,6 +673,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        insert_range(_Rg&& __rg)
        { _M_insert(ranges::begin(__rg), ranges::end(__rg)); }
 
+      template<__detail::__container_compatible_range<value_type> _Rg>
+       _GLIBCXX26_CONSTEXPR
+       void
+       insert_range(__sorted_t, _Rg&& __rg)
+       { _M_insert(ranges::begin(__rg), ranges::end(__rg), true); }
+
       _GLIBCXX26_CONSTEXPR
       void
       insert(initializer_list<value_type> __il)
@@ -728,11 +763,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       _GLIBCXX26_CONSTEXPR
       void
-      swap(_Derived& __y) noexcept
+      swap(_Derived& __y)
+      noexcept(is_nothrow_swappable_v<key_container_type>
+              && is_nothrow_swappable_v<mapped_container_type>
+              && is_nothrow_swappable_v<key_compare>)
       {
-       ranges::swap(_M_comp, __y._M_comp);
+       auto __guard = _M_make_clear_guard();
+       auto __guard_y = _ClearGuard{__y._M_cont};
        ranges::swap(_M_cont.keys, __y._M_cont.keys);
        ranges::swap(_M_cont.values, __y._M_cont.values);
+       ranges::swap(_M_comp, __y._M_comp);
+       __guard._M_disable();
+       __guard_y._M_disable();
       }
 
       _GLIBCXX26_CONSTEXPR
@@ -974,7 +1016,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       friend _GLIBCXX26_CONSTEXPR void
-      swap(_Derived& __x, _Derived& __y) noexcept
+      swap(_Derived& __x, _Derived& __y) noexcept(noexcept(__x.swap(__y)))
       { return __x.swap(__y); }
 
       template<typename _Predicate>
diff --git a/libstdc++-v3/include/std/flat_set 
b/libstdc++-v3/include/std/flat_set
index 286290896ae0..e92559949ebb 100644
--- a/libstdc++-v3/include/std/flat_set
+++ b/libstdc++-v3/include/std/flat_set
@@ -71,7 +71,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class _Flat_set_impl
     {
       static_assert(is_same_v<_Key, typename _KeyContainer::value_type>);
-      static_assert(is_nothrow_swappable_v<_KeyContainer>);
 
       using _Derived = __conditional_t<_Multi,
                                       flat_multiset<_Key, _Compare, 
_KeyContainer>,
@@ -324,6 +323,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        : _Flat_set_impl(__s, __il.begin(), __il.end(), __comp, __a)
        { }
 
+      _Flat_set_impl(const _Flat_set_impl&) = default;
+      _Flat_set_impl& operator=(const _Flat_set_impl&) = default;
+
+      _GLIBCXX26_CONSTEXPR
+      _Flat_set_impl(_Flat_set_impl&& __other)
+      noexcept(is_nothrow_move_constructible_v<container_type>
+              && is_nothrow_move_constructible_v<key_compare>)
+#if __cpp_exceptions
+      try
+#endif
+      : _M_cont(std::move(__other._M_cont)), 
_M_comp(std::move(__other._M_comp))
+      { __other.clear(); }
+#if __cpp_exceptions
+      catch (...)
+      { __other.clear(); }
+#endif
+
+      _GLIBCXX26_CONSTEXPR
+      _Flat_set_impl&
+      operator=(_Flat_set_impl&& __other)
+      noexcept(is_nothrow_move_assignable_v<container_type>
+              && is_nothrow_move_assignable_v<key_compare>)
+      {
+       auto __guard = _M_make_clear_guard();
+       auto __guard_other = _ClearGuard{__other._M_cont};
+       _M_cont = std::move(__other._M_cont);
+       _M_comp = std::move(__other._M_comp);
+       __guard._M_disable();
+       // __guard_other._M_disable is deliberately not called.
+       return *this;
+      }
+
       _GLIBCXX26_CONSTEXPR
       _Derived&
       operator=(initializer_list<value_type> __il)
@@ -540,10 +571,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          __guard._M_disable();
        }
 
-      template<__detail::__container_compatible_range<value_type> _Rg>
+      template<typename _Rg>
        _GLIBCXX26_CONSTEXPR
        void
-       insert_range(_Rg&& __rg)
+       _M_insert_range(_Rg&& __rg, bool __is_sorted = false)
        {
          auto __guard = _M_make_clear_guard();
          typename container_type::iterator __it;
@@ -561,13 +592,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                _M_cont.emplace_back(*__first);
              __it = _M_cont.begin() + __n;
            }
-         std::sort(__it, _M_cont.end(), _M_comp);
+         if (__is_sorted)
+           _GLIBCXX_DEBUG_ASSERT(ranges::is_sorted(__it, _M_cont.end(), 
_M_comp));
+         else
+           std::sort(__it, _M_cont.end(), _M_comp);
          std::inplace_merge(_M_cont.begin(), __it, _M_cont.end(), _M_comp);
          if constexpr (!_Multi)
            _M_unique();
          __guard._M_disable();
        }
 
+    template<__detail::__container_compatible_range<value_type> _Rg>
+      _GLIBCXX26_CONSTEXPR
+      void
+      insert_range(_Rg&& __rg)
+      { _M_insert_range(std::forward<_Rg>(__rg)); }
+
+    template<__detail::__container_compatible_range<value_type> _Rg>
+      _GLIBCXX26_CONSTEXPR
+      void
+      insert_range(__sorted_t, _Rg&& __rg)
+      { _M_insert_range(std::forward<_Rg>(__rg), true); }
+
       _GLIBCXX26_CONSTEXPR
       void
       insert(initializer_list<value_type> __il)
@@ -628,11 +674,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       _GLIBCXX26_CONSTEXPR
       void
-      swap(_Derived& __x) noexcept
+      swap(_Derived& __y)
+      noexcept(is_nothrow_swappable_v<container_type>
+              && is_nothrow_swappable_v<key_compare>)
       {
-       using std::swap;
-       swap(_M_cont, __x._M_cont);
-       swap(_M_comp, __x._M_comp);
+       auto __guard = _M_make_clear_guard();
+       auto __guard_y = _ClearGuard{__y._M_cont};
+       ranges::swap(_M_comp, __y._M_comp);
+       ranges::swap(_M_cont, __y._M_cont);
+       __guard._M_disable();
+       __guard_y._M_disable();
       }
 
       _GLIBCXX26_CONSTEXPR
@@ -830,7 +881,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       friend _GLIBCXX26_CONSTEXPR void
-      swap(_Derived& __x, _Derived& __y) noexcept
+      swap(_Derived& __x, _Derived& __y) noexcept(noexcept(__x.swap(__y)))
       { return __x.swap(__y); }
 
       template<typename _Predicate>
diff --git a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc 
b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
index 1cb088f511eb..7e22e0366ec2 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
@@ -3,7 +3,7 @@
 
 #include <flat_map>
 
-#if __cpp_lib_flat_map != 202207L
+#if __cpp_lib_flat_map != 202511L
 # error "Feature-test macro __cpp_lib_flat_map has wrong value in <flat_map>"
 #endif
 
@@ -299,6 +299,103 @@ test10()
   VERIFY (k == "world");
 }
 
+template<typename T>
+struct throwing_vector : std::vector<T>
+{
+  static inline bool throw_on_move = false;
+
+  throwing_vector() = default;
+  throwing_vector(const throwing_vector&) = default;
+  throwing_vector& operator=(const throwing_vector&) = default;
+
+  throwing_vector(throwing_vector&& other)
+  : std::vector<T>(std::move(other))
+  {
+    if (throw_on_move)
+      throw std::runtime_error("move ctor");
+  }
+
+  throwing_vector&
+  operator=(throwing_vector&& other)
+  {
+    static_cast<std::vector<T>&>(*this) = std::move(other);
+    if (throw_on_move)
+      throw std::runtime_error("move assign");
+    return *this;
+  }
+};
+
+template<template<typename> class KC, template<typename> class MC>
+void
+test11()
+{
+#if __cpp_exceptions
+  using flat_map = std::flat_map<int, int, std::less<int>, KC<int>, MC<int>>;
+
+  auto is_really_empty = [](const flat_map& m) {
+    return m.empty() && m.keys().empty() && m.values().empty();
+  };
+  throwing_vector<int>::throw_on_move = true;
+
+  // Verify invariant preservation upon throwing move construction.
+  flat_map source;
+  source.insert({{1, 100}, {2, 200}});
+  try
+    {
+      flat_map target(std::move(source));
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( is_really_empty(source) );
+    }
+
+  // Verify invariant preservation upon throwing move assignment.
+  source.clear();
+  source.insert({{1, 100}, {2, 200}});
+  flat_map target;
+  target.insert({{3, 300}, {4, 400}});
+  try
+    {
+      target = std::move(source);
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( is_really_empty(source) );
+      VERIFY( is_really_empty(target) );
+    }
+
+  // Verify invariant preservation upon throwing swap.
+  source.clear();
+  source.insert({{1, 100}, {2, 200}});
+  target.clear();
+  target.insert({{3, 300}, {4, 400}});
+  try
+    {
+      source.swap(target);
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( is_really_empty(source) );
+      VERIFY( is_really_empty(target) );
+    }
+#endif
+}
+
+constexpr
+void
+test12()
+{
+  // Verify usability of flat_map::insert_range(sorted_unique_t, Rg&&).
+  std::flat_map<int, int> m = {{2, 200}};
+  std::pair<int, int> s[] = {{1, 100}, {3, 300}};
+  m.insert_range(std::sorted_unique, s);
+  VERIFY( std::ranges::equal(m.keys(), (int[]){1, 2, 3}) );
+  VERIFY( std::ranges::equal(m.values(), (int[]){100, 200, 300}) );
+}
+
 void
 test()
 {
@@ -315,6 +412,9 @@ test()
   test08();
   test09();
   test10();
+  test11<std::vector, throwing_vector>();
+  test11<throwing_vector, std::vector>();
+  test12();
 }
 
 constexpr
@@ -333,6 +433,8 @@ test_constexpr()
 #if __cpp_lib_constexpr_string >= 201907L
   test10();
 #endif
+  // test11() is non-constexpr
+  test12();
   return true;
 }
 
diff --git a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc 
b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
index 00644c14061a..48688b7583ed 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
@@ -252,6 +252,103 @@ test09()
   using value_type = std::pair<int, int>;
 }
 
+template<typename T>
+struct throwing_vector : std::vector<T>
+{
+  static inline bool throw_on_move = false;
+
+  throwing_vector() = default;
+  throwing_vector(const throwing_vector&) = default;
+  throwing_vector& operator=(const throwing_vector&) = default;
+
+  throwing_vector(throwing_vector&& other)
+  : std::vector<T>(std::move(other))
+  {
+    if (throw_on_move)
+      throw std::runtime_error("move ctor");
+  }
+
+  throwing_vector&
+  operator=(throwing_vector&& other)
+  {
+    static_cast<std::vector<T>&>(*this) = std::move(other);
+    if (throw_on_move)
+      throw std::runtime_error("move assign");
+    return *this;
+  }
+};
+
+template<template<typename> class KC, template<typename> class MC>
+void
+test10()
+{
+#if __cpp_exceptions
+  using flat_multimap = std::flat_multimap<int, int, std::less<int>, KC<int>, 
MC<int>>;
+
+  auto is_really_empty = [](const flat_multimap& m) {
+    return m.empty() && m.keys().empty() && m.values().empty();
+  };
+  throwing_vector<int>::throw_on_move = true;
+
+  // Verify invariant preservation upon throwing move construction.
+  flat_multimap source;
+  source.insert({{1, 100}, {2, 200}});
+  try
+    {
+      flat_multimap target(std::move(source));
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( is_really_empty(source) );
+    }
+
+  // Verify invariant preservation upon throwing move assignment.
+  source.clear();
+  source.insert({{1, 100}, {2, 200}});
+  flat_multimap target;
+  target.insert({{3, 300}, {4, 400}});
+  try
+    {
+      target = std::move(source);
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( is_really_empty(source) );
+      VERIFY( is_really_empty(target) );
+    }
+
+  // Verify invariant preservation upon throwing swap.
+  source.clear();
+  source.insert({{1, 100}, {2, 200}});
+  target.clear();
+  target.insert({{3, 300}, {4, 400}});
+  try
+    {
+      source.swap(target);
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( is_really_empty(source) );
+      VERIFY( is_really_empty(target) );
+    }
+#endif
+}
+
+constexpr
+void
+test11()
+{
+  // Verify usability of flat_multimap::insert_range(sorted_equivalent_t, 
Rg&&).
+  std::flat_multimap<int, int> m = {{2, 200}};;
+  std::pair<int, int> s[] = {{1, 100}, {3, 300}};
+  m.insert_range(std::sorted_equivalent, s);
+  VERIFY( std::ranges::equal(m.keys(), (int[]){1, 2, 3}) );
+  VERIFY( std::ranges::equal(m.values(), (int[]){100, 200, 300}) );
+}
+
 void
 test()
 {
@@ -266,6 +363,9 @@ test()
   test06();
   test07();
   test09();
+  test10<std::vector, throwing_vector>();
+  test10<throwing_vector, std::vector>();
+  test11();
 }
 
 constexpr
@@ -280,6 +380,8 @@ test_constexpr()
   test06();
   test07();
   test09();
+  // test10() is non-constexpr
+  test11();
   return true;
 }
 
diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc 
b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
index be330fefdd05..4544adb50770 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
@@ -265,6 +265,96 @@ test09()
   VERIFY( std::ranges::equal(s, (int[]){2,2,4}) );
 }
 
+template<typename T>
+struct throwing_vector : std::vector<T>
+{
+  static inline bool throw_on_move = false;
+
+  throwing_vector() = default;
+  throwing_vector(const throwing_vector&) = default;
+  throwing_vector& operator=(const throwing_vector&) = default;
+
+  throwing_vector(throwing_vector&& other)
+  : std::vector<T>(std::move(other))
+  {
+    if (throw_on_move)
+      throw std::runtime_error("move ctor");
+  }
+
+  throwing_vector&
+  operator=(throwing_vector&& other)
+  {
+    static_cast<std::vector<T>&>(*this) = std::move(other);
+    if (throw_on_move)
+      throw std::runtime_error("move assign");
+    return *this;
+  }
+};
+
+void
+test10()
+{
+#if __cpp_exceptions
+  using flat_multiset = std::flat_multiset<int, std::less<int>, 
throwing_vector<int>>;
+
+  throwing_vector<int>::throw_on_move = true;
+
+  // Verify invariant preservation upon throwing move construction.
+  flat_multiset source = {1, 2};
+  try
+    {
+      flat_multiset target(std::move(source));
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( source.empty() );
+    }
+
+  // Verify invariant preservation upon throwing move assignment.
+  source.clear();
+  source.insert({1, 2});
+  flat_multiset target = {3, 4};
+  try
+    {
+      target = std::move(source);
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( source.empty() );
+      VERIFY( target.empty() );
+    }
+
+  // Verify invariant preservation upon throwing swap.
+  source.clear();
+  source.insert({1, 2});
+  target.clear();
+  target.insert({3, 4});
+  try
+    {
+      source.swap(target);
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( source.empty() );
+      VERIFY( target.empty() );
+    }
+#endif
+}
+
+constexpr
+void
+test11()
+{
+  // Verify usability of flat_multiset::insert_range(sorted_equivalent_t, 
Rg&&).
+  std::flat_multiset<int> m = {2};
+  int s[] = {1, 3};
+  m.insert_range(std::sorted_equivalent, s);
+  VERIFY( std::ranges::equal(m, (int[]){1, 2, 3}) );
+}
+
 void
 test()
 {
@@ -278,6 +368,8 @@ test()
   test07();
   test08();
   test09();
+  test10();
+  test11();
 }
 
 constexpr
@@ -292,6 +384,8 @@ test_constexpr()
   test07();
   test08();
   test09();
+  // test10() is non-constexpr
+  test11();
   return true;
 }
 
diff --git a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc 
b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
index a14d03476e18..ac12815bd0d4 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
@@ -2,7 +2,7 @@
 
 #include <flat_set>
 
-#if __cpp_lib_flat_set != 202207L
+#if __cpp_lib_flat_set != 202511L
 # error "Feature-test macro __cpp_lib_flat_set has wrong value in <flat_set>"
 #endif
 
@@ -277,6 +277,96 @@ test09()
   VERIFY( std::ranges::equal(s, (int[]){2,4}) );
 }
 
+template<typename T>
+struct throwing_vector : std::vector<T>
+{
+  static inline bool throw_on_move = false;
+
+  throwing_vector() = default;
+  throwing_vector(const throwing_vector&) = default;
+  throwing_vector& operator=(const throwing_vector&) = default;
+
+  throwing_vector(throwing_vector&& other)
+  : std::vector<T>(std::move(other))
+  {
+    if (throw_on_move)
+      throw std::runtime_error("move ctor");
+  }
+
+  throwing_vector&
+  operator=(throwing_vector&& other)
+  {
+    static_cast<std::vector<T>&>(*this) = std::move(other);
+    if (throw_on_move)
+      throw std::runtime_error("move assign");
+    return *this;
+  }
+};
+
+void
+test10()
+{
+#if __cpp_exceptions
+  using flat_set = std::flat_set<int, std::less<int>, throwing_vector<int>>;
+
+  throwing_vector<int>::throw_on_move = true;
+
+  // Verify invariant preservation upon throwing move construction.
+  flat_set source = {1, 2};
+  try
+    {
+      flat_set target(std::move(source));
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( source.empty() );
+    }
+
+  // Verify invariant preservation upon throwing move assignment.
+  source.clear();
+  source.insert({1, 2});
+  flat_set target = {3, 4};
+  try
+    {
+      target = std::move(source);
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( source.empty() );
+      VERIFY( target.empty() );
+    }
+
+  // Verify invariant preservation upon throwing swap.
+  source.clear();
+  source.insert({1, 2});
+  target.clear();
+  target.insert({3, 4});
+  try
+    {
+      source.swap(target);
+      VERIFY( false );
+    }
+  catch (const std::runtime_error&)
+    {
+      VERIFY( source.empty() );
+      VERIFY( target.empty() );
+    }
+#endif
+}
+
+constexpr
+void
+test11()
+{
+  // Verify usability of flat_set::insert_range(sorted_unique_t, Rg&&).
+  std::flat_set<int> m = {2};
+  int s[] = {1, 3};
+  m.insert_range(std::sorted_unique, s);
+  VERIFY( std::ranges::equal(m, (int[]){1, 2, 3}) );
+}
+
 void
 test()
 {
@@ -290,6 +380,8 @@ test()
   test07();
   test08();
   test09();
+  test10();
+  test11();
 }
 
 constexpr
@@ -304,6 +396,8 @@ test_constexpr()
   test07();
   test08();
   test09();
+  // test10() is non-constexpr
+  test11();
   return true;
 }

Reply via email to