From f9dd885cb6e6b70deff935689bb0dfb7d5b6a1a4 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Sat, 20 Jul 2024 19:59:06 +0200 Subject: [PATCH] [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 --- libcxx/include/__configuration/abi.h | 4 ++ libcxx/include/__type_traits/datasizeof.h | 1 + libcxx/include/__utility/pair.h | 46 +++++++++++++++++-- .../pairs.pair/abi.trivial_copy_move.pass.cpp | 5 ++ .../abi.trivially_copyable.compile.pass.cpp | 22 ++++++++- 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h index cbde7887becf..710548d90a64 100644 --- a/libcxx/include/__configuration/abi.h +++ b/libcxx/include/__configuration/abi.h @@ -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 diff --git a/libcxx/include/__type_traits/datasizeof.h b/libcxx/include/__type_traits/datasizeof.h index 35c12921e8ff..a27baf67cc2d 100644 --- a/libcxx/include/__type_traits/datasizeof.h +++ b/libcxx/include/__type_traits/datasizeof.h @@ -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 inline const size_t __datasizeof_v = offsetof(_FirstPaddingByte<_Tp>, __first_padding_byte_); _LIBCPP_DIAGNOSTIC_POP diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h index 0afbebcdc9f2..c0002b7abb3c 100644 --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -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> @@ -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::value && !is_reference::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() {} @@ -225,7 +258,8 @@ struct _LIBCPP_TEMPLATE_VIS pair typename __make_tuple_indices::type()) {} _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& - operator=(__conditional_t::value && is_copy_assignable::value, + operator=(__conditional_t::value && + is_copy_assignable::value, pair, __nat> const& __p) noexcept(is_nothrow_copy_assignable::value && is_nothrow_copy_assignable::value) { @@ -234,10 +268,12 @@ struct _LIBCPP_TEMPLATE_VIS pair return *this; } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=( - __conditional_t::value && is_move_assignable::value, pair, __nat>&& - __p) noexcept(is_nothrow_move_assignable::value && - is_nothrow_move_assignable::value) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& + operator=(__conditional_t::value && + is_move_assignable::value, + pair, + __nat>&& __p) noexcept(is_nothrow_move_assignable::value && + is_nothrow_move_assignable::value) { first = std::forward(__p.first); second = std::forward(__p.second); return *this; diff --git a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivial_copy_move.pass.cpp b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivial_copy_move.pass.cpp index 3ec60c08b8ea..5481ba443046 100644 --- a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivial_copy_move.pass.cpp +++ b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivial_copy_move.pass.cpp @@ -162,8 +162,13 @@ void test_trivial() static_assert(!std::is_trivially_copy_constructible

::value, ""); static_assert(!std::is_trivially_move_constructible

::value, ""); #endif // TEST_STD_VER >= 11 +#ifndef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR static_assert(!std::is_trivially_copy_assignable

::value, ""); static_assert(!std::is_trivially_move_assignable

::value, ""); +#else + static_assert(std::is_trivially_copy_assignable

::value, ""); + static_assert(std::is_trivially_move_assignable

::value, ""); +#endif static_assert(std::is_trivially_destructible

::value, ""); } } diff --git a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivially_copyable.compile.pass.cpp b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivially_copyable.compile.pass.cpp index 1132b3e5def1..c5f9c8d0f255 100644 --- a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivially_copyable.compile.pass.cpp +++ b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivially_copyable.compile.pass.cpp @@ -47,11 +47,20 @@ static_assert(!std::is_trivially_copyable >::value, ""); static_assert(!std::is_trivially_copyable >::value, ""); static_assert(!std::is_trivially_copyable >::value, ""); +#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR +static_assert(std::is_trivially_copyable >::value, ""); +static_assert(std::is_trivially_copyable >::value, ""); +static_assert(std::is_trivially_copyable >::value, ""); +static_assert(std::is_trivially_copyable, int> >::value, ""); +static_assert(std::is_trivially_copyable >::value, ""); +#else static_assert(!std::is_trivially_copyable >::value, ""); static_assert(!std::is_trivially_copyable >::value, ""); static_assert(!std::is_trivially_copyable >::value, ""); static_assert(!std::is_trivially_copyable, int> >::value, ""); static_assert(!std::is_trivially_copyable >::value, ""); +#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR + #if TEST_STD_VER == 03 // Known ABI difference static_assert(!std::is_trivially_copyable >::value, ""); static_assert(!std::is_trivially_copyable >::value, ""); @@ -59,10 +68,21 @@ static_assert(!std::is_trivially_copyable >::value, ""); static_assert(std::is_trivially_copyable >::value, ""); #endif + +#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR +static_assert(std::is_trivially_copyable >::value, ""); +#else static_assert(!std::is_trivially_copyable >::value, ""); +#endif static_assert(std::is_trivially_copy_constructible >::value, ""); static_assert(std::is_trivially_move_constructible >::value, ""); +static_assert(std::is_trivially_destructible >::value, ""); + +#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR +static_assert(std::is_trivially_copy_assignable >::value, ""); +static_assert(std::is_trivially_move_assignable >::value, ""); +#else static_assert(!std::is_trivially_copy_assignable >::value, ""); static_assert(!std::is_trivially_move_assignable >::value, ""); -static_assert(std::is_trivially_destructible >::value, ""); +#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR