[libcxx-commits] [libcxx] [libc++] Assert preconditions of operator= of containers (PR #129625)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Mar 10 21:35:00 PDT 2025


https://github.com/halbi2 updated https://github.com/llvm/llvm-project/pull/129625

>From 546663cdaf75e38fc57080fb8242a3161e30075e Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Mon, 3 Mar 2025 19:49:12 -0500
Subject: [PATCH 1/2] [libc++] Assert preconditions of operator= of containers

Container requirements require that value_type is Cpp17CopyAssignable
or Cpp17MoveAssignable when a container is copy assigned or move assigned.
Assert that requirement.

Fixes #59683
---
 libcxx/include/__vector/vector.h              |  5 +++
 libcxx/include/deque                          |  4 +++
 libcxx/include/list                           |  5 +++
 libcxx/include/map                            | 15 ++++++++
 libcxx/include/set                            |  9 +++++
 libcxx/include/unordered_map                  | 15 ++++++++
 libcxx/include/unordered_set                  |  9 +++++
 .../copy_assign.not_assignable.verify.cpp     | 31 ++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 31 ++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 31 ++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 31 ++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 30 ++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 30 ++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 30 ++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 36 +++++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 36 +++++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 36 +++++++++++++++++++
 .../copy_assign.not_assignable.verify.cpp     | 36 +++++++++++++++++++
 18 files changed, 420 insertions(+)
 create mode 100644 libcxx/test/std/containers/associative/map/map.cons/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/set.cons/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/sequences/deque/deque.cons/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/sequences/list/list.cons/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/sequences/vector/vector.cons/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/copy_assign.not_assignable.verify.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/copy_assign.not_assignable.verify.cpp

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index ef796a8ed0a9e..e6ea3b9c8d67f 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -50,6 +50,7 @@
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_assignable.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_nothrow_assignable.h>
@@ -294,6 +295,9 @@ class _LIBCPP_TEMPLATE_VIS vector {
   vector(vector&& __x, const __type_identity_t<allocator_type>& __a);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector& operator=(vector&& __x)
       _NOEXCEPT_(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+    static_assert(
+        __alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<value_type>::value,
+        "T must be Cpp17MoveAssignable");
     __move_assign(__x, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
     return *this;
   }
@@ -1011,6 +1015,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__move_assign(vector
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI vector<_Tp, _Allocator>&
 vector<_Tp, _Allocator>::operator=(const vector& __x) {
+  static_assert(is_copy_assignable<value_type>::value, "T must be Cpp17CopyAssignable");
   if (this != std::addressof(__x)) {
     __copy_assign_alloc(__x);
     assign(__x.__begin_, __x.__end_);
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 95200b4801d7f..e631fe38cd4ee 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -227,6 +227,7 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/disjunction.h>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
+#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_convertible.h>
 #  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
@@ -1328,6 +1329,7 @@ deque<_Tp, _Allocator>::deque(const deque& __c, const __type_identity_t<allocato
 
 template <class _Tp, class _Allocator>
 deque<_Tp, _Allocator>& deque<_Tp, _Allocator>::operator=(const deque& __c) {
+  static_assert(is_copy_assignable<value_type>::value, "T must be Cpp17CopyAssignable");
   if (this != std::addressof(__c)) {
     __copy_assign_alloc(__c);
     assign(__c.begin(), __c.end());
@@ -1383,6 +1385,8 @@ inline deque<_Tp, _Allocator>& deque<_Tp, _Allocator>::operator=(deque&& __c) no
     (__alloc_traits::propagate_on_container_move_assignment::value &&
      is_nothrow_move_assignable<allocator_type>::value) ||
     allocator_traits<allocator_type>::is_always_equal::value) {
+  static_assert(__alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<value_type>::value,
+                "T must be Cpp17MoveAssignable");
   __move_assign(__c, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
diff --git a/libcxx/include/list b/libcxx/include/list
index 1285174f1c384..a71bbce784c81 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -232,6 +232,7 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/container_traits.h>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
+#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_pointer.h>
@@ -1071,6 +1072,9 @@ inline list<_Tp, _Alloc>& list<_Tp, _Alloc>::operator=(list&& __c) noexcept(
     (__node_alloc_traits::propagate_on_container_move_assignment::value &&
      is_nothrow_move_assignable<__node_allocator>::value) ||
     allocator_traits<allocator_type>::is_always_equal::value) {
+  static_assert(
+      __node_alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<value_type>::value,
+      "T must be Cpp17MoveAssignable");
   __move_assign(__c, integral_constant<bool, __node_alloc_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -1096,6 +1100,7 @@ void list<_Tp, _Alloc>::__move_assign(list& __c,
 
 template <class _Tp, class _Alloc>
 inline list<_Tp, _Alloc>& list<_Tp, _Alloc>::operator=(const list& __c) {
+  static_assert(is_copy_assignable<value_type>::value, "T must be Cpp17CopyAssignable");
   if (this != std::addressof(__c)) {
     __base::__copy_assign_alloc(__c);
     assign(__c.begin(), __c.end());
diff --git a/libcxx/include/map b/libcxx/include/map
index 37a8ec91b8f1f..2c20a2d5df97f 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -600,6 +600,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred);  // C++20
 #  include <__tree>
 #  include <__type_traits/container_traits.h>
 #  include <__type_traits/is_allocator.h>
+#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/remove_const.h>
 #  include <__type_traits/type_identity.h>
 #  include <__utility/forward.h>
@@ -1084,6 +1085,8 @@ public:
   _LIBCPP_HIDE_FROM_ABI map(const map& __m) : __tree_(__m.__tree_) { insert(__m.begin(), __m.end()); }
 
   _LIBCPP_HIDE_FROM_ABI map& operator=(const map& __m) {
+    static_assert(is_copy_assignable<key_type>::value, "T must be Cpp17CopyAssignable");
+    static_assert(is_copy_assignable<mapped_type>::value, "T must be Cpp17CopyAssignable");
 #  ifndef _LIBCPP_CXX03_LANG
     __tree_ = __m.__tree_;
 #  else
@@ -1105,6 +1108,11 @@ public:
   _LIBCPP_HIDE_FROM_ABI map(map&& __m, const allocator_type& __a);
 
   _LIBCPP_HIDE_FROM_ABI map& operator=(map&& __m) noexcept(is_nothrow_move_assignable<__base>::value) {
+    static_assert(__alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<key_type>::value,
+                  "T must be Cpp17MoveAssignable");
+    static_assert(
+        __alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<mapped_type>::value,
+        "T must be Cpp17MoveAssignable");
     __tree_ = std::move(__m.__tree_);
     return *this;
   }
@@ -1771,6 +1779,8 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI multimap& operator=(const multimap& __m) {
+    static_assert(is_copy_assignable<key_type>::value, "T must be Cpp17CopyAssignable");
+    static_assert(is_copy_assignable<mapped_type>::value, "T must be Cpp17CopyAssignable");
 #  ifndef _LIBCPP_CXX03_LANG
     __tree_ = __m.__tree_;
 #  else
@@ -1792,6 +1802,11 @@ public:
   _LIBCPP_HIDE_FROM_ABI multimap(multimap&& __m, const allocator_type& __a);
 
   _LIBCPP_HIDE_FROM_ABI multimap& operator=(multimap&& __m) noexcept(is_nothrow_move_assignable<__base>::value) {
+    static_assert(__alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<key_type>::value,
+                  "T must be Cpp17MoveAssignable");
+    static_assert(
+        __alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<mapped_type>::value,
+        "T must be Cpp17MoveAssignable");
     __tree_ = std::move(__m.__tree_);
     return *this;
   }
diff --git a/libcxx/include/set b/libcxx/include/set
index bd7bfef1f3e29..74affdde6db13 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -537,6 +537,7 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);  // C++20
 #  include <__type_traits/container_traits.h>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
+#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
@@ -665,6 +666,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI set(const set& __s) : __tree_(__s.__tree_) { insert(__s.begin(), __s.end()); }
 
   _LIBCPP_HIDE_FROM_ABI set& operator=(const set& __s) {
+    static_assert(is_copy_assignable<value_type>::value, "T must be Cpp17CopyAssignable");
     __tree_ = __s.__tree_;
     return *this;
   }
@@ -704,6 +706,9 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI set& operator=(set&& __s) noexcept(is_nothrow_move_assignable<__base>::value) {
+    static_assert(
+        __alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<value_type>::value,
+        "T must be Cpp17MoveAssignable");
     __tree_ = std::move(__s.__tree_);
     return *this;
   }
@@ -1130,6 +1135,7 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) {
+    static_assert(is_copy_assignable<value_type>::value, "T must be Cpp17CopyAssignable");
     __tree_ = __s.__tree_;
     return *this;
   }
@@ -1169,6 +1175,9 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI multiset& operator=(multiset&& __s) _NOEXCEPT_(is_nothrow_move_assignable<__base>::value) {
+    static_assert(
+        __alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<value_type>::value,
+        "T must be Cpp17MoveAssignable");
     __tree_ = std::move(__s.__tree_);
     return *this;
   }
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index be36b65cb85b4..9253e04c495ca 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -612,6 +612,7 @@ template <class Key, class T, class Hash, class Pred, class Alloc>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/invoke.h>
 #  include <__type_traits/is_allocator.h>
+#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_integral.h>
 #  include <__type_traits/remove_const.h>
 #  include <__type_traits/type_identity.h>
@@ -1192,6 +1193,8 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI unordered_map& operator=(const unordered_map& __u) {
+    static_assert(is_copy_assignable<key_type>::value, "T must be Cpp17CopyAssignable");
+    static_assert(is_copy_assignable<mapped_type>::value, "T must be Cpp17CopyAssignable");
 #  ifndef _LIBCPP_CXX03_LANG
     __table_ = __u.__table_;
 #  else
@@ -1715,6 +1718,12 @@ template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
 inline unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>&
 unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator=(unordered_map&& __u)
     _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) {
+  static_assert(allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ||
+                    is_move_assignable<key_type>::value,
+                "T must be Cpp17MoveAssignable");
+  static_assert(allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ||
+                    is_move_assignable<mapped_type>::value,
+                "T must be Cpp17MoveAssignable");
   __table_ = std::move(__u.__table_);
   return *this;
 }
@@ -2001,6 +2010,8 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI unordered_multimap& operator=(const unordered_multimap& __u) {
+    static_assert(is_copy_assignable<key_type>::value, "T must be Cpp17CopyAssignable");
+    static_assert(is_copy_assignable<mapped_type>::value, "T must be Cpp17CopyAssignable");
 #  ifndef _LIBCPP_CXX03_LANG
     __table_ = __u.__table_;
 #  else
@@ -2472,6 +2483,10 @@ template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
 inline unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>&
 unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::operator=(unordered_multimap&& __u)
     _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) {
+  static_assert(__alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<key_type>::value,
+                "T must be Cpp17MoveAssignable");
+  static_assert(__alloc_traits::propagate_on_container_move_assignment::value || is_move_assignable<mapped_type>::value,
+                "T must be Cpp17MoveAssignable");
   __table_ = std::move(__u.__table_);
   return *this;
 }
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 87d98435329f3..d5ae37d575a3e 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -557,6 +557,7 @@ template <class Value, class Hash, class Pred, class Alloc>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/invoke.h>
 #  include <__type_traits/is_allocator.h>
+#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_integral.h>
 #  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
@@ -734,6 +735,7 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI unordered_set& operator=(const unordered_set& __u) {
+    static_assert(is_copy_assignable<value_type>::value, "T must be Cpp17CopyAssignable");
     __table_ = __u.__table_;
     return *this;
   }
@@ -1129,6 +1131,9 @@ template <class _Value, class _Hash, class _Pred, class _Alloc>
 inline unordered_set<_Value, _Hash, _Pred, _Alloc>&
 unordered_set<_Value, _Hash, _Pred, _Alloc>::operator=(unordered_set&& __u)
     _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) {
+  static_assert(allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ||
+                    is_move_assignable<value_type>::value,
+                "T must be Cpp17MoveAssignable");
   __table_ = std::move(__u.__table_);
   return *this;
 }
@@ -1338,6 +1343,7 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI unordered_multiset& operator=(const unordered_multiset& __u) {
+    static_assert(is_copy_assignable<value_type>::value, "T must be Cpp17CopyAssignable");
     __table_ = __u.__table_;
     return *this;
   }
@@ -1745,6 +1751,9 @@ template <class _Value, class _Hash, class _Pred, class _Alloc>
 inline unordered_multiset<_Value, _Hash, _Pred, _Alloc>&
 unordered_multiset<_Value, _Hash, _Pred, _Alloc>::operator=(unordered_multiset&& __u)
     _NOEXCEPT_(is_nothrow_move_assignable<__table>::value) {
+  static_assert(allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ||
+                    is_move_assignable<value_type>::value,
+                "T must be Cpp17MoveAssignable");
   __table_ = std::move(__u.__table_);
   return *this;
 }
diff --git a/libcxx/test/std/containers/associative/map/map.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/associative/map/map.cons/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..4be858626ea03
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.cons/copy_assign.not_assignable.verify.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::map containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <map>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+  bool operator<(const AttributeType&) const;
+};
+
+void f() {
+  std::map<int, AttributeType> v;
+  std::map<int, AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{no viable overloaded '='}}
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..c19f1818a6ce6
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.not_assignable.verify.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::multimap containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <map>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+  bool operator<(const AttributeType&) const;
+};
+
+void f() {
+  std::multimap<int, AttributeType> v;
+  std::multimap<int, AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{no viable overloaded '='}}
+}
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..00aa361cea900
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.not_assignable.verify.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::multiset containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <set>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+  bool operator<(const AttributeType&) const;
+};
+
+void f() {
+  std::multiset<AttributeType> v;
+  std::multiset<AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{overload resolution selected deleted operator '='}}
+}
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..082a6717af321
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.not_assignable.verify.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::set containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <set>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+  bool operator<(const AttributeType&) const;
+};
+
+void f() {
+  std::set<AttributeType> v;
+  std::set<AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{overload resolution selected deleted operator '='}}
+}
diff --git a/libcxx/test/std/containers/sequences/deque/deque.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/sequences/deque/deque.cons/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..7e81162ef9e45
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/deque/deque.cons/copy_assign.not_assignable.verify.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::deque containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <deque>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+};
+
+void f() {
+  std::deque<AttributeType> v;
+  std::deque<AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{overload resolution selected deleted operator '='}}
+}
diff --git a/libcxx/test/std/containers/sequences/list/list.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/sequences/list/list.cons/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..30c7020ee012d
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/list/list.cons/copy_assign.not_assignable.verify.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::list containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <list>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+};
+
+void f() {
+  std::list<AttributeType> v;
+  std::list<AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{overload resolution selected deleted operator '='}}
+}
diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..3027fd90382c0
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/copy_assign.not_assignable.verify.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::vector containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <vector>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+};
+
+void f() {
+  std::vector<AttributeType> v;
+  std::vector<AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error-re@* {{{{no matching function for call to object of type 'const __copy_n'|overload resolution selected deleted operator '='}}}}
+}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..3d2a1ec394787
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_assign.not_assignable.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::unordered_map containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <unordered_map>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+  bool operator==(const AttributeType&) const;
+};
+
+template <>
+struct std::hash<AttributeType> {
+  size_t operator()(const AttributeType&) const;
+};
+
+void f() {
+  std::unordered_map<int, AttributeType> v;
+  std::unordered_map<int, AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{no viable overloaded '='}}
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..acd70a9e6ba84
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/copy_assign.not_assignable.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::unordered_multimap containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <unordered_map>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+  bool operator==(const AttributeType&) const;
+};
+
+template <>
+struct std::hash<AttributeType> {
+  size_t operator()(const AttributeType&) const;
+};
+
+void f() {
+  std::unordered_multimap<int, AttributeType> v;
+  std::unordered_multimap<int, AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{no viable overloaded '='}}
+}
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..0f820fa5d660b
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/copy_assign.not_assignable.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::unordered_multiset containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <unordered_set>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+  bool operator==(const AttributeType&) const;
+};
+
+template <>
+struct std::hash<AttributeType> {
+  size_t operator()(const AttributeType&) const;
+};
+
+void f() {
+  std::unordered_multiset<AttributeType> v;
+  std::unordered_multiset<AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{overload resolution selected deleted operator '='}}
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/copy_assign.not_assignable.verify.cpp
new file mode 100644
index 0000000000000..503235265dcbc
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/copy_assign.not_assignable.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that a std::unordered_set containing move-only types can't be copy-assigned.
+
+// UNSUPPORTED: c++03
+
+#include <unordered_set>
+
+struct AttributeType {
+  AttributeType();
+  AttributeType(const AttributeType&);
+  AttributeType(AttributeType&&);
+  AttributeType& operator=(const AttributeType&) = delete;
+  AttributeType& operator=(AttributeType&&);
+  ~AttributeType();
+  bool operator==(const AttributeType&) const;
+};
+
+template <>
+struct std::hash<AttributeType> {
+  size_t operator()(const AttributeType&) const;
+};
+
+void f() {
+  std::unordered_set<AttributeType> v;
+  std::unordered_set<AttributeType> copy;
+  copy = v;
+  // expected-error@* {{T must be Cpp17CopyAssignable}}
+  // expected-error@* {{overload resolution selected deleted operator '='}}
+}

>From 137c437d1a57dcca9a17d18c57910acf9bb36ac6 Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Tue, 11 Mar 2025 00:33:48 -0400
Subject: [PATCH 2/2] reverse order of comment lines

---
 .../map/map.cons/copy_assign.not_assignable.verify.cpp        | 4 ++--
 .../multimap.cons/copy_assign.not_assignable.verify.cpp       | 4 ++--
 .../multiset.cons/copy_assign.not_assignable.verify.cpp       | 4 ++--
 .../set/set.cons/copy_assign.not_assignable.verify.cpp        | 4 ++--
 .../deque/deque.cons/copy_assign.not_assignable.verify.cpp    | 4 ++--
 .../list/list.cons/copy_assign.not_assignable.verify.cpp      | 4 ++--
 .../vector/vector.cons/copy_assign.not_assignable.verify.cpp  | 4 ++--
 .../unord.map.cnstr/copy_assign.not_assignable.verify.cpp     | 4 ++--
 .../copy_assign.not_assignable.verify.cpp                     | 4 ++--
 .../copy_assign.not_assignable.verify.cpp                     | 4 ++--
 .../unord.set.cnstr/copy_assign.not_assignable.verify.cpp     | 4 ++--
 11 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/libcxx/test/std/containers/associative/map/map.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/associative/map/map.cons/copy_assign.not_assignable.verify.cpp
index 4be858626ea03..5ac88a1070926 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::map containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::map containing move-only types can't be copy-assigned.
+
 #include <map>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.not_assignable.verify.cpp
index c19f1818a6ce6..e3db9c4f6782a 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::multimap containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::multimap containing move-only types can't be copy-assigned.
+
 #include <map>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.not_assignable.verify.cpp
index 00aa361cea900..b3feb32c4b6a3 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::multiset containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::multiset containing move-only types can't be copy-assigned.
+
 #include <set>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.not_assignable.verify.cpp
index 082a6717af321..e1b9e6f4a81c8 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::set containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::set containing move-only types can't be copy-assigned.
+
 #include <set>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/sequences/deque/deque.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/sequences/deque/deque.cons/copy_assign.not_assignable.verify.cpp
index 7e81162ef9e45..4c16893b69ce6 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.cons/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.cons/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::deque containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::deque containing move-only types can't be copy-assigned.
+
 #include <deque>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/sequences/list/list.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/sequences/list/list.cons/copy_assign.not_assignable.verify.cpp
index 30c7020ee012d..139f108c6ed2c 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.cons/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::list containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::list containing move-only types can't be copy-assigned.
+
 #include <list>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/copy_assign.not_assignable.verify.cpp
index 3027fd90382c0..705e87a5413af 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::vector containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::vector containing move-only types can't be copy-assigned.
+
 #include <vector>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_assign.not_assignable.verify.cpp
index 3d2a1ec394787..aed05bf5ab922 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::unordered_map containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::unordered_map containing move-only types can't be copy-assigned.
+
 #include <unordered_map>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/copy_assign.not_assignable.verify.cpp
index acd70a9e6ba84..96341dadd529e 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::unordered_multimap containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::unordered_multimap containing move-only types can't be copy-assigned.
+
 #include <unordered_map>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/copy_assign.not_assignable.verify.cpp
index 0f820fa5d660b..6d783da5d00ce 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::unordered_multiset containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::unordered_multiset containing move-only types can't be copy-assigned.
+
 #include <unordered_set>
 
 struct AttributeType {
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/copy_assign.not_assignable.verify.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/copy_assign.not_assignable.verify.cpp
index 503235265dcbc..66c9485361d62 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/copy_assign.not_assignable.verify.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/copy_assign.not_assignable.verify.cpp
@@ -6,10 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Make sure that a std::unordered_set containing move-only types can't be copy-assigned.
-
 // UNSUPPORTED: c++03
 
+// Make sure that a std::unordered_set containing move-only types can't be copy-assigned.
+
 #include <unordered_set>
 
 struct AttributeType {



More information about the libcxx-commits mailing list