Skip to content

Commit f9dd885

Browse files
philnik777cjdb
andauthored
[libc++] Make std::pair trivially copyable if its members are (#89652)
This makes `std::pair` trivially copyable if its members are and we have a way to do so. We need either C++20 with requires clauses or support for `__attribute__((enable_if))`. Only Clang has support for this attribute, so it's effectively clang or C++20. Co-authored-by: Christopher Di Bella <[email protected]>
1 parent d157884 commit f9dd885

File tree

5 files changed

+72
-6
lines changed

5 files changed

+72
-6
lines changed

libcxx/include/__configuration/abi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@
9898
// and WCHAR_MAX. This ABI setting determines whether we should instead track whether the fill
9999
// value has been initialized using a separate boolean, which changes the ABI.
100100
# define _LIBCPP_ABI_IOS_ALLOW_ARBITRARY_FILL_VALUE
101+
// Make a std::pair of trivially copyable types trivially copyable.
102+
// While this technically doesn't change the layout of pair itself, other types may decide to programatically change
103+
// their representation based on whether something is trivially copyable.
104+
# define _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
101105
#elif _LIBCPP_ABI_VERSION == 1
102106
# if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
103107
// Enable compiling copies of now inline methods into the dylib to support

libcxx/include/__type_traits/datasizeof.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct _FirstPaddingByte<_Tp, true> {
5454
// the use as an extension.
5555
_LIBCPP_DIAGNOSTIC_PUSH
5656
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
57+
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
5758
template <class _Tp>
5859
inline const size_t __datasizeof_v = offsetof(_FirstPaddingByte<_Tp>, __first_padding_byte_);
5960
_LIBCPP_DIAGNOSTIC_POP

libcxx/include/__utility/pair.h

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <__type_traits/is_implicitly_default_constructible.h>
3333
#include <__type_traits/is_nothrow_assignable.h>
3434
#include <__type_traits/is_nothrow_constructible.h>
35+
#include <__type_traits/is_reference.h>
3536
#include <__type_traits/is_same.h>
3637
#include <__type_traits/is_swappable.h>
3738
#include <__type_traits/is_trivially_relocatable.h>
@@ -80,6 +81,38 @@ struct _LIBCPP_TEMPLATE_VIS pair
8081
_LIBCPP_HIDE_FROM_ABI pair(pair const&) = default;
8182
_LIBCPP_HIDE_FROM_ABI pair(pair&&) = default;
8283

84+
// When we are requested for pair to be trivially copyable by the ABI macro, we use defaulted members
85+
// if it is both legal to do it (i.e. no references) and we have a way to actually implement it, which requires
86+
// the __enable_if__ attribute before C++20.
87+
#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
88+
// FIXME: This should really just be a static constexpr variable. It's in a struct to avoid gdb printing the value
89+
// when printing a pair
90+
struct __has_defaulted_members {
91+
static const bool value = !is_reference<first_type>::value && !is_reference<second_type>::value;
92+
};
93+
# if _LIBCPP_STD_VER >= 20
94+
_LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(const pair&)
95+
requires __has_defaulted_members::value
96+
= default;
97+
98+
_LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(pair&&)
99+
requires __has_defaulted_members::value
100+
= default;
101+
# elif __has_attribute(__enable_if__)
102+
_LIBCPP_HIDE_FROM_ABI pair& operator=(const pair&)
103+
__attribute__((__enable_if__(__has_defaulted_members::value, ""))) = default;
104+
105+
_LIBCPP_HIDE_FROM_ABI pair& operator=(pair&&)
106+
__attribute__((__enable_if__(__has_defaulted_members::value, ""))) = default;
107+
# else
108+
# error "_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR isn't supported with this compiler"
109+
# endif
110+
#else
111+
struct __has_defaulted_members {
112+
static const bool value = false;
113+
};
114+
#endif // defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR) && __has_attribute(__enable_if__)
115+
83116
#ifdef _LIBCPP_CXX03_LANG
84117
_LIBCPP_HIDE_FROM_ABI pair() : first(), second() {}
85118

@@ -225,7 +258,8 @@ struct _LIBCPP_TEMPLATE_VIS pair
225258
typename __make_tuple_indices<sizeof...(_Args2) >::type()) {}
226259

227260
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
228-
operator=(__conditional_t<is_copy_assignable<first_type>::value && is_copy_assignable<second_type>::value,
261+
operator=(__conditional_t<!__has_defaulted_members::value && is_copy_assignable<first_type>::value &&
262+
is_copy_assignable<second_type>::value,
229263
pair,
230264
__nat> const& __p) noexcept(is_nothrow_copy_assignable<first_type>::value &&
231265
is_nothrow_copy_assignable<second_type>::value) {
@@ -234,10 +268,12 @@ struct _LIBCPP_TEMPLATE_VIS pair
234268
return *this;
235269
}
236270

237-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(
238-
__conditional_t<is_move_assignable<first_type>::value && is_move_assignable<second_type>::value, pair, __nat>&&
239-
__p) noexcept(is_nothrow_move_assignable<first_type>::value &&
240-
is_nothrow_move_assignable<second_type>::value) {
271+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
272+
operator=(__conditional_t<!__has_defaulted_members::value && is_move_assignable<first_type>::value &&
273+
is_move_assignable<second_type>::value,
274+
pair,
275+
__nat>&& __p) noexcept(is_nothrow_move_assignable<first_type>::value &&
276+
is_nothrow_move_assignable<second_type>::value) {
241277
first = std::forward<first_type>(__p.first);
242278
second = std::forward<second_type>(__p.second);
243279
return *this;

libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivial_copy_move.pass.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,13 @@ void test_trivial()
162162
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
163163
static_assert(!std::is_trivially_move_constructible<P>::value, "");
164164
#endif // TEST_STD_VER >= 11
165+
#ifndef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
165166
static_assert(!std::is_trivially_copy_assignable<P>::value, "");
166167
static_assert(!std::is_trivially_move_assignable<P>::value, "");
168+
#else
169+
static_assert(std::is_trivially_copy_assignable<P>::value, "");
170+
static_assert(std::is_trivially_move_assignable<P>::value, "");
171+
#endif
167172
static_assert(std::is_trivially_destructible<P>::value, "");
168173
}
169174
}

libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivially_copyable.compile.pass.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,42 @@ static_assert(!std::is_trivially_copyable<std::pair<int&, int> >::value, "");
4747
static_assert(!std::is_trivially_copyable<std::pair<int, int&> >::value, "");
4848
static_assert(!std::is_trivially_copyable<std::pair<int&, int&> >::value, "");
4949

50+
#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
51+
static_assert(std::is_trivially_copyable<std::pair<int, int> >::value, "");
52+
static_assert(std::is_trivially_copyable<std::pair<int, char> >::value, "");
53+
static_assert(std::is_trivially_copyable<std::pair<char, int> >::value, "");
54+
static_assert(std::is_trivially_copyable<std::pair<std::pair<char, char>, int> >::value, "");
55+
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable, int> >::value, "");
56+
#else
5057
static_assert(!std::is_trivially_copyable<std::pair<int, int> >::value, "");
5158
static_assert(!std::is_trivially_copyable<std::pair<int, char> >::value, "");
5259
static_assert(!std::is_trivially_copyable<std::pair<char, int> >::value, "");
5360
static_assert(!std::is_trivially_copyable<std::pair<std::pair<char, char>, int> >::value, "");
5461
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable, int> >::value, "");
62+
#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
63+
5564
#if TEST_STD_VER == 03 // Known ABI difference
5665
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_copy_assignment, int> >::value, "");
5766
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_move_assignment, int> >::value, "");
5867
#else
5968
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_copy_assignment, int> >::value, "");
6069
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_move_assignment, int> >::value, "");
6170
#endif
71+
72+
#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
73+
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_construction, int> >::value, "");
74+
#else
6275
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_construction, int> >::value, "");
76+
#endif
6377

6478
static_assert(std::is_trivially_copy_constructible<std::pair<int, int> >::value, "");
6579
static_assert(std::is_trivially_move_constructible<std::pair<int, int> >::value, "");
80+
static_assert(std::is_trivially_destructible<std::pair<int, int> >::value, "");
81+
82+
#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
83+
static_assert(std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");
84+
static_assert(std::is_trivially_move_assignable<std::pair<int, int> >::value, "");
85+
#else
6686
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");
6787
static_assert(!std::is_trivially_move_assignable<std::pair<int, int> >::value, "");
68-
static_assert(std::is_trivially_destructible<std::pair<int, int> >::value, "");
88+
#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR

0 commit comments

Comments
 (0)