Skip to content

Commit

Permalink
[libcxx] makes pair conditionally trivially copyable for C++20
Browse files Browse the repository at this point in the history
`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.
  • Loading branch information
cjdb committed Mar 11, 2024
1 parent 725a052 commit f2ef4fd
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 1 deletion.
3 changes: 3 additions & 0 deletions libcxx/include/__config
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
// requires code not to make these assumptions.
# define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY
# define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW
// Allows std::pair's copy assignment and move assignment operators to be trivial
// when both its element types' respective assignment operators are trivial.
# define _LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE
# elif _LIBCPP_ABI_VERSION == 1
# if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
// Enable compiling copies of now inline methods into the dylib to support
Expand Down
36 changes: 36 additions & 0 deletions libcxx/include/__utility/pair.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <__compare/common_comparison_category.h>
#include <__compare/synth_three_way.h>
#include <__concepts/assignable.h>
#include <__concepts/different_from.h>
#include <__config>
#include <__fwd/array.h>
Expand All @@ -26,6 +27,7 @@
#include <__type_traits/common_type.h>
#include <__type_traits/conditional.h>
#include <__type_traits/decay.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_assignable.h>
#include <__type_traits/is_constructible.h>
Expand All @@ -40,8 +42,11 @@
#include <__type_traits/is_nothrow_copy_constructible.h>
#include <__type_traits/is_nothrow_default_constructible.h>
#include <__type_traits/is_nothrow_move_assignable.h>
#include <__type_traits/is_object.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_copy_assignable.h>
#include <__type_traits/is_trivially_move_assignable.h>
#include <__type_traits/nat.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/unwrap_ref.h>
Expand All @@ -67,6 +72,14 @@ struct __non_trivially_copyable_base {
__non_trivially_copyable_base(__non_trivially_copyable_base const&) _NOEXCEPT {}
};

#if _LIBCPP_STD_VER >= 20
template <class _Tp>
concept __trivially_copy_assignable_object = is_trivially_copy_assignable_v<_Tp> && is_object_v<_Tp>;

template <class _Tp>
concept __trivially_move_assignable_object = is_trivially_move_assignable_v<_Tp> && is_object_v<_Tp>;
#endif

#if _LIBCPP_STD_VER >= 23
template <class _Tp>
struct __is_specialization_of_subrange : false_type {};
Expand Down Expand Up @@ -236,6 +249,28 @@ struct _LIBCPP_TEMPLATE_VIS pair
typename __make_tuple_indices<sizeof...(_Args1)>::type(),
typename __make_tuple_indices<sizeof...(_Args2) >::type()) {}

# if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
_LIBCPP_HIDE_FROM_ABI pair& operator=(pair const& __p) = default;
_LIBCPP_HIDE_FROM_ABI pair& operator=(pair&& __p) = default;

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(pair const& __p)
_NOEXCEPT_(is_nothrow_copy_assignable<first_type>::value&& is_nothrow_copy_assignable<second_type>::value)
requires is_copy_assignable_v<first_type> && is_copy_assignable_v<second_type> && (!(__trivially_copy_assignable_object<first_type> && __trivially_copy_assignable_object<second_type>))
{
first = __p.first;
second = __p.second;
return *this;
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(pair&& __p)
_NOEXCEPT_(is_nothrow_move_assignable<first_type>::value&& is_nothrow_move_assignable<second_type>::value)
requires is_move_assignable_v<first_type> && is_move_assignable_v<second_type> && (!(__trivially_move_assignable_object<first_type> && __trivially_move_assignable_object<second_type>))
{
first = std::forward<first_type>(__p.first);
second = std::forward<second_type>(__p.second);
return *this;
}
# else
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
operator=(__conditional_t< is_copy_assignable<first_type>::value && is_copy_assignable<second_type>::value,
pair,
Expand All @@ -254,6 +289,7 @@ struct _LIBCPP_TEMPLATE_VIS pair
second = std::forward<second_type>(__p.second);
return *this;
}
# endif

template <
class _U1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ TEST_CONSTEXPR_CXX20 bool test() {
assert(&p.second == &inc_obj);
}

#if TEST_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
static_assert(std::is_trivially_copy_assignable<std::pair<int, int>>::value, "");
#else
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int>>::value, "");
#endif

static_assert(!std::is_trivially_copy_assignable<std::pair<CountAssign, int>>::value, "");
static_assert(!std::is_trivially_copy_assignable<std::pair<int, CountAssign>>::value, "");
static_assert(!std::is_trivially_copy_assignable<std::pair<CountAssign, CountAssign>>::value, "");

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ int main(int, char**)
assert(p.second == 'x');
}

return 0;
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");

return 0;
}

struct Incomplete {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@ TEST_CONSTEXPR_CXX20 bool test() {
static_assert(!std::is_move_assignable<P5>::value, "");
static_assert(!std::is_move_assignable<P6>::value, "");
}

#if TEST_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
static_assert(std::is_trivially_move_assignable<std::pair<int, int>>::value, "");
#else
static_assert(!std::is_trivially_move_assignable<std::pair<int, int>>::value, "");
#endif

static_assert(!std::is_trivially_move_assignable<std::pair<CountAssign, int>>::value, "");
static_assert(!std::is_trivially_move_assignable<std::pair<int, CountAssign>>::value, "");
static_assert(!std::is_trivially_move_assignable<std::pair<CountAssign, CountAssign>>::value, "");

return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// <utility>

// template <class T1, class T2> struct pair

// Checks that `std::pair` is actually trivially copyable.

#include <type_traits>
#include <utility>

#if defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE)
static_assert(std::is_trivially_copyable_v<std::pair<int, int>>);
static_assert(std::is_trivially_copyable_v<std::pair<int, int const>>);
static_assert(std::is_trivially_copyable_v<std::pair<int const, int>>);
static_assert(std::is_trivially_copyable_v<std::pair<int const, int const>>);

static_assert(!std::is_trivially_copyable_v<std::pair<int, int&>>);
static_assert(!std::is_trivially_copyable_v<std::pair<int&, int>>);
static_assert(!std::is_trivially_copyable_v<std::pair<int&, int&>>);

enum E {};
static_assert(std::is_trivially_copyable_v<std::pair<E, E>>);
static_assert(std::is_trivially_copyable_v<std::pair<E, E const>>);
static_assert(std::is_trivially_copyable_v<std::pair<E const, E>>);
static_assert(std::is_trivially_copyable_v<std::pair<E const, E const>>);

static_assert(std::is_trivially_copyable_v<std::pair<E, int>>);
static_assert(std::is_trivially_copyable_v<std::pair<E, int const>>);
static_assert(std::is_trivially_copyable_v<std::pair<E const, int>>);
static_assert(std::is_trivially_copyable_v<std::pair<E const, int const>>);

struct S {};
static_assert(std::is_trivially_copyable_v<std::pair<S, S>>);
static_assert(std::is_trivially_copyable_v<std::pair<S, int>>);
static_assert(std::is_trivially_copyable_v<std::pair<S, E>>);
#endif

0 comments on commit f2ef4fd

Please sign in to comment.