From f3c685c165dd1303e1ff72d265688b4be55404a4 Mon Sep 17 00:00:00 2001 From: Hui Date: Sun, 22 Sep 2024 16:05:23 +0100 Subject: [PATCH 1/3] [libc++] Add containter_traits (prework for `std::flat_map`) --- libcxx/include/CMakeLists.txt | 1 + .../include/__type_traits/container_traits.h | 28 +++ libcxx/include/deque | 14 ++ libcxx/include/forward_list | 12 ++ libcxx/include/list | 12 ++ libcxx/include/map | 17 ++ libcxx/include/module.modulemap | 1 + libcxx/include/set | 17 ++ libcxx/include/unordered_map | 19 ++ libcxx/include/unordered_set | 19 ++ libcxx/include/vector | 15 ++ .../container_traits.compile.pass.cpp | 173 ++++++++++++++++++ 12 files changed, 328 insertions(+) create mode 100644 libcxx/include/__type_traits/container_traits.h create mode 100644 libcxx/test/libcxx/containers/container_traits.compile.pass.cpp diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 8a63280053340..243c224f8206a 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -748,6 +748,7 @@ set(files __type_traits/common_type.h __type_traits/conditional.h __type_traits/conjunction.h + __type_traits/container_traits.h __type_traits/copy_cv.h __type_traits/copy_cvref.h __type_traits/datasizeof.h diff --git a/libcxx/include/__type_traits/container_traits.h b/libcxx/include/__type_traits/container_traits.h new file mode 100644 index 0000000000000..a00433da249a6 --- /dev/null +++ b/libcxx/include/__type_traits/container_traits.h @@ -0,0 +1,28 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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_CONTAINER_TRAITS_H +#define _LIBCPP___TYPE_TRAITS_CONTAINER_TRAITS_H + +#include <__config> +#include <__type_traits/integral_constant.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +template +struct __container_traits { + using __emplacement_has_strong_exception_safety_guarantee = false_type; +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___TYPE_TRAITS_CONTAINER_TRAITS_H diff --git a/libcxx/include/deque b/libcxx/include/deque index bab0526629f0f..a13477a35cfe5 100644 --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -220,6 +220,8 @@ template #include <__ranges/size.h> #include <__split_buffer> #include <__type_traits/conditional.h> +#include <__type_traits/container_traits.h> +#include <__type_traits/disjunction.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_convertible.h> @@ -2609,6 +2611,18 @@ inline constexpr bool __format::__enable_insertable> = true; #endif // _LIBCPP_STD_VER >= 20 +template +struct __container_traits > { + // http://eel.is/c++draft/deque.modifiers#3 + // If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move + // assignment operator of T, there are no effects. If an exception is thrown while inserting a single element at + // either end, there are no effects. Otherwise, if an exception is thrown by the move constructor of a + // non-Cpp17CopyInsertable T, the effects are unspecified. + using __emplacement_has_strong_exception_safety_guarantee = + _Or, + __is_cpp17_copy_insertable::allocator_type> >; +}; + _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list index c5ae8add511cf..c581fc17fe537 100644 --- a/libcxx/include/forward_list +++ b/libcxx/include/forward_list @@ -218,6 +218,7 @@ template #include <__ranges/container_compatible_range.h> #include <__ranges/from_range.h> #include <__type_traits/conditional.h> +#include <__type_traits/container_traits.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_const.h> @@ -1544,6 +1545,17 @@ erase(forward_list<_Tp, _Allocator>& __c, const _Up& __v) { } #endif +template +struct __container_traits > { + // http://eel.is/c++draft/container.reqmts + // 66 Unless otherwise specified (see [associative.reqmts.except], [unord.req.except], [deque.modifiers], + // [inplace.vector.modifiers], and [vector.modifiers]) all container types defined in this Clause meet the following + // additional requirements: + // - (66.1) If an exception is thrown by an insert() or emplace() function while inserting a single element, that + // function has no effects. + using __emplacement_has_strong_exception_safety_guarantee = true_type; +}; + _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/list b/libcxx/include/list index 05234f7696c6f..235bac2f5a66b 100644 --- a/libcxx/include/list +++ b/libcxx/include/list @@ -225,6 +225,7 @@ template #include <__ranges/container_compatible_range.h> #include <__ranges/from_range.h> #include <__type_traits/conditional.h> +#include <__type_traits/container_traits.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_nothrow_assignable.h> @@ -1715,6 +1716,17 @@ inline constexpr bool __format::__enable_insertable> = true; #endif // _LIBCPP_STD_VER >= 20 +template +struct __container_traits > { + // http://eel.is/c++draft/container.reqmts + // 66 Unless otherwise specified (see [associative.reqmts.except], [unord.req.except], [deque.modifiers], + // [inplace.vector.modifiers], and [vector.modifiers]) all container types defined in this Clause meet the following + // additional requirements: + // - (66.1) If an exception is thrown by an insert() or emplace() function while inserting a single element, that + // function has no effects. + using __emplacement_has_strong_exception_safety_guarantee = true_type; +}; + _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/map b/libcxx/include/map index 7fca4c8a0872f..7ed4380beb53d 100644 --- a/libcxx/include/map +++ b/libcxx/include/map @@ -594,6 +594,7 @@ erase_if(multimap& c, Predicate pred); // C++20 #include <__ranges/container_compatible_range.h> #include <__ranges/from_range.h> #include <__tree> +#include <__type_traits/container_traits.h> #include <__type_traits/is_allocator.h> #include <__type_traits/remove_const.h> #include <__type_traits/type_identity.h> @@ -1644,6 +1645,14 @@ erase_if(map<_Key, _Tp, _Compare, _Allocator>& __c, _Predicate __pred) { } #endif +template +struct __container_traits > { + // http://eel.is/c++draft/associative.reqmts.except#2 + // For associative containers, if an exception is thrown by any operation from within + // an insert or emplace function inserting a single element, the insertion has no effect. + using __emplacement_has_strong_exception_safety_guarantee = true_type; +}; + template , class _Allocator = allocator > > class _LIBCPP_TEMPLATE_VIS multimap { public: @@ -2158,6 +2167,14 @@ erase_if(multimap<_Key, _Tp, _Compare, _Allocator>& __c, _Predicate __pred) { } #endif +template +struct __container_traits > { + // http://eel.is/c++draft/associative.reqmts.except#2 + // For associative containers, if an exception is thrown by any operation from within + // an insert or emplace function inserting a single element, the insertion has no effect. + using __emplacement_has_strong_exception_safety_guarantee = true_type; +}; + _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index aa05bde939f6c..3dae64dac7303 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -74,6 +74,7 @@ module std_core [system] { module common_type { header "__type_traits/common_type.h" } module conditional { header "__type_traits/conditional.h" } module conjunction { header "__type_traits/conjunction.h" } + module container_traits { header "__type_traits/container_traits.h" } module copy_cv { header "__type_traits/copy_cv.h" } module copy_cvref { header "__type_traits/copy_cvref.h" } module datasizeof { header "__type_traits/datasizeof.h" } diff --git a/libcxx/include/set b/libcxx/include/set index 0c2ca64139e0d..8cbfca7e3b149 100644 --- a/libcxx/include/set +++ b/libcxx/include/set @@ -531,6 +531,7 @@ erase_if(multiset& c, Predicate pred); // C++20 #include <__ranges/container_compatible_range.h> #include <__ranges/from_range.h> #include <__tree> +#include <__type_traits/container_traits.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_nothrow_assignable.h> @@ -1022,6 +1023,14 @@ erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) { } #endif +template +struct __container_traits > { + // http://eel.is/c++draft/associative.reqmts.except#2 + // For associative containers, if an exception is thrown by any operation from within + // an insert or emplace function inserting a single element, the insertion has no effect. + using __emplacement_has_strong_exception_safety_guarantee = true_type; +}; + template , class _Allocator = allocator<_Key> > class _LIBCPP_TEMPLATE_VIS multiset { public: @@ -1481,6 +1490,14 @@ erase_if(multiset<_Key, _Compare, _Allocator>& __c, _Predicate __pred) { } #endif +template +struct __container_traits > { + // http://eel.is/c++draft/associative.reqmts.except#2 + // For associative containers, if an exception is thrown by any operation from within + // an insert or emplace function inserting a single element, the insertion has no effect. + using __emplacement_has_strong_exception_safety_guarantee = true_type; +}; + _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map index 0d71a51ee9e72..8e04e80c00334 100644 --- a/libcxx/include/unordered_map +++ b/libcxx/include/unordered_map @@ -604,6 +604,7 @@ template #include <__ranges/concepts.h> #include <__ranges/container_compatible_range.h> #include <__ranges/from_range.h> +#include <__type_traits/container_traits.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_integral.h> @@ -1830,6 +1831,15 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_map<_Key, _Tp, _Has #endif +template +struct __container_traits > { + // http://eel.is/c++draft/unord.req.except#2 + // For unordered associative containers, if an exception is thrown by any operation + // other than the container's hash function from within an insert or emplace function + // inserting a single element, the insertion has no effect. + using __emplacement_has_strong_exception_safety_guarantee = __nothrow_invokable<_Hash, const _Key&>; +}; + template , @@ -2520,6 +2530,15 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_multimap<_Key, _Tp, #endif +template +struct __container_traits > { + // http://eel.is/c++draft/unord.req.except#2 + // For unordered associative containers, if an exception is thrown by any operation + // other than the container's hash function from within an insert or emplace function + // inserting a single element, the insertion has no effect. + using __emplacement_has_strong_exception_safety_guarantee = __nothrow_invokable<_Hash, const _Key&>; +}; + _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set index 2b09c72b866b0..a0a5be14b165a 100644 --- a/libcxx/include/unordered_set +++ b/libcxx/include/unordered_set @@ -550,6 +550,7 @@ template #include <__ranges/concepts.h> #include <__ranges/container_compatible_range.h> #include <__ranges/from_range.h> +#include <__type_traits/container_traits.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_integral.h> @@ -1183,6 +1184,15 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_set<_Value, _Hash, #endif +template +struct __container_traits > { + // http://eel.is/c++draft/unord.req.except#2 + // For unordered associative containers, if an exception is thrown by any operation + // other than the container's hash function from within an insert or emplace function + // inserting a single element, the insertion has no effect. + using __emplacement_has_strong_exception_safety_guarantee = __nothrow_invokable<_Hash, const _Value&>; +}; + template , class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> > class _LIBCPP_TEMPLATE_VIS unordered_multiset { public: @@ -1793,6 +1803,15 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const unordered_multiset<_Value, _H #endif +template +struct __container_traits > { + // http://eel.is/c++draft/unord.req.except#2 + // For unordered associative containers, if an exception is thrown by any operation + // other than the container's hash function from within an insert or emplace function + // inserting a single element, the insertion has no effect. + using __emplacement_has_strong_exception_safety_guarantee = __nothrow_invokable<_Hash, const _Value&>; +}; + _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/vector b/libcxx/include/vector index 7d3aac5989a48..219f54a670635 100644 --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -356,6 +356,8 @@ template requires is-vector-bool-reference // Since C++ #include <__ranges/size.h> #include <__split_buffer> #include <__type_traits/conditional.h> +#include <__type_traits/container_traits.h> +#include <__type_traits/disjunction.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_constructible.h> @@ -3005,6 +3007,19 @@ public: }; #endif // _LIBCPP_STD_VER >= 23 +template +struct __container_traits > { + // http://eel.is/c++draft/vector.modifiers#2 + // If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move + // assignment operator of T or by any InputIterator operation, there are no effects. If an exception is thrown while + // inserting a single element at the end and T is Cpp17CopyInsertable or is_nothrow_move_constructible_v is true, + // there are no effects. Otherwise, if an exception is thrown by the move constructor of a non-Cpp17CopyInsertable T, + // the effects are unspecified. + using __emplacement_has_strong_exception_safety_guarantee = + _Or, + __is_cpp17_copy_insertable::allocator_type> >; +}; + _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/test/libcxx/containers/container_traits.compile.pass.cpp b/libcxx/test/libcxx/containers/container_traits.compile.pass.cpp new file mode 100644 index 0000000000000..f6ea338c803fb --- /dev/null +++ b/libcxx/test/libcxx/containers/container_traits.compile.pass.cpp @@ -0,0 +1,173 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// <__type_traits/container_traits.h> +// + +#include <__type_traits/container_traits.h> + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_allocator.h" +#include "test_macros.h" +#include "MoveOnly.h" + +struct ThrowOnMove { + ThrowOnMove(); + ThrowOnMove(const ThrowOnMove&) _NOEXCEPT_(false); + ThrowOnMove(ThrowOnMove&&) _NOEXCEPT_(false); + ThrowOnMove& operator=(ThrowOnMove&&) _NOEXCEPT_(false); + ThrowOnMove& operator=(const ThrowOnMove&) _NOEXCEPT_(false); + + bool operator<(ThrowOnMove const&) const; + bool operator==(ThrowOnMove const&) const; +}; + +struct NonCopyThrowOnMove { + NonCopyThrowOnMove(); + NonCopyThrowOnMove(ThrowOnMove&&) _NOEXCEPT_(false); + NonCopyThrowOnMove(const NonCopyThrowOnMove&) = delete; + NonCopyThrowOnMove& operator=(ThrowOnMove&&) _NOEXCEPT_(false); + NonCopyThrowOnMove& operator=(const NonCopyThrowOnMove&) = delete; + + bool operator<(NonCopyThrowOnMove const&) const; + bool operator==(NonCopyThrowOnMove const&) const; +}; + +struct ThrowingHash { + template + std::size_t operator()(const T&) const _NOEXCEPT_(false); +}; + +struct NoThrowHash { + template + std::size_t operator()(const T&) const _NOEXCEPT; +}; + +template +void test_emplacement_strong_exception() { + static_assert(std::__container_traits::__emplacement_has_strong_exception_safety_guarantee::value == Expected, ""); +} + +void test() { + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception >, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception >, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception >, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, false>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception >, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, false>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, test_allocator >, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, test_allocator >, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, test_allocator >, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, test_allocator >, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, true>(); + +#if TEST_STD_VER < 11 + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, std::less, test_allocator >, + false>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, false>(); + + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, std::less, test_allocator >, + false>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, false>(); + + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, std::less, test_allocator >, + false>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, false>(); + + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception< + std::unordered_multimap, std::less, test_allocator >, + false>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, false>(); +#else + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, std::less, test_allocator >, + true>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, false>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, std::less, test_allocator >, + true>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, false>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, std::less, test_allocator >, + true>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, false>(); + + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception< + std::unordered_multimap, std::less, test_allocator >, + true>(); + test_emplacement_strong_exception, false>(); + test_emplacement_strong_exception, true>(); + test_emplacement_strong_exception, false>(); +#endif +} From c99bc9fe4f65c00ab40e20a7406bd272c04444a9 Mon Sep 17 00:00:00 2001 From: Hui Date: Sat, 28 Sep 2024 14:44:58 +0100 Subject: [PATCH 2/3] rebase and address comments --- .../include/__type_traits/container_traits.h | 14 +- libcxx/include/deque | 5 +- libcxx/include/forward_list | 6 +- libcxx/include/list | 6 +- libcxx/include/map | 4 +- libcxx/include/set | 4 +- libcxx/include/unordered_map | 7 +- libcxx/include/unordered_set | 7 +- libcxx/include/vector | 5 +- .../container_traits.compile.pass.cpp | 220 +++++++++--------- 10 files changed, 140 insertions(+), 138 deletions(-) diff --git a/libcxx/include/__type_traits/container_traits.h b/libcxx/include/__type_traits/container_traits.h index a00433da249a6..ec5f7e631b5a8 100644 --- a/libcxx/include/__type_traits/container_traits.h +++ b/libcxx/include/__type_traits/container_traits.h @@ -6,11 +6,11 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// + #ifndef _LIBCPP___TYPE_TRAITS_CONTAINER_TRAITS_H #define _LIBCPP___TYPE_TRAITS_CONTAINER_TRAITS_H #include <__config> -#include <__type_traits/integral_constant.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -18,9 +18,17 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template +// __container_traits is a general purpose struct contains traits of containers' different operations. +// It currently only has one trait: `__emplacement_has_strong_exception_safety_guarantee`, but it's +// intended to be extended in the future. +// If a container does not support an operation. For example, `std::array` does not support `insert` +// or `emplace`, the trait of that operation will return false. +template struct __container_traits { - using __emplacement_has_strong_exception_safety_guarantee = false_type; + // A trait that tells whether a single element insertion/emplacement via member function + // `insert(...)` or `emplace(...)` has strong exception guarantee, that is, if the function + // exits via an exception, the original container is unaffected + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = false; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/deque b/libcxx/include/deque index a13477a35cfe5..15197b9daa42e 100644 --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -2618,9 +2618,8 @@ struct __container_traits > { // assignment operator of T, there are no effects. If an exception is thrown while inserting a single element at // either end, there are no effects. Otherwise, if an exception is thrown by the move constructor of a // non-Cpp17CopyInsertable T, the effects are unspecified. - using __emplacement_has_strong_exception_safety_guarantee = - _Or, - __is_cpp17_copy_insertable::allocator_type> >; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = + _Or, __is_cpp17_copy_insertable<_Allocator> >::value; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list index c581fc17fe537..ae9c2eeb10d62 100644 --- a/libcxx/include/forward_list +++ b/libcxx/include/forward_list @@ -1548,12 +1548,12 @@ erase(forward_list<_Tp, _Allocator>& __c, const _Up& __v) { template struct __container_traits > { // http://eel.is/c++draft/container.reqmts - // 66 Unless otherwise specified (see [associative.reqmts.except], [unord.req.except], [deque.modifiers], + // Unless otherwise specified (see [associative.reqmts.except], [unord.req.except], [deque.modifiers], // [inplace.vector.modifiers], and [vector.modifiers]) all container types defined in this Clause meet the following // additional requirements: - // - (66.1) If an exception is thrown by an insert() or emplace() function while inserting a single element, that + // - If an exception is thrown by an insert() or emplace() function while inserting a single element, that // function has no effects. - using __emplacement_has_strong_exception_safety_guarantee = true_type; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/list b/libcxx/include/list index 235bac2f5a66b..2e43357111cff 100644 --- a/libcxx/include/list +++ b/libcxx/include/list @@ -1719,12 +1719,12 @@ inline constexpr bool __format::__enable_insertable> = true; template struct __container_traits > { // http://eel.is/c++draft/container.reqmts - // 66 Unless otherwise specified (see [associative.reqmts.except], [unord.req.except], [deque.modifiers], + // Unless otherwise specified (see [associative.reqmts.except], [unord.req.except], [deque.modifiers], // [inplace.vector.modifiers], and [vector.modifiers]) all container types defined in this Clause meet the following // additional requirements: - // - (66.1) If an exception is thrown by an insert() or emplace() function while inserting a single element, that + // - If an exception is thrown by an insert() or emplace() function while inserting a single element, that // function has no effects. - using __emplacement_has_strong_exception_safety_guarantee = true_type; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/map b/libcxx/include/map index 7ed4380beb53d..fabf39512ab75 100644 --- a/libcxx/include/map +++ b/libcxx/include/map @@ -1650,7 +1650,7 @@ struct __container_traits > { // http://eel.is/c++draft/associative.reqmts.except#2 // For associative containers, if an exception is thrown by any operation from within // an insert or emplace function inserting a single element, the insertion has no effect. - using __emplacement_has_strong_exception_safety_guarantee = true_type; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; }; template , class _Allocator = allocator > > @@ -2172,7 +2172,7 @@ struct __container_traits > { // http://eel.is/c++draft/associative.reqmts.except#2 // For associative containers, if an exception is thrown by any operation from within // an insert or emplace function inserting a single element, the insertion has no effect. - using __emplacement_has_strong_exception_safety_guarantee = true_type; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/set b/libcxx/include/set index 8cbfca7e3b149..5db0db8086dec 100644 --- a/libcxx/include/set +++ b/libcxx/include/set @@ -1028,7 +1028,7 @@ struct __container_traits > { // http://eel.is/c++draft/associative.reqmts.except#2 // For associative containers, if an exception is thrown by any operation from within // an insert or emplace function inserting a single element, the insertion has no effect. - using __emplacement_has_strong_exception_safety_guarantee = true_type; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; }; template , class _Allocator = allocator<_Key> > @@ -1495,7 +1495,7 @@ struct __container_traits > { // http://eel.is/c++draft/associative.reqmts.except#2 // For associative containers, if an exception is thrown by any operation from within // an insert or emplace function inserting a single element, the insertion has no effect. - using __emplacement_has_strong_exception_safety_guarantee = true_type; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map index 8e04e80c00334..05aa01a3b7c30 100644 --- a/libcxx/include/unordered_map +++ b/libcxx/include/unordered_map @@ -606,6 +606,7 @@ template #include <__ranges/from_range.h> #include <__type_traits/container_traits.h> #include <__type_traits/enable_if.h> +#include <__type_traits/invoke.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_integral.h> #include <__type_traits/remove_const.h> @@ -1837,7 +1838,8 @@ struct __container_traits > { // For unordered associative containers, if an exception is thrown by any operation // other than the container's hash function from within an insert or emplace function // inserting a single element, the insertion has no effect. - using __emplacement_has_strong_exception_safety_guarantee = __nothrow_invokable<_Hash, const _Key&>; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = + __nothrow_invokable<_Hash, const _Key&>::value; }; template > // For unordered associative containers, if an exception is thrown by any operation // other than the container's hash function from within an insert or emplace function // inserting a single element, the insertion has no effect. - using __emplacement_has_strong_exception_safety_guarantee = __nothrow_invokable<_Hash, const _Key&>; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = + __nothrow_invokable<_Hash, const _Key&>::value; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set index a0a5be14b165a..7ab1c651b8c95 100644 --- a/libcxx/include/unordered_set +++ b/libcxx/include/unordered_set @@ -552,6 +552,7 @@ template #include <__ranges/from_range.h> #include <__type_traits/container_traits.h> #include <__type_traits/enable_if.h> +#include <__type_traits/invoke.h> #include <__type_traits/is_allocator.h> #include <__type_traits/is_integral.h> #include <__type_traits/is_nothrow_assignable.h> @@ -1190,7 +1191,8 @@ struct __container_traits > { // For unordered associative containers, if an exception is thrown by any operation // other than the container's hash function from within an insert or emplace function // inserting a single element, the insertion has no effect. - using __emplacement_has_strong_exception_safety_guarantee = __nothrow_invokable<_Hash, const _Value&>; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = + __nothrow_invokable<_Hash, const _Value&>::value; }; template , class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> > @@ -1809,7 +1811,8 @@ struct __container_traits > { // For unordered associative containers, if an exception is thrown by any operation // other than the container's hash function from within an insert or emplace function // inserting a single element, the insertion has no effect. - using __emplacement_has_strong_exception_safety_guarantee = __nothrow_invokable<_Hash, const _Value&>; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = + __nothrow_invokable<_Hash, const _Value&>::value; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/vector b/libcxx/include/vector index 219f54a670635..88952a9c987ed 100644 --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -3015,9 +3015,8 @@ struct __container_traits > { // inserting a single element at the end and T is Cpp17CopyInsertable or is_nothrow_move_constructible_v is true, // there are no effects. Otherwise, if an exception is thrown by the move constructor of a non-Cpp17CopyInsertable T, // the effects are unspecified. - using __emplacement_has_strong_exception_safety_guarantee = - _Or, - __is_cpp17_copy_insertable::allocator_type> >; + static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = + _Or, __is_cpp17_copy_insertable<_Allocator> >::value; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/libcxx/containers/container_traits.compile.pass.cpp b/libcxx/test/libcxx/containers/container_traits.compile.pass.cpp index f6ea338c803fb..1452bfbaf3960 100644 --- a/libcxx/test/libcxx/containers/container_traits.compile.pass.cpp +++ b/libcxx/test/libcxx/containers/container_traits.compile.pass.cpp @@ -26,10 +26,10 @@ struct ThrowOnMove { ThrowOnMove(); - ThrowOnMove(const ThrowOnMove&) _NOEXCEPT_(false); - ThrowOnMove(ThrowOnMove&&) _NOEXCEPT_(false); - ThrowOnMove& operator=(ThrowOnMove&&) _NOEXCEPT_(false); - ThrowOnMove& operator=(const ThrowOnMove&) _NOEXCEPT_(false); + ThrowOnMove(const ThrowOnMove&) TEST_NOEXCEPT_COND(false); + ThrowOnMove(ThrowOnMove&&) TEST_NOEXCEPT_COND(false); + ThrowOnMove& operator=(ThrowOnMove&&) TEST_NOEXCEPT_COND(false); + ThrowOnMove& operator=(const ThrowOnMove&) TEST_NOEXCEPT_COND(false); bool operator<(ThrowOnMove const&) const; bool operator==(ThrowOnMove const&) const; @@ -37,9 +37,9 @@ struct ThrowOnMove { struct NonCopyThrowOnMove { NonCopyThrowOnMove(); - NonCopyThrowOnMove(ThrowOnMove&&) _NOEXCEPT_(false); + NonCopyThrowOnMove(NonCopyThrowOnMove&&) TEST_NOEXCEPT_COND(false); NonCopyThrowOnMove(const NonCopyThrowOnMove&) = delete; - NonCopyThrowOnMove& operator=(ThrowOnMove&&) _NOEXCEPT_(false); + NonCopyThrowOnMove& operator=(NonCopyThrowOnMove&&) TEST_NOEXCEPT_COND(false); NonCopyThrowOnMove& operator=(const NonCopyThrowOnMove&) = delete; bool operator<(NonCopyThrowOnMove const&) const; @@ -48,126 +48,116 @@ struct NonCopyThrowOnMove { struct ThrowingHash { template - std::size_t operator()(const T&) const _NOEXCEPT_(false); + std::size_t operator()(const T&) const TEST_NOEXCEPT_COND(false); }; struct NoThrowHash { template - std::size_t operator()(const T&) const _NOEXCEPT; + std::size_t operator()(const T&) const TEST_NOEXCEPT; }; -template -void test_emplacement_strong_exception() { - static_assert(std::__container_traits::__emplacement_has_strong_exception_safety_guarantee::value == Expected, ""); +template +void check() { + static_assert( + std::__container_traits::__emplacement_has_strong_exception_safety_guarantee == Expected, ""); } void test() { - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception >, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception >, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception >, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, false>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception >, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, false>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, test_allocator >, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, test_allocator >, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, test_allocator >, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, test_allocator >, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, true>(); + check >(); + check > >(); + check >(); + check >(); + check >(); + + check >(); + check > >(); + check >(); + check >(); + check >(); + + check >(); + check > >(); + check >(); + check >(); + check >(); + + check >(); + check > >(); + check >(); + check >(); + check >(); + + check >(); + check, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, test_allocator > >(); + check >(); + check >(); + check >(); #if TEST_STD_VER < 11 - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, std::less, test_allocator >, - false>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, false>(); - - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, std::less, test_allocator >, - false>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, false>(); - - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, std::less, test_allocator >, - false>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, false>(); - - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception< - std::unordered_multimap, std::less, test_allocator >, - false>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, false>(); + check >(); + check, std::less, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, std::less, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, std::less, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, std::less, test_allocator > >(); + check >(); + check >(); + check >(); #else - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, std::less, test_allocator >, - true>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, false>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, std::less, test_allocator >, - true>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, false>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, std::less, test_allocator >, - true>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, false>(); - - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception< - std::unordered_multimap, std::less, test_allocator >, - true>(); - test_emplacement_strong_exception, false>(); - test_emplacement_strong_exception, true>(); - test_emplacement_strong_exception, false>(); + check >(); + check, std::less, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, std::less, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, std::less, test_allocator > >(); + check >(); + check >(); + check >(); + + check >(); + check, std::less, test_allocator > >(); + check >(); + check >(); + check >(); #endif } From d87c968c703636dfe8f0ddb8d7f49df6e35ad7e6 Mon Sep 17 00:00:00 2001 From: Hui Date: Sat, 12 Oct 2024 13:49:37 +0100 Subject: [PATCH 3/3] address comment --- libcxx/include/__type_traits/container_traits.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libcxx/include/__type_traits/container_traits.h b/libcxx/include/__type_traits/container_traits.h index ec5f7e631b5a8..5262cef94c61e 100644 --- a/libcxx/include/__type_traits/container_traits.h +++ b/libcxx/include/__type_traits/container_traits.h @@ -18,11 +18,18 @@ _LIBCPP_BEGIN_NAMESPACE_STD -// __container_traits is a general purpose struct contains traits of containers' different operations. +// // __container_traits is a general purpose utility containing traits describing various containers operations. // It currently only has one trait: `__emplacement_has_strong_exception_safety_guarantee`, but it's // intended to be extended in the future. -// If a container does not support an operation. For example, `std::array` does not support `insert` -// or `emplace`, the trait of that operation will return false. +// +// These traits should only be used for optimization or QoI purposes. In particular, since this is a libc++ internal +// mechanism, no user-defined containers should be expected to specialize these traits (in fact it would be illegal for +// them to do so). Hence, when using these traits to implement something, make sure that a container that fails to +// specialize these traits does not result in non-conforming code. +// +// When a trait is nonsensical for a type, this class still provides a fallback value for that trait. +// For example, `std::array` does not support `insert` or `emplace`, so +// `__emplacement_has_strong_exception_safety_guarantee` is false for such types. template struct __container_traits { // A trait that tells whether a single element insertion/emplacement via member function