From f2ef4fd60ab3686644d81c1d695c9b702e3a874f Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Mon, 11 Mar 2024 18:01:24 +0000 Subject: [PATCH] [libcxx] makes `pair` conditionally trivially copyable for C++20 `pair` can have trivial copy and move assignment operators when both its members have trivial copy and move operators, which opens `pair` up to being trivially copyable. This is the first of three possible commits to bring users a more robust implementation. It's currently unclear how to implement this without using a _requires-clause_, so the feature is restricted to C++20 for now. There has also been some interest in moving this out of the unstable ABI mode. Both of these changes are intended to arrive in future pull requests, should their feasibility pan out. --- libcxx/include/__config | 3 ++ libcxx/include/__utility/pair.h | 36 +++++++++++++++ .../pairs/pairs.pair/assign_pair.pass.cpp | 10 +++++ .../pairs.pair/assign_pair_cxx03.pass.cpp | 4 +- .../pairs/pairs.pair/assign_rv_pair.pass.cpp | 11 +++++ .../trivially_copyable.compile.pass.cpp | 45 +++++++++++++++++++ 6 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 libcxx/test/std/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp diff --git a/libcxx/include/__config b/libcxx/include/__config index 11e13e0c24986a..a8720cd76ca03d 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -180,6 +180,9 @@ // 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 +// Allows std::pair's copy assignment and move assignment operators to be trivial +// when both its element types' respective assignment operators are trivial. +# define _LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE # 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/__utility/pair.h b/libcxx/include/__utility/pair.h index b488a9829c3849..fb38f29e9078c1 100644 --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -11,6 +11,7 @@ #include <__compare/common_comparison_category.h> #include <__compare/synth_three_way.h> +#include <__concepts/assignable.h> #include <__concepts/different_from.h> #include <__config> #include <__fwd/array.h> @@ -26,6 +27,7 @@ #include <__type_traits/common_type.h> #include <__type_traits/conditional.h> #include <__type_traits/decay.h> +#include <__type_traits/enable_if.h> #include <__type_traits/integral_constant.h> #include <__type_traits/is_assignable.h> #include <__type_traits/is_constructible.h> @@ -40,8 +42,11 @@ #include <__type_traits/is_nothrow_copy_constructible.h> #include <__type_traits/is_nothrow_default_constructible.h> #include <__type_traits/is_nothrow_move_assignable.h> +#include <__type_traits/is_object.h> #include <__type_traits/is_same.h> #include <__type_traits/is_swappable.h> +#include <__type_traits/is_trivially_copy_assignable.h> +#include <__type_traits/is_trivially_move_assignable.h> #include <__type_traits/nat.h> #include <__type_traits/remove_cvref.h> #include <__type_traits/unwrap_ref.h> @@ -67,6 +72,14 @@ struct __non_trivially_copyable_base { __non_trivially_copyable_base(__non_trivially_copyable_base const&) _NOEXCEPT {} }; +#if _LIBCPP_STD_VER >= 20 +template +concept __trivially_copy_assignable_object = is_trivially_copy_assignable_v<_Tp> && is_object_v<_Tp>; + +template +concept __trivially_move_assignable_object = is_trivially_move_assignable_v<_Tp> && is_object_v<_Tp>; +#endif + #if _LIBCPP_STD_VER >= 23 template struct __is_specialization_of_subrange : false_type {}; @@ -236,6 +249,28 @@ struct _LIBCPP_TEMPLATE_VIS pair typename __make_tuple_indices::type(), typename __make_tuple_indices::type()) {} +# if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE) + _LIBCPP_HIDE_FROM_ABI pair& operator=(pair const& __p) = default; + _LIBCPP_HIDE_FROM_ABI pair& operator=(pair&& __p) = default; + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(pair const& __p) + _NOEXCEPT_(is_nothrow_copy_assignable::value&& is_nothrow_copy_assignable::value) + requires is_copy_assignable_v && is_copy_assignable_v && (!(__trivially_copy_assignable_object && __trivially_copy_assignable_object)) + { + first = __p.first; + second = __p.second; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(pair&& __p) + _NOEXCEPT_(is_nothrow_move_assignable::value&& is_nothrow_move_assignable::value) + requires is_move_assignable_v && is_move_assignable_v && (!(__trivially_move_assignable_object && __trivially_move_assignable_object)) + { + first = std::forward(__p.first); + second = std::forward(__p.second); + return *this; + } +# else _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair& operator=(__conditional_t< is_copy_assignable::value && is_copy_assignable::value, pair, @@ -254,6 +289,7 @@ struct _LIBCPP_TEMPLATE_VIS pair second = std::forward(__p.second); return *this; } +# endif template < class _U1, diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp index 253bd1b85d03ec..f45903f2a263a7 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp @@ -88,6 +88,16 @@ TEST_CONSTEXPR_CXX20 bool test() { assert(&p.second == &inc_obj); } +#if TEST_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE) + static_assert(std::is_trivially_copy_assignable>::value, ""); +#else + static_assert(!std::is_trivially_copy_assignable>::value, ""); +#endif + + static_assert(!std::is_trivially_copy_assignable>::value, ""); + static_assert(!std::is_trivially_copy_assignable>::value, ""); + static_assert(!std::is_trivially_copy_assignable>::value, ""); + return true; } diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp index 2444131b02a0cf..76982c41629ee0 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair_cxx03.pass.cpp @@ -62,7 +62,9 @@ int main(int, char**) assert(p.second == 'x'); } - return 0; + static_assert(!std::is_trivially_copy_assignable >::value, ""); + + return 0; } struct Incomplete {}; diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp index 8e5d9c39ae8868..a4c178db25492a 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp @@ -130,6 +130,17 @@ TEST_CONSTEXPR_CXX20 bool test() { static_assert(!std::is_move_assignable::value, ""); static_assert(!std::is_move_assignable::value, ""); } + +#if TEST_STD_VER >= 20 && defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE) + static_assert(std::is_trivially_move_assignable>::value, ""); +#else + static_assert(!std::is_trivially_move_assignable>::value, ""); +#endif + + static_assert(!std::is_trivially_move_assignable>::value, ""); + static_assert(!std::is_trivially_move_assignable>::value, ""); + static_assert(!std::is_trivially_move_assignable>::value, ""); + return true; } diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp new file mode 100644 index 00000000000000..ecea98b4f9dc38 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/trivially_copyable.compile.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// 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: c++03, c++11, c++14, c++17 + +// + +// template struct pair + +// Checks that `std::pair` is actually trivially copyable. + +#include +#include + +#if defined(_LIBCPP_ABI_PAIR_TRIVIALLY_COPYABLE) +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); + +static_assert(!std::is_trivially_copyable_v>); +static_assert(!std::is_trivially_copyable_v>); +static_assert(!std::is_trivially_copyable_v>); + +enum E {}; +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); + +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); + +struct S {}; +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +static_assert(std::is_trivially_copyable_v>); +#endif