From a38e80c1074ac8e001c1e5a7535db1103987df06 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Mon, 22 Apr 2024 20:58:34 +0200 Subject: [PATCH] [libc++][ABI BREAK] Make std::pair trivially copyable if its members are --- libcxx/include/__configuration/abi.h | 4 +- libcxx/include/__type_traits/datasizeof.h | 1 + libcxx/include/__utility/pair.h | 40 ++++++-- .../trivially_copyable.compile.pass.cpp | 95 +++++++++++++++++++ 4 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h index 17aceb042f524a..11042ee41132d8 100644 --- a/libcxx/include/__configuration/abi.h +++ b/libcxx/include/__configuration/abi.h @@ -90,7 +90,9 @@ # define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY # define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW // Dont' add an inline namespace for `std::filesystem` -# define _LIBCPP_ABI_NO_FILESYSTEM_INLINE_NAMESPACE +# define _LIBCPP_ABI_NO_FILESYSTEM_INLINE_NAMESPACE +// Make a std::pair of trivially copyable types 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 54fde242ebcde1..ccb57a46f0fb52 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 3ffab2f7fe4f64..be30477b33d2c9 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/nat.h> @@ -74,6 +75,30 @@ 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. +#if defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR) && _LIBCPP_STD_VER >= 20 + static const bool __has_defaulted_members = !is_reference::value && !is_reference::value; + + _LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(const pair&) + requires __has_defaulted_members + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(pair&&) + requires __has_defaulted_members + = default; +#elif defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR) && __has_attribute(__enable_if__) + static const bool __has_defaulted_members = !is_reference::value && !is_reference::value; + + _LIBCPP_HIDE_FROM_ABI pair& operator=(const pair&) + __attribute__((__enable_if__(__has_defaulted_members, ""))) = default; + + _LIBCPP_HIDE_FROM_ABI pair& operator=(pair&&) __attribute__((__enable_if__(__has_defaulted_members, ""))) = default; +#else + static const bool __has_defaulted_members = false; +#endif // defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR) && __has_attribute(__enable_if__) + #ifdef _LIBCPP_CXX03_LANG _LIBCPP_HIDE_FROM_ABI pair() : first(), second() {} @@ -222,18 +247,21 @@ struct _LIBCPP_TEMPLATE_VIS pair typename __make_tuple_indices::type()) {} _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& - operator=(__conditional_t< is_copy_assignable::value && is_copy_assignable::value, - pair, - __nat> const& __p) + operator=(__conditional_t< + !__has_defaulted_members && is_copy_assignable::value && is_copy_assignable::value, + pair, + __nat> const& __p) _NOEXCEPT_(is_nothrow_copy_assignable::value&& is_nothrow_copy_assignable::value) { first = __p.first; second = __p.second; return *this; } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=( - __conditional_t< is_move_assignable::value && is_move_assignable::value, pair, __nat>&& - __p) + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& + operator=(__conditional_t< + !__has_defaulted_members && is_move_assignable::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); diff --git a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp new file mode 100644 index 00000000000000..83ffd11d802423 --- /dev/null +++ b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc && (c++11 || c++14 || c++17) + +#include +#include + +#include "test_macros.h" + +struct trivially_copyable { + int arr[4]; +}; + +struct trivially_copyable_no_assignment { + int arr[4]; + trivially_copyable_no_assignment& operator=(const trivially_copyable_no_assignment&) = delete; +}; +static_assert(std::is_trivially_copyable::value, ""); + +static_assert(std::is_trivially_copy_constructible >::value, ""); +static_assert(std::is_trivially_move_constructible >::value, ""); +#if TEST_STD_VER >= 11 // This is https://llvm.org/PR90605 +static_assert(!std::is_trivially_copy_assignable >::value, ""); +static_assert(!std::is_trivially_move_assignable >::value, ""); +#endif // TEST_STD_VER >= 11 +static_assert(std::is_trivially_destructible >::value, ""); + +struct trivially_copyable_no_construction { + int arr[4]; + trivially_copyable_no_construction() = default; + trivially_copyable_no_construction(const trivially_copyable_no_construction&) = delete; + trivially_copyable_no_construction& operator=(const trivially_copyable_no_construction&) = default; +}; +static_assert(std::is_trivially_copyable::value, ""); + +#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR +static_assert(!std::is_trivially_copy_constructible >::value, ""); +static_assert(!std::is_trivially_move_constructible >::value, ""); +static_assert(std::is_trivially_copy_assignable >::value, ""); +static_assert(std::is_trivially_move_assignable >::value, ""); +static_assert(std::is_trivially_destructible >::value, ""); +#else // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR +# if TEST_STD_VER >= 11 +static_assert(!std::is_trivially_copy_constructible >::value, ""); +static_assert(!std::is_trivially_move_constructible >::value, ""); +# endif // TEST_STD_VER >= 11 +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 + +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, ""); +static_assert(std::is_trivially_copyable >::value, ""); +static_assert(std::is_trivially_copyable >::value, ""); + +static_assert(std::is_trivially_copy_constructible >::value, ""); +static_assert(std::is_trivially_move_constructible >::value, ""); +static_assert(std::is_trivially_copy_assignable >::value, ""); +static_assert(std::is_trivially_move_assignable >::value, ""); +static_assert(std::is_trivially_destructible >::value, ""); + +#else // _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, ""); +#ifdef TEST_COMPILER_GCC // FIXME: Why does GCC consider this trivially copyable +static_assert(std::is_trivially_copyable >::value, ""); +#else +static_assert(!std::is_trivially_copyable >::value, ""); +#endif +static_assert(!std::is_trivially_copyable >::value, ""); + +static_assert(std::is_trivially_copy_constructible >::value, ""); +static_assert(std::is_trivially_move_constructible >::value, ""); +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