Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libcxx] improves diagnostics for containers with bad value types #106296

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5529499
[libcxx] improves diagnostics for containers with bad value types
cjdb Aug 26, 2024
1e6b81b
rewords diagnostics per internal feedback and adds arrays
cjdb Aug 29, 2024
6c1e1e3
formats files
cjdb Aug 29, 2024
938299d
sorts includes missed by clang-format
cjdb Aug 29, 2024
65346a3
more include sorting
cjdb Aug 29, 2024
472ffcf
more formatting, with a successful git clang-format
cjdb Aug 29, 2024
7596a2e
excludes <array> test from C++03
cjdb Aug 29, 2024
25e999f
suppresses irrelevant diagnostic
cjdb Aug 29, 2024
5a1b902
s/__is_unbounded_array(T)/__libcpp_is_unbounded_array<T>::value/g
cjdb Aug 29, 2024
99a4509
applies Louis' request
cjdb Aug 29, 2024
45c85d6
finally gets git-clang-format on side
cjdb Aug 29, 2024
dbf872a
unifies the containers' diagnostics
cjdb Aug 30, 2024
e596964
Update libcxx/include/__type_traits/diagnostic_utilities.h
cjdb Sep 3, 2024
e2d65c0
removes extraneous headers, adds macro to `std::allocator`
cjdb Sep 3, 2024
0487281
applies clang-format
cjdb Sep 3, 2024
6d9ce9e
changes using `is_array` to `is_bounded_array`
cjdb Sep 3, 2024
c49b7e8
reduces the number of trait instantiations
cjdb Sep 5, 2024
eb2fd68
removes commented out code
cjdb Sep 6, 2024
55a2e7a
Merge branch 'main' into cleaner-libcxx-diagnostics
cjdb Sep 6, 2024
d9043c4
post-sync clang-format
cjdb Sep 6, 2024
ab41024
responds to red CI
cjdb Sep 6, 2024
7bd03b3
responds to red CI
cjdb Sep 6, 2024
ef9d9c3
responds to red CI
cjdb Sep 6, 2024
f0f88d7
responds to red CI
cjdb Sep 7, 2024
bb82c95
replaces TODOs
cjdb Sep 7, 2024
33a452d
Revert "replaces TODOs"
cjdb Sep 8, 2024
6981a2a
Update libcxx/include/__type_traits/diagnostic_utilities.h
cjdb Sep 9, 2024
9fe3f4a
Update libcxx/include/__type_traits/diagnostic_utilities.h
cjdb Sep 9, 2024
b97942e
separates the cv-unqualified object requirement for other diganostics
cjdb Sep 10, 2024
02ffa16
fixes asan diagnostic
cjdb Sep 11, 2024
f75d1ae
Merge branch 'main' into cleaner-libcxx-diagnostics
cjdb Sep 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions libcxx/include/__memory/allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
cjdb marked this conversation as resolved.
Show resolved Hide resolved
#include <__utility/forward.h>
#include <cstddef>
#include <new>
Expand Down Expand Up @@ -76,8 +79,10 @@ 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' 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");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we tie these checks back to the Standard wording that says so? It doesn't have to be in the static assert message, but at least in a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PTAL


public:
typedef size_t size_type;
Expand Down
47 changes: 47 additions & 0 deletions libcxx/include/__type_traits/diagnostic_utilities.h
Original file line number Diff line number Diff line change
@@ -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
cjdb marked this conversation as resolved.
Show resolved Hide resolved

#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) \
; \
cjdb marked this conversation as resolved.
Show resolved Hide resolved
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) \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this macro being used outside of the definition of _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL -- is that an oversight or should this be inlined into _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was experimenting to identify the bare minimum for all containers, and possibly forgot about that due to the (long) weekend.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! It was an attempt to share the content with std::allocator, not to handle std::array and the map types (the latter of which will be converted after the clean-up).

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
10 changes: 10 additions & 0 deletions libcxx/include/array
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,16 @@ 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_unbounded_array.h>
#include <__type_traits/is_void.h>
#include <__type_traits/remove_cv.h>
#include <__type_traits/remove_reference.h>
cjdb marked this conversation as resolved.
Show resolved Hide resolved
#include <__utility/empty.h>
#include <__utility/integer_sequence.h>
#include <__utility/move.h>
Expand Down Expand Up @@ -167,6 +172,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD

template <class _Tp, size_t _Size>
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(!__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>;

// types:
Expand Down
10 changes: 10 additions & 0 deletions libcxx/include/deque
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,18 @@ 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>
#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>
cjdb marked this conversation as resolved.
Show resolved Hide resolved
#include <__type_traits/type_identity.h>
#include <__utility/forward.h>
#include <__utility/move.h>
Expand Down Expand Up @@ -468,6 +476,8 @@ 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);

public:
// types:

Expand Down
10 changes: 10 additions & 0 deletions libcxx/include/forward_list
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,21 @@ 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>
#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>
Expand Down Expand Up @@ -476,6 +484,8 @@ public:

template <class _Tp, class _Alloc>
class __forward_list_base {
_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("forward_list", _Tp);

protected:
typedef _Tp value_type;
typedef _Alloc allocator_type;
Expand Down
10 changes: 10 additions & 0 deletions libcxx/include/list
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,19 @@ 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>
#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>
Expand Down Expand Up @@ -465,6 +473,8 @@ public:

template <class _Tp, class _Alloc>
class __list_imp {
_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS_FULL("list", _Tp);

public:
__list_imp(const __list_imp&) = delete;
__list_imp& operator=(const __list_imp&) = delete;
Expand Down
17 changes: 17 additions & 0 deletions libcxx/include/map
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,11 @@ 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_unbounded_array.h>
#include <__type_traits/is_void.h>
#include <__utility/forward.h>
#include <__utility/piecewise_construct.h>
#include <__utility/swap.h>
Expand Down Expand Up @@ -962,6 +967,12 @@ 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 of an unknown size");

public:
// types:
typedef _Key key_type;
Expand Down Expand Up @@ -1639,6 +1650,12 @@ 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 of an unknown size");

public:
// types:
typedef _Key key_type;
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
12 changes: 12 additions & 0 deletions libcxx/include/set
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,15 @@ 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>
#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>

Expand Down Expand Up @@ -561,6 +569,8 @@ 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);

public:
// types:
typedef _Key key_type;
Expand Down Expand Up @@ -1015,6 +1025,8 @@ 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);

public:
// types:
typedef _Key key_type;
Expand Down
14 changes: 13 additions & 1 deletion libcxx/include/string
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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' 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>;

public:
Expand Down Expand Up @@ -814,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,
Expand Down
22 changes: 22 additions & 0 deletions libcxx/include/unordered_map
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,12 @@ 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>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_void.h>
#include <__type_traits/type_identity.h>
#include <__utility/forward.h>
#include <stdexcept>
Expand Down Expand Up @@ -1024,6 +1029,14 @@ 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(!__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
typedef _Key key_type;
Expand Down Expand Up @@ -1827,6 +1840,15 @@ 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(!__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
typedef _Key key_type;
Expand Down
Loading
Loading