Skip to content

Commit

Permalink
[libc++][ABI BREAK] Make std::pair trivially copyable if its members are
Browse files Browse the repository at this point in the history
  • Loading branch information
philnik777 committed Jun 1, 2024
1 parent bfa8b64 commit a38e80c
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 7 deletions.
4 changes: 3 additions & 1 deletion libcxx/include/__configuration/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/__type_traits/datasizeof.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 34 additions & 6 deletions libcxx/include/__utility/pair.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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() {}

Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//===----------------------------------------------------------------------===//
//
// 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, "");
#ifdef TEST_COMPILER_GCC // FIXME: Why does GCC consider this trivially copyable
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, "");
#endif // _LIBCPP_ABI_TRIVIALLY_COPYABLE_PAIR

0 comments on commit a38e80c

Please sign in to comment.