[libcxx-commits] [libcxx] [libcxx] improves diagnostics for containers with bad value types (PR #106296)
Christopher Di Bella via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Aug 27 14:38:17 PDT 2024
https://github.com/cjdb created https://github.com/llvm/llvm-project/pull/106296
`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 ill-formed. In particular, `std::map<int&&, int>` should be diagnosed per [associative.reqmts.general]/p8. It's a valid production in libc++ and libstdc++ today, but `std::map<int&, int>` isn't. As such, only lvalue references are diagnosed in this commit, and a future commit can handle things that aren't already rejected by the compiler.
>From 59e11c6753d359fefc5bce9546f10f1f4e94d98d 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] [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 ill-formed. In
particular, `std::map<int&&, int>` should be diagnosed per
[associative.reqmts.general]/p8. It is currently a valid production in
libc++ and libstdc++, but `std::map<int&, int>` is not. As such, only
lvalue references are diagnosed in this commit, and a future commit will
handle everything that is not already rejected by the compiler.
---
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..3a801c0162a3f9 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_lvalue_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}}
More information about the libcxx-commits
mailing list