Skip to content

Commit b637cc9

Browse files
committed
[libcxx] makes pair conditionally trivially copyable for C++20
`pair` can have trivial copy and move assignment operators when both its members have trivial copy and move operators, which opens `pair` up to being trivially copyable. This is the first of three possible commits to bring users a more robust implementation. It's currently unclear how to implement this without using a _requires-clause_, so the feature is restricted to C++20 for now. There has also been some interest in moving this out of the unstable ABI mode. Both of these changes are intended to arrive in future pull requests, should their feasibility pan out.
1 parent 725a052 commit b637cc9

File tree

6 files changed

+137
-1
lines changed

6 files changed

+137
-1
lines changed

libcxx/include/__config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@
180180
// requires code not to make these assumptions.
181181
# define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY
182182
# define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW
183+
// Allows std::pair's copy assignment and move assignment operators to be trivial
184+
// when both its element types' respective assignment operators are trivial.
185+
# define _LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE
183186
# elif _LIBCPP_ABI_VERSION == 1
184187
# if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
185188
// Enable compiling copies of now inline methods into the dylib to support

libcxx/include/__utility/pair.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <__compare/common_comparison_category.h>
1313
#include <__compare/synth_three_way.h>
14+
#include <__concepts/assignable.h>
1415
#include <__concepts/different_from.h>
1516
#include <__config>
1617
#include <__fwd/array.h>
@@ -26,6 +27,7 @@
2627
#include <__type_traits/common_type.h>
2728
#include <__type_traits/conditional.h>
2829
#include <__type_traits/decay.h>
30+
#include <__type_traits/enable_if.h>
2931
#include <__type_traits/integral_constant.h>
3032
#include <__type_traits/is_assignable.h>
3133
#include <__type_traits/is_constructible.h>
@@ -40,8 +42,11 @@
4042
#include <__type_traits/is_nothrow_copy_constructible.h>
4143
#include <__type_traits/is_nothrow_default_constructible.h>
4244
#include <__type_traits/is_nothrow_move_assignable.h>
45+
#include <__type_traits/is_object.h>
4346
#include <__type_traits/is_same.h>
4447
#include <__type_traits/is_swappable.h>
48+
#include <__type_traits/is_trivially_copy_assignable.h>
49+
#include <__type_traits/is_trivially_move_assignable.h>
4550
#include <__type_traits/nat.h>
4651
#include <__type_traits/remove_cvref.h>
4752
#include <__type_traits/unwrap_ref.h>
@@ -67,6 +72,16 @@ struct __non_trivially_copyable_base {
6772
__non_trivially_copyable_base(__non_trivially_copyable_base const&) _NOEXCEPT {}
6873
};
6974

75+
#if _LIBCPP_STD_VER >= 20
76+
template <class _Tp>
77+
concept __trivially_copy_assignable_object =
78+
assignable_from<_Tp&, _Tp const&> && is_trivially_copy_assignable_v<_Tp> && is_object_v<_Tp>;
79+
80+
template <class _Tp>
81+
concept __trivially_move_assignable_object =
82+
assignable_from<_Tp&, _Tp> && is_trivially_move_assignable_v<_Tp> && is_object_v<_Tp>;
83+
#endif
84+
7085
#if _LIBCPP_STD_VER >= 23
7186
template <class _Tp>
7287
struct __is_specialization_of_subrange : false_type {};
@@ -236,6 +251,32 @@ struct _LIBCPP_TEMPLATE_VIS pair
236251
typename __make_tuple_indices<sizeof...(_Args1)>::type(),
237252
typename __make_tuple_indices<sizeof...(_Args2) >::type()) {}
238253

254+
# if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
255+
_LIBCPP_HIDE_FROM_ABI pair& operator=(pair const& __p)
256+
requires __trivially_copy_assignable_object<first_type> && __trivially_copy_assignable_object<second_type>
257+
= default;
258+
_LIBCPP_HIDE_FROM_ABI pair& operator=(pair&& __p)
259+
requires __trivially_move_assignable_object<first_type> && __trivially_move_assignable_object<second_type>
260+
= default;
261+
262+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(pair const& __p)
263+
_NOEXCEPT_(is_nothrow_copy_assignable<first_type>::value&& is_nothrow_copy_assignable<second_type>::value)
264+
requires assignable_from<first_type, first_type const&> && assignable_from<second_type, second_type const&>
265+
{
266+
first = __p.first;
267+
second = __p.second;
268+
return *this;
269+
}
270+
271+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(pair&& __p)
272+
_NOEXCEPT_(is_nothrow_move_assignable<first_type>::value&& is_nothrow_move_assignable<second_type>::value)
273+
requires assignable_from<first_type, first_type> && assignable_from<second_type, second_type>
274+
{
275+
first = std::forward<first_type>(__p.first);
276+
second = std::forward<second_type>(__p.second);
277+
return *this;
278+
}
279+
# else
239280
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
240281
operator=(__conditional_t< is_copy_assignable<first_type>::value && is_copy_assignable<second_type>::value,
241282
pair,
@@ -254,6 +295,7 @@ struct _LIBCPP_TEMPLATE_VIS pair
254295
second = std::forward<second_type>(__p.second);
255296
return *this;
256297
}
298+
# endif
257299

258300
template <
259301
class _U1,

libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ TEST_CONSTEXPR_CXX20 bool test() {
8888
assert(&p.second == &inc_obj);
8989
}
9090

91+
#if TEST_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
92+
static_assert(std::is_trivially_copy_assignable<std::pair<int, int>>::value, "");
93+
#else
94+
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int>>::value, "");
95+
#endif
96+
97+
static_assert(!std::is_trivially_copy_assignable<std::pair<CountAssign, int>>::value, "");
98+
static_assert(!std::is_trivially_copy_assignable<std::pair<int, CountAssign>>::value, "");
99+
static_assert(!std::is_trivially_copy_assignable<std::pair<CountAssign, CountAssign>>::value, "");
100+
91101
return true;
92102
}
93103

libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ int main(int, char**)
6262
assert(p.second == 'x');
6363
}
6464

65-
return 0;
65+
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");
66+
67+
return 0;
6668
}
6769

6870
struct Incomplete {};

libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ TEST_CONSTEXPR_CXX20 bool test() {
130130
static_assert(!std::is_move_assignable<P5>::value, "");
131131
static_assert(!std::is_move_assignable<P6>::value, "");
132132
}
133+
134+
#if TEST_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
135+
static_assert(std::is_trivially_move_assignable<std::pair<int, int>>::value, "");
136+
#else
137+
static_assert(!std::is_trivially_move_assignable<std::pair<int, int>>::value, "");
138+
#endif
139+
140+
static_assert(!std::is_trivially_move_assignable<std::pair<CountAssign, int>>::value, "");
141+
static_assert(!std::is_trivially_move_assignable<std::pair<int, CountAssign>>::value, "");
142+
static_assert(!std::is_trivially_move_assignable<std::pair<CountAssign, CountAssign>>::value, "");
143+
133144
return true;
134145
}
135146

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
11+
// <utility>
12+
13+
// template <class T1, class T2> struct pair
14+
15+
// Checks that `std::pair` is actually trivially copyable.
16+
17+
#include <type_traits>
18+
#include <utility>
19+
20+
#if defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
21+
static_assert(std::is_trivially_copyable_v<std::pair<int, int>>);
22+
static_assert(std::is_trivially_copy_constructible_v<std::pair<int, int>>);
23+
static_assert(std::is_trivially_move_constructible_v<std::pair<int, int>>);
24+
static_assert(std::is_trivially_copy_assignable_v<std::pair<int, int>>);
25+
static_assert(std::is_trivially_move_assignable_v<std::pair<int, int>>);
26+
static_assert(std::is_trivially_destructible_v<std::pair<int, int>>);
27+
28+
// static_assert(std::is_trivially_copyable_v<std::pair<int, int const>>);
29+
// static_assert(std::is_trivially_copyable_v<std::pair<int const, int>>);
30+
// static_assert(std::is_trivially_copyable_v<std::pair<int const, int const>>);
31+
32+
// static_assert(!std::is_trivially_copyable_v<std::pair<int, int&>>);
33+
// static_assert(!std::is_trivially_copyable_v<std::pair<int&, int>>);
34+
// static_assert(!std::is_trivially_copyable_v<std::pair<int&, int&>>);
35+
36+
// enum E {};
37+
// static_assert(std::is_trivially_copyable_v<std::pair<E, E>>);
38+
// static_assert(std::is_trivially_copyable_v<std::pair<E, E const>>);
39+
// static_assert(std::is_trivially_copyable_v<std::pair<E const, E>>);
40+
// static_assert(std::is_trivially_copyable_v<std::pair<E const, E const>>);
41+
42+
// static_assert(std::is_trivially_copyable_v<std::pair<E, int>>);
43+
// static_assert(std::is_trivially_copyable_v<std::pair<E, int const>>);
44+
// static_assert(std::is_trivially_copyable_v<std::pair<E const, int>>);
45+
// static_assert(std::is_trivially_copyable_v<std::pair<E const, int const>>);
46+
47+
// struct S {};
48+
// static_assert(std::is_trivially_copyable_v<std::pair<S, S>>);
49+
// static_assert(std::is_trivially_copyable_v<std::pair<S, S const>>);
50+
// static_assert(std::is_trivially_copyable_v<std::pair<S const, S>>);
51+
// static_assert(std::is_trivially_copyable_v<std::pair<S const, S const>>);
52+
53+
// static_assert(std::is_trivially_copyable_v<std::pair<S, int>>);
54+
// static_assert(std::is_trivially_copyable_v<std::pair<S, int const>>);
55+
// static_assert(std::is_trivially_copyable_v<std::pair<S const, int>>);
56+
// static_assert(std::is_trivially_copyable_v<std::pair<S const, int const>>);
57+
58+
// static_assert(std::is_trivially_copyable_v<std::pair<S, E>>);
59+
// static_assert(std::is_trivially_copyable_v<std::pair<S, E const>>);
60+
// static_assert(std::is_trivially_copyable_v<std::pair<S const, E>>);
61+
// static_assert(std::is_trivially_copyable_v<std::pair<S const, E const>>);
62+
63+
// struct S2 {
64+
// S2(S2&&) = delete;
65+
// };
66+
// static_assert(std::is_trivially_copyable_v<S2>);
67+
68+
#endif

0 commit comments

Comments
 (0)