[libcxx-commits] [libcxx] [libc++] Make std::pair trivially copyable if its members are (PR #89652)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Jun 1 00:10:47 PDT 2024
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/89652
>From 5495e2134a6c2b727b325f21e74f7d8f98c02689 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/__configuration/abi.h | 4 +-
libcxx/include/__type_traits/datasizeof.h | 1 +
libcxx/include/__utility/pair.h | 40 ++++++--
.../trivially_copyable.compile.pass.cpp | 91 +++++++++++++++++++
4 files changed, 129 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 17aceb042f524..11042ee41132d 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 54fde242ebcde..ccb57a46f0fb5 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 3ffab2f7fe4f6..be30477b33d2c 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<first_type>::value && !is_reference<second_type>::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<first_type>::value && !is_reference<second_type>::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<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);
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 0000000000000..e5b2f73c113b9
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+#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<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, "");
+#if TEST_STD_VER >= 11 // This is https://llvm.org/PR90605
+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, "");
+#endif // TEST_STD_VER >= 11
+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
+# if TEST_STD_VER >= 11
+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, "");
+# endif // TEST_STD_VER >= 11
+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