[libcxx-commits] [libcxx] [libcxx] improves diagnostics for containers with bad value types (PR #106296)

Christopher Di Bella via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 3 10:38:46 PDT 2024


https://github.com/cjdb updated https://github.com/llvm/llvm-project/pull/106296

>From 55294993360bd34174d147935de4c3812418dc5a Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Mon, 26 Aug 2024 17:32:43 +0000
Subject: [PATCH 01/12] [libcxx] improves diagnostics for containers with bad
 value types

`std::vector<int&>` generates nearly 170 lines of diagnostic, most of
which are redundant, and it's only helpful if you are already familiar
with the rule that containers can't hold reference types. This commit
reduces it to only a handful of lines, all of which are dedicated to
clearly communicating the problem. It also applies this to all the
other containers, and for all non-cv-qualified and non-object types.

These static_asserts are placed at the very top of each container
because they short-circuit further instantiation errors, thereby
leading a smaller set of diagnostics for the programmer. Placing them
in `std::allocator` (which is the common denominator for all containers)
doesn't do the short circuiting, and we thus end up with several
unhelpful diagnostics after the helpful one.

This commit only cleans up things that are already diagnosed as
compile-time errors. In particular, `map<int&&, int>` should be
ill-formed, per [associative.reqmts.general]/p8. libc++ and libstdc++
currently permit that, but not `map<int&, int>`. As such, this
commit only adds a static assert for `map<K&, V>`, and defers diagnosing
valid productions that should be ill-formed to a future commit.
---
 libcxx/include/__memory/allocator.h           |  15 +-
 libcxx/include/array                          |   9 ++
 libcxx/include/deque                          |  12 ++
 libcxx/include/forward_list                   | 133 +++++++++++++++++
 libcxx/include/list                           | 134 ++++++++++++++++++
 libcxx/include/map                            |  17 +++
 libcxx/include/set                            |  18 +++
 libcxx/include/string                         |  13 ++
 libcxx/include/unordered_map                  |  17 +++
 libcxx/include/unordered_set                  |  18 +++
 libcxx/include/vector                         |  13 ++
 .../map/non_cv_object_types.verify.cpp        |  53 +++++++
 .../multimap/non_cv_object_types.verify.cpp   |  53 +++++++
 .../multiset/non_cv_object_types.verify.cpp   |  29 ++++
 .../set/non_cv_object_types.verify.cpp        |  29 ++++
 .../sequences/array/non_cv_objects.verify.cpp |  25 ++++
 .../sequences/deque/non_cv_objects.verify.cpp |  29 ++++
 .../forward_list/non_cv_objects.verify.cpp    |  32 +++++
 .../sequences/list/non_cv_objects.verify.cpp  |  29 ++++
 .../vector/non_cv_objects.verify.cpp          |  29 ++++
 .../basic.string/non_cv_objects.verify.cpp    |  32 +++++
 .../unord.map/non_cv_object_types.verify.cpp  |  46 ++++++
 .../non_cv_object_types.verify.cpp            |  46 ++++++
 .../non_cv_object_types.verify.cpp            |  33 +++++
 .../unord.set/non_cv_object_types.verify.cpp  |  32 +++++
 .../allocator_non_cv_objects_only.verify.cpp  |  25 ++++
 .../memory/allocator_volatile.verify.cpp      |  14 --
 27 files changed, 919 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/containers/associative/map/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/associative/multimap/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/strings/basic.string/non_cv_objects.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.map/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.set/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/memory/allocator_non_cv_objects_only.verify.cpp
 delete mode 100644 libcxx/test/libcxx/memory/allocator_volatile.verify.cpp

diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index 0dbdc41d3c3d14..36890f4e1a97d6 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -16,9 +16,12 @@
 #include <__memory/allocator_traits.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/is_volatile.h>
+#include <__type_traits/remove_reference.h>
 #include <__utility/forward.h>
 #include <cstddef>
 #include <new>
@@ -76,8 +79,16 @@ struct __non_trivial_if<true, _Unique> {
 
 template <class _Tp>
 class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::value, allocator<_Tp> > {
-  static_assert(!is_const<_Tp>::value, "std::allocator does not support const types");
-  static_assert(!is_volatile<_Tp>::value, "std::allocator does not support volatile types");
+  static_assert(!is_const<_Tp>::value, "'std::allocator' can only allocate non-const object types");
+  static_assert(!is_volatile<_Tp>::value, "'std::allocator' can only allocate non-volatile object types");
+  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value,
+                "'std::allocator' can only allocate object types; function references are not objects (consider using "
+                "a function pointer)");
+  static_assert(!is_reference<_Tp>::value,
+                "'std::allocator' can only allocate object types; references are not objects");
+  static_assert(
+      !is_function<_Tp>::value,
+      "'std::allocator' can only allocate object types; functions are not objects (consider using a function pointer)");
 
 public:
   typedef size_t size_type;
diff --git a/libcxx/include/array b/libcxx/include/array
index 4db0cb7bd7e3b5..0476f57a86ac24 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -127,11 +127,15 @@ template <size_t I, class T, size_t N> const T&& get(const array<T, N>&&) noexce
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_function.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/is_void.h>
 #include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_reference.h>
 #include <__utility/empty.h>
 #include <__utility/integer_sequence.h>
 #include <__utility/move.h>
@@ -167,6 +171,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp, size_t _Size>
 struct _LIBCPP_TEMPLATE_VIS array {
+  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::array' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Tp>::value, "'std::array' can only hold object types; references are not objects");
+  static_assert(!is_function<_Tp>::value, "'std::array' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Tp>::value, "'std::array' can only hold object types; 'void' is not an object");
+
   using __trivially_relocatable = __conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, array, void>;
 
   // types:
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 759de5d3a030a6..df0ddaebc39e92 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -212,9 +212,15 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/size.h>
 #include <__split_buffer>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/remove_reference.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -468,6 +474,12 @@ const _DiffType __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer,
 
 template <class _Tp, class _Allocator /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS deque {
+  static_assert(!is_const<_Tp>::value, "'std::deque' can only hold non-const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::deque' can only hold non-volatile types");
+  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::deque' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Tp>::value, "'std::deque' can only hold object types; references are not objects");
+  static_assert(!is_function<_Tp>::value, "'std::deque' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Tp>::value, "'std::deque' can only hold object types; 'void' is not an object");
 public:
   // types:
 
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index b8e3d05588f96e..2b6ad3bc100f90 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -220,11 +220,16 @@ template <class T, class Allocator, class Predicate>
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
 #include <__type_traits/is_nothrow_assignable.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/remove_reference.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -635,8 +640,136 @@ void __forward_list_base<_Tp, _Alloc>::clear() _NOEXCEPT {
   __before_begin()->__next_ = nullptr;
 }
 
+// begin-diagnostic-helpers
+// std::forward_list can only have non-cv-qualified object types as its value type, which we diagnose.
+// In order to short-cirucit redundant (and cryptic) diagnostics, std::forward_list must be the one
+// to fire the static_assert. Since std::forward_list inherits from __forward_list_base, we also need
+// to create specialisations for the rejected types, so that everything appears "good" to std::forward_list
+// (otherwise we'll get lots of unhelpful diagnostics that suppress the one std::forward_list offers).
+template <class _Tp, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp const, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Tp, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp volatile, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Tp, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp&, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Tp, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp&&, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Tp, class... _Args, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp(_Args...), _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __forward_list_base<void, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+// end-diagnostic-helpers
+
 template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS forward_list : private __forward_list_base<_Tp, _Alloc> {
+  static_assert(!is_const<_Tp>::value, "'std::forward_list' can only hold non-const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::forward_list' can only hold non-volatile types");
+  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::forward_list' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Tp>::value, "'std::forward_list' can only hold object types; references are not objects");
+  static_assert(!is_function<_Tp>::value, "'std::forward_list' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Tp>::value, "'std::forward_list' can only hold object types; 'void' is not an object");
+
   typedef __forward_list_base<_Tp, _Alloc> base;
   typedef typename base::__node_allocator __node_allocator;
   typedef typename base::__node_type __node_type;
diff --git a/libcxx/include/list b/libcxx/include/list
index 929c84de7be449..8da28ff1873d89 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -226,10 +226,16 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/from_range.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
 #include <__type_traits/is_nothrow_assignable.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/remove_reference.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -659,8 +665,136 @@ void __list_imp<_Tp, _Alloc>::swap(__list_imp& __c)
     __c.__end_.__prev_->__next_ = __c.__end_.__next_->__prev_ = __c.__end_as_link();
 }
 
+// begin-diagnostic-helpers
+// std::list can only have non-cv-qualified object types as its value type, which we diagnose. In order
+// to short-cirucit redundant (and cryptic) diagnostics, std::list must be the one to fire the static_assert.
+// Since std::list inherits from __list_imp, we also need to create specialisations for the rejected types,
+// so that everything appears "good" to std::list (otherwise we'll get lots of unhelpful diagnostics that
+// suppress the one std::list offers).
+template <class _Tp, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp const, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Tp, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp volatile, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Tp, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp&, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Tp, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp&&, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Tp, class... _Args, class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp(_Args...), _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+
+template <class _Alloc>
+class _LIBCPP_TEMPLATE_VIS __list_imp<void, _Alloc> {
+public:
+  using __node_allocator = void*;
+  using __node_alloc_traits = void*;
+  using __node_pointer = void*;
+  using __node_type = void*;
+  using __node_base = void*;
+  using __node_base_pointer = void*;
+  using __link_pointer = void*;
+
+  using pointer = int*;
+  using const_pointer = int const*;
+  using size_type = unsigned int;
+  using difference_type = int;
+  using iterator = int*;
+  using const_iterator = int const*;
+};
+// end-diagnostic-helpers
+
 template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS list : private __list_imp<_Tp, _Alloc> {
+  static_assert(!is_const<_Tp>::value, "'std::list' can only hold non-const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::list' can only hold non-volatile types");
+  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::list' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Tp>::value, "'std::list' can only hold object types; references are not objects");
+  static_assert(!is_function<_Tp>::value, "'std::list' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Tp>::value, "'std::list' can only hold object types; 'void' is not an object");
+
   typedef __list_imp<_Tp, _Alloc> base;
   typedef typename base::__node_type __node_type;
   typedef typename base::__node_allocator __node_allocator;
diff --git a/libcxx/include/map b/libcxx/include/map
index 02bd17ccb4e8cb..4653058b0e52f2 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -592,6 +592,9 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__ranges/from_range.h>
 #include <__tree>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
 #include <__utility/forward.h>
 #include <__utility/piecewise_construct.h>
 #include <__utility/swap.h>
@@ -962,6 +965,13 @@ public:
 
 template <class _Key, class _Tp, class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS map {
+  static_assert(!is_lvalue_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::map::key_type' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_lvalue_reference<_Key>::value, "'std::map::key_type' can only hold object types; references are not objects");
+  static_assert(!is_function<_Key>::value, "'std::map::key_type' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Key>::value, "'std::map::key_type' can only hold object types; 'void' is not an object");
+
+  static_assert(!is_function<_Tp>::value, "'std::map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)");
+  static_assert(!is_void<_Tp>::value, "'std::map::mapped_type' can only hold object types; 'void' is not an object");
 public:
   // types:
   typedef _Key key_type;
@@ -1639,6 +1649,13 @@ erase_if(map<_Key, _Tp, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Tp, class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS multimap {
+  static_assert(!is_lvalue_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_lvalue_reference<_Key>::value, "'std::multimap::key_type' can only hold object types; references are not objects");
+  static_assert(!is_function<_Key>::value, "'std::multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Key>::value, "'std::multimap::key_type' can only hold object types; 'void' is not an object");
+
+  static_assert(!is_function<_Tp>::value, "'std::multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)");
+  static_assert(!is_void<_Tp>::value, "'std::multimap::mapped_type' can only hold object types; 'void' is not an object");
 public:
   // types:
   typedef _Key key_type;
diff --git a/libcxx/include/set b/libcxx/include/set
index 94533583798699..c5c1f664283648 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -531,6 +531,12 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__ranges/from_range.h>
 #include <__tree>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/remove_reference.h>
 #include <__utility/forward.h>
 #include <version>
 
@@ -561,6 +567,12 @@ class multiset;
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS set {
+  static_assert(!is_const<_Key>::value, "'std::set' can only hold non-const types");
+  static_assert(!is_volatile<_Key>::value, "'std::set' can only hold non-volatile types");
+  static_assert(!is_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::set' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Key>::value, "'std::set' can only hold object types; references are not objects");
+  static_assert(!is_function<_Key>::value, "'std::set' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Key>::value, "'std::set' can only hold object types; 'void' is not an object");
 public:
   // types:
   typedef _Key key_type;
@@ -1015,6 +1027,12 @@ erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS multiset {
+  static_assert(!is_const<_Key>::value, "'std::multiset' can only hold non-const types");
+  static_assert(!is_volatile<_Key>::value, "'std::multiset' can only hold non-volatile types");
+  static_assert(!is_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::multiset' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Key>::value, "'std::multiset' can only hold object types; references are not objects");
+  static_assert(!is_function<_Key>::value, "'std::multiset' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Key>::value, "'std::multiset' can only hold object types; 'void' is not an object");
 public:
   // types:
   typedef _Key key_type;
diff --git a/libcxx/include/string b/libcxx/include/string
index 05d42afb7c9c3d..3611e2c7b8ac61 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -622,15 +622,21 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
+#include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
 #include <__type_traits/is_nothrow_assignable.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_standard_layout.h>
 #include <__type_traits/is_trivial.h>
 #include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/is_volatile.h>
 #include <__type_traits/noexcept_move_assign_container.h>
 #include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
 #include <__type_traits/void_t.h>
 #include <__utility/auto_cast.h>
 #include <__utility/declval.h>
@@ -751,6 +757,13 @@ struct __init_with_sentinel_tag {};
 template <class _CharT, class _Traits, class _Allocator>
 class basic_string {
 private:
+  static_assert(!is_const<_CharT>::value, "'std::basic_string' can only hold non-const types");
+  static_assert(!is_volatile<_CharT>::value, "'std::basic_string' can only hold non-volatile types");
+  static_assert(!is_reference<_CharT>::value || !is_function<typename remove_reference<_CharT>::type>::value, "'std::basic_string' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_CharT>::value, "'std::basic_string' can only hold object types; references are not objects");
+  static_assert(!is_function<_CharT>::value, "'std::basic_string' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_CharT>::value, "'std::basic_string' can only hold object types; 'void' is not an object");
+
   using __default_allocator_type = allocator<_CharT>;
 
 public:
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index acf3485e644afb..eadafa4ad31294 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -601,6 +601,9 @@ template <class Key, class T, class Hash, class Pred, class Alloc>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/forward.h>
 #include <stdexcept>
@@ -1024,6 +1027,13 @@ template <class _Key,
           class _Pred  = equal_to<_Key>,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_map {
+  static_assert(!is_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::unordered_map::key_type' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Key>::value, "'std::unordered_map::key_type' can only hold object types; references are not objects");
+  static_assert(!is_function<_Key>::value, "'std::unordered_map::key_type' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Key>::value, "'std::unordered_map::key_type' can only hold object types; 'void' is not an object");
+
+  static_assert(!is_function<_Tp>::value, "'std::unordered_map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)");
+  static_assert(!is_void<_Tp>::value, "'std::unordered_map::mapped_type' can only hold object types; 'void' is not an object");
 public:
   // types
   typedef _Key key_type;
@@ -1827,6 +1837,13 @@ template <class _Key,
           class _Pred  = equal_to<_Key>,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_multimap {
+  static_assert(!is_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::unordered_multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Key>::value, "'std::unordered_multimap::key_type' can only hold object types; references are not objects");
+  static_assert(!is_function<_Key>::value, "'std::unordered_multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Key>::value, "'std::unordered_multimap::key_type' can only hold object types; 'void' is not an object");
+
+  static_assert(!is_function<_Tp>::value, "'std::unordered_multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)");
+  static_assert(!is_void<_Tp>::value, "'std::unordered_multimap::mapped_type' can only hold object types; 'void' is not an object");
 public:
   // types
   typedef _Key key_type;
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index d11ceacba81433..ffa920fa2b585b 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -549,6 +549,12 @@ template <class Value, class Hash, class Pred, class Alloc>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_reference.h>
 #include <__utility/forward.h>
 #include <version>
 
@@ -579,6 +585,12 @@ class unordered_multiset;
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_set {
+  static_assert(!is_const<_Value>::value, "'std::unordered_set' can only hold non-const types");
+  static_assert(!is_volatile<_Value>::value, "'std::unordered_set' can only hold non-volatile types");
+  static_assert(!is_reference<_Value>::value || !is_function<typename remove_reference<_Value>::type>::value, "'std::unordered_set' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Value>::value, "'std::unordered_set' can only hold object types; references are not objects");
+  static_assert(!is_function<_Value>::value, "'std::unordered_set' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Value>::value, "'std::unordered_set' can only hold object types; 'void' is not an object");
 public:
   // types
   typedef _Value key_type;
@@ -1174,6 +1186,12 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_set<_Value, _Hash,
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_multiset {
+  static_assert(!is_const<_Value>::value, "'std::unordered_multiset' can only hold non-const types");
+  static_assert(!is_volatile<_Value>::value, "'std::unordered_multiset' can only hold non-volatile types");
+  static_assert(!is_reference<_Value>::value || !is_function<typename remove_reference<_Value>::type>::value, "'std::unordered_multiset' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Value>::value, "'std::unordered_multiset' can only hold object types; references are not objects");
+  static_assert(!is_function<_Value>::value, "'std::unordered_multiset' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Value>::value, "'std::unordered_multiset' can only hold object types; 'void' is not an object");
 public:
   // types
   typedef _Value key_type;
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 81aab9407714cc..21b528be1da29e 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -346,8 +346,14 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
 #include <__ranges/size.h>
 #include <__split_buffer>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_const.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_function.h>
 #include <__type_traits/is_nothrow_assignable.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/remove_reference.h>
 #include <__type_traits/noexcept_move_assign_container.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/exception_guard.h>
@@ -387,6 +393,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp, class _Allocator /* = allocator<_Tp> */>
 class _LIBCPP_TEMPLATE_VIS vector {
 private:
+  static_assert(!is_const<_Tp>::value, "'std::vector' can only hold non-const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::vector' can only hold non-volatile types");
+  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::vector' can only hold object types; function references are not objects (consider using a function pointer)");
+  static_assert(!is_reference<_Tp>::value, "'std::vector' can only hold object types; references are not objects");
+  static_assert(!is_function<_Tp>::value, "'std::vector' can only hold object types; functions are not objects (consider using a function pointer)");
+  static_assert(!is_void<_Tp>::value, "'std::vector' can only hold object types; 'void' is not an object");
+
   typedef allocator<_Tp> __default_allocator_type;
 
 public:
diff --git a/libcxx/test/libcxx/containers/associative/map/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/map/non_cv_object_types.verify.cpp
new file mode 100644
index 00000000000000..ebd292c17c4aab
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/map/non_cv_object_types.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form maps of object types.
+
+#include <map>
+
+std::map<const int, int> K1;
+std::map<volatile int, int> K2;
+std::map<int&, int>
+    K3; // expected-error@*:*{{'std::map::key_type' can only hold object types; references are not objects}}
+std::map<int const&, int>
+    K4; // expected-error@*:*{{'std::map::key_type' can only hold object types; references are not objects}}
+std::map<int&&, int> K5;
+std::map<int const&&, int> K6;
+std::map<int(), int>
+    K7; // expected-error@*:*{{'std::map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::map<int(int), int>
+    K8; // expected-error@*:*{{'std::map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::map<int(int, int), int>
+    K9; // expected-error@*:*{{'std::map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::map<int (&)(), int>
+    K10; // expected-error@*:*{{'std::map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::map<int (&)(int), int>
+    K11; // expected-error@*:*{{'std::map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::map<int (&)(int, int), int>
+    K12; // expected-error@*:*{{'std::map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::map<int (&&)(), int> K13;
+std::map<int (&&)(int), int> K14;
+std::map<int (&&)(int, int), int> K15;
+std::map<void, int>
+    K16; // expected-error@*:*{{'std::map::key_type' can only hold object types; 'void' is not an object}}
+
+std::map<int, const int> M1;
+std::map<int, volatile int> M2;
+std::map<int, int&> M3;
+std::map<int, int&&> M4;
+std::map<int, int()>
+    M5; // expected-error@*:*{{'std::map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::map<int, int(int)>
+    M6; // expected-error@*:*{{'std::map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::map<int, int(int, int)>
+    M7; // expected-error@*:*{{'std::map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::map<int, int (&)()> M8;
+std::map<int, int (&)(int)> M9;
+std::map<int, int (&)(int, int)> M10;
+std::map<int, void>
+    M11; // expected-error@*:*{{'std::map::mapped_type' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/associative/multimap/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/multimap/non_cv_object_types.verify.cpp
new file mode 100644
index 00000000000000..d9f067d1f63b1c
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/multimap/non_cv_object_types.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form multimaps of object types.
+
+#include <map>
+
+std::multimap<const int, int> K1;
+std::multimap<volatile int, int> K2;
+std::multimap<int&, int>
+    K3; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; references are not objects}}
+std::multimap<int const&, int>
+    K4; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; references are not objects}}
+std::multimap<int&&, int> K5;
+std::multimap<int const&&, int> K6;
+std::multimap<int(), int>
+    K7; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::multimap<int(int), int>
+    K8; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::multimap<int(int, int), int>
+    K9; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::multimap<int (&)(), int>
+    K10; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::multimap<int (&)(int), int>
+    K11; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::multimap<int (&)(int, int), int>
+    K12; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::multimap<int (&&)(), int> K13;
+std::multimap<int (&&)(int), int> K14;
+std::multimap<int (&&)(int, int), int> K15;
+std::multimap<void, int>
+    K16; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; 'void' is not an object}}
+
+std::multimap<int, const int> M1;
+std::multimap<int, volatile int> M2;
+std::multimap<int, int&> M3;
+std::multimap<int, int&&> M4;
+std::multimap<int, int()>
+    M5; // expected-error@*:*{{'std::multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::multimap<int, int(int)>
+    M6; // expected-error@*:*{{'std::multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::multimap<int, int(int, int)>
+    M7; // expected-error@*:*{{'std::multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::multimap<int, int (&)()> M8;
+std::multimap<int, int (&)(int)> M9;
+std::multimap<int, int (&)(int, int)> M10;
+std::multimap<int, void>
+    M11; // expected-error@*:*{{'std::multimap::mapped_type' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
new file mode 100644
index 00000000000000..920486307588ac
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form multisets of object types.
+
+#include <set>
+
+std::multiset<const int> C1;    // expected-error@*:* {{'std::multiset' can only hold non-const types}}
+std::multiset<volatile int> C2; // expected-error@*:* {{'std::multiset' can only hold non-volatile types}}
+std::multiset<int&> C3;  // expected-error@*:*{{'std::multiset' can only hold object types; references are not objects}}
+std::multiset<int&&> C4; // expected-error@*:*{{'std::multiset' can only hold object types; references are not objects}}
+std::multiset<int()>
+    C5; // expected-error@*:*{{'std::multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::multiset<int(int)>
+    C6; // expected-error@*:*{{'std::multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::multiset<int(int, int)>
+    C7; // expected-error@*:*{{'std::multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::multiset<int (&)()>
+    C8; // expected-error@*:*{{'std::multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::multiset<int (&)(int)>
+    C9; // expected-error@*:*{{'std::multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::multiset<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::multiset<void> C11; // expected-error@*:*{{'std::multiset' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
new file mode 100644
index 00000000000000..e517f0bf825834
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form sets of object types.
+
+#include <set>
+
+std::set<const int> C1;    // expected-error@*:* {{'std::set' can only hold non-const types}}
+std::set<volatile int> C2; // expected-error@*:* {{'std::set' can only hold non-volatile types}}
+std::set<int&> C3;         // expected-error@*:*{{'std::set' can only hold object types; references are not objects}}
+std::set<int&&> C4;        // expected-error@*:*{{'std::set' can only hold object types; references are not objects}}
+std::set<int()>
+    C5; // expected-error@*:*{{'std::set' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::set<int(int)>
+    C6; // expected-error@*:*{{'std::set' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::set<int(int, int)>
+    C7; // expected-error@*:*{{'std::set' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::set<int (&)()>
+    C8; // expected-error@*:*{{'std::set' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::set<int (&)(int)>
+    C9; // expected-error@*:*{{'std::set' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::set<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::set' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::set<void> C11; // expected-error@*:*{{'std::set' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..7bc8a7454709a3
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form arrays of object types.
+
+#include <array>
+
+std::array<const int, 1> A1{};  // okay
+std::array<volatile int, 1> A2; // okay
+
+int I;
+
+std::array<int&, 1> A3{I}; // expected-error@*:*{{'std::array' can only hold object types; references are not objects}}
+std::array<int&&, 1> A4{
+    static_cast<int&&>(I)}; // expected-error@*:*{{'std::array' can only hold object types; references are not objects}}
+std::array<int(), 1>
+    A5; // expected-error@*:*{{'std::array' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::array<int (&)(), 1>
+    A6; // expected-error@*:*{{'std::array' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::array<void, 1> A7; // expected-error@*:*{{'std::array' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..229829017c0394
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form deques of object types.
+
+#include <deque>
+
+std::deque<const int> C1;    // expected-error@*:* {{'std::deque' can only hold non-const types}}
+std::deque<volatile int> C2; // expected-error@*:* {{'std::deque' can only hold non-volatile types}}
+std::deque<int&> C3;  // expected-error@*:*{{'std::deque' can only hold object types; references are not objects}}
+std::deque<int&&> C4; // expected-error@*:*{{'std::deque' can only hold object types; references are not objects}}
+std::deque<int()>
+    C5; // expected-error@*:*{{'std::deque' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::deque<int(int)>
+    C6; // expected-error@*:*{{'std::deque' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::deque<int(int, int)>
+    C7; // expected-error@*:*{{'std::deque' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::deque<int (&)()>
+    C8; // expected-error@*:*{{'std::deque' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::deque<int (&)(int)>
+    C9; // expected-error@*:*{{'std::deque' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::deque<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::deque' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::deque<void> C11; // expected-error@*:*{{'std::deque' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..542a03a284d256
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form forward_lists of object types.
+
+#include <forward_list>
+
+std::forward_list<const int> C1;    // expected-error@*:* {{'std::forward_list' can only hold non-const types}}
+std::forward_list<volatile int> C2; // expected-error@*:* {{'std::forward_list' can only hold non-volatile types}}
+std::forward_list<int&>
+    C3; // expected-error@*:*{{'std::forward_list' can only hold object types; references are not objects}}
+std::forward_list<int&&>
+    C4; // expected-error@*:*{{'std::forward_list' can only hold object types; references are not objects}}
+std::forward_list<int()>
+    C5; // expected-error@*:*{{'std::forward_list' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::forward_list<int(int)>
+    C6; // expected-error@*:*{{'std::forward_list' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::forward_list<int(int, int)>
+    C7; // expected-error@*:*{{'std::forward_list' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::forward_list<int (&)()>
+    C8; // expected-error@*:*{{'std::forward_list' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::forward_list<int (&)(int)>
+    C9; // expected-error@*:*{{'std::forward_list' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::forward_list<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::forward_list' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::forward_list<void>
+    C11; // expected-error@*:*{{'std::forward_list' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..741a7e4cebcadc
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form lists of object types.
+
+#include <list>
+
+std::list<const int> C1;    // expected-error@*:* {{'std::list' can only hold non-const types}}
+std::list<volatile int> C2; // expected-error@*:* {{'std::list' can only hold non-volatile types}}
+std::list<int&> C3;         // expected-error@*:*{{'std::list' can only hold object types; references are not objects}}
+std::list<int&&> C4;        // expected-error@*:*{{'std::list' can only hold object types; references are not objects}}
+std::list<int()>
+    C5; // expected-error@*:*{{'std::list' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::list<int(int)>
+    C6; // expected-error@*:*{{'std::list' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::list<int(int, int)>
+    C7; // expected-error@*:*{{'std::list' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::list<int (&)()>
+    C8; // expected-error@*:*{{'std::list' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::list<int (&)(int)>
+    C9; // expected-error@*:*{{'std::list' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::list<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::list' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::list<void> C11; // expected-error@*:*{{'std::list' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..8f37c218bbeeaf
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form vectors of object types.
+
+#include <vector>
+
+std::vector<const int> C1;    // expected-error@*:* {{'std::vector' can only hold non-const types}}
+std::vector<volatile int> C2; // expected-error@*:* {{'std::vector' can only hold non-volatile types}}
+std::vector<int&> C3;  // expected-error@*:*{{'std::vector' can only hold object types; references are not objects}}
+std::vector<int&&> C4; // expected-error@*:*{{'std::vector' can only hold object types; references are not objects}}
+std::vector<int()>
+    C5; // expected-error@*:*{{'std::vector' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::vector<int(int)>
+    C6; // expected-error@*:*{{'std::vector' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::vector<int(int, int)>
+    C7; // expected-error@*:*{{'std::vector' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::vector<int (&)()>
+    C8; // expected-error@*:*{{'std::vector' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::vector<int (&)(int)>
+    C9; // expected-error@*:*{{'std::vector' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::vector<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::vector' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::vector<void> C11; // expected-error@*:*{{'std::vector' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/strings/basic.string/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/strings/basic.string/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..16b226dd557dec
--- /dev/null
+++ b/libcxx/test/libcxx/containers/strings/basic.string/non_cv_objects.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form basic_strings of object types.
+
+#include <string>
+
+std::basic_string<const int> C1;    // expected-error@*:* {{'std::basic_string' can only hold non-const types}}
+std::basic_string<volatile int> C2; // expected-error@*:* {{'std::basic_string' can only hold non-volatile types}}
+std::basic_string<int&>
+    C3; // expected-error@*:*{{'std::basic_string' can only hold object types; references are not objects}}
+std::basic_string<int&&>
+    C4; // expected-error@*:*{{'std::basic_string' can only hold object types; references are not objects}}
+std::basic_string<int()>
+    C5; // expected-error@*:*{{'std::basic_string' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::basic_string<int(int)>
+    C6; // expected-error@*:*{{'std::basic_string' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::basic_string<int(int, int)>
+    C7; // expected-error@*:*{{'std::basic_string' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::basic_string<int (&)()>
+    C8; // expected-error@*:*{{'std::basic_string' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::basic_string<int (&)(int)>
+    C9; // expected-error@*:*{{'std::basic_string' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::basic_string<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::basic_string' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::basic_string<void>
+    C11; // expected-error@*:*{{'std::basic_string' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_object_types.verify.cpp
new file mode 100644
index 00000000000000..7eb3ec7048531f
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_object_types.verify.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form maps of object types.
+
+#include <unordered_map>
+
+std::unordered_map<int&, int>
+    K3; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; references are not objects}}
+std::unordered_map<int&&, int>
+    K4; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; references are not objects}}
+std::unordered_map<int(), int>
+    K5; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_map<int(int), int>
+    K6; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_map<int(int, int), int>
+    K7; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_map<int (&)(), int>
+    K8; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_map<int (&)(int), int>
+    K9; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_map<int (&)(int, int), int>
+    K10; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_map<void, int>
+    K11; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; 'void' is not an object}}
+
+std::unordered_map<int, const int> M1;
+std::unordered_map<int, volatile int> M2;
+std::unordered_map<int, int&> M3;
+std::unordered_map<int, int&&> M4;
+std::unordered_map<int, int()>
+    M5; // expected-error@*:*{{'std::unordered_map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::unordered_map<int, int(int)>
+    M6; // expected-error@*:*{{'std::unordered_map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::unordered_map<int, int(int, int)>
+    M7; // expected-error@*:*{{'std::unordered_map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::unordered_map<int, int (&)()> M8;
+std::unordered_map<int, int (&)(int)> M9;
+std::unordered_map<int, int (&)(int, int)> M10;
+std::unordered_map<int, void>
+    M11; // expected-error@*:*{{'std::unordered_map::mapped_type' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_object_types.verify.cpp
new file mode 100644
index 00000000000000..49bae8ff93172c
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_object_types.verify.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form multimaps of object types.
+
+#include <unordered_map>
+
+std::unordered_multimap<int&, int>
+    K3; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; references are not objects}}
+std::unordered_multimap<int&&, int>
+    K4; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; references are not objects}}
+std::unordered_multimap<int(), int>
+    K5; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_multimap<int(int), int>
+    K6; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_multimap<int(int, int), int>
+    K7; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_multimap<int (&)(), int>
+    K8; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_multimap<int (&)(int), int>
+    K9; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_multimap<int (&)(int, int), int>
+    K10; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_multimap<void, int>
+    K11; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; 'void' is not an object}}
+
+std::unordered_multimap<int, const int> M1;
+std::unordered_multimap<int, volatile int> M2;
+std::unordered_multimap<int, int&> M3;
+std::unordered_multimap<int, int&&> M4;
+std::unordered_multimap<int, int()>
+    M5; // expected-error@*:*{{'std::unordered_multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::unordered_multimap<int, int(int)>
+    M6; // expected-error@*:*{{'std::unordered_multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::unordered_multimap<int, int(int, int)>
+    M7; // expected-error@*:*{{'std::unordered_multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
+std::unordered_multimap<int, int (&)()> M8;
+std::unordered_multimap<int, int (&)(int)> M9;
+std::unordered_multimap<int, int (&)(int, int)> M10;
+std::unordered_multimap<int, void>
+    M11; // expected-error@*:*{{'std::unordered_multimap::mapped_type' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_object_types.verify.cpp
new file mode 100644
index 00000000000000..702b0c2eb5044c
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_object_types.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form unordered_multisets of object types.
+
+#include <unordered_set>
+
+std::unordered_multiset<const int> C1; // expected-error@*:* {{'std::unordered_multiset' can only hold non-const types}}
+std::unordered_multiset<volatile int>
+    C2; // expected-error@*:* {{'std::unordered_multiset' can only hold non-volatile types}}
+std::unordered_multiset<int&>
+    C3; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; references are not objects}}
+std::unordered_multiset<int&&>
+    C4; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; references are not objects}}
+std::unordered_multiset<int()>
+    C5; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_multiset<int(int)>
+    C6; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_multiset<int(int, int)>
+    C7; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_multiset<int (&)()>
+    C8; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_multiset<int (&)(int)>
+    C9; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_multiset<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_multiset<void>
+    C11; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_object_types.verify.cpp
new file mode 100644
index 00000000000000..e090128d20bdc8
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_object_types.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form unordered_sets of object types.
+
+#include <unordered_set>
+
+std::unordered_set<const int> C1;    // expected-error@*:* {{'std::unordered_set' can only hold non-const types}}
+std::unordered_set<volatile int> C2; // expected-error@*:* {{'std::unordered_set' can only hold non-volatile types}}
+std::unordered_set<int&>
+    C3; // expected-error@*:*{{'std::unordered_set' can only hold object types; references are not objects}}
+std::unordered_set<int&&>
+    C4; // expected-error@*:*{{'std::unordered_set' can only hold object types; references are not objects}}
+std::unordered_set<int()>
+    C5; // expected-error@*:*{{'std::unordered_set' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_set<int(int)>
+    C6; // expected-error@*:*{{'std::unordered_set' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_set<int(int, int)>
+    C7; // expected-error@*:*{{'std::unordered_set' can only hold object types; functions are not objects (consider using a function pointer)}}
+std::unordered_set<int (&)()>
+    C8; // expected-error@*:*{{'std::unordered_set' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_set<int (&)(int)>
+    C9; // expected-error@*:*{{'std::unordered_set' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_set<int (&)(int, int)>
+    C10; // expected-error@*:*{{'std::unordered_set' can only hold object types; function references are not objects (consider using a function pointer)}}
+std::unordered_set<void>
+    C11; // expected-error@*:*{{'std::unordered_set' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/memory/allocator_non_cv_objects_only.verify.cpp b/libcxx/test/libcxx/memory/allocator_non_cv_objects_only.verify.cpp
new file mode 100644
index 00000000000000..9e111ab3a1691e
--- /dev/null
+++ b/libcxx/test/libcxx/memory/allocator_non_cv_objects_only.verify.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// http://wg21.link/LWG2447 gives implementors freedom to reject cv-qualified and non-object types in `std::allocator`.
+
+#include <memory>
+#include "test_macros.h"
+
+std::allocator<const int> A1;    // expected-error@*:* {{'std::allocator' can only allocate non-const object types}}
+std::allocator<volatile int> A2; // expected-error@*:* {{'std::allocator' can only allocate non-volatile object types}}
+std::allocator<int&>
+    A3; // expected-error@*:*{{'std::allocator' can only allocate object types; references are not objects}}
+std::allocator<int&&>
+    A4; // expected-error@*:*{{'std::allocator' can only allocate object types; references are not objects}}
+std::allocator<int()>
+    A5; // expected-error@*:*{{'std::allocator' can only allocate object types; functions are not objects (consider using a function pointer)}}
+std::allocator<int(int)>
+    A6; // expected-error@*:*{{'std::allocator' can only allocate object types; functions are not objects (consider using a function pointer)}}
+std::allocator<int(int, int)>
+    A7; // expected-error@*:*{{'std::allocator' can only allocate object types; functions are not objects (consider using a function pointer)}}
diff --git a/libcxx/test/libcxx/memory/allocator_volatile.verify.cpp b/libcxx/test/libcxx/memory/allocator_volatile.verify.cpp
deleted file mode 100644
index 53fdc08e7a024d..00000000000000
--- a/libcxx/test/libcxx/memory/allocator_volatile.verify.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// http://wg21.link/LWG2447 gives implementors freedom to reject const or volatile types in `std::allocator`.
-
-#include <memory>
-
-std::allocator<const int> A1; // expected-error@*:* {{std::allocator does not support const types}}
-std::allocator<volatile int> A2; // expected-error@*:* {{std::allocator does not support volatile types}}

>From 1e6b81b9b62abbb0c90059a77fadfec205ab378a Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 00:35:26 +0000
Subject: [PATCH 02/12] rewords diagnostics per internal feedback and adds
 arrays

---
 libcxx/include/__memory/allocator.h           | 12 ++--
 libcxx/include/array                          |  8 +--
 libcxx/include/deque                          | 13 ++--
 libcxx/include/forward_list                   | 13 ++--
 libcxx/include/list                           | 13 ++--
 libcxx/include/map                            | 23 +++----
 libcxx/include/set                            | 25 +++----
 libcxx/include/string                         | 13 ++--
 libcxx/include/unordered_map                  | 23 +++----
 libcxx/include/unordered_set                  | 25 +++----
 libcxx/include/vector                         | 13 ++--
 .../map/non_cv_object_types.verify.cpp        | 53 ---------------
 .../associative/map/non_cv_objects.verify.cpp | 47 +++++++++++++
 .../multimap/non_cv_object.verify.cpp         | 47 +++++++++++++
 .../multimap/non_cv_object_types.verify.cpp   | 53 ---------------
 .../multiset/non_cv_object_types.verify.cpp   | 38 ++++++-----
 .../set/non_cv_object_types.verify.cpp        | 38 ++++++-----
 .../sequences/array/non_cv_objects.verify.cpp | 26 +++++---
 .../sequences/deque/non_cv_objects.verify.cpp | 38 ++++++-----
 .../forward_list/non_cv_objects.verify.cpp    | 41 ++++++------
 .../sequences/list/non_cv_objects.verify.cpp  | 38 ++++++-----
 .../vector/non_cv_objects.verify.cpp          | 38 ++++++-----
 .../basic.string/non_cv_objects.verify.cpp    | 49 ++++++++------
 .../unord.map/non_cv_object_types.verify.cpp  | 46 -------------
 .../unord/unord.map/non_cv_objects.verify.cpp | 66 +++++++++++++++++++
 .../non_cv_object_types.verify.cpp            | 46 -------------
 .../unord.multimap/non_cv_objects.verify.cpp  | 66 +++++++++++++++++++
 .../non_cv_object_types.verify.cpp            | 33 ----------
 .../unord.multiset/non_cv_objects.verify.cpp  | 33 ++++++++++
 .../unord.set/non_cv_object_types.verify.cpp  | 32 ---------
 .../unord/unord.set/non_cv_objects.verify.cpp | 33 ++++++++++
 .../allocator_non_cv_objects_only.verify.cpp  | 26 ++++----
 .../strings/basic.string/char.bad.verify.cpp  |  2 +-
 33 files changed, 564 insertions(+), 506 deletions(-)
 delete mode 100644 libcxx/test/libcxx/containers/associative/map/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/associative/map/non_cv_objects.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/associative/multimap/non_cv_object.verify.cpp
 delete mode 100644 libcxx/test/libcxx/containers/associative/multimap/non_cv_object_types.verify.cpp
 delete mode 100644 libcxx/test/libcxx/containers/unord/unord.map/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
 delete mode 100644 libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
 delete mode 100644 libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
 delete mode 100644 libcxx/test/libcxx/containers/unord/unord.set/non_cv_object_types.verify.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp

diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index 36890f4e1a97d6..ade678e6542a6a 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -79,16 +79,12 @@ struct __non_trivial_if<true, _Unique> {
 
 template <class _Tp>
 class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::value, allocator<_Tp> > {
-  static_assert(!is_const<_Tp>::value, "'std::allocator' can only allocate non-const object types");
-  static_assert(!is_volatile<_Tp>::value, "'std::allocator' can only allocate non-volatile object types");
-  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value,
-                "'std::allocator' can only allocate object types; function references are not objects (consider using "
-                "a function pointer)");
-  static_assert(!is_reference<_Tp>::value,
-                "'std::allocator' can only allocate object types; references are not objects");
+  static_assert(!is_const<_Tp>::value, "'std::allocator' cannot allocate const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::allocator' cannot allocate volatile types");
+  static_assert(!is_reference<_Tp>::value, "'std::allocator' cannot allocate references");
   static_assert(
       !is_function<_Tp>::value,
-      "'std::allocator' can only allocate object types; functions are not objects (consider using a function pointer)");
+      "'std::allocator' cannot allocate functions");
 
 public:
   typedef size_t size_type;
diff --git a/libcxx/include/array b/libcxx/include/array
index 0476f57a86ac24..e982109097d402 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -171,10 +171,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp, size_t _Size>
 struct _LIBCPP_TEMPLATE_VIS array {
-  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::array' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Tp>::value, "'std::array' can only hold object types; references are not objects");
-  static_assert(!is_function<_Tp>::value, "'std::array' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Tp>::value, "'std::array' can only hold object types; 'void' is not an object");
+  static_assert(!is_reference<_Tp>::value, "'std::array' cannot hold references");
+  static_assert(!is_function<_Tp>::value, "'std::array' cannot hold functions");
+  static_assert(!is_void<_Tp>::value, "'std::array' cannot hold 'void'");
+  static_assert(!__is_unbounded_array(_Tp), "'std::array' cannot hold C arrays of an unknown size");
 
   using __trivially_relocatable = __conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, array, void>;
 
diff --git a/libcxx/include/deque b/libcxx/include/deque
index df0ddaebc39e92..eb857c20c1c818 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -212,6 +212,7 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/size.h>
 #include <__split_buffer>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_function.h>
@@ -474,12 +475,12 @@ const _DiffType __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer,
 
 template <class _Tp, class _Allocator /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS deque {
-  static_assert(!is_const<_Tp>::value, "'std::deque' can only hold non-const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::deque' can only hold non-volatile types");
-  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::deque' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Tp>::value, "'std::deque' can only hold object types; references are not objects");
-  static_assert(!is_function<_Tp>::value, "'std::deque' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Tp>::value, "'std::deque' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_Tp>::value, "'std::deque' cannot hold const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::deque' cannot hold volatile types");
+  static_assert(!is_reference<_Tp>::value, "'std::deque' cannot hold references");
+  static_assert(!is_function<_Tp>::value, "'std::deque' cannot hold functions");
+  static_assert(!is_void<_Tp>::value, "'std::deque' cannot hold 'void'");
+  static_assert(!is_array<_Tp>::value, "'std::deque' cannot hold C arrays");
 public:
   // types:
 
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 2b6ad3bc100f90..a69013c1cf3a6b 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -219,6 +219,7 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/from_range.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_nothrow_assignable.h>
@@ -763,12 +764,12 @@ public:
 
 template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS forward_list : private __forward_list_base<_Tp, _Alloc> {
-  static_assert(!is_const<_Tp>::value, "'std::forward_list' can only hold non-const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::forward_list' can only hold non-volatile types");
-  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::forward_list' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Tp>::value, "'std::forward_list' can only hold object types; references are not objects");
-  static_assert(!is_function<_Tp>::value, "'std::forward_list' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Tp>::value, "'std::forward_list' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_Tp>::value, "'std::forward_list' cannot hold const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::forward_list' cannot hold volatile types");
+  static_assert(!is_reference<_Tp>::value, "'std::forward_list' cannot hold references");
+  static_assert(!is_function<_Tp>::value, "'std::forward_list' cannot hold functions");
+  static_assert(!is_void<_Tp>::value, "'std::forward_list' cannot hold 'void'");
+  static_assert(!is_array<_Tp>::value, "'std::forward_list' cannot hold C arrays");
 
   typedef __forward_list_base<_Tp, _Alloc> base;
   typedef typename base::__node_allocator __node_allocator;
diff --git a/libcxx/include/list b/libcxx/include/list
index 8da28ff1873d89..2f5d00ab8ba385 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -225,6 +225,7 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
@@ -788,12 +789,12 @@ public:
 
 template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS list : private __list_imp<_Tp, _Alloc> {
-  static_assert(!is_const<_Tp>::value, "'std::list' can only hold non-const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::list' can only hold non-volatile types");
-  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::list' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Tp>::value, "'std::list' can only hold object types; references are not objects");
-  static_assert(!is_function<_Tp>::value, "'std::list' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Tp>::value, "'std::list' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_Tp>::value, "'std::list' cannot hold const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::list' cannot hold volatile types");
+  static_assert(!is_reference<_Tp>::value, "'std::list' cannot hold references");
+  static_assert(!is_function<_Tp>::value, "'std::list' cannot hold functions");
+  static_assert(!is_void<_Tp>::value, "'std::list' cannot hold 'void'");
+  static_assert(!is_array<_Tp>::value, "'std::list' cannot hold C arrays");
 
   typedef __list_imp<_Tp, _Alloc> base;
   typedef typename base::__node_type __node_type;
diff --git a/libcxx/include/map b/libcxx/include/map
index 4653058b0e52f2..486a456f69343b 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -592,6 +592,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__ranges/from_range.h>
 #include <__tree>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_void.h>
@@ -965,13 +966,10 @@ public:
 
 template <class _Key, class _Tp, class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS map {
-  static_assert(!is_lvalue_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::map::key_type' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_lvalue_reference<_Key>::value, "'std::map::key_type' can only hold object types; references are not objects");
-  static_assert(!is_function<_Key>::value, "'std::map::key_type' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Key>::value, "'std::map::key_type' can only hold object types; 'void' is not an object");
-
-  static_assert(!is_function<_Tp>::value, "'std::map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)");
-  static_assert(!is_void<_Tp>::value, "'std::map::mapped_type' can only hold object types; 'void' is not an object");
+  static_assert(!is_lvalue_reference<_Key>::value, "'std::map' cannot hold references");
+  static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::map' cannot hold functions");
+  static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::map' cannot hold 'void'");
+  static_assert(!__is_unbounded_array(_Key), "'std::map' cannot hold C arrays");
 public:
   // types:
   typedef _Key key_type;
@@ -1649,13 +1647,10 @@ erase_if(map<_Key, _Tp, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Tp, class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS multimap {
-  static_assert(!is_lvalue_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_lvalue_reference<_Key>::value, "'std::multimap::key_type' can only hold object types; references are not objects");
-  static_assert(!is_function<_Key>::value, "'std::multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Key>::value, "'std::multimap::key_type' can only hold object types; 'void' is not an object");
-
-  static_assert(!is_function<_Tp>::value, "'std::multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)");
-  static_assert(!is_void<_Tp>::value, "'std::multimap::mapped_type' can only hold object types; 'void' is not an object");
+  static_assert(!is_lvalue_reference<_Key>::value, "'std::multimap' cannot hold references");
+  static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::multimap' cannot hold functions");
+  static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::multimap' cannot hold 'void'");
+  static_assert(!__is_unbounded_array(_Key), "'std::multimap' cannot hold C arrays");
 public:
   // types:
   typedef _Key key_type;
diff --git a/libcxx/include/set b/libcxx/include/set
index c5c1f664283648..be23050c18795e 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -530,6 +530,7 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__tree>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
@@ -567,12 +568,12 @@ class multiset;
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS set {
-  static_assert(!is_const<_Key>::value, "'std::set' can only hold non-const types");
-  static_assert(!is_volatile<_Key>::value, "'std::set' can only hold non-volatile types");
-  static_assert(!is_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::set' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Key>::value, "'std::set' can only hold object types; references are not objects");
-  static_assert(!is_function<_Key>::value, "'std::set' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Key>::value, "'std::set' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_Key>::value, "'std::set' cannot hold const types");
+  static_assert(!is_volatile<_Key>::value, "'std::set' cannot hold volatile types");
+  static_assert(!is_reference<_Key>::value, "'std::set' cannot hold references");
+  static_assert(!is_function<_Key>::value, "'std::set' cannot hold functions");
+  static_assert(!is_void<_Key>::value, "'std::set' cannot hold 'void'");
+  static_assert(!is_array<_Key>::value, "'std::set' cannot hold C arrays");
 public:
   // types:
   typedef _Key key_type;
@@ -1027,12 +1028,12 @@ erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS multiset {
-  static_assert(!is_const<_Key>::value, "'std::multiset' can only hold non-const types");
-  static_assert(!is_volatile<_Key>::value, "'std::multiset' can only hold non-volatile types");
-  static_assert(!is_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::multiset' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Key>::value, "'std::multiset' can only hold object types; references are not objects");
-  static_assert(!is_function<_Key>::value, "'std::multiset' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Key>::value, "'std::multiset' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_Key>::value, "'std::multiset' cannot hold const types");
+  static_assert(!is_volatile<_Key>::value, "'std::multiset' cannot hold volatile types");
+  static_assert(!is_reference<_Key>::value, "'std::multiset' cannot hold references");
+  static_assert(!is_function<_Key>::value, "'std::multiset' cannot hold functions");
+  static_assert(!is_void<_Key>::value, "'std::multiset' cannot hold 'void'");
+  static_assert(!is_array<_Key>::value, "'std::multiset' cannot hold C arrays");
 public:
   // types:
   typedef _Key key_type;
diff --git a/libcxx/include/string b/libcxx/include/string
index 3611e2c7b8ac61..370f7706ec57a7 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -757,12 +757,12 @@ struct __init_with_sentinel_tag {};
 template <class _CharT, class _Traits, class _Allocator>
 class basic_string {
 private:
-  static_assert(!is_const<_CharT>::value, "'std::basic_string' can only hold non-const types");
-  static_assert(!is_volatile<_CharT>::value, "'std::basic_string' can only hold non-volatile types");
-  static_assert(!is_reference<_CharT>::value || !is_function<typename remove_reference<_CharT>::type>::value, "'std::basic_string' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_CharT>::value, "'std::basic_string' can only hold object types; references are not objects");
-  static_assert(!is_function<_CharT>::value, "'std::basic_string' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_CharT>::value, "'std::basic_string' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_CharT>::value, "'std::basic_string' cannot hold const types");
+  static_assert(!is_volatile<_CharT>::value, "'std::basic_string' cannot hold volatile types");
+  static_assert(!is_reference<_CharT>::value, "'std::basic_string' cannot hold references");
+  static_assert(!is_function<_CharT>::value, "'std::basic_string' cannot hold functions");
+  static_assert(!is_void<_CharT>::value, "'std::basic_string' cannot hold 'void'");
+  static_assert(!is_array<_CharT>::value, "'std::basic_string' cannot hold C arrays");
 
   using __default_allocator_type = allocator<_CharT>;
 
@@ -827,7 +827,6 @@ public:
 #  define _LIBCPP_ASAN_VOLATILE_WRAPPER(PTR) PTR
 #endif
 
-  static_assert(!is_array<value_type>::value, "Character type of basic_string must not be an array");
   static_assert(is_standard_layout<value_type>::value, "Character type of basic_string must be standard-layout");
   static_assert(is_trivial<value_type>::value, "Character type of basic_string must be trivial");
   static_assert(is_same<_CharT, typename traits_type::char_type>::value,
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index eadafa4ad31294..27d8c6056f6867 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -601,6 +601,7 @@ template <class Key, class T, class Hash, class Pred, class Alloc>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_void.h>
@@ -1027,13 +1028,10 @@ template <class _Key,
           class _Pred  = equal_to<_Key>,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_map {
-  static_assert(!is_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::unordered_map::key_type' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Key>::value, "'std::unordered_map::key_type' can only hold object types; references are not objects");
-  static_assert(!is_function<_Key>::value, "'std::unordered_map::key_type' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Key>::value, "'std::unordered_map::key_type' can only hold object types; 'void' is not an object");
-
-  static_assert(!is_function<_Tp>::value, "'std::unordered_map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)");
-  static_assert(!is_void<_Tp>::value, "'std::unordered_map::mapped_type' can only hold object types; 'void' is not an object");
+  static_assert(!is_reference<_Key>::value, "'std::unordered_map' cannot hold references");
+  static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::unordered_map' cannot hold functions");
+  static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::unordered_map' cannot hold 'void'");
+  static_assert(!is_array<_Key>::value, "'std::unordered_map' cannot hold C arrays");
 public:
   // types
   typedef _Key key_type;
@@ -1837,13 +1835,10 @@ template <class _Key,
           class _Pred  = equal_to<_Key>,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_multimap {
-  static_assert(!is_reference<_Key>::value || !is_function<typename remove_reference<_Key>::type>::value, "'std::unordered_multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Key>::value, "'std::unordered_multimap::key_type' can only hold object types; references are not objects");
-  static_assert(!is_function<_Key>::value, "'std::unordered_multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Key>::value, "'std::unordered_multimap::key_type' can only hold object types; 'void' is not an object");
-
-  static_assert(!is_function<_Tp>::value, "'std::unordered_multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)");
-  static_assert(!is_void<_Tp>::value, "'std::unordered_multimap::mapped_type' can only hold object types; 'void' is not an object");
+  static_assert(!is_reference<_Key>::value, "'std::unordered_multimap' cannot hold references");
+  static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::unordered_multimap' cannot hold functions");
+  static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::unordered_multimap' cannot hold 'void'");
+  static_assert(!is_array<_Key>::value, "'std::unordered_multimap' cannot hold C arrays");
 public:
   // types
   typedef _Key key_type;
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index ffa920fa2b585b..c5c3afc870e893 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -549,6 +549,7 @@ template <class Value, class Hash, class Pred, class Alloc>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_volatile.h>
 #include <__type_traits/is_reference.h>
@@ -585,12 +586,12 @@ class unordered_multiset;
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_set {
-  static_assert(!is_const<_Value>::value, "'std::unordered_set' can only hold non-const types");
-  static_assert(!is_volatile<_Value>::value, "'std::unordered_set' can only hold non-volatile types");
-  static_assert(!is_reference<_Value>::value || !is_function<typename remove_reference<_Value>::type>::value, "'std::unordered_set' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Value>::value, "'std::unordered_set' can only hold object types; references are not objects");
-  static_assert(!is_function<_Value>::value, "'std::unordered_set' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Value>::value, "'std::unordered_set' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_Value>::value, "'std::unordered_set' cannot hold const types");
+  static_assert(!is_volatile<_Value>::value, "'std::unordered_set' cannot hold volatile types");
+  static_assert(!is_reference<_Value>::value, "'std::unordered_set' cannot hold references");
+  static_assert(!is_function<_Value>::value, "'std::unordered_set' cannot hold functions");
+  static_assert(!is_void<_Value>::value, "'std::unordered_set' cannot hold 'void'");
+  static_assert(!is_array<_Value>::value, "'std::unordered_set' cannot hold C arrays");
 public:
   // types
   typedef _Value key_type;
@@ -1186,12 +1187,12 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_set<_Value, _Hash,
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_multiset {
-  static_assert(!is_const<_Value>::value, "'std::unordered_multiset' can only hold non-const types");
-  static_assert(!is_volatile<_Value>::value, "'std::unordered_multiset' can only hold non-volatile types");
-  static_assert(!is_reference<_Value>::value || !is_function<typename remove_reference<_Value>::type>::value, "'std::unordered_multiset' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Value>::value, "'std::unordered_multiset' can only hold object types; references are not objects");
-  static_assert(!is_function<_Value>::value, "'std::unordered_multiset' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Value>::value, "'std::unordered_multiset' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_Value>::value, "'std::unordered_multiset' cannot hold const types");
+  static_assert(!is_volatile<_Value>::value, "'std::unordered_multiset' cannot hold volatile types");
+  static_assert(!is_reference<_Value>::value, "'std::unordered_multiset' cannot hold references");
+  static_assert(!is_function<_Value>::value, "'std::unordered_multiset' cannot hold functions");
+  static_assert(!is_void<_Value>::value, "'std::unordered_multiset' cannot hold 'void'");
+  static_assert(!is_array<_Value>::value, "'std::unordered_multiset' cannot hold C arrays");
 public:
   // types
   typedef _Value key_type;
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 21b528be1da29e..0c4d2589d0feb8 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -345,6 +345,7 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
 #include <__ranges/from_range.h>
 #include <__ranges/size.h>
 #include <__split_buffer>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_constructible.h>
@@ -393,12 +394,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp, class _Allocator /* = allocator<_Tp> */>
 class _LIBCPP_TEMPLATE_VIS vector {
 private:
-  static_assert(!is_const<_Tp>::value, "'std::vector' can only hold non-const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::vector' can only hold non-volatile types");
-  static_assert(!is_reference<_Tp>::value || !is_function<typename remove_reference<_Tp>::type>::value, "'std::vector' can only hold object types; function references are not objects (consider using a function pointer)");
-  static_assert(!is_reference<_Tp>::value, "'std::vector' can only hold object types; references are not objects");
-  static_assert(!is_function<_Tp>::value, "'std::vector' can only hold object types; functions are not objects (consider using a function pointer)");
-  static_assert(!is_void<_Tp>::value, "'std::vector' can only hold object types; 'void' is not an object");
+  static_assert(!is_const<_Tp>::value, "'std::vector' cannot hold const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::vector' cannot hold volatile types");
+  static_assert(!is_reference<_Tp>::value, "'std::vector' cannot hold references");
+  static_assert(!is_function<_Tp>::value, "'std::vector' cannot hold functions");
+  static_assert(!is_void<_Tp>::value, "'std::vector' cannot hold 'void'");
+  static_assert(!is_array<_Tp>::value, "'std::vector' cannot hold C arrays");
 
   typedef allocator<_Tp> __default_allocator_type;
 
diff --git a/libcxx/test/libcxx/containers/associative/map/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/map/non_cv_object_types.verify.cpp
deleted file mode 100644
index ebd292c17c4aab..00000000000000
--- a/libcxx/test/libcxx/containers/associative/map/non_cv_object_types.verify.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// Checks that we can only form maps of object types.
-
-#include <map>
-
-std::map<const int, int> K1;
-std::map<volatile int, int> K2;
-std::map<int&, int>
-    K3; // expected-error@*:*{{'std::map::key_type' can only hold object types; references are not objects}}
-std::map<int const&, int>
-    K4; // expected-error@*:*{{'std::map::key_type' can only hold object types; references are not objects}}
-std::map<int&&, int> K5;
-std::map<int const&&, int> K6;
-std::map<int(), int>
-    K7; // expected-error@*:*{{'std::map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::map<int(int), int>
-    K8; // expected-error@*:*{{'std::map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::map<int(int, int), int>
-    K9; // expected-error@*:*{{'std::map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::map<int (&)(), int>
-    K10; // expected-error@*:*{{'std::map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::map<int (&)(int), int>
-    K11; // expected-error@*:*{{'std::map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::map<int (&)(int, int), int>
-    K12; // expected-error@*:*{{'std::map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::map<int (&&)(), int> K13;
-std::map<int (&&)(int), int> K14;
-std::map<int (&&)(int, int), int> K15;
-std::map<void, int>
-    K16; // expected-error@*:*{{'std::map::key_type' can only hold object types; 'void' is not an object}}
-
-std::map<int, const int> M1;
-std::map<int, volatile int> M2;
-std::map<int, int&> M3;
-std::map<int, int&&> M4;
-std::map<int, int()>
-    M5; // expected-error@*:*{{'std::map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::map<int, int(int)>
-    M6; // expected-error@*:*{{'std::map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::map<int, int(int, int)>
-    M7; // expected-error@*:*{{'std::map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::map<int, int (&)()> M8;
-std::map<int, int (&)(int)> M9;
-std::map<int, int (&)(int, int)> M10;
-std::map<int, void>
-    M11; // expected-error@*:*{{'std::map::mapped_type' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/associative/map/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/associative/map/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..7c0cca74aab445
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/map/non_cv_objects.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form maps of object types.
+
+#include <map>
+
+std::map<int const, int> K1;
+std::map<int, int const> M1;
+// TODO(cjdb): turn this into a compile-time error
+
+std::map<int volatile, int> K2;
+std::map<int, int volatile> M2;
+// TODO(cjdb): turn this into a compile-time error
+
+std::map<int&, int> K3;
+std::map<int, int&> M3; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:* 1 {{'std::map' cannot hold references}}
+
+std::map<int&&, int> K4;
+std::map<int, int&&> M4;
+// TODO(cjdb): turn this into a compile-time error
+
+std::map<int(), int> K5;
+std::map<int(int), int> K6;
+std::map<int(int, int), int> K7;
+std::map<int, int()> M5;
+std::map<int, int(int)> M6;
+std::map<int, int(int, int)> M7;
+// expected-error@*:* 6 {{'std::map' cannot hold functions}}
+
+std::map<void, int> K8;
+std::map<int, void> M8;
+// expected-error@*:* 2 {{'std::map' cannot hold 'void'}}
+
+std::map<int[], int> K9;
+std::map<int, int[]> M9; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:*{{'std::map' cannot hold C arrays}}
+
+std::map<int[2], int> K10;
+std::map<int, int[2]> M10;
+// TODO(cjdb): turn this into a compile-time error
diff --git a/libcxx/test/libcxx/containers/associative/multimap/non_cv_object.verify.cpp b/libcxx/test/libcxx/containers/associative/multimap/non_cv_object.verify.cpp
new file mode 100644
index 00000000000000..a90ff6a00c2c93
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/multimap/non_cv_object.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form multimaps of object types.
+
+#include <map>
+
+std::multimap<int const, int> K1;
+std::multimap<int, int const> M1;
+// TODO(cjdb): turn this into a compile-time error
+
+std::multimap<int volatile, int> K2;
+std::multimap<int, int volatile> M2;
+// TODO(cjdb): turn this into a compile-time error
+
+std::multimap<int&, int> K3;
+std::multimap<int, int&> M3; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:* 1 {{'std::multimap' cannot hold references}}
+
+std::multimap<int&&, int> K4;
+std::multimap<int, int&&> M4;
+// TODO(cjdb): turn this into a compile-time error
+
+std::multimap<int(), int> K5;
+std::multimap<int(int), int> K6;
+std::multimap<int(int, int), int> K7;
+std::multimap<int, int()> M5;
+std::multimap<int, int(int)> M6;
+std::multimap<int, int(int, int)> M7;
+// expected-error@*:* 6 {{'std::multimap' cannot hold functions}}
+
+std::multimap<void, int> K8;
+std::multimap<int, void> M8;
+// expected-error@*:* 2 {{'std::multimap' cannot hold 'void'}}
+
+std::multimap<int[], int> K9;
+std::multimap<int, int[]> M9; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:*{{'std::multimap' cannot hold C arrays}}
+
+std::multimap<int[2], int> K10;
+std::multimap<int, int[2]> M10;
+// TODO(cjdb): turn this into a compile-time error
diff --git a/libcxx/test/libcxx/containers/associative/multimap/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/multimap/non_cv_object_types.verify.cpp
deleted file mode 100644
index d9f067d1f63b1c..00000000000000
--- a/libcxx/test/libcxx/containers/associative/multimap/non_cv_object_types.verify.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// Checks that we can only form multimaps of object types.
-
-#include <map>
-
-std::multimap<const int, int> K1;
-std::multimap<volatile int, int> K2;
-std::multimap<int&, int>
-    K3; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; references are not objects}}
-std::multimap<int const&, int>
-    K4; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; references are not objects}}
-std::multimap<int&&, int> K5;
-std::multimap<int const&&, int> K6;
-std::multimap<int(), int>
-    K7; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::multimap<int(int), int>
-    K8; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::multimap<int(int, int), int>
-    K9; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::multimap<int (&)(), int>
-    K10; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::multimap<int (&)(int), int>
-    K11; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::multimap<int (&)(int, int), int>
-    K12; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::multimap<int (&&)(), int> K13;
-std::multimap<int (&&)(int), int> K14;
-std::multimap<int (&&)(int, int), int> K15;
-std::multimap<void, int>
-    K16; // expected-error@*:*{{'std::multimap::key_type' can only hold object types; 'void' is not an object}}
-
-std::multimap<int, const int> M1;
-std::multimap<int, volatile int> M2;
-std::multimap<int, int&> M3;
-std::multimap<int, int&&> M4;
-std::multimap<int, int()>
-    M5; // expected-error@*:*{{'std::multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::multimap<int, int(int)>
-    M6; // expected-error@*:*{{'std::multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::multimap<int, int(int, int)>
-    M7; // expected-error@*:*{{'std::multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::multimap<int, int (&)()> M8;
-std::multimap<int, int (&)(int)> M9;
-std::multimap<int, int (&)(int, int)> M10;
-std::multimap<int, void>
-    M11; // expected-error@*:*{{'std::multimap::mapped_type' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
index 920486307588ac..876f2d4185a097 100644
--- a/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
@@ -10,20 +10,24 @@
 
 #include <set>
 
-std::multiset<const int> C1;    // expected-error@*:* {{'std::multiset' can only hold non-const types}}
-std::multiset<volatile int> C2; // expected-error@*:* {{'std::multiset' can only hold non-volatile types}}
-std::multiset<int&> C3;  // expected-error@*:*{{'std::multiset' can only hold object types; references are not objects}}
-std::multiset<int&&> C4; // expected-error@*:*{{'std::multiset' can only hold object types; references are not objects}}
-std::multiset<int()>
-    C5; // expected-error@*:*{{'std::multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::multiset<int(int)>
-    C6; // expected-error@*:*{{'std::multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::multiset<int(int, int)>
-    C7; // expected-error@*:*{{'std::multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::multiset<int (&)()>
-    C8; // expected-error@*:*{{'std::multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::multiset<int (&)(int)>
-    C9; // expected-error@*:*{{'std::multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::multiset<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::multiset<void> C11; // expected-error@*:*{{'std::multiset' can only hold object types; 'void' is not an object}}
+std::multiset<const int> C1;
+// expected-error@*:*{{'std::multiset' cannot hold const types}}
+
+std::multiset<volatile int> C2;
+// expected-error@*:*{{'std::multiset' cannot hold volatile types}}
+
+std::multiset<int&> C3;
+std::multiset<int&&> C4;
+// expected-error@*:* 2 {{'std::multiset' cannot hold references}}
+
+std::multiset<int()> C5;
+std::multiset<int(int)> C6;
+std::multiset<int(int, int)> C7;
+// expected-error@*:* 3 {{'std::multiset' cannot hold functions}}
+
+std::multiset<void> C8;
+// expected-error@*:*{{'std::multiset' cannot hold 'void'}}
+
+std::multiset<int[]> C9;
+std::multiset<int[2]> C10;
+// expected-error@*:* 2 {{'std::multiset' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
index e517f0bf825834..a88328b29c046b 100644
--- a/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
@@ -10,20 +10,24 @@
 
 #include <set>
 
-std::set<const int> C1;    // expected-error@*:* {{'std::set' can only hold non-const types}}
-std::set<volatile int> C2; // expected-error@*:* {{'std::set' can only hold non-volatile types}}
-std::set<int&> C3;         // expected-error@*:*{{'std::set' can only hold object types; references are not objects}}
-std::set<int&&> C4;        // expected-error@*:*{{'std::set' can only hold object types; references are not objects}}
-std::set<int()>
-    C5; // expected-error@*:*{{'std::set' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::set<int(int)>
-    C6; // expected-error@*:*{{'std::set' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::set<int(int, int)>
-    C7; // expected-error@*:*{{'std::set' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::set<int (&)()>
-    C8; // expected-error@*:*{{'std::set' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::set<int (&)(int)>
-    C9; // expected-error@*:*{{'std::set' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::set<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::set' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::set<void> C11; // expected-error@*:*{{'std::set' can only hold object types; 'void' is not an object}}
+std::set<const int> C1;
+// expected-error@*:*{{'std::set' cannot hold const types}}
+
+std::set<volatile int> C2;
+// expected-error@*:*{{'std::set' cannot hold volatile types}}
+
+std::set<int&> C3;
+std::set<int&&> C4;
+// expected-error@*:* 2 {{'std::set' cannot hold references}}
+
+std::set<int()> C5;
+std::set<int(int)> C6;
+std::set<int(int, int)> C7;
+// expected-error@*:* 3 {{'std::set' cannot hold functions}}
+
+std::set<void> C8;
+// expected-error@*:*{{'std::set' cannot hold 'void'}}
+
+std::set<int[]> C9;
+std::set<int[2]> C10;
+// expected-error@*:* 2 {{'std::set' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
index 7bc8a7454709a3..ba1d7d1c92cd2c 100644
--- a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
@@ -13,13 +13,19 @@
 std::array<const int, 1> A1{};  // okay
 std::array<volatile int, 1> A2; // okay
 
-int I;
-
-std::array<int&, 1> A3{I}; // expected-error@*:*{{'std::array' can only hold object types; references are not objects}}
-std::array<int&&, 1> A4{
-    static_cast<int&&>(I)}; // expected-error@*:*{{'std::array' can only hold object types; references are not objects}}
-std::array<int(), 1>
-    A5; // expected-error@*:*{{'std::array' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::array<int (&)(), 1>
-    A6; // expected-error@*:*{{'std::array' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::array<void, 1> A7; // expected-error@*:*{{'std::array' can only hold object types; 'void' is not an object}}
+std::array<int&, 1>  A3{};
+std::array<int&&, 1> A4{};
+// expected-error@*:* 2 {{'std::array' cannot hold references}}
+
+std::array<int(), 1> A5;
+std::array<int(int), 1> A6;
+std::array<int(int, int), 1> A7;
+// expected-error@*:* 3 {{'std::array' cannot hold functions}}
+
+std::array<void, 1> A8;
+// expected-error@*:*{{'std::array' cannot hold 'void'}}
+
+std::array<int[], 1> A9;
+// expected-error@*:*{{'std::array' cannot hold C arrays of an unknown size}}
+
+std::array<int[2], 1> A10; // okay
diff --git a/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
index 229829017c0394..75e8bfea65ada2 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
@@ -10,20 +10,24 @@
 
 #include <deque>
 
-std::deque<const int> C1;    // expected-error@*:* {{'std::deque' can only hold non-const types}}
-std::deque<volatile int> C2; // expected-error@*:* {{'std::deque' can only hold non-volatile types}}
-std::deque<int&> C3;  // expected-error@*:*{{'std::deque' can only hold object types; references are not objects}}
-std::deque<int&&> C4; // expected-error@*:*{{'std::deque' can only hold object types; references are not objects}}
-std::deque<int()>
-    C5; // expected-error@*:*{{'std::deque' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::deque<int(int)>
-    C6; // expected-error@*:*{{'std::deque' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::deque<int(int, int)>
-    C7; // expected-error@*:*{{'std::deque' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::deque<int (&)()>
-    C8; // expected-error@*:*{{'std::deque' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::deque<int (&)(int)>
-    C9; // expected-error@*:*{{'std::deque' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::deque<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::deque' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::deque<void> C11; // expected-error@*:*{{'std::deque' can only hold object types; 'void' is not an object}}
+std::deque<const int> C1;
+// expected-error@*:*{{'std::deque' cannot hold const types}}
+
+std::deque<volatile int> C2;
+// expected-error@*:*{{'std::deque' cannot hold volatile types}}
+
+std::deque<int&> C3;
+std::deque<int&&> C4;
+// expected-error@*:* 2 {{'std::deque' cannot hold references}}
+
+std::deque<int()> C5;
+std::deque<int(int)> C6;
+std::deque<int(int, int)> C7;
+// expected-error@*:* 3 {{'std::deque' cannot hold functions}}
+
+std::deque<void> C8;
+// expected-error@*:*{{'std::deque' cannot hold 'void'}}
+
+std::deque<int[]> C9;
+std::deque<int[2]> C10;
+// expected-error@*:* 2 {{'std::deque' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
index 542a03a284d256..8fc4f33f1e201c 100644
--- a/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
@@ -10,23 +10,24 @@
 
 #include <forward_list>
 
-std::forward_list<const int> C1;    // expected-error@*:* {{'std::forward_list' can only hold non-const types}}
-std::forward_list<volatile int> C2; // expected-error@*:* {{'std::forward_list' can only hold non-volatile types}}
-std::forward_list<int&>
-    C3; // expected-error@*:*{{'std::forward_list' can only hold object types; references are not objects}}
-std::forward_list<int&&>
-    C4; // expected-error@*:*{{'std::forward_list' can only hold object types; references are not objects}}
-std::forward_list<int()>
-    C5; // expected-error@*:*{{'std::forward_list' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::forward_list<int(int)>
-    C6; // expected-error@*:*{{'std::forward_list' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::forward_list<int(int, int)>
-    C7; // expected-error@*:*{{'std::forward_list' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::forward_list<int (&)()>
-    C8; // expected-error@*:*{{'std::forward_list' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::forward_list<int (&)(int)>
-    C9; // expected-error@*:*{{'std::forward_list' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::forward_list<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::forward_list' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::forward_list<void>
-    C11; // expected-error@*:*{{'std::forward_list' can only hold object types; 'void' is not an object}}
+std::forward_list<const int> C1;
+// expected-error@*:*{{'std::forward_list' cannot hold const types}}
+
+std::forward_list<volatile int> C2;
+// expected-error@*:*{{'std::forward_list' cannot hold volatile types}}
+
+std::forward_list<int&> C3;
+std::forward_list<int&&> C4;
+// expected-error@*:* 2 {{'std::forward_list' cannot hold references}}
+
+std::forward_list<int()> C5;
+std::forward_list<int(int)> C6;
+std::forward_list<int(int, int)> C7;
+// expected-error@*:* 3 {{'std::forward_list' cannot hold functions}}
+
+std::forward_list<void> C8;
+// expected-error@*:*{{'std::forward_list' cannot hold 'void'}}
+
+std::forward_list<int[]> C9;
+std::forward_list<int[2]> C10;
+// expected-error@*:* 2 {{'std::forward_list' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
index 741a7e4cebcadc..36c61cc34a7478 100644
--- a/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
@@ -10,20 +10,24 @@
 
 #include <list>
 
-std::list<const int> C1;    // expected-error@*:* {{'std::list' can only hold non-const types}}
-std::list<volatile int> C2; // expected-error@*:* {{'std::list' can only hold non-volatile types}}
-std::list<int&> C3;         // expected-error@*:*{{'std::list' can only hold object types; references are not objects}}
-std::list<int&&> C4;        // expected-error@*:*{{'std::list' can only hold object types; references are not objects}}
-std::list<int()>
-    C5; // expected-error@*:*{{'std::list' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::list<int(int)>
-    C6; // expected-error@*:*{{'std::list' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::list<int(int, int)>
-    C7; // expected-error@*:*{{'std::list' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::list<int (&)()>
-    C8; // expected-error@*:*{{'std::list' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::list<int (&)(int)>
-    C9; // expected-error@*:*{{'std::list' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::list<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::list' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::list<void> C11; // expected-error@*:*{{'std::list' can only hold object types; 'void' is not an object}}
+std::list<const int> C1;
+// expected-error@*:*{{'std::list' cannot hold const types}}
+
+std::list<volatile int> C2;
+// expected-error@*:*{{'std::list' cannot hold volatile types}}
+
+std::list<int&> C3;
+std::list<int&&> C4;
+// expected-error@*:* 2 {{'std::list' cannot hold references}}
+
+std::list<int()> C5;
+std::list<int(int)> C6;
+std::list<int(int, int)> C7;
+// expected-error@*:* 3 {{'std::list' cannot hold functions}}
+
+std::list<void> C8;
+// expected-error@*:*{{'std::list' cannot hold 'void'}}
+
+std::list<int[]> C9;
+std::list<int[2]> C10;
+// expected-error@*:* 2 {{'std::list' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
index 8f37c218bbeeaf..afc0433ee5b996 100644
--- a/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
@@ -10,20 +10,24 @@
 
 #include <vector>
 
-std::vector<const int> C1;    // expected-error@*:* {{'std::vector' can only hold non-const types}}
-std::vector<volatile int> C2; // expected-error@*:* {{'std::vector' can only hold non-volatile types}}
-std::vector<int&> C3;  // expected-error@*:*{{'std::vector' can only hold object types; references are not objects}}
-std::vector<int&&> C4; // expected-error@*:*{{'std::vector' can only hold object types; references are not objects}}
-std::vector<int()>
-    C5; // expected-error@*:*{{'std::vector' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::vector<int(int)>
-    C6; // expected-error@*:*{{'std::vector' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::vector<int(int, int)>
-    C7; // expected-error@*:*{{'std::vector' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::vector<int (&)()>
-    C8; // expected-error@*:*{{'std::vector' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::vector<int (&)(int)>
-    C9; // expected-error@*:*{{'std::vector' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::vector<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::vector' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::vector<void> C11; // expected-error@*:*{{'std::vector' can only hold object types; 'void' is not an object}}
+std::vector<const int> C1;
+// expected-error@*:*{{'std::vector' cannot hold const types}}
+
+std::vector<volatile int> C2;
+// expected-error@*:*{{'std::vector' cannot hold volatile types}}
+
+std::vector<int&> C3;
+std::vector<int&&> C4;
+// expected-error@*:* 2 {{'std::vector' cannot hold references}}
+
+std::vector<int()> C5;
+std::vector<int(int)> C6;
+std::vector<int(int, int)> C7;
+// expected-error@*:* 3 {{'std::vector' cannot hold functions}}
+
+std::vector<void> C8;
+// expected-error@*:*{{'std::vector' cannot hold 'void'}}
+
+std::vector<int[]> C9;
+std::vector<int[2]> C10;
+// expected-error@*:* 2 {{'std::vector' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/containers/strings/basic.string/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/strings/basic.string/non_cv_objects.verify.cpp
index 16b226dd557dec..f81d4e4fa14245 100644
--- a/libcxx/test/libcxx/containers/strings/basic.string/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/strings/basic.string/non_cv_objects.verify.cpp
@@ -10,23 +10,32 @@
 
 #include <string>
 
-std::basic_string<const int> C1;    // expected-error@*:* {{'std::basic_string' can only hold non-const types}}
-std::basic_string<volatile int> C2; // expected-error@*:* {{'std::basic_string' can only hold non-volatile types}}
-std::basic_string<int&>
-    C3; // expected-error@*:*{{'std::basic_string' can only hold object types; references are not objects}}
-std::basic_string<int&&>
-    C4; // expected-error@*:*{{'std::basic_string' can only hold object types; references are not objects}}
-std::basic_string<int()>
-    C5; // expected-error@*:*{{'std::basic_string' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::basic_string<int(int)>
-    C6; // expected-error@*:*{{'std::basic_string' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::basic_string<int(int, int)>
-    C7; // expected-error@*:*{{'std::basic_string' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::basic_string<int (&)()>
-    C8; // expected-error@*:*{{'std::basic_string' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::basic_string<int (&)(int)>
-    C9; // expected-error@*:*{{'std::basic_string' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::basic_string<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::basic_string' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::basic_string<void>
-    C11; // expected-error@*:*{{'std::basic_string' can only hold object types; 'void' is not an object}}
+std::basic_string<const int> C1;
+// expected-error@*:*{{'std::basic_string' cannot hold const types}}
+
+std::basic_string<volatile int> C2;
+// expected-error@*:*{{'std::basic_string' cannot hold volatile types}}
+
+std::basic_string<int&> C3;
+// expected-error@*:*{{'std::basic_string' cannot hold references}}
+
+std::basic_string<int&&> C4;
+// expected-error@*:*{{'std::basic_string' cannot hold references}}
+
+std::basic_string<int()> C5;
+// expected-error@*:*{{'std::basic_string' cannot hold functions}}
+
+std::basic_string<int(int)> C6;
+// expected-error@*:*{{'std::basic_string' cannot hold functions}}
+
+std::basic_string<int(int, int)> C7;
+// expected-error@*:*{{'std::basic_string' cannot hold functions}}
+
+std::basic_string<void> C8;
+// expected-error@*:*{{'std::basic_string' cannot hold 'void'}}
+
+std::basic_string<int[]> C9;
+// expected-error@*:*{{'std::basic_string' cannot hold C arrays}}
+
+std::basic_string<int[2]> C10;
+// expected-error@*:*{{'std::basic_string' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_object_types.verify.cpp
deleted file mode 100644
index 7eb3ec7048531f..00000000000000
--- a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_object_types.verify.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// Checks that we can only form maps of object types.
-
-#include <unordered_map>
-
-std::unordered_map<int&, int>
-    K3; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; references are not objects}}
-std::unordered_map<int&&, int>
-    K4; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; references are not objects}}
-std::unordered_map<int(), int>
-    K5; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_map<int(int), int>
-    K6; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_map<int(int, int), int>
-    K7; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_map<int (&)(), int>
-    K8; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_map<int (&)(int), int>
-    K9; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_map<int (&)(int, int), int>
-    K10; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_map<void, int>
-    K11; // expected-error@*:*{{'std::unordered_map::key_type' can only hold object types; 'void' is not an object}}
-
-std::unordered_map<int, const int> M1;
-std::unordered_map<int, volatile int> M2;
-std::unordered_map<int, int&> M3;
-std::unordered_map<int, int&&> M4;
-std::unordered_map<int, int()>
-    M5; // expected-error@*:*{{'std::unordered_map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::unordered_map<int, int(int)>
-    M6; // expected-error@*:*{{'std::unordered_map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::unordered_map<int, int(int, int)>
-    M7; // expected-error@*:*{{'std::unordered_map::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::unordered_map<int, int (&)()> M8;
-std::unordered_map<int, int (&)(int)> M9;
-std::unordered_map<int, int (&)(int, int)> M10;
-std::unordered_map<int, void>
-    M11; // expected-error@*:*{{'std::unordered_map::mapped_type' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..a35b3ac46a643f
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form maps of object types.
+
+#include <unordered_map>
+
+struct S {};
+
+namespace std {
+	template<>
+	struct hash<S> {
+		using argument_type = S;
+		using result_type = size_t;
+
+		size_t operator()(S) const;
+	};
+
+	template<>
+	struct hash<S const> : hash<S> {};
+
+	template<>
+	struct hash<S volatile> : hash<S> {};
+}
+
+std::unordered_map<S const, int> K1;
+std::unordered_map<int, int const> M1;
+// TODO(cjdb): turn this into a compile-time error
+
+std::unordered_map<S volatile, int> K2;
+std::unordered_map<int, int volatile> M2;
+// TODO(cjdb): turn this into a compile-time error
+
+std::unordered_map<int&, int> K3;
+std::unordered_map<int, int&> M3; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:* 1 {{'std::unordered_map' cannot hold references}}
+
+std::unordered_map<int&&, int> K4;
+std::unordered_map<int, int&&> M4; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_map' cannot hold references}}
+
+std::unordered_map<int(), int> K5;
+std::unordered_map<int(int), int> K6;
+std::unordered_map<int(int, int), int> K7;
+std::unordered_map<int, int()> M5;
+std::unordered_map<int, int(int)> M6;
+std::unordered_map<int, int(int, int)> M7;
+// expected-error@*:* 6 {{'std::unordered_map' cannot hold functions}}
+
+std::unordered_map<void, int> K8;
+std::unordered_map<int, void> M8;
+// expected-error@*:* 2 {{'std::unordered_map' cannot hold 'void'}}
+
+std::unordered_map<int[], int> K9;
+std::unordered_map<int, int[]> M9; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_map' cannot hold C arrays}}
+
+std::unordered_map<int[2], int> K10;
+std::unordered_map<int, int[2]> M10; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_map' cannot hold C arrays}}
+// expected-error@*:*{{the specified hash does not meet the Hash requirements}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_object_types.verify.cpp
deleted file mode 100644
index 49bae8ff93172c..00000000000000
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_object_types.verify.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// Checks that we can only form multimaps of object types.
-
-#include <unordered_map>
-
-std::unordered_multimap<int&, int>
-    K3; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; references are not objects}}
-std::unordered_multimap<int&&, int>
-    K4; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; references are not objects}}
-std::unordered_multimap<int(), int>
-    K5; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_multimap<int(int), int>
-    K6; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_multimap<int(int, int), int>
-    K7; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_multimap<int (&)(), int>
-    K8; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_multimap<int (&)(int), int>
-    K9; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_multimap<int (&)(int, int), int>
-    K10; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_multimap<void, int>
-    K11; // expected-error@*:*{{'std::unordered_multimap::key_type' can only hold object types; 'void' is not an object}}
-
-std::unordered_multimap<int, const int> M1;
-std::unordered_multimap<int, volatile int> M2;
-std::unordered_multimap<int, int&> M3;
-std::unordered_multimap<int, int&&> M4;
-std::unordered_multimap<int, int()>
-    M5; // expected-error@*:*{{'std::unordered_multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::unordered_multimap<int, int(int)>
-    M6; // expected-error@*:*{{'std::unordered_multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::unordered_multimap<int, int(int, int)>
-    M7; // expected-error@*:*{{'std::unordered_multimap::mapped_type' can only hold object or reference types; functions are neither (consider using a function pointer or reference)}}
-std::unordered_multimap<int, int (&)()> M8;
-std::unordered_multimap<int, int (&)(int)> M9;
-std::unordered_multimap<int, int (&)(int, int)> M10;
-std::unordered_multimap<int, void>
-    M11; // expected-error@*:*{{'std::unordered_multimap::mapped_type' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..d52ceb859b1616
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form multimaps of object types.
+
+#include <unordered_map>
+
+struct S {};
+
+namespace std {
+	template<>
+	struct hash<S> {
+		using argument_type = S;
+		using result_type = size_t;
+
+		size_t operator()(S) const;
+	};
+
+	template<>
+	struct hash<S const> : hash<S> {};
+
+	template<>
+	struct hash<S volatile> : hash<S> {};
+}
+
+std::unordered_multimap<S const, int> K1;
+std::unordered_multimap<int, int const> M1;
+// TODO(cjdb): turn this into a compile-time error
+
+std::unordered_multimap<S volatile, int> K2;
+std::unordered_multimap<int, int volatile> M2;
+// TODO(cjdb): turn this into a compile-time error
+
+std::unordered_multimap<int&, int> K3;
+std::unordered_multimap<int, int&> M3; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:* 1 {{'std::unordered_multimap' cannot hold references}}
+
+std::unordered_multimap<int&&, int> K4;
+std::unordered_multimap<int, int&&> M4; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_multimap' cannot hold references}}
+
+std::unordered_multimap<int(), int> K5;
+std::unordered_multimap<int(int), int> K6;
+std::unordered_multimap<int(int, int), int> K7;
+std::unordered_multimap<int, int()> M5;
+std::unordered_multimap<int, int(int)> M6;
+std::unordered_multimap<int, int(int, int)> M7;
+// expected-error@*:* 6 {{'std::unordered_multimap' cannot hold functions}}
+
+std::unordered_multimap<void, int> K8;
+std::unordered_multimap<int, void> M8;
+// expected-error@*:* 2 {{'std::unordered_multimap' cannot hold 'void'}}
+
+std::unordered_multimap<int[], int> K9;
+std::unordered_multimap<int, int[]> M9; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_multimap' cannot hold C arrays}}
+
+std::unordered_multimap<int[2], int> K10;
+std::unordered_multimap<int, int[2]> M10; // TODO(cjdb): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_multimap' cannot hold C arrays}}
+// expected-error@*:*{{the specified hash does not meet the Hash requirements}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_object_types.verify.cpp
deleted file mode 100644
index 702b0c2eb5044c..00000000000000
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_object_types.verify.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// Checks that we can only form unordered_multisets of object types.
-
-#include <unordered_set>
-
-std::unordered_multiset<const int> C1; // expected-error@*:* {{'std::unordered_multiset' can only hold non-const types}}
-std::unordered_multiset<volatile int>
-    C2; // expected-error@*:* {{'std::unordered_multiset' can only hold non-volatile types}}
-std::unordered_multiset<int&>
-    C3; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; references are not objects}}
-std::unordered_multiset<int&&>
-    C4; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; references are not objects}}
-std::unordered_multiset<int()>
-    C5; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_multiset<int(int)>
-    C6; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_multiset<int(int, int)>
-    C7; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_multiset<int (&)()>
-    C8; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_multiset<int (&)(int)>
-    C9; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_multiset<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_multiset<void>
-    C11; // expected-error@*:*{{'std::unordered_multiset' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..ce575afe69d8bf
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form unordered_multisets of object types.
+
+#include <unordered_set>
+
+std::unordered_multiset<const int> C1;
+// expected-error@*:*{{'std::unordered_multiset' cannot hold const types}}
+
+std::unordered_multiset<volatile int> C2;
+// expected-error@*:*{{'std::unordered_multiset' cannot hold volatile types}}
+
+std::unordered_multiset<int&> C3;
+std::unordered_multiset<int&&> C4;
+// expected-error@*:* 2 {{'std::unordered_multiset' cannot hold references}}
+
+std::unordered_multiset<int()> C5;
+std::unordered_multiset<int(int)> C6;
+std::unordered_multiset<int(int, int)> C7;
+// expected-error@*:* 3 {{'std::unordered_multiset' cannot hold functions}}
+
+std::unordered_multiset<void> C8;
+// expected-error@*:*{{'std::unordered_multiset' cannot hold 'void'}}
+
+std::unordered_multiset<int[]> C9;
+std::unordered_multiset<int[2]> C10;
+// expected-error@*:* 2 {{'std::unordered_multiset' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_object_types.verify.cpp
deleted file mode 100644
index e090128d20bdc8..00000000000000
--- a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_object_types.verify.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// Checks that we can only form unordered_sets of object types.
-
-#include <unordered_set>
-
-std::unordered_set<const int> C1;    // expected-error@*:* {{'std::unordered_set' can only hold non-const types}}
-std::unordered_set<volatile int> C2; // expected-error@*:* {{'std::unordered_set' can only hold non-volatile types}}
-std::unordered_set<int&>
-    C3; // expected-error@*:*{{'std::unordered_set' can only hold object types; references are not objects}}
-std::unordered_set<int&&>
-    C4; // expected-error@*:*{{'std::unordered_set' can only hold object types; references are not objects}}
-std::unordered_set<int()>
-    C5; // expected-error@*:*{{'std::unordered_set' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_set<int(int)>
-    C6; // expected-error@*:*{{'std::unordered_set' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_set<int(int, int)>
-    C7; // expected-error@*:*{{'std::unordered_set' can only hold object types; functions are not objects (consider using a function pointer)}}
-std::unordered_set<int (&)()>
-    C8; // expected-error@*:*{{'std::unordered_set' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_set<int (&)(int)>
-    C9; // expected-error@*:*{{'std::unordered_set' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_set<int (&)(int, int)>
-    C10; // expected-error@*:*{{'std::unordered_set' can only hold object types; function references are not objects (consider using a function pointer)}}
-std::unordered_set<void>
-    C11; // expected-error@*:*{{'std::unordered_set' can only hold object types; 'void' is not an object}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
new file mode 100644
index 00000000000000..e4f36c90ff3284
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// Checks that we can only form unordered_sets of object types.
+
+#include <unordered_set>
+
+std::unordered_set<const int> C1;
+// expected-error@*:*{{'std::unordered_set' cannot hold const types}}
+
+std::unordered_set<volatile int> C2;
+// expected-error@*:*{{'std::unordered_set' cannot hold volatile types}}
+
+std::unordered_set<int&> C3;
+std::unordered_set<int&&> C4;
+// expected-error@*:* 2 {{'std::unordered_set' cannot hold references}}
+
+std::unordered_set<int()> C5;
+std::unordered_set<int(int)> C6;
+std::unordered_set<int(int, int)> C7;
+// expected-error@*:* 3 {{'std::unordered_set' cannot hold functions}}
+
+std::unordered_set<void> C8;
+// expected-error@*:*{{'std::unordered_set' cannot hold 'void'}}
+
+std::unordered_set<int[]> C9;
+std::unordered_set<int[2]> C10;
+// expected-error@*:* 2 {{'std::unordered_set' cannot hold C arrays}}
diff --git a/libcxx/test/libcxx/memory/allocator_non_cv_objects_only.verify.cpp b/libcxx/test/libcxx/memory/allocator_non_cv_objects_only.verify.cpp
index 9e111ab3a1691e..1374ffef8cce45 100644
--- a/libcxx/test/libcxx/memory/allocator_non_cv_objects_only.verify.cpp
+++ b/libcxx/test/libcxx/memory/allocator_non_cv_objects_only.verify.cpp
@@ -11,15 +11,17 @@
 #include <memory>
 #include "test_macros.h"
 
-std::allocator<const int> A1;    // expected-error@*:* {{'std::allocator' can only allocate non-const object types}}
-std::allocator<volatile int> A2; // expected-error@*:* {{'std::allocator' can only allocate non-volatile object types}}
-std::allocator<int&>
-    A3; // expected-error@*:*{{'std::allocator' can only allocate object types; references are not objects}}
-std::allocator<int&&>
-    A4; // expected-error@*:*{{'std::allocator' can only allocate object types; references are not objects}}
-std::allocator<int()>
-    A5; // expected-error@*:*{{'std::allocator' can only allocate object types; functions are not objects (consider using a function pointer)}}
-std::allocator<int(int)>
-    A6; // expected-error@*:*{{'std::allocator' can only allocate object types; functions are not objects (consider using a function pointer)}}
-std::allocator<int(int, int)>
-    A7; // expected-error@*:*{{'std::allocator' can only allocate object types; functions are not objects (consider using a function pointer)}}
+std::allocator<const int> A1;
+// expected-error@*:* {{'std::allocator' cannot allocate const types}}
+
+std::allocator<volatile int> A2;
+// expected-error@*:* {{'std::allocator' cannot allocate volatile types}}
+
+std::allocator<int&> A3;
+std::allocator<int&&> A4;
+// expected-error@*:* 2 {{'std::allocator' cannot allocate references}}
+
+std::allocator<int()> A5;
+std::allocator<int(int)> A6;
+std::allocator<int(int, int)> A7;
+// expected-error@*:* 3 {{'std::allocator' cannot allocate functions}}
diff --git a/libcxx/test/std/strings/basic.string/char.bad.verify.cpp b/libcxx/test/std/strings/basic.string/char.bad.verify.cpp
index bedc5f19a6a881..5bce7057e33b52 100644
--- a/libcxx/test/std/strings/basic.string/char.bad.verify.cpp
+++ b/libcxx/test/std/strings/basic.string/char.bad.verify.cpp
@@ -33,7 +33,7 @@ void f() {
     typedef char C[3];
     static_assert(std::is_array<C>::value, "");
     std::basic_string<C, test_traits<C> > s;
-    // expected-error-re at string:* {{static assertion failed{{.*}}Character type of basic_string must not be an array}}
+    // expected-error-re at string:* {{static assertion failed{{.*}}'std::basic_string' cannot hold C arrays}}
   }
 
   {

>From 6c1e1e3b5c69f0e16a6aadb808549651cdf39bf6 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 00:48:29 +0000
Subject: [PATCH 03/12] formats files

---
 libcxx/include/__memory/allocator.h           |  4 +---
 .../sequences/array/non_cv_objects.verify.cpp |  2 +-
 .../unord/unord.map/non_cv_objects.verify.cpp | 22 +++++++++----------
 .../unord.multimap/non_cv_objects.verify.cpp  | 22 +++++++++----------
 4 files changed, 24 insertions(+), 26 deletions(-)

diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index ade678e6542a6a..5c2afdb96343b8 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -82,9 +82,7 @@ class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::v
   static_assert(!is_const<_Tp>::value, "'std::allocator' cannot allocate const types");
   static_assert(!is_volatile<_Tp>::value, "'std::allocator' cannot allocate volatile types");
   static_assert(!is_reference<_Tp>::value, "'std::allocator' cannot allocate references");
-  static_assert(
-      !is_function<_Tp>::value,
-      "'std::allocator' cannot allocate functions");
+  static_assert(!is_function<_Tp>::value, "'std::allocator' cannot allocate functions");
 
 public:
   typedef size_t size_type;
diff --git a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
index ba1d7d1c92cd2c..a458b1bab043b6 100644
--- a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
@@ -13,7 +13,7 @@
 std::array<const int, 1> A1{};  // okay
 std::array<volatile int, 1> A2; // okay
 
-std::array<int&, 1>  A3{};
+std::array<int&, 1> A3{};
 std::array<int&&, 1> A4{};
 // expected-error@*:* 2 {{'std::array' cannot hold references}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
index a35b3ac46a643f..9c50def8a9b2c4 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
@@ -13,20 +13,20 @@
 struct S {};
 
 namespace std {
-	template<>
-	struct hash<S> {
-		using argument_type = S;
-		using result_type = size_t;
+template <>
+struct hash<S> {
+  using argument_type = S;
+  using result_type   = size_t;
 
-		size_t operator()(S) const;
-	};
+  size_t operator()(S) const;
+};
 
-	template<>
-	struct hash<S const> : hash<S> {};
+template <>
+struct hash<S const> : hash<S> {};
 
-	template<>
-	struct hash<S volatile> : hash<S> {};
-}
+template <>
+struct hash<S volatile> : hash<S> {};
+} // namespace std
 
 std::unordered_map<S const, int> K1;
 std::unordered_map<int, int const> M1;
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
index d52ceb859b1616..f7f6dfb5a3cbe3 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
@@ -13,20 +13,20 @@
 struct S {};
 
 namespace std {
-	template<>
-	struct hash<S> {
-		using argument_type = S;
-		using result_type = size_t;
+template <>
+struct hash<S> {
+  using argument_type = S;
+  using result_type   = size_t;
 
-		size_t operator()(S) const;
-	};
+  size_t operator()(S) const;
+};
 
-	template<>
-	struct hash<S const> : hash<S> {};
+template <>
+struct hash<S const> : hash<S> {};
 
-	template<>
-	struct hash<S volatile> : hash<S> {};
-}
+template <>
+struct hash<S volatile> : hash<S> {};
+} // namespace std
 
 std::unordered_multimap<S const, int> K1;
 std::unordered_multimap<int, int const> M1;

>From 938299d594e6e4b044761efcd752ab9df21667a2 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 01:03:57 +0000
Subject: [PATCH 04/12] sorts includes missed by clang-format

---
 libcxx/include/vector | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/vector b/libcxx/include/vector
index 0c4d2589d0feb8..5ca4868278431d 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -345,8 +345,8 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
 #include <__ranges/from_range.h>
 #include <__ranges/size.h>
 #include <__split_buffer>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_function.h>
@@ -354,8 +354,8 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/is_volatile.h>
-#include <__type_traits/remove_reference.h>
 #include <__type_traits/noexcept_move_assign_container.h>
+#include <__type_traits/remove_reference.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/exception_guard.h>
 #include <__utility/forward.h>

>From 65346a3fe4a8bc10fa394cd6e6fd4b9644bf6849 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 16:27:41 +0000
Subject: [PATCH 05/12] more include sorting

---
 libcxx/include/list                                           | 2 +-
 libcxx/include/set                                            | 2 +-
 libcxx/include/unordered_set                                  | 4 ++--
 .../containers/sequences/array/non_cv_objects.verify.cpp      | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/list b/libcxx/include/list
index 2f5d00ab8ba385..c34e473e46fb1f 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -225,8 +225,8 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__type_traits/conditional.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_nothrow_assignable.h>
diff --git a/libcxx/include/set b/libcxx/include/set
index be23050c18795e..8554ca9c3975e2 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -530,8 +530,8 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__tree>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_allocator.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_reference.h>
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index c5c3afc870e893..5170b62f4857dc 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -551,10 +551,10 @@ template <class Value, class Hash, class Pred, class Alloc>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
-#include <__type_traits/is_volatile.h>
-#include <__type_traits/is_reference.h>
 #include <__type_traits/is_function.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/is_volatile.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/forward.h>
 #include <version>
diff --git a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
index a458b1bab043b6..0f5bbecf1d52dd 100644
--- a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
@@ -10,7 +10,7 @@
 
 #include <array>
 
-std::array<const int, 1> A1{};  // okay
+std::array<const int, 1> A1{0};  // okay
 std::array<volatile int, 1> A2; // okay
 
 std::array<int&, 1> A3{};

>From 472ffcfe252b5c6f5f15ee084ad794bd75a84646 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 17:20:50 +0000
Subject: [PATCH 06/12] more formatting, with a successful git clang-format

I think the best way to enact `git clang-format` might be to use `git
clang-format <hash-before-your-commit-first>` instead of `git
clang-format HEAD~n`. This ensures that all commits in a PR are
formatted, instead of just the last n (which one might miscount, like I
probably did).
---
 .../libcxx/containers/sequences/array/non_cv_objects.verify.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
index 0f5bbecf1d52dd..38ef6cd7e231b9 100644
--- a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
@@ -10,7 +10,7 @@
 
 #include <array>
 
-std::array<const int, 1> A1{0};  // okay
+std::array<const int, 1> A1{0}; // okay
 std::array<volatile int, 1> A2; // okay
 
 std::array<int&, 1> A3{};

>From 7596a2e90a1e4bfe52dcdf8422c7d7c646f5c3dd Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 18:32:18 +0000
Subject: [PATCH 07/12] excludes <array> test from C++03

---
 .../libcxx/containers/sequences/array/non_cv_objects.verify.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
index 38ef6cd7e231b9..d4fcb21036b589 100644
--- a/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/array/non_cv_objects.verify.cpp
@@ -8,6 +8,8 @@
 
 // Checks that we can only form arrays of object types.
 
+// UNSUPPORTED: c++03
+
 #include <array>
 
 std::array<const int, 1> A1{0}; // okay

>From 25e999f106d1cbcc7fbdbc3d9a2c0e5a0fb4cb6d Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 18:32:48 +0000
Subject: [PATCH 08/12] suppresses irrelevant diagnostic

---
 .../unord/unord.map/non_cv_objects.verify.cpp     | 15 ++++++++++++---
 .../unord.multimap/non_cv_objects.verify.cpp      |  8 ++++++--
 2 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
index 9c50def8a9b2c4..22c41b1d38b89d 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
@@ -22,10 +22,20 @@ struct hash<S> {
 };
 
 template <>
-struct hash<S const> : hash<S> {};
+struct hash<S const> {
+  using argument_type = S;
+  using result_type   = size_t;
+
+  size_t operator()(S) const;
+};
 
 template <>
-struct hash<S volatile> : hash<S> {};
+struct hash<S volatile> {
+  using argument_type = S;
+  using result_type   = size_t;
+
+  size_t operator()(S const volatile&) const;
+};
 } // namespace std
 
 std::unordered_map<S const, int> K1;
@@ -63,4 +73,3 @@ std::unordered_map<int, int[]> M9; // TODO(cjdb): turn this into a compile-time
 std::unordered_map<int[2], int> K10;
 std::unordered_map<int, int[2]> M10; // TODO(cjdb): turn this into a compile-time error
 // expected-error@*:*{{'std::unordered_map' cannot hold C arrays}}
-// expected-error@*:*{{the specified hash does not meet the Hash requirements}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
index f7f6dfb5a3cbe3..2c68ad07d965f0 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
@@ -25,7 +25,12 @@ template <>
 struct hash<S const> : hash<S> {};
 
 template <>
-struct hash<S volatile> : hash<S> {};
+struct hash<S volatile> {
+  using argument_type = S;
+  using result_type   = size_t;
+
+  size_t operator()(S const volatile&) const;
+};
 } // namespace std
 
 std::unordered_multimap<S const, int> K1;
@@ -63,4 +68,3 @@ std::unordered_multimap<int, int[]> M9; // TODO(cjdb): turn this into a compile-
 std::unordered_multimap<int[2], int> K10;
 std::unordered_multimap<int, int[2]> M10; // TODO(cjdb): turn this into a compile-time error
 // expected-error@*:*{{'std::unordered_multimap' cannot hold C arrays}}
-// expected-error@*:*{{the specified hash does not meet the Hash requirements}}

>From 5a1b902ebc7529fb716492b1d8c19e281d5232c6 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 18:35:28 +0000
Subject: [PATCH 09/12] 
 s/__is_unbounded_array(T)/__libcpp_is_unbounded_array<T>::value/g

---
 libcxx/include/array | 3 ++-
 libcxx/include/map   | 5 +++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/array b/libcxx/include/array
index e982109097d402..1c4aaa89ed5222 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -133,6 +133,7 @@ template <size_t I, class T, size_t N> const T&& get(const array<T, N>&&) noexce
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/is_unbounded_array.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_reference.h>
@@ -174,7 +175,7 @@ struct _LIBCPP_TEMPLATE_VIS array {
   static_assert(!is_reference<_Tp>::value, "'std::array' cannot hold references");
   static_assert(!is_function<_Tp>::value, "'std::array' cannot hold functions");
   static_assert(!is_void<_Tp>::value, "'std::array' cannot hold 'void'");
-  static_assert(!__is_unbounded_array(_Tp), "'std::array' cannot hold C arrays of an unknown size");
+  static_assert(!__libcpp_is_unbounded_array<_Tp>::value, "'std::array' cannot hold C arrays of an unknown size");
 
   using __trivially_relocatable = __conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, array, void>;
 
diff --git a/libcxx/include/map b/libcxx/include/map
index 486a456f69343b..8c8b6d276822f8 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -595,6 +595,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_reference.h>
+#include <__type_traits/is_unbounded_array.h>
 #include <__type_traits/is_void.h>
 #include <__utility/forward.h>
 #include <__utility/piecewise_construct.h>
@@ -969,7 +970,7 @@ class _LIBCPP_TEMPLATE_VIS map {
   static_assert(!is_lvalue_reference<_Key>::value, "'std::map' cannot hold references");
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::map' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::map' cannot hold 'void'");
-  static_assert(!__is_unbounded_array(_Key), "'std::map' cannot hold C arrays");
+  static_assert(!__libcpp_is_unbounded_array<_Key>::value, "'std::map' cannot hold C arrays");
 public:
   // types:
   typedef _Key key_type;
@@ -1650,7 +1651,7 @@ class _LIBCPP_TEMPLATE_VIS multimap {
   static_assert(!is_lvalue_reference<_Key>::value, "'std::multimap' cannot hold references");
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::multimap' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::multimap' cannot hold 'void'");
-  static_assert(!__is_unbounded_array(_Key), "'std::multimap' cannot hold C arrays");
+  static_assert(!__libcpp_is_unbounded_array<_Key>::value, "'std::multimap' cannot hold C arrays");
 public:
   // types:
   typedef _Key key_type;

>From 99a450905e447219a6be53b13ba6f0fcd6bc0350 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 19:22:06 +0000
Subject: [PATCH 10/12] applies Louis' request

---
 libcxx/include/forward_list                   | 134 +-----------------
 libcxx/include/list                           | 133 +----------------
 .../forward_list/non_cv_objects.verify.cpp    |   7 +
 .../sequences/list/non_cv_objects.verify.cpp  |   7 +
 4 files changed, 26 insertions(+), 255 deletions(-)

diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index a69013c1cf3a6b..172a1febf2a56a 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -482,6 +482,12 @@ public:
 
 template <class _Tp, class _Alloc>
 class __forward_list_base {
+  static_assert(!is_const<_Tp>::value, "'std::forward_list' cannot hold const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::forward_list' cannot hold volatile types");
+  static_assert(!is_reference<_Tp>::value, "'std::forward_list' cannot hold references");
+  static_assert(!is_function<_Tp>::value, "'std::forward_list' cannot hold functions");
+  static_assert(!is_void<_Tp>::value, "'std::forward_list' cannot hold 'void'");
+  static_assert(!is_array<_Tp>::value, "'std::forward_list' cannot hold C arrays");
 protected:
   typedef _Tp value_type;
   typedef _Alloc allocator_type;
@@ -641,136 +647,8 @@ void __forward_list_base<_Tp, _Alloc>::clear() _NOEXCEPT {
   __before_begin()->__next_ = nullptr;
 }
 
-// begin-diagnostic-helpers
-// std::forward_list can only have non-cv-qualified object types as its value type, which we diagnose.
-// In order to short-cirucit redundant (and cryptic) diagnostics, std::forward_list must be the one
-// to fire the static_assert. Since std::forward_list inherits from __forward_list_base, we also need
-// to create specialisations for the rejected types, so that everything appears "good" to std::forward_list
-// (otherwise we'll get lots of unhelpful diagnostics that suppress the one std::forward_list offers).
-template <class _Tp, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp const, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Tp, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp volatile, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Tp, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp&, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Tp, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp&&, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Tp, class... _Args, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __forward_list_base<_Tp(_Args...), _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __forward_list_base<void, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-// end-diagnostic-helpers
-
 template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS forward_list : private __forward_list_base<_Tp, _Alloc> {
-  static_assert(!is_const<_Tp>::value, "'std::forward_list' cannot hold const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::forward_list' cannot hold volatile types");
-  static_assert(!is_reference<_Tp>::value, "'std::forward_list' cannot hold references");
-  static_assert(!is_function<_Tp>::value, "'std::forward_list' cannot hold functions");
-  static_assert(!is_void<_Tp>::value, "'std::forward_list' cannot hold 'void'");
-  static_assert(!is_array<_Tp>::value, "'std::forward_list' cannot hold C arrays");
-
   typedef __forward_list_base<_Tp, _Alloc> base;
   typedef typename base::__node_allocator __node_allocator;
   typedef typename base::__node_type __node_type;
diff --git a/libcxx/include/list b/libcxx/include/list
index c34e473e46fb1f..48e206c5968558 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -472,6 +472,12 @@ public:
 
 template <class _Tp, class _Alloc>
 class __list_imp {
+  static_assert(!is_const<_Tp>::value, "'std::list' cannot hold const types");
+  static_assert(!is_volatile<_Tp>::value, "'std::list' cannot hold volatile types");
+  static_assert(!is_reference<_Tp>::value, "'std::list' cannot hold references");
+  static_assert(!is_function<_Tp>::value, "'std::list' cannot hold functions");
+  static_assert(!is_void<_Tp>::value, "'std::list' cannot hold 'void'");
+  static_assert(!is_array<_Tp>::value, "'std::list' cannot hold C arrays");
 public:
   __list_imp(const __list_imp&) = delete;
   __list_imp& operator=(const __list_imp&) = delete;
@@ -666,135 +672,8 @@ void __list_imp<_Tp, _Alloc>::swap(__list_imp& __c)
     __c.__end_.__prev_->__next_ = __c.__end_.__next_->__prev_ = __c.__end_as_link();
 }
 
-// begin-diagnostic-helpers
-// std::list can only have non-cv-qualified object types as its value type, which we diagnose. In order
-// to short-cirucit redundant (and cryptic) diagnostics, std::list must be the one to fire the static_assert.
-// Since std::list inherits from __list_imp, we also need to create specialisations for the rejected types,
-// so that everything appears "good" to std::list (otherwise we'll get lots of unhelpful diagnostics that
-// suppress the one std::list offers).
-template <class _Tp, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp const, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Tp, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp volatile, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Tp, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp&, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Tp, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp&&, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Tp, class... _Args, class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __list_imp<_Tp(_Args...), _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-
-template <class _Alloc>
-class _LIBCPP_TEMPLATE_VIS __list_imp<void, _Alloc> {
-public:
-  using __node_allocator = void*;
-  using __node_alloc_traits = void*;
-  using __node_pointer = void*;
-  using __node_type = void*;
-  using __node_base = void*;
-  using __node_base_pointer = void*;
-  using __link_pointer = void*;
-
-  using pointer = int*;
-  using const_pointer = int const*;
-  using size_type = unsigned int;
-  using difference_type = int;
-  using iterator = int*;
-  using const_iterator = int const*;
-};
-// end-diagnostic-helpers
-
 template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS list : private __list_imp<_Tp, _Alloc> {
-  static_assert(!is_const<_Tp>::value, "'std::list' cannot hold const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::list' cannot hold volatile types");
-  static_assert(!is_reference<_Tp>::value, "'std::list' cannot hold references");
-  static_assert(!is_function<_Tp>::value, "'std::list' cannot hold functions");
-  static_assert(!is_void<_Tp>::value, "'std::list' cannot hold 'void'");
-  static_assert(!is_array<_Tp>::value, "'std::list' cannot hold C arrays");
 
   typedef __list_imp<_Tp, _Alloc> base;
   typedef typename base::__node_type __node_type;
diff --git a/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
index 8fc4f33f1e201c..3b93ee90683dfd 100644
--- a/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
@@ -31,3 +31,10 @@ std::forward_list<void> C8;
 std::forward_list<int[]> C9;
 std::forward_list<int[2]> C10;
 // expected-error@*:* 2 {{'std::forward_list' cannot hold C arrays}}
+
+// Spurious errors below
+
+// expected-error@*:* 7 {{'std::allocator'}}
+// expected-error@*:* 2 {{multiple overloads of}}
+// expected-error@*:* 12 {{cannot form a reference to 'void'}}
+// expected-error@*:* 84 {{no type named}}
diff --git a/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
index 36c61cc34a7478..48132f34b53b6c 100644
--- a/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
@@ -31,3 +31,10 @@ std::list<void> C8;
 std::list<int[]> C9;
 std::list<int[2]> C10;
 // expected-error@*:* 2 {{'std::list' cannot hold C arrays}}
+
+// Spurious errors below
+
+// expected-error@*:* 7 {{'std::allocator'}}
+// expected-error@*:* 3 {{multiple overloads of}}
+// expected-error@*:* 14 {{cannot form a reference to 'void'}}
+// expected-error@*:* 144 {{no type named}}

>From 45c85d622c21905c1ab8071c278f943e95f6e5e4 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 Aug 2024 19:35:09 +0000
Subject: [PATCH 11/12] finally gets git-clang-format on side

The trick is to run

```
$ git-clang-format --diff <base-hash> --extensions ,cpp,h | patch -p1
```
---
 libcxx/include/deque         | 1 +
 libcxx/include/forward_list  | 1 +
 libcxx/include/list          | 2 +-
 libcxx/include/map           | 2 ++
 libcxx/include/set           | 2 ++
 libcxx/include/unordered_map | 5 ++++-
 libcxx/include/unordered_set | 2 ++
 7 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index eb857c20c1c818..9d4e185e94e4a3 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -481,6 +481,7 @@ class _LIBCPP_TEMPLATE_VIS deque {
   static_assert(!is_function<_Tp>::value, "'std::deque' cannot hold functions");
   static_assert(!is_void<_Tp>::value, "'std::deque' cannot hold 'void'");
   static_assert(!is_array<_Tp>::value, "'std::deque' cannot hold C arrays");
+
 public:
   // types:
 
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 172a1febf2a56a..6fc42a7487e4ff 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -488,6 +488,7 @@ class __forward_list_base {
   static_assert(!is_function<_Tp>::value, "'std::forward_list' cannot hold functions");
   static_assert(!is_void<_Tp>::value, "'std::forward_list' cannot hold 'void'");
   static_assert(!is_array<_Tp>::value, "'std::forward_list' cannot hold C arrays");
+
 protected:
   typedef _Tp value_type;
   typedef _Alloc allocator_type;
diff --git a/libcxx/include/list b/libcxx/include/list
index 48e206c5968558..69961b77ad3da5 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -478,6 +478,7 @@ class __list_imp {
   static_assert(!is_function<_Tp>::value, "'std::list' cannot hold functions");
   static_assert(!is_void<_Tp>::value, "'std::list' cannot hold 'void'");
   static_assert(!is_array<_Tp>::value, "'std::list' cannot hold C arrays");
+
 public:
   __list_imp(const __list_imp&) = delete;
   __list_imp& operator=(const __list_imp&) = delete;
@@ -674,7 +675,6 @@ void __list_imp<_Tp, _Alloc>::swap(__list_imp& __c)
 
 template <class _Tp, class _Alloc /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS list : private __list_imp<_Tp, _Alloc> {
-
   typedef __list_imp<_Tp, _Alloc> base;
   typedef typename base::__node_type __node_type;
   typedef typename base::__node_allocator __node_allocator;
diff --git a/libcxx/include/map b/libcxx/include/map
index 8c8b6d276822f8..61af7a03d93ae2 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -971,6 +971,7 @@ class _LIBCPP_TEMPLATE_VIS map {
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::map' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::map' cannot hold 'void'");
   static_assert(!__libcpp_is_unbounded_array<_Key>::value, "'std::map' cannot hold C arrays");
+
 public:
   // types:
   typedef _Key key_type;
@@ -1652,6 +1653,7 @@ class _LIBCPP_TEMPLATE_VIS multimap {
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::multimap' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::multimap' cannot hold 'void'");
   static_assert(!__libcpp_is_unbounded_array<_Key>::value, "'std::multimap' cannot hold C arrays");
+
 public:
   // types:
   typedef _Key key_type;
diff --git a/libcxx/include/set b/libcxx/include/set
index 8554ca9c3975e2..79c74e0e3c1249 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -574,6 +574,7 @@ class _LIBCPP_TEMPLATE_VIS set {
   static_assert(!is_function<_Key>::value, "'std::set' cannot hold functions");
   static_assert(!is_void<_Key>::value, "'std::set' cannot hold 'void'");
   static_assert(!is_array<_Key>::value, "'std::set' cannot hold C arrays");
+
 public:
   // types:
   typedef _Key key_type;
@@ -1034,6 +1035,7 @@ class _LIBCPP_TEMPLATE_VIS multiset {
   static_assert(!is_function<_Key>::value, "'std::multiset' cannot hold functions");
   static_assert(!is_void<_Key>::value, "'std::multiset' cannot hold 'void'");
   static_assert(!is_array<_Key>::value, "'std::multiset' cannot hold C arrays");
+
 public:
   // types:
   typedef _Key key_type;
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 27d8c6056f6867..8786a940086728 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1032,6 +1032,7 @@ class _LIBCPP_TEMPLATE_VIS unordered_map {
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::unordered_map' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::unordered_map' cannot hold 'void'");
   static_assert(!is_array<_Key>::value, "'std::unordered_map' cannot hold C arrays");
+
 public:
   // types
   typedef _Key key_type;
@@ -1836,9 +1837,11 @@ template <class _Key,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_multimap {
   static_assert(!is_reference<_Key>::value, "'std::unordered_multimap' cannot hold references");
-  static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::unordered_multimap' cannot hold functions");
+  static_assert(!is_function<_Key>::value && !is_function<_Tp>::value,
+                "'std::unordered_multimap' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::unordered_multimap' cannot hold 'void'");
   static_assert(!is_array<_Key>::value, "'std::unordered_multimap' cannot hold C arrays");
+
 public:
   // types
   typedef _Key key_type;
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 5170b62f4857dc..3262a565927baa 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -592,6 +592,7 @@ class _LIBCPP_TEMPLATE_VIS unordered_set {
   static_assert(!is_function<_Value>::value, "'std::unordered_set' cannot hold functions");
   static_assert(!is_void<_Value>::value, "'std::unordered_set' cannot hold 'void'");
   static_assert(!is_array<_Value>::value, "'std::unordered_set' cannot hold C arrays");
+
 public:
   // types
   typedef _Value key_type;
@@ -1193,6 +1194,7 @@ class _LIBCPP_TEMPLATE_VIS unordered_multiset {
   static_assert(!is_function<_Value>::value, "'std::unordered_multiset' cannot hold functions");
   static_assert(!is_void<_Value>::value, "'std::unordered_multiset' cannot hold 'void'");
   static_assert(!is_array<_Value>::value, "'std::unordered_multiset' cannot hold C arrays");
+
 public:
   // types
   typedef _Value key_type;

>From dbf872a2584aa5e149282674bb708c4ed4f520a5 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Fri, 30 Aug 2024 21:46:12 +0000
Subject: [PATCH 12/12] unifies the containers' diagnostics

A Clang pragma that lets us "hide" macro expansion from diagnostics so
that users don't need to read implementation details would be a good
thing to explore in the future.
---
 libcxx/include/CMakeLists.txt                 |  1 +
 .../__type_traits/diagnostic_utilities.h      | 47 +++++++++++++++++++
 libcxx/include/deque                          |  8 +---
 libcxx/include/forward_list                   |  9 ++--
 libcxx/include/list                           |  8 +---
 libcxx/include/map                            |  6 ++-
 libcxx/include/module.modulemap               |  4 ++
 libcxx/include/set                            | 15 ++----
 libcxx/include/unordered_map                  | 11 ++++-
 libcxx/include/unordered_set                  | 15 ++----
 libcxx/include/vector                         |  8 +---
 .../associative/map/non_cv_objects.verify.cpp | 14 +++---
 ...t.verify.cpp => non_cv_objects.verify.cpp} | 14 +++---
 .../multiset/non_cv_object_types.verify.cpp   |  7 ++-
 .../set/non_cv_object_types.verify.cpp        |  7 ++-
 .../sequences/deque/non_cv_objects.verify.cpp |  7 ++-
 .../forward_list/non_cv_objects.verify.cpp    | 15 ++++--
 .../sequences/list/non_cv_objects.verify.cpp  | 15 ++++--
 .../vector/non_cv_objects.verify.cpp          |  7 ++-
 .../unord/unord.map/non_cv_objects.verify.cpp | 16 +++----
 .../unord.multimap/non_cv_objects.verify.cpp  | 16 +++----
 .../unord.multiset/non_cv_objects.verify.cpp  | 19 +++++++-
 .../unord/unord.set/non_cv_objects.verify.cpp | 19 +++++++-
 23 files changed, 188 insertions(+), 100 deletions(-)
 create mode 100644 libcxx/include/__type_traits/diagnostic_utilities.h
 rename libcxx/test/libcxx/containers/associative/multimap/{non_cv_object.verify.cpp => non_cv_objects.verify.cpp} (72%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8e..d4e8d586352074 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -744,6 +744,7 @@ set(files
   __type_traits/decay.h
   __type_traits/dependent_type.h
   __type_traits/desugars_to.h
+  __type_traits/diagnostic_utilities.h
   __type_traits/disjunction.h
   __type_traits/enable_if.h
   __type_traits/extent.h
diff --git a/libcxx/include/__type_traits/diagnostic_utilities.h b/libcxx/include/__type_traits/diagnostic_utilities.h
new file mode 100644
index 00000000000000..a1a745575c83d6
--- /dev/null
+++ b/libcxx/include/__type_traits/diagnostic_utilities.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H
+#define _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H
+
+#include <__config>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_unbounded_array.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/is_volatile.h>
+
+#pragma GCC system_header
+
+#if _LIBCPP_STD_VER >= 20
+#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Container, _Tp)
+#else
+#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Container, _Tp)                                \
+    ;                                                                                                                  \
+    static_assert(!is_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays before C++20")
+#endif
+
+// Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
+// allocator that meets the Cpp17Allocator requirements (https://eel.is/c++draft/allocator.requirements).
+// In particular, this means that containers should only accept non-cv-qualified object types, and
+// types that are Cpp17Erasable.
+#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_BASE(_Container, _Tp)                                          \
+  static_assert(!is_const<_Tp>::value, "'std::" _Container "' cannot hold const types");                               \
+  static_assert(!is_volatile<_Tp>::value, "'std::" _Container "' cannot hold volatile types");                         \
+  static_assert(!is_reference<_Tp>::value, "'std::" _Container "' cannot hold references");                            \
+  static_assert(!is_function<_Tp>::value, "'std::" _Container "' cannot hold functions");                              \
+  static_assert(                                                                                                       \
+      !__libcpp_is_unbounded_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays of an unknown size")       \
+      _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Container, _Tp)
+
+#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL(_Container, _Tp)                                          \
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_BASE(_Container, _Tp);                                               \
+  static_assert(!is_void<_Tp>::value, "'std::" _Container "' cannot hold 'void'")
+
+#endif // _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 9d4e185e94e4a3..801387cbf323bd 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -211,6 +211,7 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/from_range.h>
 #include <__ranges/size.h>
 #include <__split_buffer>
+#include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
@@ -475,12 +476,7 @@ const _DiffType __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer,
 
 template <class _Tp, class _Allocator /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS deque {
-  static_assert(!is_const<_Tp>::value, "'std::deque' cannot hold const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::deque' cannot hold volatile types");
-  static_assert(!is_reference<_Tp>::value, "'std::deque' cannot hold references");
-  static_assert(!is_function<_Tp>::value, "'std::deque' cannot hold functions");
-  static_assert(!is_void<_Tp>::value, "'std::deque' cannot hold 'void'");
-  static_assert(!is_array<_Tp>::value, "'std::deque' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("deque", _Tp);
 
 public:
   // types:
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 6fc42a7487e4ff..f2787e416f248e 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -218,6 +218,7 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
@@ -228,6 +229,7 @@ template <class T, class Allocator, class Predicate>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_unbounded_array.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/is_volatile.h>
 #include <__type_traits/remove_reference.h>
@@ -482,12 +484,7 @@ public:
 
 template <class _Tp, class _Alloc>
 class __forward_list_base {
-  static_assert(!is_const<_Tp>::value, "'std::forward_list' cannot hold const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::forward_list' cannot hold volatile types");
-  static_assert(!is_reference<_Tp>::value, "'std::forward_list' cannot hold references");
-  static_assert(!is_function<_Tp>::value, "'std::forward_list' cannot hold functions");
-  static_assert(!is_void<_Tp>::value, "'std::forward_list' cannot hold 'void'");
-  static_assert(!is_array<_Tp>::value, "'std::forward_list' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("forward_list", _Tp);
 
 protected:
   typedef _Tp value_type;
diff --git a/libcxx/include/list b/libcxx/include/list
index 69961b77ad3da5..993ef9726b5142 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -225,6 +225,7 @@ template <class T, class Allocator, class Predicate>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
@@ -472,12 +473,7 @@ public:
 
 template <class _Tp, class _Alloc>
 class __list_imp {
-  static_assert(!is_const<_Tp>::value, "'std::list' cannot hold const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::list' cannot hold volatile types");
-  static_assert(!is_reference<_Tp>::value, "'std::list' cannot hold references");
-  static_assert(!is_function<_Tp>::value, "'std::list' cannot hold functions");
-  static_assert(!is_void<_Tp>::value, "'std::list' cannot hold 'void'");
-  static_assert(!is_array<_Tp>::value, "'std::list' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("list", _Tp);
 
 public:
   __list_imp(const __list_imp&) = delete;
diff --git a/libcxx/include/map b/libcxx/include/map
index 61af7a03d93ae2..e66316b11cb6ba 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -967,10 +967,11 @@ public:
 
 template <class _Key, class _Tp, class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS map {
+  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL
   static_assert(!is_lvalue_reference<_Key>::value, "'std::map' cannot hold references");
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::map' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::map' cannot hold 'void'");
-  static_assert(!__libcpp_is_unbounded_array<_Key>::value, "'std::map' cannot hold C arrays");
+  static_assert(!__libcpp_is_unbounded_array<_Key>::value, "'std::map' cannot hold C arrays of an unknown size");
 
 public:
   // types:
@@ -1649,10 +1650,11 @@ erase_if(map<_Key, _Tp, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Tp, class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS multimap {
+  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL
   static_assert(!is_lvalue_reference<_Key>::value, "'std::multimap' cannot hold references");
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::multimap' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::multimap' cannot hold 'void'");
-  static_assert(!__libcpp_is_unbounded_array<_Key>::value, "'std::multimap' cannot hold C arrays");
+  static_assert(!__libcpp_is_unbounded_array<_Key>::value, "'std::multimap' cannot hold C arrays of an unknown size");
 
 public:
   // types:
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 13d0dce34d97e3..cbd274eb15b686 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1897,6 +1897,10 @@ module std_private_type_traits_decay                                     [system
 }
 module std_private_type_traits_dependent_type                            [system] { header "__type_traits/dependent_type.h" }
 module std_private_type_traits_desugars_to                               [system] { header "__type_traits/desugars_to.h" }
+module std_private_type_traits_diagnostic_utilities [system] {
+  textual header "__type_traits/diagnostic_utilities.h"
+  export *
+}
 module std_private_type_traits_disjunction                               [system] { header "__type_traits/disjunction.h" }
 module std_private_type_traits_enable_if                                 [system] { header "__type_traits/enable_if.h" }
 module std_private_type_traits_extent                                    [system] { header "__type_traits/extent.h" }
diff --git a/libcxx/include/set b/libcxx/include/set
index 79c74e0e3c1249..89e5b128a93b37 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -530,6 +530,7 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__tree>
+#include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
@@ -568,12 +569,7 @@ class multiset;
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS set {
-  static_assert(!is_const<_Key>::value, "'std::set' cannot hold const types");
-  static_assert(!is_volatile<_Key>::value, "'std::set' cannot hold volatile types");
-  static_assert(!is_reference<_Key>::value, "'std::set' cannot hold references");
-  static_assert(!is_function<_Key>::value, "'std::set' cannot hold functions");
-  static_assert(!is_void<_Key>::value, "'std::set' cannot hold 'void'");
-  static_assert(!is_array<_Key>::value, "'std::set' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("set", _Key);
 
 public:
   // types:
@@ -1029,12 +1025,7 @@ erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS multiset {
-  static_assert(!is_const<_Key>::value, "'std::multiset' cannot hold const types");
-  static_assert(!is_volatile<_Key>::value, "'std::multiset' cannot hold volatile types");
-  static_assert(!is_reference<_Key>::value, "'std::multiset' cannot hold references");
-  static_assert(!is_function<_Key>::value, "'std::multiset' cannot hold functions");
-  static_assert(!is_void<_Key>::value, "'std::multiset' cannot hold 'void'");
-  static_assert(!is_array<_Key>::value, "'std::multiset' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("multiset", _Key);
 
 public:
   // types:
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8786a940086728..0b3bdb75a8fc4e 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -600,6 +600,7 @@ template <class Key, class T, class Hash, class Pred, class Alloc>
 #include <__ranges/concepts.h>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
+#include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_function.h>
@@ -1028,10 +1029,13 @@ template <class _Key,
           class _Pred  = equal_to<_Key>,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_map {
+  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL
   static_assert(!is_reference<_Key>::value, "'std::unordered_map' cannot hold references");
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::unordered_map' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::unordered_map' cannot hold 'void'");
-  static_assert(!is_array<_Key>::value, "'std::unordered_map' cannot hold C arrays");
+  static_assert(!__libcpp_is_unbounded_array<_Key>::value,
+                "'std::unordered_map' cannot hold C arrays of an unknown size");
+  static_assert(!is_array<_Key>::value, "'std::unordered_map' cannot hold C arrays before C++20");
 
 public:
   // types
@@ -1836,11 +1840,14 @@ template <class _Key,
           class _Pred  = equal_to<_Key>,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_multimap {
+  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL
   static_assert(!is_reference<_Key>::value, "'std::unordered_multimap' cannot hold references");
   static_assert(!is_function<_Key>::value && !is_function<_Tp>::value,
                 "'std::unordered_multimap' cannot hold functions");
   static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::unordered_multimap' cannot hold 'void'");
-  static_assert(!is_array<_Key>::value, "'std::unordered_multimap' cannot hold C arrays");
+  static_assert(!__libcpp_is_unbounded_array<_Key>::value,
+                "'std::unordered_multimap' cannot hold C arrays of an unknown size");
+  static_assert(!is_array<_Key>::value, "'std::unordered_multimap' cannot hold C arrays before C++20");
 
 public:
   // types
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 3262a565927baa..a2f92424d48f74 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -548,6 +548,7 @@ template <class Value, class Hash, class Pred, class Alloc>
 #include <__ranges/concepts.h>
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
+#include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
@@ -586,12 +587,7 @@ class unordered_multiset;
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_set {
-  static_assert(!is_const<_Value>::value, "'std::unordered_set' cannot hold const types");
-  static_assert(!is_volatile<_Value>::value, "'std::unordered_set' cannot hold volatile types");
-  static_assert(!is_reference<_Value>::value, "'std::unordered_set' cannot hold references");
-  static_assert(!is_function<_Value>::value, "'std::unordered_set' cannot hold functions");
-  static_assert(!is_void<_Value>::value, "'std::unordered_set' cannot hold 'void'");
-  static_assert(!is_array<_Value>::value, "'std::unordered_set' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("unordered_set", _Value);
 
 public:
   // types
@@ -1188,12 +1184,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_set<_Value, _Hash,
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_multiset {
-  static_assert(!is_const<_Value>::value, "'std::unordered_multiset' cannot hold const types");
-  static_assert(!is_volatile<_Value>::value, "'std::unordered_multiset' cannot hold volatile types");
-  static_assert(!is_reference<_Value>::value, "'std::unordered_multiset' cannot hold references");
-  static_assert(!is_function<_Value>::value, "'std::unordered_multiset' cannot hold functions");
-  static_assert(!is_void<_Value>::value, "'std::unordered_multiset' cannot hold 'void'");
-  static_assert(!is_array<_Value>::value, "'std::unordered_multiset' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("unordered_multiset", _Value);
 
 public:
   // types
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 5ca4868278431d..15813fe5569c1b 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -345,6 +345,7 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
 #include <__ranges/from_range.h>
 #include <__ranges/size.h>
 #include <__split_buffer>
+#include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
@@ -394,12 +395,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp, class _Allocator /* = allocator<_Tp> */>
 class _LIBCPP_TEMPLATE_VIS vector {
 private:
-  static_assert(!is_const<_Tp>::value, "'std::vector' cannot hold const types");
-  static_assert(!is_volatile<_Tp>::value, "'std::vector' cannot hold volatile types");
-  static_assert(!is_reference<_Tp>::value, "'std::vector' cannot hold references");
-  static_assert(!is_function<_Tp>::value, "'std::vector' cannot hold functions");
-  static_assert(!is_void<_Tp>::value, "'std::vector' cannot hold 'void'");
-  static_assert(!is_array<_Tp>::value, "'std::vector' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("vector", _Tp);
 
   typedef allocator<_Tp> __default_allocator_type;
 
diff --git a/libcxx/test/libcxx/containers/associative/map/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/associative/map/non_cv_objects.verify.cpp
index 7c0cca74aab445..2d87420f7bbe8b 100644
--- a/libcxx/test/libcxx/containers/associative/map/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/non_cv_objects.verify.cpp
@@ -12,19 +12,19 @@
 
 std::map<int const, int> K1;
 std::map<int, int const> M1;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::map<int volatile, int> K2;
 std::map<int, int volatile> M2;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::map<int&, int> K3;
-std::map<int, int&> M3; // TODO(cjdb): turn this into a compile-time error
+std::map<int, int&> M3; // TODO(#106635): turn this into a compile-time error
 // expected-error@*:* 1 {{'std::map' cannot hold references}}
 
 std::map<int&&, int> K4;
 std::map<int, int&&> M4;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::map<int(), int> K5;
 std::map<int(int), int> K6;
@@ -39,9 +39,9 @@ std::map<int, void> M8;
 // expected-error@*:* 2 {{'std::map' cannot hold 'void'}}
 
 std::map<int[], int> K9;
-std::map<int, int[]> M9; // TODO(cjdb): turn this into a compile-time error
-// expected-error@*:*{{'std::map' cannot hold C arrays}}
+std::map<int, int[]> M9; // TODO(#106635): turn this into a compile-time error
+// expected-error@*:*{{'std::map' cannot hold C arrays of an unknown size}}
 
 std::map<int[2], int> K10;
 std::map<int, int[2]> M10;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
diff --git a/libcxx/test/libcxx/containers/associative/multimap/non_cv_object.verify.cpp b/libcxx/test/libcxx/containers/associative/multimap/non_cv_objects.verify.cpp
similarity index 72%
rename from libcxx/test/libcxx/containers/associative/multimap/non_cv_object.verify.cpp
rename to libcxx/test/libcxx/containers/associative/multimap/non_cv_objects.verify.cpp
index a90ff6a00c2c93..4318f5ccbd7c4d 100644
--- a/libcxx/test/libcxx/containers/associative/multimap/non_cv_object.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/multimap/non_cv_objects.verify.cpp
@@ -12,19 +12,19 @@
 
 std::multimap<int const, int> K1;
 std::multimap<int, int const> M1;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::multimap<int volatile, int> K2;
 std::multimap<int, int volatile> M2;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::multimap<int&, int> K3;
-std::multimap<int, int&> M3; // TODO(cjdb): turn this into a compile-time error
+std::multimap<int, int&> M3; // TODO(#106635): turn this into a compile-time error
 // expected-error@*:* 1 {{'std::multimap' cannot hold references}}
 
 std::multimap<int&&, int> K4;
 std::multimap<int, int&&> M4;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::multimap<int(), int> K5;
 std::multimap<int(int), int> K6;
@@ -39,9 +39,9 @@ std::multimap<int, void> M8;
 // expected-error@*:* 2 {{'std::multimap' cannot hold 'void'}}
 
 std::multimap<int[], int> K9;
-std::multimap<int, int[]> M9; // TODO(cjdb): turn this into a compile-time error
-// expected-error@*:*{{'std::multimap' cannot hold C arrays}}
+std::multimap<int, int[]> M9; // TODO(#106635): turn this into a compile-time error
+// expected-error@*:*{{'std::multimap' cannot hold C arrays of an unknown size}}
 
 std::multimap<int[2], int> K10;
 std::multimap<int, int[2]> M10;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
diff --git a/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
index 876f2d4185a097..76734ffc8e71d3 100644
--- a/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/multiset/non_cv_object_types.verify.cpp
@@ -9,6 +9,7 @@
 // Checks that we can only form multisets of object types.
 
 #include <set>
+#include "test_macros.h"
 
 std::multiset<const int> C1;
 // expected-error@*:*{{'std::multiset' cannot hold const types}}
@@ -29,5 +30,9 @@ std::multiset<void> C8;
 // expected-error@*:*{{'std::multiset' cannot hold 'void'}}
 
 std::multiset<int[]> C9;
+// expected-error@*:*{{'std::multiset' cannot hold C arrays of an unknown size}}
+
 std::multiset<int[2]> C10;
-// expected-error@*:* 2 {{'std::multiset' cannot hold C arrays}}
+#if TEST_STD_VER < 20
+// expected-error@*:*{{'std::multiset' cannot hold C arrays before C++20}}
+#endif
diff --git a/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp b/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
index a88328b29c046b..2d14819b636218 100644
--- a/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/non_cv_object_types.verify.cpp
@@ -9,6 +9,7 @@
 // Checks that we can only form sets of object types.
 
 #include <set>
+#include "test_macros.h"
 
 std::set<const int> C1;
 // expected-error@*:*{{'std::set' cannot hold const types}}
@@ -29,5 +30,9 @@ std::set<void> C8;
 // expected-error@*:*{{'std::set' cannot hold 'void'}}
 
 std::set<int[]> C9;
+// expected-error@*:*{{'std::set' cannot hold C arrays of an unknown size}}
+
 std::set<int[2]> C10;
-// expected-error@*:* 2 {{'std::set' cannot hold C arrays}}
+#if TEST_STD_VER < 20
+// expected-error@*:*{{'std::set' cannot hold C arrays before C++20}}
+#endif
diff --git a/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
index 75e8bfea65ada2..b4f14630bf4089 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/non_cv_objects.verify.cpp
@@ -9,6 +9,7 @@
 // Checks that we can only form deques of object types.
 
 #include <deque>
+#include "test_macros.h"
 
 std::deque<const int> C1;
 // expected-error@*:*{{'std::deque' cannot hold const types}}
@@ -29,5 +30,9 @@ std::deque<void> C8;
 // expected-error@*:*{{'std::deque' cannot hold 'void'}}
 
 std::deque<int[]> C9;
+// expected-error@*:*{{'std::deque' cannot hold C arrays of an unknown size}}
+
 std::deque<int[2]> C10;
-// expected-error@*:* 2 {{'std::deque' cannot hold C arrays}}
+#if TEST_STD_VER < 20
+// expected-error@*:*{{'std::deque' cannot hold C arrays before C++20}}
+#endif
diff --git a/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
index 3b93ee90683dfd..a34f43d8d8504d 100644
--- a/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
@@ -9,6 +9,7 @@
 // Checks that we can only form forward_lists of object types.
 
 #include <forward_list>
+#include "test_macros.h"
 
 std::forward_list<const int> C1;
 // expected-error@*:*{{'std::forward_list' cannot hold const types}}
@@ -29,12 +30,16 @@ std::forward_list<void> C8;
 // expected-error@*:*{{'std::forward_list' cannot hold 'void'}}
 
 std::forward_list<int[]> C9;
+// expected-error@*:*{{'std::forward_list' cannot hold C arrays of an unknown size}}
+
 std::forward_list<int[2]> C10;
-// expected-error@*:* 2 {{'std::forward_list' cannot hold C arrays}}
+#if TEST_STD_VER < 20
+// expected-error@*:*{{'std::forward_list' cannot hold C arrays before C++20}}
+#endif
 
 // Spurious errors below
 
-// expected-error@*:* 7 {{'std::allocator'}}
-// expected-error@*:* 2 {{multiple overloads of}}
-// expected-error@*:* 12 {{cannot form a reference to 'void'}}
-// expected-error@*:* 84 {{no type named}}
+// expected-error@*:*  7  {{'std::allocator'}}
+// expected-error@*:* 77+ {{no type named}}
+// expected-error@*:*  9+ {{cannot form a reference to 'void'}}
+// expected-error@*:*  0+ {{multiple overloads of}}
diff --git a/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
index 48132f34b53b6c..df5901b33e7456 100644
--- a/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
@@ -9,6 +9,7 @@
 // Checks that we can only form lists of object types.
 
 #include <list>
+#include "test_macros.h"
 
 std::list<const int> C1;
 // expected-error@*:*{{'std::list' cannot hold const types}}
@@ -29,12 +30,16 @@ std::list<void> C8;
 // expected-error@*:*{{'std::list' cannot hold 'void'}}
 
 std::list<int[]> C9;
+// expected-error@*:*{{'std::list' cannot hold C arrays of an unknown size}}
+
 std::list<int[2]> C10;
-// expected-error@*:* 2 {{'std::list' cannot hold C arrays}}
+#if TEST_STD_VER < 20
+// expected-error@*:*{{'std::list' cannot hold C arrays before C++20}}
+#endif
 
 // Spurious errors below
 
-// expected-error@*:* 7 {{'std::allocator'}}
-// expected-error@*:* 3 {{multiple overloads of}}
-// expected-error@*:* 14 {{cannot form a reference to 'void'}}
-// expected-error@*:* 144 {{no type named}}
+// expected-error@*:*   0+ {{multiple overloads of}}
+// expected-error@*:*   7  {{'std::allocator'}}
+// expected-error@*:*  11+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 130+ {{no type named}}
diff --git a/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
index afc0433ee5b996..fa00e10ffbaebc 100644
--- a/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/sequences/vector/non_cv_objects.verify.cpp
@@ -9,6 +9,7 @@
 // Checks that we can only form vectors of object types.
 
 #include <vector>
+#include "test_macros.h"
 
 std::vector<const int> C1;
 // expected-error@*:*{{'std::vector' cannot hold const types}}
@@ -29,5 +30,9 @@ std::vector<void> C8;
 // expected-error@*:*{{'std::vector' cannot hold 'void'}}
 
 std::vector<int[]> C9;
+// expected-error@*:*{{'std::vector' cannot hold C arrays of an unknown size}}
+
 std::vector<int[2]> C10;
-// expected-error@*:* 2 {{'std::vector' cannot hold C arrays}}
+#if TEST_STD_VER < 20
+// expected-error@*:*{{'std::vector' cannot hold C arrays before C++20}}
+#endif
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
index 22c41b1d38b89d..7f8576379efb9d 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/non_cv_objects.verify.cpp
@@ -40,18 +40,18 @@ struct hash<S volatile> {
 
 std::unordered_map<S const, int> K1;
 std::unordered_map<int, int const> M1;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::unordered_map<S volatile, int> K2;
 std::unordered_map<int, int volatile> M2;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::unordered_map<int&, int> K3;
-std::unordered_map<int, int&> M3; // TODO(cjdb): turn this into a compile-time error
+std::unordered_map<int, int&> M3; // TODO(#106635): turn this into a compile-time error
 // expected-error@*:* 1 {{'std::unordered_map' cannot hold references}}
 
 std::unordered_map<int&&, int> K4;
-std::unordered_map<int, int&&> M4; // TODO(cjdb): turn this into a compile-time error
+std::unordered_map<int, int&&> M4; // TODO(#106635): turn this into a compile-time error
 // expected-error@*:*{{'std::unordered_map' cannot hold references}}
 
 std::unordered_map<int(), int> K5;
@@ -67,9 +67,9 @@ std::unordered_map<int, void> M8;
 // expected-error@*:* 2 {{'std::unordered_map' cannot hold 'void'}}
 
 std::unordered_map<int[], int> K9;
-std::unordered_map<int, int[]> M9; // TODO(cjdb): turn this into a compile-time error
-// expected-error@*:*{{'std::unordered_map' cannot hold C arrays}}
+std::unordered_map<int, int[]> M9; // TODO(#106635): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_map' cannot hold C arrays of an unknown size}}
 
 std::unordered_map<int[2], int> K10;
-std::unordered_map<int, int[2]> M10; // TODO(cjdb): turn this into a compile-time error
-// expected-error@*:*{{'std::unordered_map' cannot hold C arrays}}
+std::unordered_map<int, int[2]> M10; // TODO(#106635): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_map' cannot hold C arrays before C++20}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
index 2c68ad07d965f0..082090679ad2b9 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/non_cv_objects.verify.cpp
@@ -35,18 +35,18 @@ struct hash<S volatile> {
 
 std::unordered_multimap<S const, int> K1;
 std::unordered_multimap<int, int const> M1;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::unordered_multimap<S volatile, int> K2;
 std::unordered_multimap<int, int volatile> M2;
-// TODO(cjdb): turn this into a compile-time error
+// TODO(#106635): turn this into a compile-time error
 
 std::unordered_multimap<int&, int> K3;
-std::unordered_multimap<int, int&> M3; // TODO(cjdb): turn this into a compile-time error
+std::unordered_multimap<int, int&> M3; // TODO(#106635): turn this into a compile-time error
 // expected-error@*:* 1 {{'std::unordered_multimap' cannot hold references}}
 
 std::unordered_multimap<int&&, int> K4;
-std::unordered_multimap<int, int&&> M4; // TODO(cjdb): turn this into a compile-time error
+std::unordered_multimap<int, int&&> M4; // TODO(#106635): turn this into a compile-time error
 // expected-error@*:*{{'std::unordered_multimap' cannot hold references}}
 
 std::unordered_multimap<int(), int> K5;
@@ -62,9 +62,9 @@ std::unordered_multimap<int, void> M8;
 // expected-error@*:* 2 {{'std::unordered_multimap' cannot hold 'void'}}
 
 std::unordered_multimap<int[], int> K9;
-std::unordered_multimap<int, int[]> M9; // TODO(cjdb): turn this into a compile-time error
-// expected-error@*:*{{'std::unordered_multimap' cannot hold C arrays}}
+std::unordered_multimap<int, int[]> M9; // TODO(#106635): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_multimap' cannot hold C arrays of an unknown size}}
 
 std::unordered_multimap<int[2], int> K10;
-std::unordered_multimap<int, int[2]> M10; // TODO(cjdb): turn this into a compile-time error
-// expected-error@*:*{{'std::unordered_multimap' cannot hold C arrays}}
+std::unordered_multimap<int, int[2]> M10; // TODO(#106635): turn this into a compile-time error
+// expected-error@*:*{{'std::unordered_multimap' cannot hold C arrays before C++20}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
index ce575afe69d8bf..8cf58a9741f07e 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
@@ -9,6 +9,7 @@
 // Checks that we can only form unordered_multisets of object types.
 
 #include <unordered_set>
+#include "test_macros.h"
 
 std::unordered_multiset<const int> C1;
 // expected-error@*:*{{'std::unordered_multiset' cannot hold const types}}
@@ -29,5 +30,19 @@ std::unordered_multiset<void> C8;
 // expected-error@*:*{{'std::unordered_multiset' cannot hold 'void'}}
 
 std::unordered_multiset<int[]> C9;
-std::unordered_multiset<int[2]> C10;
-// expected-error@*:* 2 {{'std::unordered_multiset' cannot hold C arrays}}
+// expected-error@*:*{{'std::unordered_multiset' cannot hold C arrays of an unknown size}}
+
+// std::hash doesn't work with C arrays, so we need to test it with something else to ensure the
+// correct diagnostic is issued.
+template <class T>
+struct test_hash {
+  using argument_type = T;
+  using result_type   = std::size_t;
+
+  result_type operator()(T const&) const;
+};
+
+std::unordered_multiset<int[2], test_hash<int[2]> > C10;
+#if TEST_STD_VER < 20
+// expected-error@*:*{{'std::unordered_multiset' cannot hold C arrays before C++20}}
+#endif
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
index e4f36c90ff3284..d2b2e7373d9a92 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
@@ -9,6 +9,7 @@
 // Checks that we can only form unordered_sets of object types.
 
 #include <unordered_set>
+#include "test_macros.h"
 
 std::unordered_set<const int> C1;
 // expected-error@*:*{{'std::unordered_set' cannot hold const types}}
@@ -29,5 +30,19 @@ std::unordered_set<void> C8;
 // expected-error@*:*{{'std::unordered_set' cannot hold 'void'}}
 
 std::unordered_set<int[]> C9;
-std::unordered_set<int[2]> C10;
-// expected-error@*:* 2 {{'std::unordered_set' cannot hold C arrays}}
+// expected-error@*:*{{'std::unordered_set' cannot hold C arrays of an unknown size}}
+
+// std::hash doesn't work with C arrays, so we need to test it with something else to ensure the
+// correct diagnostic is issued.
+template <class T>
+struct test_hash {
+  using argument_type = T;
+  using result_type   = std::size_t;
+
+  result_type operator()(T const&) const;
+};
+
+std::unordered_set<int[2], test_hash<int[2]> > C10;
+#if TEST_STD_VER < 20
+// expected-error@*:*{{'std::unordered_set' cannot hold C arrays before C++20}}
+#endif



More information about the libcxx-commits mailing list