[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
Wed Aug 28 17:48:52 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 1/3] [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 2/3] 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 3/3] 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;



More information about the libcxx-commits mailing list