Fix std::pair on FreeBSD

Summary:
FreeBSD ships an old ABI for std::pair which requires that it have non-trivial copy/move constructors. Currently the non-trivial copy/move is achieved by providing explicit definitions of the constructors. This is problematic because it means the constructors don't SFINAE properly. In order to SFINAE copy/move constructors they have to be explicitly defaulted and hense non-trivial.

This patch attempts to provide SFINAE'ing copy/move constructors for std::pair while still making them non-trivial. It does this by adding a base class with a non-trivial copy constructor and then allowing pair's constructors to be generated by the compiler. This also allows the constructors to be constexpr.


Reviewers: emaste, theraven, rsmith, dim

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D25389

llvm-svn: 283944
This commit is contained in:
Eric Fiselier 2016-10-11 21:22:21 +00:00
parent a58f92f09d
commit 9595fb21dd
2 changed files with 125 additions and 40 deletions

View File

@ -291,9 +291,20 @@ extern const piecewise_construct_t piecewise_construct;// = piecewise_construct_
constexpr piecewise_construct_t piecewise_construct = piecewise_construct_t();
#endif
#if defined(_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR)
struct __non_trivially_copyable_base {
_LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY
__non_trivially_copyable_base() _NOEXCEPT {}
_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
__non_trivially_copyable_base(__non_trivially_copyable_base const&) _NOEXCEPT {}
};
#endif
template <class _T1, class _T2>
struct _LIBCPP_TYPE_VIS_ONLY pair
#if defined(_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR)
: private __non_trivially_copyable_base
#endif
{
typedef _T1 first_type;
typedef _T2 second_type;
@ -301,26 +312,7 @@ struct _LIBCPP_TYPE_VIS_ONLY pair
_T1 first;
_T2 second;
#if defined(_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR)
_LIBCPP_INLINE_VISIBILITY
pair(const pair& __p)
_NOEXCEPT_(is_nothrow_copy_constructible<first_type>::value &&
is_nothrow_copy_constructible<second_type>::value)
: first(__p.first),
second(__p.second)
{
}
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
pair(pair&& __p) _NOEXCEPT_(is_nothrow_move_constructible<first_type>::value &&
is_nothrow_move_constructible<second_type>::value)
: first(_VSTD::forward<first_type>(__p.first)),
second(_VSTD::forward<second_type>(__p.second))
{
}
# endif
#elif !defined(_LIBCPP_CXX03_LANG)
#if !defined(_LIBCPP_CXX03_LANG)
pair(pair const&) = default;
pair(pair&&) = default;
#else

View File

@ -7,49 +7,142 @@
//
//===----------------------------------------------------------------------===//
// The test fails due to the missing is_trivially_constructible intrinsic.
// XFAIL: gcc-4.9
// <utility>
// template <class T1, class T2> struct pair
// Test that we properly provide the old non-trivial copy operations
// when the ABI macro is defined.
// Test that we properly provide the trivial copy operations by default.
// FreeBSD provides the old ABI. This test checks the new ABI so we need
// to manually turn it on.
#undef _LIBCPP_ABI_UNSTABLE
#undef _LIBCPP_ABI_VERSION
#define _LIBCPP_ABI_VERSION 1
#define _LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR
#include <utility>
#include <type_traits>
#include <cstdlib>
#include <cassert>
#include "test_macros.h"
#if TEST_STD_VER >= 11
struct Dummy {
Dummy(Dummy const&) = delete;
Dummy(Dummy &&) = default;
};
#if !defined(_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR)
#error trivial ctor ABI macro defined
#endif
template <class T>
struct HasNonTrivialABI : std::integral_constant<bool,
!std::is_trivially_destructible<T>::value
|| (std::is_copy_constructible<T>::value && !std::is_trivially_copy_constructible<T>::value)
#if TEST_STD_VER >= 11
|| (std::is_move_constructible<T>::value && !std::is_trivially_move_constructible<T>::value)
#endif
> {};
#if TEST_STD_VER >= 11
struct NonTrivialDtor {
NonTrivialDtor(NonTrivialDtor const&) = default;
~NonTrivialDtor();
};
NonTrivialDtor::~NonTrivialDtor() {}
static_assert(HasNonTrivialABI<NonTrivialDtor>::value, "");
struct NonTrivialCopy {
NonTrivialCopy(NonTrivialCopy const&);
};
NonTrivialCopy::NonTrivialCopy(NonTrivialCopy const&) {}
static_assert(HasNonTrivialABI<NonTrivialCopy>::value, "");
struct NonTrivialMove {
NonTrivialMove(NonTrivialMove const&) = default;
NonTrivialMove(NonTrivialMove&&);
};
NonTrivialMove::NonTrivialMove(NonTrivialMove&&) {}
static_assert(HasNonTrivialABI<NonTrivialMove>::value, "");
struct DeletedCopy {
DeletedCopy(DeletedCopy const&) = delete;
DeletedCopy(DeletedCopy&&) = default;
};
static_assert(!HasNonTrivialABI<DeletedCopy>::value, "");
struct TrivialMove {
TrivialMove(TrivialMove &&) = default;
};
static_assert(!HasNonTrivialABI<TrivialMove>::value, "");
struct Trivial {
Trivial(Trivial const&) = default;
};
static_assert(!HasNonTrivialABI<Trivial>::value, "");
#endif
int main()
{
typedef std::pair<int, short> P;
{
typedef std::pair<int, short> P;
static_assert(std::is_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_copyable<P>::value, "");
static_assert(HasNonTrivialABI<P>::value, "");
}
#if TEST_STD_VER >= 11
{
typedef std::pair<int, short> P;
static_assert(std::is_move_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
static_assert(!std::is_trivially_copyable<P>::value, "");
static_assert(HasNonTrivialABI<P>::value, "");
}
{
using P1 = std::pair<Dummy, int>;
// These lines fail because the non-trivial constructors do not provide
// SFINAE.
// static_assert(!std::is_copy_constructible<P1>::value, "");
// static_assert(!std::is_trivially_copy_constructible<P1>::value, "");
static_assert(std::is_move_constructible<P1>::value, "");
static_assert(!std::is_trivially_move_constructible<P1>::value, "");
static_assert(!std::is_trivially_copyable<P>::value, "");
using P = std::pair<NonTrivialDtor, int>;
static_assert(!std::is_trivially_destructible<P>::value, "");
static_assert(std::is_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(std::is_move_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
static_assert(HasNonTrivialABI<P>::value, "");
}
{
using P = std::pair<NonTrivialCopy, int>;
static_assert(std::is_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(std::is_move_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
static_assert(HasNonTrivialABI<P>::value, "");
}
{
using P = std::pair<NonTrivialMove, int>;
static_assert(std::is_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(std::is_move_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
static_assert(HasNonTrivialABI<P>::value, "");
}
{
using P = std::pair<DeletedCopy, int>;
static_assert(!std::is_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(std::is_move_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
static_assert(HasNonTrivialABI<P>::value, "");
}
{
using P = std::pair<Trivial, int>;
static_assert(std::is_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(std::is_move_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
static_assert(HasNonTrivialABI<P>::value, "");
}
{
using P = std::pair<TrivialMove, int>;
static_assert(!std::is_copy_constructible<P>::value, "");
static_assert(!std::is_trivially_copy_constructible<P>::value, "");
static_assert(std::is_move_constructible<P>::value, "");
static_assert(!std::is_trivially_move_constructible<P>::value, "");
static_assert(HasNonTrivialABI<P>::value, "");
}
#endif
}