[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
Thu Jun 13 10:50:56 PDT 2024
https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/95444
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
>From 88fc16f26588224762b42bb5ea0e469ad02a59dc 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] [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 3ffab2f7fe4f6..380a2a21b9d4b 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -261,6 +261,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>)
@@ -270,6 +271,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), "");
More information about the libcxx-commits
mailing list