Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
32 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
20e0996
Merge branch 'main' into cleaner-libcxx-diagnostics
cjdb Oct 14, 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
6 changes: 2 additions & 4 deletions libcxx/include/__memory/allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
#include <__memory/addressof.h>
#include <__memory/allocate_at_least.h>
#include <__memory/allocator_traits.h>
#include <__type_traits/is_const.h>
#include <__type_traits/diagnostic_utilities.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_void.h>
#include <__type_traits/is_volatile.h>
#include <__utility/forward.h>
#include <cstddef>
#include <new>
Expand Down Expand Up @@ -76,8 +75,7 @@ struct __non_trivial_if<true, _Unique> {

template <class _Tp>
class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::value, allocator<_Tp> > {
static_assert(!is_const<_Tp>::value, "std::allocator does not support const types");
static_assert(!is_volatile<_Tp>::value, "std::allocator does not support volatile types");
_LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS("allocator", _Tp, "allocate");

public:
typedef size_t size_type;
Expand Down
50 changes: 50 additions & 0 deletions libcxx/include/__type_traits/diagnostic_utilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// 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_bounded_array.h>
#include <__type_traits/is_const.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_unbounded_array.h>
#include <__type_traits/is_void.h>
#include <__type_traits/is_volatile.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

#if _LIBCPP_STD_VER >= 20
# define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)
#else
# define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb) \
; \
static_assert(!__libcpp_is_bounded_array<_Tp>::value, "'std::" _Template "' cannot " _Verb " C arrays before C++20")
#endif

// Per https://eel.is/c++draft/containers#container.reqmts-64, allocator-aware containers must have an
// allocator that meets the Cpp17Allocator requirements (https://eel.is/c++draft/allocator.requirements).
// In particular, this means that containers should only accept non-cv-qualified object types, and
// types that are Cpp17Erasable.
#define _LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Template, _Tp, _Verb) \
static_assert(!is_const<_Tp>::value, "'std::" _Template "' cannot " _Verb " const types"); \
static_assert(!is_volatile<_Tp>::value, "'std::" _Template "' cannot " _Verb " volatile types"); \
static_assert(!is_reference<_Tp>::value, "'std::" _Template "' cannot " _Verb " references"); \
static_assert(!is_function<_Tp>::value, "'std::" _Template "' cannot " _Verb " functions") \
_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_IS_NOT_ARRAY_BEFORE_CXX20(_Template, _Tp, _Verb)

#define _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS(_Container, _Tp) \
static_assert( \
!__libcpp_is_unbounded_array<_Tp>::value, "'std::" _Container "' cannot hold C arrays of an unknown size"); \
Copy link
Member

Choose a reason for hiding this comment

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

Instead of passing vector as the _Container argument, I'd pass std::vector and simplify the message here. The message would become _Container " cannot hold C arrays of an unknown size".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I'd prefer to keep the single quotes so that it's consistent with how Clang outputs type information. For example, this is the diagnostic we get for this erroneous code:

namespace std {
  template<class T>
  struct S1;

  template<class T>
  struct S2;
}

std::S1<std::S2> y;
error: use of class template 'std::S2' requires template arguments

This helps us remain more consistent as a whole implementation:

error: static assertion failed: 'std::vector' cannot hold C arrays of an unknown size

WDYT?

_LIBCPP_CHECK_ALLOCATOR_VALUE_TYPE_REQUIREMENTS(_Container, _Tp, "hold"); \
static_assert(!is_void<_Tp>::value, "'std::" _Container "' cannot hold 'void'")

#endif // _LIBCPP___TYPE_TRAITS_DIAGNOSTIC_UTILITIES_H
9 changes: 9 additions & 0 deletions libcxx/include/array
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,14 @@ 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 <__utility/empty.h>
#include <__utility/integer_sequence.h>
Expand Down Expand Up @@ -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, "'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
3 changes: 3 additions & 0 deletions libcxx/include/deque
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ template <class T, class Allocator, class Predicate>
#include <__ranges/from_range.h>
#include <__ranges/size.h>
#include <__split_buffer>
#include <__type_traits/diagnostic_utilities.h>
#include <__type_traits/is_allocator.h>
#include <__type_traits/is_convertible.h>
#include <__type_traits/is_same.h>
Expand Down Expand Up @@ -468,6 +469,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("deque", _Tp);

public:
// types:

Expand Down
3 changes: 3 additions & 0 deletions libcxx/include/forward_list
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ template <class T, class Allocator, class Predicate>
#include <__ranges/container_compatible_range.h>
#include <__ranges/from_range.h>
#include <__type_traits/conditional.h>
#include <__type_traits/diagnostic_utilities.h>
#include <__type_traits/is_allocator.h>
#include <__type_traits/is_const.h>
#include <__type_traits/is_nothrow_assignable.h>
Expand Down Expand Up @@ -476,6 +477,8 @@ public:

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

protected:
typedef _Tp value_type;
typedef _Alloc allocator_type;
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/list
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ 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_const.h>
#include <__type_traits/is_nothrow_assignable.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_pointer.h>
Expand Down Expand Up @@ -465,6 +467,8 @@ public:

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

public:
__list_imp(const __list_imp&) = delete;
__list_imp& operator=(const __list_imp&) = delete;
Expand Down
19 changes: 19 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,13 @@ 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
// Remember to remove relevant headers when this is completed.
static_assert(!is_lvalue_reference<_Key>::value, "'std::map' cannot hold references");
static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::map' cannot hold functions");
static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::map' cannot hold 'void'");
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 +1651,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 {
// TODO(#106635): replace with _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS
// Remember to remove relevant headers when this is completed.
static_assert(!is_lvalue_reference<_Key>::value, "'std::multimap' cannot hold references");
static_assert(!is_function<_Key>::value && !is_function<_Tp>::value, "'std::multimap' cannot hold functions");
static_assert(!is_void<_Key>::value && !is_void<_Tp>::value, "'std::multimap' cannot hold 'void'");
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
16 changes: 15 additions & 1 deletion libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -1505,7 +1505,17 @@ module std_private_memory_align [system] { header "__m
module std_private_memory_aligned_alloc [system] { header "__memory/aligned_alloc.h" }
module std_private_memory_allocate_at_least [system] { header "__memory/allocate_at_least.h" }
module std_private_memory_allocation_guard [system] { header "__memory/allocation_guard.h" }
module std_private_memory_allocator [system] { header "__memory/allocator.h" }
module std_private_memory_allocator [system] {
header "__memory/allocator.h"
export std_private_type_traits_diagnostic_utilities
export std_private_type_traits_is_bounded_array
export std_private_type_traits_is_const
export std_private_type_traits_is_function
export std_private_type_traits_is_reference
export std_private_type_traits_is_unbounded_array
export std_private_type_traits_is_void
export std_private_type_traits_is_volatile
Copy link
Member

Choose a reason for hiding this comment

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

Did you run into CI issues if you didn't do that? I don't think these need to be exported from the module since they are only being used in the implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I'm puzzled too. Our CI build script wasn't happy when building the modules. Might be worth trying on your end to see if I'm doing something silly?

}
module std_private_memory_allocator_arg_t [system] { header "__memory/allocator_arg_t.h" }
module std_private_memory_allocator_destructor [system] { header "__memory/allocator_destructor.h" }
module std_private_memory_allocator_traits [system] { header "__memory/allocator_traits.h" }
Expand Down Expand Up @@ -1897,6 +1907,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
5 changes: 5 additions & 0 deletions libcxx/include/set
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred); // C++20
#include <__ranges/container_compatible_range.h>
#include <__ranges/from_range.h>
#include <__tree>
#include <__type_traits/diagnostic_utilities.h>
#include <__type_traits/is_allocator.h>
#include <__utility/forward.h>
#include <version>
Expand Down Expand Up @@ -561,6 +562,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("set", _Key);

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

public:
// types:
typedef _Key key_type;
Expand Down
9 changes: 8 additions & 1 deletion libcxx/include/string
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
#include <__string/char_traits.h>
#include <__string/extern_template_lists.h>
#include <__type_traits/conditional.h>
#include <__type_traits/diagnostic_utilities.h>
#include <__type_traits/is_allocator.h>
#include <__type_traits/is_array.h>
#include <__type_traits/is_convertible.h>
Expand Down Expand Up @@ -751,6 +752,13 @@ struct __init_with_sentinel_tag {};
template <class _CharT, class _Traits, class _Allocator>
class basic_string {
private:
// This is_array check precedes _LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS since basic_string
// never permits arrays, and earlier static_asserts suppress later ones (meaning that this one is
// always emitted for both 'basic_string<char[]>' and 'basic_string<char[10]>', and doesn't say
// "before C++20").
static_assert(!is_array<_CharT>::value, "'std::basic_string' cannot hold C arrays");
_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("basic_string", _CharT);

using __default_allocator_type = allocator<_CharT>;

public:
Expand Down Expand Up @@ -814,7 +822,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
24 changes: 24 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,15 @@ 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
// Remember to remove relevant headers when this is completed.
Copy link
Member

Choose a reason for hiding this comment

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

I read the description of microsoft/STL#3660 and it looks like the Standard does allow these types to be used. If that's the case, then #106635 should be closed until we have a LWG issue or a paper that changes the Standard, and these TODOs should be removed since we won't be able to address them.

Note that I have no love for reference types in these locations, but I also want to avoid adding non-actionable TODOs.

Copy link
Contributor Author

@cjdb cjdb Sep 5, 2024

Choose a reason for hiding this comment

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

I think MS/STL is allowing these as an extension? I see that the PR mentions that the standard permits this, but my interpretation of the linked wording from #106635 already prohibits unordered_map<int, int&>. I've asked for input in microsoft/STL#3660.

Nonetheless, we weren't catching it before, and have been Hyrum's Law'd.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I get what's happening, thanks to cplusplus/draft#7249. I've parked #106635, and have replaced the TODOs with // not an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Per #106635 (comment), I've reopened the issue, and have reverted bb82c95.

Copy link
Member

Choose a reason for hiding this comment

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

So IIUC basically references in these locations are not permitted, and we should clean up the usage.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

References, const, volatile, etc. I'm especially leery of map<int const, int>, because it's in no way interface-distinguishable from map<int, int>, yet is its own instantiation.

I'm happy to take point on that over the next few releases, but we'll need to come up with a plan (and potentially coordinate with MS/STL and libstdc++).

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 +1841,16 @@ 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
// Remember to remove relevant headers when this is completed.
static_assert(!is_reference<_Key>::value, "'std::unordered_multimap' cannot hold references");
static_assert(!is_function<_Key>::value && !is_function<_Tp>::value,
"'std::unordered_multimap' cannot hold functions");
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
5 changes: 5 additions & 0 deletions libcxx/include/unordered_set
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ template <class Value, class Hash, class Pred, class Alloc>
#include <__ranges/concepts.h>
#include <__ranges/container_compatible_range.h>
#include <__ranges/from_range.h>
#include <__type_traits/diagnostic_utilities.h>
#include <__type_traits/is_allocator.h>
#include <__utility/forward.h>
#include <version>
Expand Down Expand Up @@ -579,6 +580,8 @@ class unordered_multiset;

template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
class _LIBCPP_TEMPLATE_VIS unordered_set {
_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("unordered_set", _Value);

public:
// types
typedef _Value key_type;
Expand Down Expand Up @@ -1174,6 +1177,8 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_set<_Value, _Hash,

template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
class _LIBCPP_TEMPLATE_VIS unordered_multiset {
_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("unordered_multiset", _Value);

public:
// types
typedef _Value key_type;
Expand Down
3 changes: 3 additions & 0 deletions libcxx/include/vector
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
#include <__ranges/from_range.h>
#include <__ranges/size.h>
#include <__split_buffer>
#include <__type_traits/diagnostic_utilities.h>
#include <__type_traits/is_allocator.h>
#include <__type_traits/is_constructible.h>
#include <__type_traits/is_nothrow_assignable.h>
Expand Down Expand Up @@ -387,6 +388,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp, class _Allocator /* = allocator<_Tp> */>
class _LIBCPP_TEMPLATE_VIS vector {
private:
_LIBCPP_CHECK_CONTAINER_VALUE_TYPE_REQUIREMENTS("vector", _Tp);

typedef allocator<_Tp> __default_allocator_type;

public:
Expand Down
Loading