[libcxx-commits] [libcxx] [libc++] Fix triviality of std::pair for trivially copyable types without an assignment operator (PR #95444)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jun 17 13:52:46 PDT 2024
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/95444
>From 9facb7729f96dbc99739b68f5acb97a8d7c4ccf7 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 13 Jun 2024 13:31:55 -0400
Subject: [PATCH 1/2] [libc++] Fix triviality of std::pair for trivially
copyable types without an assignment operator
Since 83ead2b, std::pair would not be trivially copyable when it holds
a trivially copyable type without an assignment operator. That is because
pair gained an elligible copy-assignment-operator (the const version) in
83ead2b in C++ >= 23.
This means that the trivially copyable property of std::pair for such
types would be inconsistent between C++11/14/17/20 (trivially copyable)
and C++23/26 (not trivially copyable). This patch makes std::pair's
behavior consistent in all Standard modes EXCEPT C++03, which is a
pre-existing condition and we have no way of changing (also, it shouldn't
matter because the std::is_trivially_copyable trait was introduced in
C++11).
While this is not technically an ABI break, in practice we do know that
folks sometimes use a different representation based on whether a type
is trivially copyable. So we're treating 83ead2b as an ABI break and
this patch is fixing said breakage.
This patch also adds tests stolen from #89652 that pin down the ABI
of std::pair with respect to being trivially copyable.
Fixes #95428
---
libcxx/include/__utility/pair.h | 2 +
...cpp => abi.non_trivial_copy_move.pass.cpp} | 0
...ass.cpp => abi.trivial_copy_move.pass.cpp} | 31 ++++++++++
.../abi.trivially_copyable.compile.pass.cpp | 56 +++++++++++++++++++
4 files changed, 89 insertions(+)
rename libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/{non_trivial_copy_move_ABI.pass.cpp => abi.non_trivial_copy_move.pass.cpp} (100%)
rename libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/{trivial_copy_move_ABI.pass.cpp => abi.trivial_copy_move.pass.cpp} (79%)
create mode 100644 libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivially_copyable.compile.pass.cpp
diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h
index 7e17c4c415c41..6f293e1b5d65c 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -267,6 +267,7 @@ struct _LIBCPP_TEMPLATE_VIS pair
}
# if _LIBCPP_STD_VER >= 23
+ template <class = void>
_LIBCPP_HIDE_FROM_ABI constexpr const pair& operator=(pair const& __p) const
noexcept(is_nothrow_copy_assignable_v<const first_type> && is_nothrow_copy_assignable_v<const second_type>)
requires(is_copy_assignable_v<const first_type> && is_copy_assignable_v<const second_type>)
@@ -276,6 +277,7 @@ struct _LIBCPP_TEMPLATE_VIS pair
return *this;
}
+ template <class = void>
_LIBCPP_HIDE_FROM_ABI constexpr const pair& operator=(pair&& __p) const
noexcept(is_nothrow_assignable_v<const first_type&, first_type> &&
is_nothrow_assignable_v<const second_type&, second_type>)
diff --git a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/non_trivial_copy_move_ABI.pass.cpp b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.non_trivial_copy_move.pass.cpp
similarity index 100%
rename from libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/non_trivial_copy_move_ABI.pass.cpp
rename to libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.non_trivial_copy_move.pass.cpp
diff --git a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivial_copy_move_ABI.pass.cpp b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivial_copy_move.pass.cpp
similarity index 79%
rename from libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivial_copy_move_ABI.pass.cpp
rename to libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivial_copy_move.pass.cpp
index fe098a7af7e8f..3ec60c08b8eab 100644
--- a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/trivial_copy_move_ABI.pass.cpp
+++ b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivial_copy_move.pass.cpp
@@ -71,6 +71,17 @@ struct Trivial {
static_assert(HasTrivialABI<Trivial>::value, "");
#endif
+struct TrivialNoAssignment {
+ int arr[4];
+ TrivialNoAssignment& operator=(const TrivialNoAssignment&) = delete;
+};
+
+struct TrivialNoConstruction {
+ int arr[4];
+ TrivialNoConstruction() = default;
+ TrivialNoConstruction(const TrivialNoConstruction&) = delete;
+ TrivialNoConstruction& operator=(const TrivialNoConstruction&) = default;
+};
void test_trivial()
{
@@ -135,6 +146,26 @@ void test_trivial()
static_assert(HasTrivialABI<P>::value, "");
}
#endif
+ {
+ using P = std::pair<TrivialNoAssignment, int>;
+ static_assert(std::is_trivially_copy_constructible<P>::value, "");
+ static_assert(std::is_trivially_move_constructible<P>::value, "");
+#if TEST_STD_VER >= 11 // This is https://llvm.org/PR90605
+ static_assert(!std::is_trivially_copy_assignable<P>::value, "");
+ static_assert(!std::is_trivially_move_assignable<P>::value, "");
+#endif // TEST_STD_VER >= 11
+ static_assert(std::is_trivially_destructible<P>::value, "");
+ }
+ {
+ using P = std::pair<TrivialNoConstruction, int>;
+#if TEST_STD_VER >= 11
+ static_assert(!std::is_trivially_copy_constructible<P>::value, "");
+ static_assert(!std::is_trivially_move_constructible<P>::value, "");
+#endif // TEST_STD_VER >= 11
+ static_assert(!std::is_trivially_copy_assignable<P>::value, "");
+ static_assert(!std::is_trivially_move_assignable<P>::value, "");
+ static_assert(std::is_trivially_destructible<P>::value, "");
+ }
}
void test_layout() {
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
new file mode 100644
index 0000000000000..86c014213947c
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.trivially_copyable.compile.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+//
+// This test pins down the ABI of std::pair with respect to being "trivially copyable".
+//
+
+#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, "");
+
+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, "");
+
+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), "");
+
+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), "");
+#if TEST_STD_VER == 03 // Known ABI difference
+static_assert((!std::is_trivially_copyable<std::pair<trivially_copyable_no_assignment, int> >::value), "");
+#else
+static_assert((std::is_trivially_copyable<std::pair<trivially_copyable_no_assignment, int> >::value), "");
+#endif
+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), "");
>From 836f0efbf1eeaa00b8933c242d85bae3ba6c6b4d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 17 Jun 2024 16:52:32 -0400
Subject: [PATCH 2/2] UNSUPPORT on FreeBSD
---
.../pairs/pairs.pair/abi.trivially_copyable.compile.pass.cpp | 4 ++++
libcxx/utils/libcxx/test/features.py | 1 +
2 files changed, 5 insertions(+)
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 86c014213947c..821c103519714 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
@@ -10,6 +10,10 @@
// This test pins down the ABI of std::pair with respect to being "trivially copyable".
//
+// This test doesn't work when the deprecated ABI to turn off pair triviality is enabled.
+// See libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/abi.non_trivial_copy_move.pass.cpp instead.
+// UNSUPPORTED: libcpp-deprecated-abi-disable-pair-trivial-copy-ctor
+
#include <type_traits>
#include <utility>
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 7a9631a56e4bb..a3395436a9bc0 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -352,6 +352,7 @@ def _mingwSupportsModules(cfg):
"_LIBCPP_NO_VCRUNTIME": "libcpp-no-vcruntime",
"_LIBCPP_ABI_VERSION": "libcpp-abi-version",
"_LIBCPP_ABI_BOUNDED_ITERATORS": "libcpp-has-abi-bounded-iterators",
+ "_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR": "libcpp-deprecated-abi-disable-pair-trivial-copy-ctor",
"_LIBCPP_HAS_NO_FILESYSTEM": "no-filesystem",
"_LIBCPP_HAS_NO_RANDOM_DEVICE": "no-random-device",
"_LIBCPP_HAS_NO_LOCALIZATION": "no-localization",
More information about the libcxx-commits
mailing list