Skip to content

Commit

Permalink
[libc++] Make std::pair trivially copyable if its members are (#89652)
Browse files Browse the repository at this point in the history
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 <cjdb@google.com>
  • Loading branch information
philnik777 and cjdb authored Jul 20, 2024
1 parent d157884 commit f9dd885
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 6 deletions.
4 changes: 4 additions & 0 deletions libcxx/include/__configuration/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@
// and WCHAR_MAX. This ABI setting determines whether we should instead track whether the fill
// value has been initialized using a separate boolean, which changes the ABI.
# define _LIBCPP_ABI_IOS_ALLOW_ARBITRARY_FILL_VALUE
// Make a std::pair of trivially copyable types trivially copyable.
// While this technically doesn't change the layout of pair itself, other types may decide to programatically change
// their representation based on whether something is trivially copyable.
# define _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
#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
1 change: 1 addition & 0 deletions libcxx/include/__type_traits/datasizeof.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct _FirstPaddingByte<_Tp, true> {
// the use as an extension.
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
template <class _Tp>
inline const size_t __datasizeof_v = offsetof(_FirstPaddingByte<_Tp>, __first_padding_byte_);
_LIBCPP_DIAGNOSTIC_POP
Expand Down
46 changes: 41 additions & 5 deletions libcxx/include/__utility/pair.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <__type_traits/is_implicitly_default_constructible.h>
#include <__type_traits/is_nothrow_assignable.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>
Expand Down Expand Up @@ -80,6 +81,38 @@ struct _LIBCPP_TEMPLATE_VIS pair
_LIBCPP_HIDE_FROM_ABI pair(pair const&) = default;
_LIBCPP_HIDE_FROM_ABI pair(pair&&) = default;

// When we are requested for pair to be trivially copyable by the ABI macro, we use defaulted members
// if it is both legal to do it (i.e. no references) and we have a way to actually implement it, which requires
// the __enable_if__ attribute before C++20.
#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
// FIXME: This should really just be a static constexpr variable. It's in a struct to avoid gdb printing the value
// when printing a pair
struct __has_defaulted_members {
static const bool value = !is_reference<first_type>::value && !is_reference<second_type>::value;
};
# if _LIBCPP_STD_VER >= 20
_LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(const pair&)
requires __has_defaulted_members::value
= default;

_LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(pair&&)
requires __has_defaulted_members::value
= default;
# elif __has_attribute(__enable_if__)
_LIBCPP_HIDE_FROM_ABI pair& operator=(const pair&)
__attribute__((__enable_if__(__has_defaulted_members::value, ""))) = default;

_LIBCPP_HIDE_FROM_ABI pair& operator=(pair&&)
__attribute__((__enable_if__(__has_defaulted_members::value, ""))) = default;
# else
# error "_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR isn't supported with this compiler"
# endif
#else
struct __has_defaulted_members {
static const bool value = false;
};
#endif // defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR) && __has_attribute(__enable_if__)

#ifdef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI pair() : first(), second() {}

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

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
operator=(__conditional_t<is_copy_assignable<first_type>::value && is_copy_assignable<second_type>::value,
operator=(__conditional_t<!__has_defaulted_members::value && is_copy_assignable<first_type>::value &&
is_copy_assignable<second_type>::value,
pair,
__nat> const& __p) noexcept(is_nothrow_copy_assignable<first_type>::value &&
is_nothrow_copy_assignable<second_type>::value) {
Expand All @@ -234,10 +268,12 @@ struct _LIBCPP_TEMPLATE_VIS pair
return *this;
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(
__conditional_t<is_move_assignable<first_type>::value && is_move_assignable<second_type>::value, pair, __nat>&&
__p) noexcept(is_nothrow_move_assignable<first_type>::value &&
is_nothrow_move_assignable<second_type>::value) {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
operator=(__conditional_t<!__has_defaulted_members::value && is_move_assignable<first_type>::value &&
is_move_assignable<second_type>::value,
pair,
__nat>&& __p) noexcept(is_nothrow_move_assignable<first_type>::value &&
is_nothrow_move_assignable<second_type>::value) {
first = std::forward<first_type>(__p.first);
second = std::forward<second_type>(__p.second);
return *this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,13 @@ void test_trivial()
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
#endif // TEST_STD_VER >= 11
#ifndef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
static_assert(!std::is_trivially_copy_assignable<P>::value, "");
static_assert(!std::is_trivially_move_assignable<P>::value, "");
#else
static_assert(std::is_trivially_copy_assignable<P>::value, "");
static_assert(std::is_trivially_move_assignable<P>::value, "");
#endif
static_assert(std::is_trivially_destructible<P>::value, "");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,42 @@ static_assert(!std::is_trivially_copyable<std::pair<int&, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<int, int&> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<int&, int&> >::value, "");

#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
static_assert(std::is_trivially_copyable<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<int, char> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<char, int> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<std::pair<char, char>, int> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable, int> >::value, "");
#else
static_assert(!std::is_trivially_copyable<std::pair<int, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<int, char> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<char, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<std::pair<char, char>, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable, int> >::value, "");
#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR

#if TEST_STD_VER == 03 // Known ABI difference
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_copy_assignment, int> >::value, "");
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_move_assignment, int> >::value, "");
#else
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_copy_assignment, int> >::value, "");
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_move_assignment, int> >::value, "");
#endif

#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_construction, int> >::value, "");
#else
static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_construction, int> >::value, "");
#endif

static_assert(std::is_trivially_copy_constructible<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_move_constructible<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_destructible<std::pair<int, int> >::value, "");

#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
static_assert(std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_move_assignable<std::pair<int, int> >::value, "");
#else
static_assert(!std::is_trivially_copy_assignable<std::pair<int, int> >::value, "");
static_assert(!std::is_trivially_move_assignable<std::pair<int, int> >::value, "");
static_assert(std::is_trivially_destructible<std::pair<int, int> >::value, "");
#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR

0 comments on commit f9dd885

Please sign in to comment.