Skip to content

Commit

Permalink
Avoid ABI issues due to MSVC EBCO issues (#1739)
Browse files Browse the repository at this point in the history
* Ensure that `variant` SFINAE base classes do use EBCO
* Ensure that `expected` SFINAE base classes do use EBCO
* Ensure that `optional` SFINAE base classes do use EBCO

MSVC allows empty base class optimization only for the last base class. So we cannot use multiple inheritance
  • Loading branch information
miscco authored May 15, 2024
1 parent deaa918 commit 08a4b66
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 134 deletions.
10 changes: 2 additions & 8 deletions libcudacxx/include/cuda/std/__expected/expected.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,7 @@ _LIBCUDACXX_INLINE_VAR constexpr bool __can_swap<void, _Err> =
} // namespace __expected

template <class _Tp, class _Err>
class expected
: private __expected_move_assign<_Tp, _Err>
, private __expected_sfinae_ctor_base_t<_Tp, _Err>
, private __expected_sfinae_assign_base_t<_Tp, _Err>
class expected : private __expected_move_assign<_Tp, _Err>
{
using __base = __expected_move_assign<_Tp, _Err>;

Expand Down Expand Up @@ -1212,10 +1209,7 @@ class expected
};

template <class _Err>
class expected<void, _Err>
: private __expected_move_assign<void, _Err>
, private __expected_void_sfinae_ctor_base_t<_Err>
, private __expected_void_sfinae_assign_base_t<_Err>
class expected<void, _Err> : private __expected_move_assign<void, _Err>
{
using __base = __expected_move_assign<void, _Err>;
static_assert(__unexpected::__valid_unexpected<_Err>,
Expand Down
164 changes: 112 additions & 52 deletions libcudacxx/include/cuda/std/__expected/expected_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -518,17 +518,24 @@ struct __expected_storage : __expected_destruct<_Tp, _Err>
}
};

template <class _Tp,
class _Err,
bool = (_CCCL_TRAIT(is_trivially_copy_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_copy_constructible, _Err)>
template <class _Tp, class _Err>
_LIBCUDACXX_INLINE_VAR constexpr __smf_availability __expected_can_copy_construct =
(_CCCL_TRAIT(is_trivially_copy_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_copy_constructible, _Err)
? __smf_availability::__trivial
: (_CCCL_TRAIT(is_copy_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_copy_constructible, _Err)
? __smf_availability::__available
: __smf_availability::__deleted;

template <class _Tp, class _Err, __smf_availability = __expected_can_copy_construct<_Tp, _Err>>
struct __expected_copy : __expected_storage<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_copy, __expected_storage, _Tp, _Err);
};

template <class _Tp, class _Err>
struct __expected_copy<_Tp, _Err, false> : __expected_storage<_Tp, _Err>
struct __expected_copy<_Tp, _Err, __smf_availability::__available> : __expected_storage<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_copy, __expected_storage, _Tp, _Err);

Expand All @@ -551,17 +558,35 @@ struct __expected_copy<_Tp, _Err, false> : __expected_storage<_Tp, _Err>
__expected_copy& operator=(__expected_copy&&) = default;
};

template <class _Tp,
class _Err,
bool = (_CCCL_TRAIT(is_trivially_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_move_constructible, _Err)>
template <class _Tp, class _Err>
struct __expected_copy<_Tp, _Err, __smf_availability::__deleted> : __expected_storage<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_copy, __expected_storage, _Tp, _Err);

__expected_copy(const __expected_copy&) = delete;
__expected_copy(__expected_copy&&) = default;
__expected_copy& operator=(const __expected_copy&) = default;
__expected_copy& operator=(__expected_copy&&) = default;
};

template <class _Tp, class _Err>
_LIBCUDACXX_INLINE_VAR constexpr __smf_availability __expected_can_move_construct =
(_CCCL_TRAIT(is_trivially_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_move_constructible, _Err)
? __smf_availability::__trivial
: (_CCCL_TRAIT(is_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_move_constructible, _Err)
? __smf_availability::__available
: __smf_availability::__deleted;

template <class _Tp, class _Err, __smf_availability = __expected_can_move_construct<_Tp, _Err>>
struct __expected_move : __expected_copy<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_move, __expected_copy, _Tp, _Err);
};

template <class _Tp, class _Err>
struct __expected_move<_Tp, _Err, false> : __expected_copy<_Tp, _Err>
struct __expected_move<_Tp, _Err, __smf_availability::__available> : __expected_copy<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_move, __expected_copy, _Tp, _Err);

Expand All @@ -585,21 +610,46 @@ struct __expected_move<_Tp, _Err, false> : __expected_copy<_Tp, _Err>
__expected_move& operator=(__expected_move&&) = default;
};

template <class _Tp,
class _Err,
bool = (_CCCL_TRAIT(is_trivially_destructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_destructible, _Err)
&& (_CCCL_TRAIT(is_trivially_copy_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_copy_constructible, _Err)
&& (_CCCL_TRAIT(is_trivially_copy_assignable, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_copy_assignable, _Err)>
template <class _Tp, class _Err>
struct __expected_move<_Tp, _Err, __smf_availability::__deleted> : __expected_copy<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_move, __expected_copy, _Tp, _Err);

__expected_move(const __expected_move&) = default;
__expected_move(__expected_move&&) = delete;
__expected_move& operator=(const __expected_move&) = default;
__expected_move& operator=(__expected_move&&) = default;
};

// Need to also check against is_nothrow_move_constructible in the trivial case as that is stupidly in the constraints
template <class _Tp, class _Err>
_LIBCUDACXX_INLINE_VAR constexpr __smf_availability __expected_can_copy_assign =
(_CCCL_TRAIT(is_trivially_destructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_destructible, _Err)
&& (_CCCL_TRAIT(is_trivially_copy_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_copy_constructible, _Err)
&& (_CCCL_TRAIT(is_trivially_copy_assignable, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_copy_assignable, _Err)
&& (_CCCL_TRAIT(is_nothrow_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void)
|| _CCCL_TRAIT(is_nothrow_move_constructible, _Err))
? __smf_availability::__trivial
: (_CCCL_TRAIT(is_copy_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_copy_constructible, _Err)
&& (_CCCL_TRAIT(is_copy_assignable, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_copy_assignable, _Err)
&& (_CCCL_TRAIT(is_nothrow_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void)
|| _CCCL_TRAIT(is_nothrow_move_constructible, _Err))
? __smf_availability::__available
: __smf_availability::__deleted;

template <class _Tp, class _Err, __smf_availability = __expected_can_copy_assign<_Tp, _Err>>
struct __expected_copy_assign : __expected_move<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_copy_assign, __expected_move, _Tp, _Err);
};

template <class _Tp, class _Err>
struct __expected_copy_assign<_Tp, _Err, false> : __expected_move<_Tp, _Err>
struct __expected_copy_assign<_Tp, _Err, __smf_availability::__available> : __expected_move<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_copy_assign, __expected_move, _Tp, _Err);

Expand Down Expand Up @@ -636,21 +686,43 @@ struct __expected_copy_assign<_Tp, _Err, false> : __expected_move<_Tp, _Err>
__expected_copy_assign& operator=(__expected_copy_assign&&) = default;
};

template <class _Tp,
class _Err,
bool = (_CCCL_TRAIT(is_trivially_destructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_destructible, _Err)
&& (_CCCL_TRAIT(is_trivially_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_move_constructible, _Err)
&& (_CCCL_TRAIT(is_trivially_move_assignable, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_move_assignable, _Err)>
template <class _Tp, class _Err>
struct __expected_copy_assign<_Tp, _Err, __smf_availability::__deleted> : __expected_move<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_copy_assign, __expected_move, _Tp, _Err);

__expected_copy_assign(const __expected_copy_assign&) = default;
__expected_copy_assign(__expected_copy_assign&&) = default;
__expected_copy_assign& operator=(const __expected_copy_assign&) = delete;
__expected_copy_assign& operator=(__expected_copy_assign&&) = default;
};

template <class _Tp, class _Err>
_LIBCUDACXX_INLINE_VAR constexpr __smf_availability __expected_can_move_assign =
(_CCCL_TRAIT(is_trivially_destructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_destructible, _Err)
&& (_CCCL_TRAIT(is_trivially_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_move_constructible, _Err)
&& (_CCCL_TRAIT(is_trivially_move_assignable, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_trivially_move_assignable, _Err)
? __smf_availability::__trivial
: (_CCCL_TRAIT(is_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_move_constructible, _Err)
&& (_CCCL_TRAIT(is_move_assignable, _Tp) || _CCCL_TRAIT(is_same, _Tp, void))
&& _CCCL_TRAIT(is_move_assignable, _Err)
&& (_CCCL_TRAIT(is_nothrow_move_constructible, _Tp) || _CCCL_TRAIT(is_same, _Tp, void)
|| _CCCL_TRAIT(is_nothrow_move_constructible, _Err))
? __smf_availability::__available
: __smf_availability::__deleted;

template <class _Tp, class _Err, __smf_availability = __expected_can_move_assign<_Tp, _Err>>
struct __expected_move_assign : __expected_copy_assign<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_move_assign, __expected_copy_assign, _Tp, _Err);
};

template <class _Tp, class _Err>
struct __expected_move_assign<_Tp, _Err, false> : __expected_copy_assign<_Tp, _Err>
struct __expected_move_assign<_Tp, _Err, __smf_availability::__available> : __expected_copy_assign<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_move_assign, __expected_copy_assign, _Tp, _Err);

Expand Down Expand Up @@ -687,18 +759,15 @@ struct __expected_move_assign<_Tp, _Err, false> : __expected_copy_assign<_Tp, _E
};

template <class _Tp, class _Err>
using __expected_sfinae_ctor_base_t =
__sfinae_ctor_base<_CCCL_TRAIT(is_copy_constructible, _Tp) && _CCCL_TRAIT(is_copy_constructible, _Err),
_CCCL_TRAIT(is_move_constructible, _Tp) && _CCCL_TRAIT(is_move_constructible, _Err)>;
struct __expected_move_assign<_Tp, _Err, __smf_availability::__deleted> : __expected_copy_assign<_Tp, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_move_assign, __expected_copy_assign, _Tp, _Err);

template <class _Tp, class _Err>
using __expected_sfinae_assign_base_t = __sfinae_assign_base<
_CCCL_TRAIT(is_copy_constructible, _Tp) && _CCCL_TRAIT(is_copy_constructible, _Err)
&& _CCCL_TRAIT(is_copy_assignable, _Tp) && _CCCL_TRAIT(is_copy_assignable, _Err)
&& (_CCCL_TRAIT(is_nothrow_move_constructible, _Tp) || _CCCL_TRAIT(is_nothrow_move_constructible, _Err)),
_CCCL_TRAIT(is_move_constructible, _Tp) && _CCCL_TRAIT(is_move_constructible, _Err)
&& _CCCL_TRAIT(is_move_assignable, _Tp) && _CCCL_TRAIT(is_move_assignable, _Err)
&& (_CCCL_TRAIT(is_nothrow_move_constructible, _Tp) || _CCCL_TRAIT(is_nothrow_move_constructible, _Err))>;
__expected_move_assign(const __expected_move_assign&) = default;
__expected_move_assign(__expected_move_assign&&) = default;
__expected_move_assign& operator=(const __expected_move_assign&) = default;
__expected_move_assign& operator=(__expected_move_assign&&) = delete;
};

// expected<void, E> base classtemplate <class _Tp, class _Err>
// MSVC complains about [[no_unique_address]] prior to C++20 as a vendor extension
Expand Down Expand Up @@ -863,7 +932,7 @@ struct __expected_storage<void, _Err> : __expected_destruct<void, _Err>
};

template <class _Err>
struct __expected_copy<void, _Err, false> : __expected_storage<void, _Err>
struct __expected_copy<void, _Err, __smf_availability::__available> : __expected_storage<void, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_copy, __expected_storage, void, _Err);

Expand All @@ -883,7 +952,7 @@ struct __expected_copy<void, _Err, false> : __expected_storage<void, _Err>
};

template <class _Err>
struct __expected_move<void, _Err, false> : __expected_copy<void, _Err>
struct __expected_move<void, _Err, __smf_availability::__available> : __expected_copy<void, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_move, __expected_copy, void, _Err);

Expand All @@ -904,7 +973,7 @@ struct __expected_move<void, _Err, false> : __expected_copy<void, _Err>
};

template <class _Err>
struct __expected_copy_assign<void, _Err, false> : __expected_move<void, _Err>
struct __expected_copy_assign<void, _Err, __smf_availability::__available> : __expected_move<void, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_copy_assign, __expected_move, void, _Err);

Expand Down Expand Up @@ -940,7 +1009,7 @@ struct __expected_copy_assign<void, _Err, false> : __expected_move<void, _Err>
};

template <class _Err>
struct __expected_move_assign<void, _Err, false> : __expected_copy_assign<void, _Err>
struct __expected_move_assign<void, _Err, __smf_availability::__available> : __expected_copy_assign<void, _Err>
{
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__expected_move_assign, __expected_copy_assign, void, _Err);

Expand Down Expand Up @@ -974,15 +1043,6 @@ struct __expected_move_assign<void, _Err, false> : __expected_copy_assign<void,
}
};

template <class _Err>
using __expected_void_sfinae_ctor_base_t =
__sfinae_ctor_base<_CCCL_TRAIT(is_copy_constructible, _Err), _CCCL_TRAIT(is_move_constructible, _Err)>;

template <class _Err>
using __expected_void_sfinae_assign_base_t =
__sfinae_assign_base<_CCCL_TRAIT(is_copy_constructible, _Err) && _CCCL_TRAIT(is_copy_assignable, _Err),
_CCCL_TRAIT(is_move_constructible, _Err) && _CCCL_TRAIT(is_move_assignable, _Err)>;

_LIBCUDACXX_END_NAMESPACE_STD

#endif // _CCCL_STD_VER > 2011
Expand Down
95 changes: 48 additions & 47 deletions libcudacxx/include/cuda/std/__tuple_dir/sfinae_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,67 +147,68 @@ struct _LIBCUDACXX_TYPE_VIS __check_tuple_constructor_fail

#if _CCCL_STD_VER > 2011

template <bool _CanCopy, bool _CanMove>
struct __sfinae_ctor_base
{};
template <>
struct __sfinae_ctor_base<false, false>
enum class __smf_availability
{
__sfinae_ctor_base() = default;
__sfinae_ctor_base(__sfinae_ctor_base const&) = delete;
__sfinae_ctor_base(__sfinae_ctor_base&&) = delete;
__sfinae_ctor_base& operator=(__sfinae_ctor_base const&) = default;
__sfinae_ctor_base& operator=(__sfinae_ctor_base&&) = default;
};
template <>
struct __sfinae_ctor_base<true, false>
{
__sfinae_ctor_base() = default;
__sfinae_ctor_base(__sfinae_ctor_base const&) = default;
__sfinae_ctor_base(__sfinae_ctor_base&&) = delete;
__sfinae_ctor_base& operator=(__sfinae_ctor_base const&) = default;
__sfinae_ctor_base& operator=(__sfinae_ctor_base&&) = default;
__trivial,
__available,
__deleted,
};

template <bool _CanCopy>
struct __sfinae_copy_base
{};
template <>
struct __sfinae_ctor_base<false, true>
struct __sfinae_copy_base<false>
{
__sfinae_ctor_base() = default;
__sfinae_ctor_base(__sfinae_ctor_base const&) = delete;
__sfinae_ctor_base(__sfinae_ctor_base&&) = default;
__sfinae_ctor_base& operator=(__sfinae_ctor_base const&) = default;
__sfinae_ctor_base& operator=(__sfinae_ctor_base&&) = default;
__sfinae_copy_base() = default;
__sfinae_copy_base(__sfinae_copy_base const&) = delete;
__sfinae_copy_base(__sfinae_copy_base&&) = default;
__sfinae_copy_base& operator=(__sfinae_copy_base const&) = default;
__sfinae_copy_base& operator=(__sfinae_copy_base&&) = default;
};

template <bool _CanCopy, bool _CanMove>
struct __sfinae_assign_base
struct __sfinae_move_base : __sfinae_copy_base<_CanCopy>
{};
template <>
struct __sfinae_assign_base<false, false>
template <bool _CanCopy>
struct __sfinae_move_base<_CanCopy, false> : __sfinae_copy_base<_CanCopy>
{
__sfinae_assign_base() = default;
__sfinae_assign_base(__sfinae_assign_base const&) = default;
__sfinae_assign_base(__sfinae_assign_base&&) = default;
__sfinae_assign_base& operator=(__sfinae_assign_base const&) = delete;
__sfinae_assign_base& operator=(__sfinae_assign_base&&) = delete;
__sfinae_move_base() = default;
__sfinae_move_base(__sfinae_move_base const&) = default;
__sfinae_move_base(__sfinae_move_base&&) = delete;
__sfinae_move_base& operator=(__sfinae_move_base const&) = default;
__sfinae_move_base& operator=(__sfinae_move_base&&) = default;
};
template <>
struct __sfinae_assign_base<true, false>

template <bool _CanCopy, bool _CanMove, bool _CanCopyAssign>
struct __sfinae_copy_assign_base : __sfinae_move_base<_CanCopy, _CanMove>
{};
template <bool _CanCopy, bool _CanMove>
struct __sfinae_copy_assign_base<_CanCopy, _CanMove, false> : __sfinae_move_base<_CanCopy, _CanMove>
{
__sfinae_assign_base() = default;
__sfinae_assign_base(__sfinae_assign_base const&) = default;
__sfinae_assign_base(__sfinae_assign_base&&) = default;
__sfinae_assign_base& operator=(__sfinae_assign_base const&) = default;
__sfinae_assign_base& operator=(__sfinae_assign_base&&) = delete;
__sfinae_copy_assign_base() = default;
__sfinae_copy_assign_base(__sfinae_copy_assign_base const&) = default;
__sfinae_copy_assign_base(__sfinae_copy_assign_base&&) = default;
__sfinae_copy_assign_base& operator=(__sfinae_copy_assign_base const&) = delete;
__sfinae_copy_assign_base& operator=(__sfinae_copy_assign_base&&) = default;
};
template <>
struct __sfinae_assign_base<false, true>

template <bool _CanCopy, bool _CanMove, bool _CanCopyAssign, bool _CanMoveAssign>
struct __sfinae_move_assign_base : __sfinae_copy_assign_base<_CanCopy, _CanMove, _CanCopyAssign>
{};
template <bool _CanCopy, bool _CanMove, bool _CanCopyAssign>
struct __sfinae_move_assign_base<_CanCopy, _CanMove, _CanCopyAssign, false>
: __sfinae_copy_assign_base<_CanCopy, _CanMove, _CanCopyAssign>
{
__sfinae_assign_base() = default;
__sfinae_assign_base(__sfinae_assign_base const&) = default;
__sfinae_assign_base(__sfinae_assign_base&&) = default;
__sfinae_assign_base& operator=(__sfinae_assign_base const&) = delete;
__sfinae_assign_base& operator=(__sfinae_assign_base&&) = default;
__sfinae_move_assign_base() = default;
__sfinae_move_assign_base(__sfinae_move_assign_base const&) = default;
__sfinae_move_assign_base(__sfinae_move_assign_base&&) = default;
__sfinae_move_assign_base& operator=(__sfinae_move_assign_base const&) = default;
__sfinae_move_assign_base& operator=(__sfinae_move_assign_base&&) = delete;
};

template <bool _CanCopy, bool _CanMove, bool _CanCopyAssign, bool _CanMoveAssign>
using __sfinae_base = __sfinae_move_assign_base<_CanCopy, _CanMove, _CanCopyAssign, _CanMoveAssign>;
#endif // _CCCL_STD_VER > 2011

_LIBCUDACXX_END_NAMESPACE_STD
Expand Down
Loading

0 comments on commit 08a4b66

Please sign in to comment.