[libcxx-commits] [libcxx] [libc++][ABI BREAK] Make std::pair trivially copyable if its members are (PR #89652)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Tue Apr 30 05:33:11 PDT 2024


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/89652

>From 9a4773d6daeef793c0d90a3625b4f5e4fd810ae8 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
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/__config                       |  2 +
 libcxx/include/__type_traits/datasizeof.h     |  1 +
 libcxx/include/__utility/pair.h               | 78 +++++++++++------
 .../trivially_copyable.compile.pass.cpp       | 85 +++++++++++++++++++
 4 files changed, 142 insertions(+), 24 deletions(-)
 create mode 100644 libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 97cdd020c55d1f..1db8a298175ea7 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -166,6 +166,8 @@
 // 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
+// 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 <class _Tp>
 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 e05250ba05717f..ae08aaf75cc64f 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>
@@ -73,6 +74,31 @@ struct _LIBCPP_TEMPLATE_VIS pair
 
   _LIBCPP_HIDE_FROM_ABI pair(pair const&) = default;
   _LIBCPP_HIDE_FROM_ABI pair(pair&&)      = default;
+#define _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
+  // Make pair trivially copyable if we have a way to do it
+  static const bool __enable_defaulted_assignment_operators =
+      !is_reference<first_type>::value && !is_reference<second_type>::value;
+#if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR)
+  static const bool __has_defaulted_members = __enable_defaulted_assignment_operators;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(const pair&)
+    requires __enable_defaulted_assignment_operators
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr pair& operator=(pair&&)
+    requires __enable_defaulted_assignment_operators
+  = default;
+#elif __has_attribute(__enable_if__) && defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR)
+  static const bool __has_defaulted_members = __enable_defaulted_assignment_operators;
+
+  _LIBCPP_HIDE_FROM_ABI pair& operator=(const pair&)
+      __attribute__((__enable_if__(__enable_defaulted_assignment_operators, ""))) = default;
+
+  _LIBCPP_HIDE_FROM_ABI pair& operator=(pair&&)
+      __attribute__((__enable_if__(__enable_defaulted_assignment_operators, ""))) = default;
+#else
+  static const bool __has_defaulted_members = false;
+#endif // __has_attribute(__enable_if__) && defined(_LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR)
 
 #ifdef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI pair() : first(), second() {}
@@ -129,9 +155,9 @@ struct _LIBCPP_TEMPLATE_VIS pair
       typename conditional< _MaybeEnable, _CheckArgs, __check_tuple_constructor_fail>::type;
 
   template <bool _Dummy = true, __enable_if_t<_CheckArgsDep<_Dummy>::__enable_default(), int> = 0>
-  explicit(!_CheckArgsDep<_Dummy>::__enable_implicit_default()) _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR pair()
-      _NOEXCEPT_(
-          is_nothrow_default_constructible<first_type>::value&& is_nothrow_default_constructible<second_type>::value)
+  explicit(!_CheckArgsDep<_Dummy>::__enable_implicit_default()) _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR pair() _NOEXCEPT_(
+      is_nothrow_default_constructible<first_type>::value&& is_nothrow_default_constructible<second_type>::value)
       : first(), second() {}
 
   template <bool _Dummy = true,
@@ -150,10 +176,10 @@ struct _LIBCPP_TEMPLATE_VIS pair
       class _U2,
 #  endif
       __enable_if_t<_CheckArgs::template __is_pair_constructible<_U1, _U2>(), int> = 0 >
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgs::template __is_implicit<_U1, _U2>())
-      pair(_U1&& __u1, _U2&& __u2)
-          _NOEXCEPT_((is_nothrow_constructible<first_type, _U1>::value &&
-                      is_nothrow_constructible<second_type, _U2>::value))
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgs::template __is_implicit<_U1, _U2>()) pair(_U1&& __u1, _U2&& __u2)
+      _NOEXCEPT_((is_nothrow_constructible<first_type, _U1>::value &&
+                  is_nothrow_constructible<second_type, _U2>::value))
       : first(std::forward<_U1>(__u1)), second(std::forward<_U2>(__u2)) {
   }
 
@@ -168,17 +194,18 @@ struct _LIBCPP_TEMPLATE_VIS pair
   template <class _U1,
             class _U2,
             __enable_if_t<_CheckArgs::template __is_pair_constructible<_U1 const&, _U2 const&>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(
-      !_CheckArgs::template __is_implicit<_U1 const&, _U2 const&>()) pair(pair<_U1, _U2> const& __p)
-      _NOEXCEPT_((is_nothrow_constructible<first_type, _U1 const&>::value &&
-                  is_nothrow_constructible<second_type, _U2 const&>::value))
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgs::template __is_implicit<_U1 const&, _U2 const&>())
+      pair(pair<_U1, _U2> const& __p)
+          _NOEXCEPT_((is_nothrow_constructible<first_type, _U1 const&>::value &&
+                      is_nothrow_constructible<second_type, _U2 const&>::value))
       : first(__p.first), second(__p.second) {}
 
   template <class _U1, class _U2, __enable_if_t<_CheckArgs::template __is_pair_constructible<_U1, _U2>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgs::template __is_implicit<_U1, _U2>())
-      pair(pair<_U1, _U2>&& __p)
-          _NOEXCEPT_((is_nothrow_constructible<first_type, _U1&&>::value &&
-                      is_nothrow_constructible<second_type, _U2&&>::value))
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgs::template __is_implicit<_U1, _U2>()) pair(pair<_U1, _U2>&& __p)
+      _NOEXCEPT_((is_nothrow_constructible<first_type, _U1&&>::value &&
+                  is_nothrow_constructible<second_type, _U2&&>::value))
       : first(std::forward<_U1>(__p.first)), second(std::forward<_U2>(__p.second)) {}
 
 #  if _LIBCPP_STD_VER >= 23
@@ -221,18 +248,21 @@ 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,
-                             pair,
-                             __nat> const& __p)
+  operator=(__conditional_t<
+            !__has_defaulted_members && 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) {
     first  = __p.first;
     second = __p.second;
     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)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair&
+  operator=(__conditional_t<
+            !__has_defaulted_members && 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);
@@ -528,9 +558,9 @@ swap(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y) noexcept(noexcept(__x
 #endif
 
 template <class _T1, class _T2>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14
-    pair<typename __unwrap_ref_decay<_T1>::type, typename __unwrap_ref_decay<_T2>::type>
-    make_pair(_T1&& __t1, _T2&& __t2) {
+inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX14 pair<typename __unwrap_ref_decay<_T1>::type, typename __unwrap_ref_decay<_T2>::type>
+make_pair(_T1&& __t1, _T2&& __t2) {
   return pair<typename __unwrap_ref_decay<_T1>::type, typename __unwrap_ref_decay<_T2>::type>(
       std::forward<_T1>(__t1), std::forward<_T2>(__t2));
 }
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..fd9f3746d95e48
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <type_traits>
+#include <utility>
+
+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<trivially_copyable_no_assignment>::value, "");
+
+static_assert(std::is_trivially_copy_constructible<std::pair<trivially_copyable_no_assignment, int> >::value, "");
+static_assert(std::is_trivially_move_constructible<std::pair<trivially_copyable_no_assignment, int> >::value, "");
+static_assert(!std::is_trivially_copy_assignable<std::pair<trivially_copyable_no_assignment, int> >::value, "");
+static_assert(!std::is_trivially_move_assignable<std::pair<trivially_copyable_no_assignment, int> >::value, "");
+static_assert(std::is_trivially_destructible<std::pair<trivially_copyable_no_assignment, int> >::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<trivially_copyable_no_construction>::value, "");
+
+#ifdef _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
+static_assert(!std::is_trivially_copy_constructible<std::pair<trivially_copyable_no_construction, int> >::value, "");
+static_assert(!std::is_trivially_move_constructible<std::pair<trivially_copyable_no_construction, int> >::value, "");
+static_assert(std::is_trivially_copy_assignable<std::pair<trivially_copyable_no_construction, int> >::value, "");
+static_assert(std::is_trivially_move_assignable<std::pair<trivially_copyable_no_construction, int> >::value, "");
+static_assert(std::is_trivially_destructible<std::pair<trivially_copyable_no_construction, int> >::value, "");
+#else // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR
+static_assert(!std::is_trivially_copy_constructible<std::pair<trivially_copyable_no_construction, int> >::value, "");
+static_assert(!std::is_trivially_move_constructible<std::pair<trivially_copyable_no_construction, int> >::value, "");
+static_assert(!std::is_trivially_copy_assignable<std::pair<trivially_copyable_no_construction, int> >::value, "");
+static_assert(!std::is_trivially_move_assignable<std::pair<trivially_copyable_no_construction, int> >::value, "");
+static_assert(std::is_trivially_destructible<std::pair<trivially_copyable_no_construction, int> >::value, "");
+#endif // _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, 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, "");
+static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_assignment, int> >::value, "");
+static_assert(std::is_trivially_copyable<std::pair<trivially_copyable_no_construction, int> >::value, "");
+
+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_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, "");
+
+#else  // _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, "");
+static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_assignment, int> >::value, "");
+static_assert(!std::is_trivially_copyable<std::pair<trivially_copyable_no_construction, int> >::value, "");
+
+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_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



More information about the libcxx-commits mailing list