[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
Fri Sep 6 13:24:50 PDT 2024


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The trick is to run

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

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

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

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

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

>From e5969640e4d79a6a9130cb1db07d9eab9d1d491a Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb.ns at gmail.com>
Date: Tue, 3 Sep 2024 11:13:20 -0700
Subject: [PATCH 13/22] Update
 libcxx/include/__type_traits/diagnostic_utilities.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__type_traits/diagnostic_utilities.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__type_traits/diagnostic_utilities.h b/libcxx/include/__type_traits/diagnostic_utilities.h
index a1a745575c83d6..a58868da91cf69 100644
--- a/libcxx/include/__type_traits/diagnostic_utilities.h
+++ b/libcxx/include/__type_traits/diagnostic_utilities.h
@@ -17,7 +17,9 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/is_volatile.h>
 
-#pragma GCC system_header
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
 
 #if _LIBCPP_STD_VER >= 20
 #  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Container, _Tp)

>From e2d65c0ab991cb310a0f7649fc50c189efe79ffd Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Tue, 3 Sep 2024 21:05:55 +0000
Subject: [PATCH 14/22] removes extraneous headers, adds macro to
 `std::allocator`

---
 libcxx/include/__memory/allocator.h           | 11 ++------
 .../__type_traits/diagnostic_utilities.h      | 27 ++++++++++---------
 libcxx/include/array                          |  1 -
 libcxx/include/deque                          |  9 +------
 libcxx/include/forward_list                   |  9 +------
 libcxx/include/list                           |  8 +-----
 libcxx/include/map                            |  6 +++--
 libcxx/include/module.modulemap               | 12 ++++++++-
 libcxx/include/set                            | 11 ++------
 libcxx/include/string                         | 17 +++++-------
 libcxx/include/unordered_map                  |  6 +++--
 libcxx/include/unordered_set                  | 11 ++------
 libcxx/include/vector                         |  9 +------
 .../forward_list/non_cv_objects.verify.cpp    |  6 ++---
 .../sequences/list/non_cv_objects.verify.cpp  |  2 +-
 15 files changed, 53 insertions(+), 92 deletions(-)

diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index 5c2afdb96343b8..41c2d92d2e8459 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -14,14 +14,10 @@
 #include <__memory/addressof.h>
 #include <__memory/allocate_at_least.h>
 #include <__memory/allocator_traits.h>
-#include <__type_traits/is_const.h>
+#include <__type_traits/diagnostic_utilities.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>
@@ -79,10 +75,7 @@ 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' 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");
+  _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS("allocator", _Tp, "allocate");
 
 public:
   typedef size_t size_type;
diff --git a/libcxx/include/__type_traits/diagnostic_utilities.h b/libcxx/include/__type_traits/diagnostic_utilities.h
index a58868da91cf69..8fe81447953e3a 100644
--- a/libcxx/include/__type_traits/diagnostic_utilities.h
+++ b/libcxx/include/__type_traits/diagnostic_utilities.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_unbounded_array.h>
 #include <__type_traits/is_void.h>
@@ -22,28 +23,28 @@
 #endif
 
 #if _LIBCPP_STD_VER >= 20
-#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Container, _Tp)
+#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
 #else
-#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Container, _Tp)                                \
+#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)                                \
     ;                                                                                                                  \
-    static_assert(!is_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays before C++20")
+    static_assert(!is_array<_Tp>::value, "'std::" _Template "' cannot " _Verb " C arrays before C++20")
 #endif
 
 // Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
 // allocator that meets the Cpp17Allocator requirements (https://eel.is/c++draft/allocator.requirements).
 // In particular, this means that containers should only accept non-cv-qualified object types, and
 // types that are Cpp17Erasable.
-#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_BASE(_Container, _Tp)                                          \
-  static_assert(!is_const<_Tp>::value, "'std::" _Container "' cannot hold const types");                               \
-  static_assert(!is_volatile<_Tp>::value, "'std::" _Container "' cannot hold volatile types");                         \
-  static_assert(!is_reference<_Tp>::value, "'std::" _Container "' cannot hold references");                            \
-  static_assert(!is_function<_Tp>::value, "'std::" _Container "' cannot hold functions");                              \
-  static_assert(                                                                                                       \
-      !__libcpp_is_unbounded_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays of an unknown size")       \
-      _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Container, _Tp)
+#define _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, _Tp, _Verb)                                             \
+  static_assert(!is_const<_Tp>::value, "'std::" _Template "' cannot " _Verb " const types");                               \
+  static_assert(!is_volatile<_Tp>::value, "'std::" _Template "' cannot " _Verb " volatile types");                         \
+  static_assert(!is_reference<_Tp>::value, "'std::" _Template "' cannot " _Verb " references");                            \
+  static_assert(!is_function<_Tp>::value, "'std::" _Template "' cannot " _Verb " functions")                              \
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
 
-#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL(_Container, _Tp)                                          \
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_BASE(_Container, _Tp);                                               \
+#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(_Container, _Tp)          \
+  static_assert(                                                                                                       \
+      !__libcpp_is_unbounded_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays of an unknown size");      \
+  _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Container, _Tp, "hold");       \
   static_assert(!is_void<_Tp>::value, "'std::" _Container "' cannot hold 'void'")
 
 #endif // _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H
diff --git a/libcxx/include/array b/libcxx/include/array
index 1c4aaa89ed5222..c812bfa914a937 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -136,7 +136,6 @@ template <size_t I, class T, size_t N> const T&& get(const array<T, N>&&) noexce
 #include <__type_traits/is_unbounded_array.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_cv.h>
-#include <__type_traits/remove_reference.h>
 #include <__utility/empty.h>
 #include <__utility/integer_sequence.h>
 #include <__utility/move.h>
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 801387cbf323bd..9c1a09d9ddb7ae 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -213,16 +213,9 @@ template <class T, class Allocator, class Predicate>
 #include <__split_buffer>
 #include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
-#include <__type_traits/is_array.h>
-#include <__type_traits/is_const.h>
 #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>
@@ -476,7 +469,7 @@ const _DiffType __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer,
 
 template <class _Tp, class _Allocator /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS deque {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("deque", _Tp);
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("deque", _Tp);
 
 public:
   // types:
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index f2787e416f248e..3f19a07ff06286 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -220,19 +220,12 @@ template <class T, class Allocator, class Predicate>
 #include <__type_traits/conditional.h>
 #include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
-#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_unbounded_array.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>
@@ -484,7 +477,7 @@ public:
 
 template <class _Tp, class _Alloc>
 class __forward_list_base {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("forward_list", _Tp);
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("forward_list", _Tp);
 
 protected:
   typedef _Tp value_type;
diff --git a/libcxx/include/list b/libcxx/include/list
index 993ef9726b5142..519dac2e6996b7 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -227,17 +227,11 @@ template <class T, class Allocator, class Predicate>
 #include <__type_traits/conditional.h>
 #include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_const.h>
-#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>
@@ -473,7 +467,7 @@ public:
 
 template <class _Tp, class _Alloc>
 class __list_imp {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("list", _Tp);
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("list", _Tp);
 
 public:
   __list_imp(const __list_imp&) = delete;
diff --git a/libcxx/include/map b/libcxx/include/map
index e66316b11cb6ba..c51fbec60b2795 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -967,7 +967,8 @@ public:
 
 template <class _Key, class _Tp, class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS map {
-  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL
+  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS
+  // Remember to remove relevant headers when this is completed.
   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'");
@@ -1650,7 +1651,8 @@ erase_if(map<_Key, _Tp, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Tp, class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS multimap {
-  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL
+  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS
+  // Remember to remove relevant headers when this is completed.
   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'");
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index cbd274eb15b686..c7408aadb2ec36 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1505,7 +1505,17 @@ module std_private_memory_align                           [system] { header "__m
 module std_private_memory_aligned_alloc                   [system] { header "__memory/aligned_alloc.h" }
 module std_private_memory_allocate_at_least               [system] { header "__memory/allocate_at_least.h" }
 module std_private_memory_allocation_guard                [system] { header "__memory/allocation_guard.h" }
-module std_private_memory_allocator                       [system] { header "__memory/allocator.h" }
+module std_private_memory_allocator                       [system] {
+  header "__memory/allocator.h"
+  export std_private_type_traits_diagnostic_utilities
+  export std_private_type_traits_is_array
+  export std_private_type_traits_is_const
+  export std_private_type_traits_is_function
+  export std_private_type_traits_is_reference
+  export std_private_type_traits_is_unbounded_array
+  export std_private_type_traits_is_void
+  export std_private_type_traits_is_volatile
+}
 module std_private_memory_allocator_arg_t                 [system] { header "__memory/allocator_arg_t.h" }
 module std_private_memory_allocator_destructor            [system] { header "__memory/allocator_destructor.h" }
 module std_private_memory_allocator_traits                [system] { header "__memory/allocator_traits.h" }
diff --git a/libcxx/include/set b/libcxx/include/set
index 89e5b128a93b37..0750ae46d90624 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -532,13 +532,6 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__tree>
 #include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
-#include <__type_traits/is_array.h>
-#include <__type_traits/is_const.h>
-#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>
 
@@ -569,7 +562,7 @@ class multiset;
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS set {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("set", _Key);
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("set", _Key);
 
 public:
   // types:
@@ -1025,7 +1018,7 @@ erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS multiset {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("multiset", _Key);
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("multiset", _Key);
 
 public:
   // types:
diff --git a/libcxx/include/string b/libcxx/include/string
index 370f7706ec57a7..f79ce3fa353463 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -620,23 +620,18 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #include <__string/char_traits.h>
 #include <__string/extern_template_lists.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
-#include <__type_traits/is_const.h>
 #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>
@@ -757,12 +752,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' 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'");
+  // This is_array check precedes _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS since basic_string
+  // never permits arrays, and earlier static_asserts suppress later ones (meaning that this one is
+  // always emitted for both 'basic_string<char[]>' and 'basic_string<char[10]>', and doesn't say
+  // "before C++20").
   static_assert(!is_array<_CharT>::value, "'std::basic_string' cannot hold C arrays");
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("basic_string", _CharT);
 
   using __default_allocator_type = allocator<_CharT>;
 
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 0b3bdb75a8fc4e..446618ea599f7d 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1029,7 +1029,8 @@ template <class _Key,
           class _Pred  = equal_to<_Key>,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_map {
-  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL
+  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS
+  // Remember to remove relevant headers when this is completed.
   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'");
@@ -1840,7 +1841,8 @@ template <class _Key,
           class _Pred  = equal_to<_Key>,
           class _Alloc = allocator<pair<const _Key, _Tp> > >
 class _LIBCPP_TEMPLATE_VIS unordered_multimap {
-  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL
+  // TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS
+  // Remember to remove relevant headers when this is completed.
   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");
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index a2f92424d48f74..ef82436750346b 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -550,13 +550,6 @@ template <class Value, class Hash, class Pred, class Alloc>
 #include <__ranges/from_range.h>
 #include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
-#include <__type_traits/is_array.h>
-#include <__type_traits/is_const.h>
-#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>
 
@@ -587,7 +580,7 @@ class unordered_multiset;
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_set {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("unordered_set", _Value);
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("unordered_set", _Value);
 
 public:
   // types
@@ -1184,7 +1177,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_set<_Value, _Hash,
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_multiset {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("unordered_multiset", _Value);
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("unordered_multiset", _Value);
 
 public:
   // types
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 15813fe5569c1b..6b80d24ee645e1 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -347,16 +347,9 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
 #include <__split_buffer>
 #include <__type_traits/diagnostic_utilities.h>
 #include <__type_traits/is_allocator.h>
-#include <__type_traits/is_array.h>
-#include <__type_traits/is_const.h>
 #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/noexcept_move_assign_container.h>
-#include <__type_traits/remove_reference.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/exception_guard.h>
 #include <__utility/forward.h>
@@ -395,7 +388,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp, class _Allocator /* = allocator<_Tp> */>
 class _LIBCPP_TEMPLATE_VIS vector {
 private:
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("vector", _Tp);
+  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("vector", _Tp);
 
   typedef allocator<_Tp> __default_allocator_type;
 
diff --git a/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/forward_list/non_cv_objects.verify.cpp
index a34f43d8d8504d..72a60e13bfc547 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
@@ -39,7 +39,7 @@ std::forward_list<int[2]> C10;
 
 // Spurious errors below
 
-// expected-error@*:*  7  {{'std::allocator'}}
-// expected-error@*:* 77+ {{no type named}}
-// expected-error@*:*  9+ {{cannot form a reference to 'void'}}
 // expected-error@*:*  0+ {{multiple overloads of}}
+// expected-error@*:*  7+ {{'std::allocator'}}
+// expected-error@*:*  9+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 77+ {{no type named}}
diff --git a/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/sequences/list/non_cv_objects.verify.cpp
index df5901b33e7456..621937f05480a1 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
@@ -40,6 +40,6 @@ std::list<int[2]> C10;
 // Spurious errors below
 
 // expected-error@*:*   0+ {{multiple overloads of}}
-// expected-error@*:*   7  {{'std::allocator'}}
+// expected-error@*:*   7+ {{'std::allocator'}}
 // expected-error@*:*  11+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 130+ {{no type named}}

>From 0487281f38524a2cc1cc62dca2b00a4b77bb25a4 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Tue, 3 Sep 2024 21:09:50 +0000
Subject: [PATCH 15/22] applies clang-format

---
 .../__type_traits/diagnostic_utilities.h       | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__type_traits/diagnostic_utilities.h b/libcxx/include/__type_traits/diagnostic_utilities.h
index 8fe81447953e3a..966b1d5a202019 100644
--- a/libcxx/include/__type_traits/diagnostic_utilities.h
+++ b/libcxx/include/__type_traits/diagnostic_utilities.h
@@ -25,7 +25,7 @@
 #if _LIBCPP_STD_VER >= 20
 #  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
 #else
-#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)                                \
+#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)                          \
     ;                                                                                                                  \
     static_assert(!is_array<_Tp>::value, "'std::" _Template "' cannot " _Verb " C arrays before C++20")
 #endif
@@ -34,17 +34,17 @@
 // allocator that meets the Cpp17Allocator requirements (https://eel.is/c++draft/allocator.requirements).
 // In particular, this means that containers should only accept non-cv-qualified object types, and
 // types that are Cpp17Erasable.
-#define _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, _Tp, _Verb)                                             \
-  static_assert(!is_const<_Tp>::value, "'std::" _Template "' cannot " _Verb " const types");                               \
-  static_assert(!is_volatile<_Tp>::value, "'std::" _Template "' cannot " _Verb " volatile types");                         \
-  static_assert(!is_reference<_Tp>::value, "'std::" _Template "' cannot " _Verb " references");                            \
-  static_assert(!is_function<_Tp>::value, "'std::" _Template "' cannot " _Verb " functions")                              \
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
+#define _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, _Tp, _Verb)                                         \
+  static_assert(!is_const<_Tp>::value, "'std::" _Template "' cannot " _Verb " const types");                           \
+  static_assert(!is_volatile<_Tp>::value, "'std::" _Template "' cannot " _Verb " volatile types");                     \
+  static_assert(!is_reference<_Tp>::value, "'std::" _Template "' cannot " _Verb " references");                        \
+  static_assert(!is_function<_Tp>::value, "'std::" _Template "' cannot " _Verb " functions")                           \
+      _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
 
-#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(_Container, _Tp)          \
+#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(_Container, _Tp)                                               \
   static_assert(                                                                                                       \
       !__libcpp_is_unbounded_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays of an unknown size");      \
-  _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Container, _Tp, "hold");       \
+  _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Container, _Tp, "hold");                                            \
   static_assert(!is_void<_Tp>::value, "'std::" _Container "' cannot hold 'void'")
 
 #endif // _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H

>From 6d9ce9e78171b34e9f753caa5a5f567210a8e4a0 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Tue, 3 Sep 2024 23:18:25 +0000
Subject: [PATCH 16/22] changes using `is_array` to `is_bounded_array`

`std::llocator` apparently supports unbounded arrays prior to C++20, so
we need to change the `is_array` check to only check for bounded arrays.
---
 libcxx/include/__type_traits/diagnostic_utilities.h | 4 ++--
 libcxx/include/module.modulemap                     | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__type_traits/diagnostic_utilities.h b/libcxx/include/__type_traits/diagnostic_utilities.h
index 966b1d5a202019..c27211e8fe5b7a 100644
--- a/libcxx/include/__type_traits/diagnostic_utilities.h
+++ b/libcxx/include/__type_traits/diagnostic_utilities.h
@@ -10,7 +10,7 @@
 #define _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H
 
 #include <__config>
-#include <__type_traits/is_array.h>
+#include <__type_traits/is_bounded_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_reference.h>
@@ -27,7 +27,7 @@
 #else
 #  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)                          \
     ;                                                                                                                  \
-    static_assert(!is_array<_Tp>::value, "'std::" _Template "' cannot " _Verb " C arrays before C++20")
+    static_assert(!__libcpp_is_bounded_array<_Tp>::value, "'std::" _Template "' cannot " _Verb " C arrays before C++20")
 #endif
 
 // Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index c7408aadb2ec36..9f98ab4210d63c 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1508,7 +1508,7 @@ module std_private_memory_allocation_guard                [system] { header "__m
 module std_private_memory_allocator                       [system] {
   header "__memory/allocator.h"
   export std_private_type_traits_diagnostic_utilities
-  export std_private_type_traits_is_array
+  export std_private_type_traits_is_bounded_array
   export std_private_type_traits_is_const
   export std_private_type_traits_is_function
   export std_private_type_traits_is_reference

>From c49b7e802771289738f3b440e0f66097353925ba Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 5 Sep 2024 23:56:13 +0000
Subject: [PATCH 17/22] reduces the number of trait instantiations

Nikolas pointed out that checking all the traits in all cases will
probably grind builds to a halt as we improve our diagnostics, so this
commit makes a point of reducing the number of instantiations as much as
possible, and preserves the highly specific messages.
---
 libcxx/include/__memory/allocator.h           |  4 +-
 .../__type_traits/diagnostic_utilities.h      | 83 ++++++++++++++-----
 libcxx/include/deque                          |  4 +-
 libcxx/include/forward_list                   |  4 +-
 libcxx/include/list                           |  4 +-
 libcxx/include/set                            |  8 +-
 libcxx/include/string                         |  4 +-
 libcxx/include/unordered_set                  |  8 +-
 libcxx/include/vector                         |  4 +-
 .../multiset/non_cv_object_types.verify.cpp   | 10 +++
 .../set/non_cv_object_types.verify.cpp        | 10 +++
 .../sequences/deque/non_cv_objects.verify.cpp | 14 ++++
 .../forward_list/non_cv_objects.verify.cpp    | 20 +++--
 .../sequences/list/non_cv_objects.verify.cpp  | 21 +++--
 .../vector/non_cv_objects.verify.cpp          | 13 +++
 .../basic.string/non_cv_objects.verify.cpp    | 12 +++
 .../unord.multiset/non_cv_objects.verify.cpp  | 12 +++
 .../unord/unord.set/non_cv_objects.verify.cpp | 12 +++
 .../allocator_non_cv_objects_only.verify.cpp  |  3 +
 19 files changed, 208 insertions(+), 42 deletions(-)

diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index 41c2d92d2e8459..50c93ff173c4b7 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -75,7 +75,7 @@ struct __non_trivial_if<true, _Unique> {
 
 template <class _Tp>
 class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::value, allocator<_Tp> > {
-  _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS("allocator", _Tp, "allocate");
+  static_assert(__allocator_requirements<allocator, _Tp>::value);
 
 public:
   typedef size_t size_type;
@@ -168,6 +168,8 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const allocator<_Tp>&, const alloca
 
 #endif
 
+_LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(allocator, "allocate");
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___MEMORY_ALLOCATOR_H
diff --git a/libcxx/include/__type_traits/diagnostic_utilities.h b/libcxx/include/__type_traits/diagnostic_utilities.h
index c27211e8fe5b7a..1c87b48365c508 100644
--- a/libcxx/include/__type_traits/diagnostic_utilities.h
+++ b/libcxx/include/__type_traits/diagnostic_utilities.h
@@ -10,10 +10,13 @@
 #define _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H
 
 #include <__config>
+#include <__type_traits/decay.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_bounded_array.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
 #include <__type_traits/is_unbounded_array.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/is_volatile.h>
@@ -22,29 +25,69 @@
 #  pragma GCC system_header
 #endif
 
+// // Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
+// // allocator that meets the Cpp17Allocator requirements (https://eel.is/c++draft/allocator.requirements).
+// // In particular, this means that containers should only accept non-cv-qualified object types, and
+// // types that are Cpp17Erasable.
+// #define _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, _Tp, _Verb) \
+//   static_assert(!is_const<_Tp>::value, "'std::" _Template "' cannot " _Verb " const types"); \
+//   static_assert(!is_volatile<_Tp>::value, "'std::" _Template "' cannot " _Verb " volatile types"); \
+//   static_assert(!is_reference<_Tp>::value, "'std::" _Template "' cannot " _Verb " references"); \
+//   static_assert(!is_function<_Tp>::value, "'std::" _Template "' cannot " _Verb " functions") \
+//       _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
+
+// #define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(_Container, _Tp)                                               \
+//   static_assert(                                                                                                       \
+//       !__libcpp_is_unbounded_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays of an unknown size");      \
+//   _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Container, _Tp, "hold");                                            \
+//   static_assert(!is_void<_Tp>::value, "'std::" _Container "' cannot hold 'void'")
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <template <class...> class _Template, class _Tp, bool = is_same<typename decay<_Tp>::type, _Tp>::value>
+struct __allocator_requirements : true_type {};
+
 #if _LIBCPP_STD_VER >= 20
-#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
+template <class _Tp>
+struct __bounded_arrays_allowed_only_after_cxx20 : false_type {};
 #else
-#  define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)                          \
-    ;                                                                                                                  \
-    static_assert(!__libcpp_is_bounded_array<_Tp>::value, "'std::" _Template "' cannot " _Verb " C arrays before C++20")
+template <class _Tp>
+struct __bounded_arrays_allowed_only_after_cxx20 : integral_constant<bool, __libcpp_is_bounded_array<_Tp>::value> {};
 #endif
 
-// Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
-// allocator that meets the Cpp17Allocator requirements (https://eel.is/c++draft/allocator.requirements).
-// In particular, this means that containers should only accept non-cv-qualified object types, and
-// types that are Cpp17Erasable.
-#define _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, _Tp, _Verb)                                         \
-  static_assert(!is_const<_Tp>::value, "'std::" _Template "' cannot " _Verb " const types");                           \
-  static_assert(!is_volatile<_Tp>::value, "'std::" _Template "' cannot " _Verb " volatile types");                     \
-  static_assert(!is_reference<_Tp>::value, "'std::" _Template "' cannot " _Verb " references");                        \
-  static_assert(!is_function<_Tp>::value, "'std::" _Template "' cannot " _Verb " functions")                           \
-      _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
-
-#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(_Container, _Tp)                                               \
-  static_assert(                                                                                                       \
-      !__libcpp_is_unbounded_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays of an unknown size");      \
-  _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Container, _Tp, "hold");                                            \
-  static_assert(!is_void<_Tp>::value, "'std::" _Container "' cannot hold 'void'")
+#define _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, _Verb)                                              \
+  template <class _Tp>                                                                                                 \
+  struct __allocator_requirements<_Template, _Tp, false>                                                               \
+      : integral_constant<bool,                                                                                        \
+                          !(is_const<_Tp>::value || is_volatile<_Tp>::value || is_reference<_Tp>::value ||             \
+                            is_function<_Tp>::value || __bounded_arrays_allowed_only_after_cxx20<_Tp>::value)> {       \
+    static_assert(!is_const<_Tp>::value, "'std::" #_Template "' cannot " _Verb " const types");                        \
+    static_assert(!is_volatile<_Tp>::value, "'std::" #_Template "' cannot " _Verb " volatile types");                  \
+    static_assert(!is_reference<_Tp>::value, "'std::" #_Template "' cannot " _Verb " references");                     \
+    static_assert(!is_function<_Tp>::value, "'std::" #_Template "' cannot " _Verb " functions");                       \
+    static_assert(!__bounded_arrays_allowed_only_after_cxx20<_Tp>::value,                                              \
+                  "'std::" #_Template "' cannot " _Verb " C arrays before C++20");                                     \
+  }
+
+template <template <class...> class, class>
+struct __container_requirements : false_type {
+  static_assert(false,
+                "a new container has been defined; please define '_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS' for "
+                "that container");
+};
+
+#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(_Template)                                                     \
+  _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, "hold");                                                  \
+  template <class _Tp>                                                                                                 \
+  struct __container_requirements<_Template, _Tp>                                                                      \
+      : integral_constant<bool,                                                                                        \
+                          __allocator_requirements<_Template, _Tp>::value &&                                           \
+                              !(is_void<_Tp>::value || __libcpp_is_unbounded_array<_Tp>::value)> {                     \
+    static_assert(!is_void<_Tp>::value, "'std::" #_Template "' cannot hold 'void'");                                   \
+    static_assert(!__libcpp_is_unbounded_array<_Tp>::value,                                                            \
+                  "'std::" #_Template "' cannot hold C arrays of an unknown size");                                    \
+  }
+
+_LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 9c1a09d9ddb7ae..90f07d1ab056e4 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -469,7 +469,7 @@ const _DiffType __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer,
 
 template <class _Tp, class _Allocator /*= allocator<_Tp>*/>
 class _LIBCPP_TEMPLATE_VIS deque {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("deque", _Tp);
+  static_assert(__container_requirements<deque, _Tp>::value);
 
 public:
   // types:
@@ -2594,6 +2594,8 @@ inline constexpr bool __format::__enable_insertable<std::deque<wchar_t>> = true;
 
 #endif // _LIBCPP_STD_VER >= 20
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(deque);
+
 _LIBCPP_END_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 3f19a07ff06286..5c427db4ffaeec 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -477,7 +477,7 @@ public:
 
 template <class _Tp, class _Alloc>
 class __forward_list_base {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("forward_list", _Tp);
+  static_assert(__container_requirements<forward_list, _Tp>::value);
 
 protected:
   typedef _Tp value_type;
@@ -590,6 +590,8 @@ private:
   }
 };
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(forward_list);
+
 #ifndef _LIBCPP_CXX03_LANG
 
 template <class _Tp, class _Alloc>
diff --git a/libcxx/include/list b/libcxx/include/list
index 519dac2e6996b7..312da7e6b55c64 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -467,7 +467,7 @@ public:
 
 template <class _Tp, class _Alloc>
 class __list_imp {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("list", _Tp);
+  static_assert(__container_requirements<list, _Tp>::value);
 
 public:
   __list_imp(const __list_imp&) = delete;
@@ -595,6 +595,8 @@ private:
   _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp&, false_type) _NOEXCEPT {}
 };
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(list);
+
 // Unlink nodes [__f, __l]
 template <class _Tp, class _Alloc>
 inline void __list_imp<_Tp, _Alloc>::__unlink_nodes(__link_pointer __f, __link_pointer __l) _NOEXCEPT {
diff --git a/libcxx/include/set b/libcxx/include/set
index 0750ae46d90624..05943343ee841c 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -562,7 +562,7 @@ class multiset;
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS set {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("set", _Key);
+  static_assert(__container_requirements<set, _Key>::value);
 
 public:
   // types:
@@ -894,6 +894,8 @@ public:
 #endif
 };
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(set);
+
 #if _LIBCPP_STD_VER >= 17
 template <class _InputIterator,
           class _Compare   = less<__iter_value_type<_InputIterator>>,
@@ -1018,7 +1020,7 @@ erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
 
 template <class _Key, class _Compare = less<_Key>, class _Allocator = allocator<_Key> >
 class _LIBCPP_TEMPLATE_VIS multiset {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("multiset", _Key);
+  static_assert(__container_requirements<multiset, _Key>::value);
 
 public:
   // types:
@@ -1353,6 +1355,8 @@ public:
 #endif
 };
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(multiset);
+
 #if _LIBCPP_STD_VER >= 17
 template <class _InputIterator,
           class _Compare   = less<__iter_value_type<_InputIterator>>,
diff --git a/libcxx/include/string b/libcxx/include/string
index f79ce3fa353463..6f64fc5bbc1c46 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -757,7 +757,7 @@ private:
   // always emitted for both 'basic_string<char[]>' and 'basic_string<char[10]>', and doesn't say
   // "before C++20").
   static_assert(!is_array<_CharT>::value, "'std::basic_string' cannot hold C arrays");
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("basic_string", _CharT);
+  static_assert(__container_requirements<basic_string, _CharT>::value);
 
   using __default_allocator_type = allocator<_CharT>;
 
@@ -2259,6 +2259,8 @@ private:
   operator==(const basic_string<_CharT2, _Traits2, _Allocator2>&, const _CharT2*) _NOEXCEPT;
 };
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(basic_string);
+
 // These declarations must appear before any functions are implicitly used
 // so that they have the correct visibility specifier.
 #define _LIBCPP_DECLARE(...) extern template __VA_ARGS__;
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index ef82436750346b..5e79d5bf4da776 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -580,7 +580,7 @@ class unordered_multiset;
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_set {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("unordered_set", _Value);
+  static_assert(__container_requirements<unordered_set, _Value>::value);
 
 public:
   // types
@@ -902,6 +902,8 @@ public:
   _LIBCPP_HIDE_FROM_ABI void reserve(size_type __n) { __table_.__reserve_unique(__n); }
 };
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(unordered_set);
+
 #if _LIBCPP_STD_VER >= 17
 template <class _InputIterator,
           class _Hash      = hash<__iter_value_type<_InputIterator>>,
@@ -1177,7 +1179,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_set<_Value, _Hash,
 
 template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
 class _LIBCPP_TEMPLATE_VIS unordered_multiset {
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("unordered_multiset", _Value);
+  static_assert(__container_requirements<unordered_multiset, _Value>::value);
 
 public:
   // types
@@ -1501,6 +1503,8 @@ public:
   _LIBCPP_HIDE_FROM_ABI void reserve(size_type __n) { __table_.__reserve_multi(__n); }
 };
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(unordered_multiset);
+
 #if _LIBCPP_STD_VER >= 17
 template <class _InputIterator,
           class _Hash      = hash<__iter_value_type<_InputIterator>>,
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 6b80d24ee645e1..18827b61f4007a 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -388,7 +388,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp, class _Allocator /* = allocator<_Tp> */>
 class _LIBCPP_TEMPLATE_VIS vector {
 private:
-  _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("vector", _Tp);
+  static_assert(__container_requirements<vector, _Tp>::value);
 
   typedef allocator<_Tp> __default_allocator_type;
 
@@ -1022,6 +1022,8 @@ private:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector&, false_type) _NOEXCEPT {}
 };
 
+_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(vector);
+
 #if _LIBCPP_STD_VER >= 17
 template <class _InputIterator,
           class _Alloc = allocator<__iter_value_type<_InputIterator>>,
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 76734ffc8e71d3..bb9c2a03ef0e3e 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
@@ -36,3 +36,13 @@ std::multiset<int[2]> C10;
 #if TEST_STD_VER < 20
 // expected-error@*:*{{'std::multiset' cannot hold C arrays before C++20}}
 #endif
+
+// Spurious errors
+// expected-error at __tree:* 1+ {{}}
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no member named 'rebind'}}
+// expected-error@*:* 1+ {{std::__tree}}
+// expected-error@*:* 1+ {{multiple overloads of}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
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 2d14819b636218..e6c0e151f09037 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
@@ -36,3 +36,13 @@ std::set<int[2]> C10;
 #if TEST_STD_VER < 20
 // expected-error@*:*{{'std::set' cannot hold C arrays before C++20}}
 #endif
+
+// Spurious errors
+// expected-error at __tree:* 1+ {{}}
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no member named 'rebind'}}
+// expected-error@*:* 1+ {{std::__tree}}
+// expected-error@*:* 1+ {{multiple overloads of}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
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 b4f14630bf4089..3428d1a8626d09 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
@@ -36,3 +36,17 @@ std::deque<int[2]> C10;
 #if TEST_STD_VER < 20
 // expected-error@*:*{{'std::deque' cannot hold C arrays before C++20}}
 #endif
+
+// Spurious errors
+// expected-error@*:* 0+ {{[allocator.requirements]}}
+// expected-error@*:* 0+ {{object expression of non-scalar type}}
+// expected-error@*:* 1+ {{arithmetic on}}
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{indirection}}
+// expected-error@*:* 1+ {{invalid application of 'sizeof'}}
+// expected-error@*:* 1+ {{multiple overloads of}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no member named}}
+// expected-error@*:* 1+ {{no type named 'type' in 'std::enable_if}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
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 72a60e13bfc547..cc865e399ce40e 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
@@ -37,9 +37,17 @@ std::forward_list<int[2]> C10;
 // expected-error@*:*{{'std::forward_list' cannot hold C arrays before C++20}}
 #endif
 
-// Spurious errors below
-
-// expected-error@*:*  0+ {{multiple overloads of}}
-// expected-error@*:*  7+ {{'std::allocator'}}
-// expected-error@*:*  9+ {{cannot form a reference to 'void'}}
-// expected-error@*:* 77+ {{no type named}}
+// Spurious errors
+// expected-error@*:* 0+ {{[allocator.requirements]}}
+// expected-error@*:* 0+ {{object expression of non-scalar type}}
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{multiple overloads of}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no member named}}
+// expected-error@*:* 1+ {{no type named 'type' in 'std::enable_if}}
+// expected-error@*:* 1+ {{__node_}}
+// expected-error@*:* 1+ {{data member instantiated}}
+// expected-error@*:* 1+ {{pointer}}
+// expected-error@*:* 1+ {{iterator}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
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 621937f05480a1..bd791347a99dd7 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
@@ -37,9 +37,18 @@ std::list<int[2]> C10;
 // expected-error@*:*{{'std::list' cannot hold C arrays before C++20}}
 #endif
 
-// Spurious errors below
-
-// expected-error@*:*   0+ {{multiple overloads of}}
-// expected-error@*:*   7+ {{'std::allocator'}}
-// expected-error@*:*  11+ {{cannot form a reference to 'void'}}
-// expected-error@*:* 130+ {{no type named}}
+// Spurious errors
+// expected-error@*:* 0+ {{[allocator.requirements]}}
+// expected-error@*:* 0+ {{object expression of non-scalar type}}
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{multiple overloads of}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no member named}}
+// expected-error@*:* 1+ {{no type named 'type' in 'std::enable_if}}
+// expected-error@*:* 1+ {{__node_}}
+// expected-error@*:* 1+ {{data member instantiated}}
+// expected-error@*:* 1+ {{pointer}}
+// expected-error@*:* 1+ {{iterator}}
+// expected-error@*:* 1+ {{difference_type}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
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 fa00e10ffbaebc..0f68fc64eaf23d 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
@@ -36,3 +36,16 @@ std::vector<int[2]> C10;
 #if TEST_STD_VER < 20
 // expected-error@*:*{{'std::vector' cannot hold C arrays before C++20}}
 #endif
+
+// Bogus errors
+// expected-error@*:* 0+ {{[allocator.requirements]}}
+// expected-error@*:* 0+ {{object expression of non-scalar type}}
+// expected-error@*:* 1+ {{arithmetic on}}
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{invalid application of 'sizeof'}}
+// expected-error@*:* 1+ {{multiple overloads of}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no member named 'rebind'}}
+// expected-error@*:* 1+ {{no type named 'type' in 'std::enable_if}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
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 f81d4e4fa14245..1636bb81761a50 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
@@ -39,3 +39,15 @@ std::basic_string<int[]> C9;
 
 std::basic_string<int[2]> C10;
 // expected-error@*:*{{'std::basic_string' cannot hold C arrays}}
+
+// Bogus errors
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no function template}}
+// expected-error@*:* 1+ {{no member named 'rebind'}}
+// expected-error@*:* 1+ {{call to implicitly-deleted}}
+// expected-error@*:* 1+ {{implicit instantiation of undefined template 'std::char_traits}}
+// expected-error@*:* 1+ {{must be standard-layout}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
+// expected-warning@*:* 1+ {{volatile-qualified parameter type}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
index 8cf58a9741f07e..ae36de1422c691 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
@@ -46,3 +46,15 @@ std::unordered_multiset<int[2], test_hash<int[2]> > C10;
 #if TEST_STD_VER < 20
 // expected-error@*:*{{'std::unordered_multiset' cannot hold C arrays before C++20}}
 #endif
+
+// Spurious errors
+// expected-error at __hash_table:* 1+ {{}}
+// expected-error@*:* 1+ {{call to implicitly-deleted}}
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{multiple overloads of}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no member named 'rebind'}}
+// expected-error@*:* 1+ {{no type named 'const_iterator'}}
+// expected-error@*:* 1+ {{no type named 'const_local_iterator'}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
index d2b2e7373d9a92..56c428eb063d54 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
@@ -46,3 +46,15 @@ std::unordered_set<int[2], test_hash<int[2]> > C10;
 #if TEST_STD_VER < 20
 // expected-error@*:*{{'std::unordered_set' cannot hold C arrays before C++20}}
 #endif
+
+// Spurious errors
+// expected-error at __hash_table:* 1+ {{}}
+// expected-error@*:* 1+ {{call to implicitly-deleted}}
+// expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+// expected-error@*:* 1+ {{declared as a pointer}}
+// expected-error@*:* 1+ {{multiple overloads of}}
+// expected-error@*:* 1+ {{no matching function}}
+// expected-error@*:* 1+ {{no member named 'rebind'}}
+// expected-error@*:* 1+ {{no type named 'const_iterator'}}
+// expected-error@*:* 1+ {{no type named 'const_local_iterator'}}
+// expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
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 1374ffef8cce45..45197e83b79812 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
@@ -25,3 +25,6 @@ std::allocator<int()> A5;
 std::allocator<int(int)> A6;
 std::allocator<int(int, int)> A7;
 // expected-error@*:* 3 {{'std::allocator' cannot allocate functions}}
+
+// Spurious errors
+// expected-error@*:* 1+ {{declared as a pointer to a reference}}

>From eb2fd682f9253e03128f99b8c1886d929567ae4b Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Fri, 6 Sep 2024 00:05:14 +0000
Subject: [PATCH 18/22] removes commented out code

---
 .../__type_traits/diagnostic_utilities.h      | 20 ++++---------------
 1 file changed, 4 insertions(+), 16 deletions(-)

diff --git a/libcxx/include/__type_traits/diagnostic_utilities.h b/libcxx/include/__type_traits/diagnostic_utilities.h
index 1c87b48365c508..a9487cb65173e3 100644
--- a/libcxx/include/__type_traits/diagnostic_utilities.h
+++ b/libcxx/include/__type_traits/diagnostic_utilities.h
@@ -25,25 +25,13 @@
 #  pragma GCC system_header
 #endif
 
-// // Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
-// // allocator that meets the Cpp17Allocator requirements (https://eel.is/c++draft/allocator.requirements).
-// // In particular, this means that containers should only accept non-cv-qualified object types, and
-// // types that are Cpp17Erasable.
-// #define _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, _Tp, _Verb) \
-//   static_assert(!is_const<_Tp>::value, "'std::" _Template "' cannot " _Verb " const types"); \
-//   static_assert(!is_volatile<_Tp>::value, "'std::" _Template "' cannot " _Verb " volatile types"); \
-//   static_assert(!is_reference<_Tp>::value, "'std::" _Template "' cannot " _Verb " references"); \
-//   static_assert(!is_function<_Tp>::value, "'std::" _Template "' cannot " _Verb " functions") \
-//       _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
-
-// #define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(_Container, _Tp)                                               \
-//   static_assert(                                                                                                       \
-//       !__libcpp_is_unbounded_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays of an unknown size");      \
-//   _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Container, _Tp, "hold");                                            \
-//   static_assert(!is_void<_Tp>::value, "'std::" _Container "' cannot hold 'void'")
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+// Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
+// allocator that meets the Cpp17Allocator requirements (https://eel.is/c++draft/allocator.requirements).
+// In particular, this means that containers should only accept non-cv-qualified object types, and
+// types that are Cpp17Erasable.
 template <template <class...> class _Template, class _Tp, bool = is_same<typename decay<_Tp>::type, _Tp>::value>
 struct __allocator_requirements : true_type {};
 

>From d9043c445fd22073844c02e152a80506e740266e Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Fri, 6 Sep 2024 16:44:50 +0000
Subject: [PATCH 19/22] post-sync clang-format

---
 libcxx/include/__type_traits/diagnostic_utilities.h | 1 -
 libcxx/include/unordered_map                        | 5 ++---
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__type_traits/diagnostic_utilities.h b/libcxx/include/__type_traits/diagnostic_utilities.h
index a9487cb65173e3..436d11727eea95 100644
--- a/libcxx/include/__type_traits/diagnostic_utilities.h
+++ b/libcxx/include/__type_traits/diagnostic_utilities.h
@@ -25,7 +25,6 @@
 #  pragma GCC system_header
 #endif
 
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 // Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 12f29ccbc583f8..6538283a711406 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -601,14 +601,13 @@ 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/diagnostic_utilities.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_function.h>
+#include <__type_traits/is_integral.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_void.h>
-#include <__type_traits/enable_if.h>
-#include <__type_traits/is_allocator.h>
-#include <__type_traits/is_integral.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/forward.h>
 #include <stdexcept>

>From ab41024707e40efbcea3f7a445a2a21fc83cd992 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Fri, 6 Sep 2024 17:42:38 +0000
Subject: [PATCH 20/22] responds to red CI

---
 .../containers/sequences/deque/non_cv_objects.verify.cpp     | 2 +-
 .../sequences/forward_list/non_cv_objects.verify.cpp         | 3 ++-
 .../containers/sequences/list/non_cv_objects.verify.cpp      | 3 ++-
 .../strings/basic.string/non_cv_objects.verify.cpp           | 5 +++--
 .../unord/unord.multiset/non_cv_objects.verify.cpp           | 3 ++-
 .../containers/unord/unord.set/non_cv_objects.verify.cpp     | 3 ++-
 .../libcxx/memory/allocator_non_cv_objects_only.verify.cpp   | 1 +
 7 files changed, 13 insertions(+), 7 deletions(-)

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 3428d1a8626d09..8a0611f5eaa209 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
@@ -40,10 +40,10 @@ std::deque<int[2]> C10;
 // Spurious errors
 // expected-error@*:* 0+ {{[allocator.requirements]}}
 // expected-error@*:* 0+ {{object expression of non-scalar type}}
+// expected-error@*:* 0+ {{indirection}}
 // expected-error@*:* 1+ {{arithmetic on}}
 // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 1+ {{declared as a pointer}}
-// expected-error@*:* 1+ {{indirection}}
 // expected-error@*:* 1+ {{invalid application of 'sizeof'}}
 // expected-error@*:* 1+ {{multiple overloads of}}
 // expected-error@*:* 1+ {{no matching function}}
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 cc865e399ce40e..04d9934aec0c42 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
@@ -40,6 +40,8 @@ std::forward_list<int[2]> C10;
 // Spurious errors
 // expected-error@*:* 0+ {{[allocator.requirements]}}
 // expected-error@*:* 0+ {{object expression of non-scalar type}}
+// expected-error@*:* 0+ {{data member instantiated}}
+// expected-error@*:* 0+ {{invalid application of 'sizeof'}}
 // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 1+ {{declared as a pointer}}
 // expected-error@*:* 1+ {{multiple overloads of}}
@@ -47,7 +49,6 @@ std::forward_list<int[2]> C10;
 // expected-error@*:* 1+ {{no member named}}
 // expected-error@*:* 1+ {{no type named 'type' in 'std::enable_if}}
 // expected-error@*:* 1+ {{__node_}}
-// expected-error@*:* 1+ {{data member instantiated}}
 // expected-error@*:* 1+ {{pointer}}
 // expected-error@*:* 1+ {{iterator}}
 // expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
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 bd791347a99dd7..c5dd48953a94e4 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
@@ -40,6 +40,8 @@ std::list<int[2]> C10;
 // Spurious errors
 // expected-error@*:* 0+ {{[allocator.requirements]}}
 // expected-error@*:* 0+ {{object expression of non-scalar type}}
+// expected-error@*:* 0+ {{data member instantiated}}
+// expected-error@*:* 0+ {{invalid application of 'sizeof'}}
 // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 1+ {{declared as a pointer}}
 // expected-error@*:* 1+ {{multiple overloads of}}
@@ -47,7 +49,6 @@ std::list<int[2]> C10;
 // expected-error@*:* 1+ {{no member named}}
 // expected-error@*:* 1+ {{no type named 'type' in 'std::enable_if}}
 // expected-error@*:* 1+ {{__node_}}
-// expected-error@*:* 1+ {{data member instantiated}}
 // expected-error@*:* 1+ {{pointer}}
 // expected-error@*:* 1+ {{iterator}}
 // expected-error@*:* 1+ {{difference_type}}
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 1636bb81761a50..86a9c78f8f7fe0 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
@@ -41,13 +41,14 @@ std::basic_string<int[2]> C10;
 // expected-error@*:*{{'std::basic_string' cannot hold C arrays}}
 
 // Bogus errors
+// expected-error@*:* 0+ {{call to implicitly-deleted}}
+// expected-error@*:* 0+ {{multiple overloads}}
 // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 1+ {{declared as a pointer}}
 // expected-error@*:* 1+ {{no matching function}}
 // expected-error@*:* 1+ {{no function template}}
 // expected-error@*:* 1+ {{no member named 'rebind'}}
-// expected-error@*:* 1+ {{call to implicitly-deleted}}
 // expected-error@*:* 1+ {{implicit instantiation of undefined template 'std::char_traits}}
 // expected-error@*:* 1+ {{must be standard-layout}}
 // expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
-// expected-warning@*:* 1+ {{volatile-qualified parameter type}}
+// expected-warning@*:* 0+ {{volatile-qualified parameter type}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
index ae36de1422c691..534f90ee0e3527 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/non_cv_objects.verify.cpp
@@ -49,7 +49,8 @@ std::unordered_multiset<int[2], test_hash<int[2]> > C10;
 
 // Spurious errors
 // expected-error at __hash_table:* 1+ {{}}
-// expected-error@*:* 1+ {{call to implicitly-deleted}}
+// expected-error@*:* 0+ {{call to implicitly-deleted}}
+// expected-error@*:* 0+ {{call to deleted}}
 // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 1+ {{declared as a pointer}}
 // expected-error@*:* 1+ {{multiple overloads of}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
index 56c428eb063d54..640b2a12e105d3 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/non_cv_objects.verify.cpp
@@ -49,7 +49,8 @@ std::unordered_set<int[2], test_hash<int[2]> > C10;
 
 // Spurious errors
 // expected-error at __hash_table:* 1+ {{}}
-// expected-error@*:* 1+ {{call to implicitly-deleted}}
+// expected-error@*:* 0+ {{call to implicitly-deleted}}
+// expected-error@*:* 0+ {{call to deleted}}
 // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 1+ {{declared as a pointer}}
 // expected-error@*:* 1+ {{multiple overloads of}}
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 45197e83b79812..79f77734480787 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
@@ -27,4 +27,5 @@ std::allocator<int(int, int)> A7;
 // expected-error@*:* 3 {{'std::allocator' cannot allocate functions}}
 
 // Spurious errors
+// expected-error@*:* 0+ {{multiple overloads}}
 // expected-error@*:* 1+ {{declared as a pointer to a reference}}

>From 7bd03b3ac4af73a57f426def2eec2e5653124a95 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Fri, 6 Sep 2024 17:55:04 +0000
Subject: [PATCH 21/22] responds to red CI

---
 .../containers/strings/basic.string/non_cv_objects.verify.cpp    | 1 +
 1 file changed, 1 insertion(+)

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 86a9c78f8f7fe0..852d01c7e48507 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
@@ -43,6 +43,7 @@ std::basic_string<int[2]> C10;
 // Bogus errors
 // expected-error@*:* 0+ {{call to implicitly-deleted}}
 // expected-error@*:* 0+ {{multiple overloads}}
+// expected-error@*:* 0+ {{[allocator.requirements] states that rebinding}}
 // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 1+ {{declared as a pointer}}
 // expected-error@*:* 1+ {{no matching function}}

>From ef9d9c3f9154db59773ad538dd73bbb09c6a0c09 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Fri, 6 Sep 2024 20:24:11 +0000
Subject: [PATCH 22/22] responds to red CI

---
 .../libcxx/containers/sequences/deque/non_cv_objects.verify.cpp | 2 +-
 .../containers/sequences/vector/non_cv_objects.verify.cpp       | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

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 8a0611f5eaa209..fc21558f648219 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
@@ -41,7 +41,7 @@ std::deque<int[2]> C10;
 // expected-error@*:* 0+ {{[allocator.requirements]}}
 // expected-error@*:* 0+ {{object expression of non-scalar type}}
 // expected-error@*:* 0+ {{indirection}}
-// expected-error@*:* 1+ {{arithmetic on}}
+// expected-error@*:* 0+ {{arithmetic on}}
 // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
 // expected-error@*:* 1+ {{declared as a pointer}}
 // expected-error@*:* 1+ {{invalid application of 'sizeof'}}
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 0f68fc64eaf23d..183b5b0e4f0a3d 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
@@ -49,3 +49,4 @@ std::vector<int[2]> C10;
 // expected-error@*:* 1+ {{no member named 'rebind'}}
 // expected-error@*:* 1+ {{no type named 'type' in 'std::enable_if}}
 // expected-error@*:* 1+ {{'std::allocator' cannot allocate}}
+// expected-warning@*:* 0+ {{is a Microsoft extension}}



More information about the libcxx-commits mailing list