From 4933bd82c2d574fcd24163ae347176d584867729 Mon Sep 17 00:00:00 2001 From: Hui Date: Wed, 28 Feb 2024 21:06:51 +0000 Subject: [PATCH 1/2] [libc++] Make `constexpr std::variant`. Implement P2231R1 constexpr test for default ctor fix c++17 --- libcxx/docs/ReleaseNotes/19.rst | 1 + libcxx/docs/Status/Cxx20.rst | 1 - libcxx/docs/Status/Cxx20Papers.csv | 2 +- libcxx/include/variant | 212 ++++---- .../variant.variant/variant.assign/T.pass.cpp | 202 ++++++- .../variant.assign/copy.pass.cpp | 430 +++++++++------ .../variant.assign/move.pass.cpp | 316 ++++++----- .../variant.ctor/copy.pass.cpp | 106 ++-- .../variant.ctor/default.pass.cpp | 26 +- .../variant.ctor/move.pass.cpp | 113 ++-- .../variant.dtor/dtor.pass.cpp | 71 ++- .../variant.mod/emplace_index_args.pass.cpp | 44 +- .../emplace_index_init_list_args.pass.cpp | 35 +- .../variant.mod/emplace_type_args.pass.cpp | 51 +- .../emplace_type_init_list_args.pass.cpp | 44 +- .../variant.swap/swap.pass.cpp | 504 +++++++++--------- 16 files changed, 1322 insertions(+), 836 deletions(-) diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst index 5a07b11cbcd50..370de2fd4346c 100644 --- a/libcxx/docs/ReleaseNotes/19.rst +++ b/libcxx/docs/ReleaseNotes/19.rst @@ -51,6 +51,7 @@ Implemented Papers - P3029R1 - Better ``mdspan``'s CTAD - P2387R3 - Pipe support for user-defined range adaptors - P2713R1 - Escaping improvements in ``std::format`` +- P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant`` Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx20.rst b/libcxx/docs/Status/Cxx20.rst index 23289dc6e5961..b08b99394fbb7 100644 --- a/libcxx/docs/Status/Cxx20.rst +++ b/libcxx/docs/Status/Cxx20.rst @@ -47,7 +47,6 @@ Paper Status .. [#note-P0619] P0619: Only sections D.8, D.9, D.10 and D.13 are implemented. Sections D.4, D.7, D.11, and D.12 remain undone. .. [#note-P0883.1] P0883: shared_ptr and floating-point changes weren't applied as they themselves aren't implemented yet. .. [#note-P0883.2] P0883: ``ATOMIC_FLAG_INIT`` was marked deprecated in version 14.0, but was undeprecated with the implementation of LWG3659 in version 15.0. - .. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet. .. [#note-P0660] P0660: The paper is implemented but the features are experimental and can be enabled via ``-fexperimental-library``. .. [#note-P0355] P0355: The implementation status is: diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv index d31720b7576d7..955aa5f614afb 100644 --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -192,7 +192,7 @@ "`P2106R0 `__","LWG","Alternative wording for GB315 and GB316","Prague","|Complete|","15.0","|ranges|" "`P2116R0 `__","LWG","Remove tuple-like protocol support from fixed-extent span","Prague","|Complete|","11.0" "","","","","","","" -"`P2231R1 `__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Partial| [#note-P2231]_","13.0" +"`P2231R1 `__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Complete|","19.0" "`P2325R3 `__","LWG","Views should not be required to be default constructible","June 2021","|Complete|","16.0","|ranges|" "`P2210R2 `__","LWG","Superior String Splitting","June 2021","|Complete|","16.0","|ranges|" "`P2216R3 `__","LWG","std::format improvements","June 2021","|Complete|","15.0" diff --git a/libcxx/include/variant b/libcxx/include/variant index 34150bd452842..953ffea5dea88 100644 --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -42,26 +42,28 @@ namespace std { in_place_index_t, initializer_list, Args&&...); // 20.7.2.2, destructor - ~variant(); + constexpr ~variant(); // constexpr since c++20 // 20.7.2.3, assignment constexpr variant& operator=(const variant&); constexpr variant& operator=(variant&&) noexcept(see below); - template variant& operator=(T&&) noexcept(see below); + template + constexpr variant& operator=(T&&) noexcept(see below); // constexpr since c++20 // 20.7.2.4, modifiers template - T& emplace(Args&&...); + constexpr T& emplace(Args&&...); // constexpr since c++20 template - T& emplace(initializer_list, Args&&...); + constexpr T& emplace(initializer_list, Args&&...); // constexpr since c++20 template - variant_alternative_t& emplace(Args&&...); + constexpr variant_alternative_t& emplace(Args&&...); // constexpr since c++20 template - variant_alternative_t& emplace(initializer_list, Args&&...); + constexpr variant_alternative_t& + emplace(initializer_list, Args&&...); // constexpr since c++20 // 20.7.2.5, value status constexpr bool valueless_by_exception() const noexcept; @@ -221,6 +223,7 @@ namespace std { #include <__functional/operations.h> #include <__functional/unary_function.h> #include <__memory/addressof.h> +#include <__memory/construct_at.h> #include <__tuple/find_index.h> #include <__tuple/sfinae_helpers.h> #include <__type_traits/add_const.h> @@ -663,7 +666,8 @@ private: template struct _LIBCPP_TEMPLATE_VIS __alt { - using __value_type = _Tp; + using __value_type = _Tp; + static constexpr size_t __index = _Index; template _LIBCPP_HIDE_FROM_ABI explicit constexpr __alt(in_place_t, _Args&&... __args) @@ -678,7 +682,7 @@ union _LIBCPP_TEMPLATE_VIS __union; template <_Trait _DestructibleTrait, size_t _Index> union _LIBCPP_TEMPLATE_VIS __union<_DestructibleTrait, _Index> {}; -# define _LIBCPP_VARIANT_UNION(destructible_trait, destructor) \ +# define _LIBCPP_VARIANT_UNION(destructible_trait, destructor_definition) \ template \ union _LIBCPP_TEMPLATE_VIS __union { \ public: \ @@ -692,13 +696,11 @@ union _LIBCPP_TEMPLATE_VIS __union<_DestructibleTrait, _Index> {}; _LIBCPP_HIDE_FROM_ABI explicit constexpr __union(in_place_index_t<_Ip>, _Args&&... __args) \ : __tail(in_place_index<_Ip - 1>, std::forward<_Args>(__args)...) {} \ \ - __union(const __union&) = default; \ - __union(__union&&) = default; \ - \ - destructor; \ - \ - __union& operator=(const __union&) = default; \ - __union& operator=(__union&&) = default; \ + _LIBCPP_HIDE_FROM_ABI __union(const __union&) = default; \ + _LIBCPP_HIDE_FROM_ABI __union(__union&&) = default; \ + _LIBCPP_HIDE_FROM_ABI __union& operator=(const __union&) = default; \ + _LIBCPP_HIDE_FROM_ABI __union& operator=(__union&&) = default; \ + destructor_definition; \ \ private: \ char __dummy; \ @@ -708,10 +710,11 @@ union _LIBCPP_TEMPLATE_VIS __union<_DestructibleTrait, _Index> {}; friend struct __access::__union; \ } -_LIBCPP_VARIANT_UNION(_Trait::_TriviallyAvailable, ~__union() = default); +_LIBCPP_VARIANT_UNION(_Trait::_TriviallyAvailable, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union() = default); _LIBCPP_VARIANT_UNION( - _Trait::_Available, _LIBCPP_HIDE_FROM_ABI ~__union() {} _LIBCPP_EAT_SEMICOLON); -_LIBCPP_VARIANT_UNION(_Trait::_Unavailable, ~__union() = delete); + _Trait::_Available, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union() {} _LIBCPP_EAT_SEMICOLON); +_LIBCPP_VARIANT_UNION(_Trait::_Unavailable, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union() = delete); # undef _LIBCPP_VARIANT_UNION @@ -754,7 +757,7 @@ protected: template class _LIBCPP_TEMPLATE_VIS __dtor; -# define _LIBCPP_VARIANT_DESTRUCTOR(destructible_trait, destructor, destroy) \ +# define _LIBCPP_VARIANT_DESTRUCTOR(destructible_trait, destructor_definition, destroy) \ template \ class _LIBCPP_TEMPLATE_VIS __dtor<__traits<_Types...>, destructible_trait> \ : public __base { \ @@ -764,28 +767,27 @@ class _LIBCPP_TEMPLATE_VIS __dtor; public: \ using __base_type::__base_type; \ using __base_type::operator=; \ - \ - __dtor(const __dtor&) = default; \ - __dtor(__dtor&&) = default; \ - __dtor& operator=(const __dtor&) = default; \ - __dtor& operator=(__dtor&&) = default; \ - destructor; \ + _LIBCPP_HIDE_FROM_ABI __dtor(const __dtor&) = default; \ + _LIBCPP_HIDE_FROM_ABI __dtor(__dtor&&) = default; \ + _LIBCPP_HIDE_FROM_ABI __dtor& operator=(const __dtor&) = default; \ + _LIBCPP_HIDE_FROM_ABI __dtor& operator=(__dtor&&) = default; \ + destructor_definition; \ \ protected: \ - inline _LIBCPP_HIDE_FROM_ABI destroy; \ + destroy; \ } _LIBCPP_VARIANT_DESTRUCTOR( _Trait::_TriviallyAvailable, - ~__dtor() = default, // - _LIBCPP_HIDE_FROM_ABI void __destroy() noexcept { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() = default, + inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept { this->__index = __variant_npos<__index_t>; } _LIBCPP_EAT_SEMICOLON); _LIBCPP_VARIANT_DESTRUCTOR( _Trait::_Available, - _LIBCPP_HIDE_FROM_ABI ~__dtor() { __destroy(); } _LIBCPP_EAT_SEMICOLON, - _LIBCPP_HIDE_FROM_ABI void __destroy() noexcept { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() { __destroy(); } _LIBCPP_EAT_SEMICOLON, + inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept { if (!this->valueless_by_exception()) { __visitation::__base::__visit_alt( [](auto& __alt) noexcept { @@ -797,7 +799,9 @@ _LIBCPP_VARIANT_DESTRUCTOR( this->__index = __variant_npos<__index_t>; } _LIBCPP_EAT_SEMICOLON); -_LIBCPP_VARIANT_DESTRUCTOR(_Trait::_Unavailable, ~__dtor() = delete, void __destroy() noexcept = delete); +_LIBCPP_VARIANT_DESTRUCTOR(_Trait::_Unavailable, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() = delete, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept = delete); # undef _LIBCPP_VARIANT_DESTRUCTOR @@ -810,23 +814,18 @@ public: using __base_type::operator=; protected: - template - _LIBCPP_HIDE_FROM_ABI static _Tp& __construct_alt(__alt<_Ip, _Tp>& __a, _Args&&... __args) { - ::new ((void*)std::addressof(__a)) __alt<_Ip, _Tp>(in_place, std::forward<_Args>(__args)...); - return __a.__value; - } - template - _LIBCPP_HIDE_FROM_ABI static void __generic_construct(__ctor& __lhs, _Rhs&& __rhs) { + _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX20 void __generic_construct(__ctor& __lhs, _Rhs&& __rhs) { __lhs.__destroy(); if (!__rhs.valueless_by_exception()) { auto __rhs_index = __rhs.index(); __visitation::__base::__visit_alt_at( __rhs_index, - [](auto& __lhs_alt, auto&& __rhs_alt) { - __construct_alt(__lhs_alt, std::forward(__rhs_alt).__value); + [&__lhs](auto&& __rhs_alt) { + std::__construct_at(std::addressof(__lhs.__data), + in_place_index<__decay_t::__index>, + std::forward(__rhs_alt).__value); }, - __lhs, std::forward<_Rhs>(__rhs)); __lhs.__index = __rhs_index; } @@ -836,7 +835,7 @@ protected: template class _LIBCPP_TEMPLATE_VIS __move_constructor; -# define _LIBCPP_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, move_constructor) \ +# define _LIBCPP_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, move_constructor_definition) \ template \ class _LIBCPP_TEMPLATE_VIS __move_constructor<__traits<_Types...>, move_constructible_trait> \ : public __ctor<__traits<_Types...>> { \ @@ -846,32 +845,35 @@ class _LIBCPP_TEMPLATE_VIS __move_constructor; using __base_type::__base_type; \ using __base_type::operator=; \ \ - __move_constructor(const __move_constructor&) = default; \ - ~__move_constructor() = default; \ - __move_constructor& operator=(const __move_constructor&) = default; \ - __move_constructor& operator=(__move_constructor&&) = default; \ - move_constructor; \ + _LIBCPP_HIDE_FROM_ABI __move_constructor(const __move_constructor&) = default; \ + _LIBCPP_HIDE_FROM_ABI ~__move_constructor() = default; \ + _LIBCPP_HIDE_FROM_ABI __move_constructor& operator=(const __move_constructor&) = default; \ + _LIBCPP_HIDE_FROM_ABI __move_constructor& operator=(__move_constructor&&) = default; \ + move_constructor_definition; \ } -_LIBCPP_VARIANT_MOVE_CONSTRUCTOR(_Trait::_TriviallyAvailable, - __move_constructor(__move_constructor&& __that) = default); +_LIBCPP_VARIANT_MOVE_CONSTRUCTOR( + _Trait::_TriviallyAvailable, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&& __that) = default); _LIBCPP_VARIANT_MOVE_CONSTRUCTOR( _Trait::_Available, - _LIBCPP_HIDE_FROM_ABI __move_constructor(__move_constructor&& __that) noexcept( + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&& __that) noexcept( __all...>::value) : __move_constructor(__valueless_t{}) { this->__generic_construct(*this, std::move(__that)); } _LIBCPP_EAT_SEMICOLON); -_LIBCPP_VARIANT_MOVE_CONSTRUCTOR(_Trait::_Unavailable, __move_constructor(__move_constructor&&) = delete); +_LIBCPP_VARIANT_MOVE_CONSTRUCTOR( + _Trait::_Unavailable, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&&) = delete); # undef _LIBCPP_VARIANT_MOVE_CONSTRUCTOR template class _LIBCPP_TEMPLATE_VIS __copy_constructor; -# define _LIBCPP_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, copy_constructor) \ +# define _LIBCPP_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, copy_constructor_definition) \ template \ class _LIBCPP_TEMPLATE_VIS __copy_constructor<__traits<_Types...>, copy_constructible_trait> \ : public __move_constructor<__traits<_Types...>> { \ @@ -881,21 +883,25 @@ class _LIBCPP_TEMPLATE_VIS __copy_constructor; using __base_type::__base_type; \ using __base_type::operator=; \ \ - __copy_constructor(__copy_constructor&&) = default; \ - ~__copy_constructor() = default; \ - __copy_constructor& operator=(const __copy_constructor&) = default; \ - __copy_constructor& operator=(__copy_constructor&&) = default; \ - copy_constructor; \ - } // namespace __variant_detail + _LIBCPP_HIDE_FROM_ABI __copy_constructor(__copy_constructor&&) = default; \ + _LIBCPP_HIDE_FROM_ABI ~__copy_constructor() = default; \ + _LIBCPP_HIDE_FROM_ABI __copy_constructor& operator=(const __copy_constructor&) = default; \ + _LIBCPP_HIDE_FROM_ABI __copy_constructor& operator=(__copy_constructor&&) = default; \ + copy_constructor_definition; \ + } -_LIBCPP_VARIANT_COPY_CONSTRUCTOR(_Trait::_TriviallyAvailable, - __copy_constructor(const __copy_constructor& __that) = default); +_LIBCPP_VARIANT_COPY_CONSTRUCTOR( + _Trait::_TriviallyAvailable, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that) = default); _LIBCPP_VARIANT_COPY_CONSTRUCTOR( - _Trait::_Available, _LIBCPP_HIDE_FROM_ABI __copy_constructor(const __copy_constructor& __that) + _Trait::_Available, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that) : __copy_constructor(__valueless_t{}) { this->__generic_construct(*this, __that); } _LIBCPP_EAT_SEMICOLON); -_LIBCPP_VARIANT_COPY_CONSTRUCTOR(_Trait::_Unavailable, __copy_constructor(const __copy_constructor&) = delete); +_LIBCPP_VARIANT_COPY_CONSTRUCTOR( + _Trait::_Unavailable, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor&) = delete); # undef _LIBCPP_VARIANT_COPY_CONSTRUCTOR @@ -908,22 +914,24 @@ public: using __base_type::operator=; template - _LIBCPP_HIDE_FROM_ABI auto& __emplace(_Args&&... __args) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto& __emplace(_Args&&... __args) { this->__destroy(); - auto& __res = this->__construct_alt(__access::__base::__get_alt<_Ip>(*this), std::forward<_Args>(__args)...); + std::__construct_at(std::addressof(this->__data), in_place_index<_Ip>, std::forward<_Args>(__args)...); this->__index = _Ip; - return __res; + return __access::__base::__get_alt<_Ip>(*this).__value; } protected: template - _LIBCPP_HIDE_FROM_ABI void __assign_alt(__alt<_Ip, _Tp>& __a, _Arg&& __arg) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_alt(__alt<_Ip, _Tp>& __a, _Arg&& __arg) { if (this->index() == _Ip) { __a.__value = std::forward<_Arg>(__arg); } else { struct { - _LIBCPP_HIDDEN void operator()(true_type) const { __this->__emplace<_Ip>(std::forward<_Arg>(__arg)); } - _LIBCPP_HIDDEN void operator()(false_type) const { + _LIBCPP_HIDDEN _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()(true_type) const { + __this->__emplace<_Ip>(std::forward<_Arg>(__arg)); + } + _LIBCPP_HIDDEN _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()(false_type) const { __this->__emplace<_Ip>(_Tp(std::forward<_Arg>(__arg))); } __assignment* __this; @@ -934,7 +942,7 @@ protected: } template - _LIBCPP_HIDE_FROM_ABI void __generic_assign(_That&& __that) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __generic_assign(_That&& __that) { if (this->valueless_by_exception() && __that.valueless_by_exception()) { // do nothing. } else if (__that.valueless_by_exception()) { @@ -954,7 +962,7 @@ protected: template class _LIBCPP_TEMPLATE_VIS __move_assignment; -# define _LIBCPP_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, move_assignment) \ +# define _LIBCPP_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, move_assignment_definition) \ template \ class _LIBCPP_TEMPLATE_VIS __move_assignment<__traits<_Types...>, move_assignable_trait> \ : public __assignment<__traits<_Types...>> { \ @@ -964,33 +972,36 @@ class _LIBCPP_TEMPLATE_VIS __move_assignment; using __base_type::__base_type; \ using __base_type::operator=; \ \ - __move_assignment(const __move_assignment&) = default; \ - __move_assignment(__move_assignment&&) = default; \ - ~__move_assignment() = default; \ - __move_assignment& operator=(const __move_assignment&) = default; \ - move_assignment; \ + _LIBCPP_HIDE_FROM_ABI __move_assignment(const __move_assignment&) = default; \ + _LIBCPP_HIDE_FROM_ABI __move_assignment(__move_assignment&&) = default; \ + _LIBCPP_HIDE_FROM_ABI ~__move_assignment() = default; \ + _LIBCPP_HIDE_FROM_ABI __move_assignment& operator=(const __move_assignment&) = default; \ + move_assignment_definition; \ } _LIBCPP_VARIANT_MOVE_ASSIGNMENT(_Trait::_TriviallyAvailable, - __move_assignment& operator=(__move_assignment&& __that) = default); + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_assignment& operator=( + __move_assignment&& __that) = default); _LIBCPP_VARIANT_MOVE_ASSIGNMENT( _Trait::_Available, - _LIBCPP_HIDE_FROM_ABI __move_assignment& + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_assignment& operator=(__move_assignment&& __that) noexcept( __all<(is_nothrow_move_constructible_v<_Types> && is_nothrow_move_assignable_v<_Types>)...>::value) { this->__generic_assign(std::move(__that)); return *this; } _LIBCPP_EAT_SEMICOLON); -_LIBCPP_VARIANT_MOVE_ASSIGNMENT(_Trait::_Unavailable, __move_assignment& operator=(__move_assignment&&) = delete); +_LIBCPP_VARIANT_MOVE_ASSIGNMENT( + _Trait::_Unavailable, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_assignment& operator=(__move_assignment&&) = delete); # undef _LIBCPP_VARIANT_MOVE_ASSIGNMENT template class _LIBCPP_TEMPLATE_VIS __copy_assignment; -# define _LIBCPP_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, copy_assignment) \ +# define _LIBCPP_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, copy_assignment_definition) \ template \ class _LIBCPP_TEMPLATE_VIS __copy_assignment<__traits<_Types...>, copy_assignable_trait> \ : public __move_assignment<__traits<_Types...>> { \ @@ -1000,23 +1011,28 @@ class _LIBCPP_TEMPLATE_VIS __copy_assignment; using __base_type::__base_type; \ using __base_type::operator=; \ \ - __copy_assignment(const __copy_assignment&) = default; \ - __copy_assignment(__copy_assignment&&) = default; \ - ~__copy_assignment() = default; \ - __copy_assignment& operator=(__copy_assignment&&) = default; \ - copy_assignment; \ + _LIBCPP_HIDE_FROM_ABI __copy_assignment(const __copy_assignment&) = default; \ + _LIBCPP_HIDE_FROM_ABI __copy_assignment(__copy_assignment&&) = default; \ + _LIBCPP_HIDE_FROM_ABI ~__copy_assignment() = default; \ + _LIBCPP_HIDE_FROM_ABI __copy_assignment& operator=(__copy_assignment&&) = default; \ + copy_assignment_definition; \ } _LIBCPP_VARIANT_COPY_ASSIGNMENT(_Trait::_TriviallyAvailable, - __copy_assignment& operator=(const __copy_assignment& __that) = default); + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_assignment& operator=( + const __copy_assignment& __that) = default); _LIBCPP_VARIANT_COPY_ASSIGNMENT( - _Trait::_Available, _LIBCPP_HIDE_FROM_ABI __copy_assignment& operator=(const __copy_assignment& __that) { + _Trait::_Available, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_assignment& + operator=(const __copy_assignment& __that) { this->__generic_assign(__that); return *this; } _LIBCPP_EAT_SEMICOLON); -_LIBCPP_VARIANT_COPY_ASSIGNMENT(_Trait::_Unavailable, __copy_assignment& operator=(const __copy_assignment&) = delete); +_LIBCPP_VARIANT_COPY_ASSIGNMENT(_Trait::_Unavailable, + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_assignment& operator=( + const __copy_assignment&) = delete); # undef _LIBCPP_VARIANT_COPY_ASSIGNMENT @@ -1032,11 +1048,11 @@ public: _LIBCPP_HIDE_FROM_ABI __impl& operator=(__impl&&) = default; template - _LIBCPP_HIDE_FROM_ABI void __assign(_Arg&& __arg) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign(_Arg&& __arg) { this->__assign_alt(__access::__base::__get_alt<_Ip>(*this), std::forward<_Arg>(__arg)); } - inline _LIBCPP_HIDE_FROM_ABI void __swap(__impl& __that) { + inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__impl& __that) { if (this->valueless_by_exception() && __that.valueless_by_exception()) { // do nothing. } else if (this->index() == __that.index()) { @@ -1081,7 +1097,7 @@ public: } private: - inline _LIBCPP_HIDE_FROM_ABI bool __move_nothrow() const { + constexpr inline _LIBCPP_HIDE_FROM_ABI bool __move_nothrow() const { constexpr bool __results[] = {is_nothrow_move_constructible_v<_Types>...}; return this->valueless_by_exception() || __results[this->index()]; } @@ -1223,7 +1239,7 @@ public: _Args&&... __args) noexcept(is_nothrow_constructible_v<_Tp, initializer_list< _Up>&, _Args...>) : __impl_(in_place_index<_Ip>, __il, std::forward<_Args>(__args)...) {} - _LIBCPP_HIDE_FROM_ABI ~variant() = default; + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~variant() = default; _LIBCPP_HIDE_FROM_ABI constexpr variant& operator=(const variant&) = default; _LIBCPP_HIDE_FROM_ABI constexpr variant& operator=(variant&&) = default; @@ -1233,7 +1249,7 @@ public: class _Tp = __variant_detail::__best_match_t<_Arg, _Types...>, size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value, enable_if_t && is_constructible_v<_Tp, _Arg>, int> = 0> - _LIBCPP_HIDE_FROM_ABI variant& + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 variant& operator=(_Arg&& __arg) noexcept(is_nothrow_assignable_v<_Tp&, _Arg> && is_nothrow_constructible_v<_Tp, _Arg>) { __impl_.template __assign<_Ip>(std::forward<_Arg>(__arg)); return *this; @@ -1244,7 +1260,7 @@ public: enable_if_t<(_Ip < sizeof...(_Types)), int> = 0, class _Tp = variant_alternative_t<_Ip, variant<_Types...>>, enable_if_t, int> = 0> - _LIBCPP_HIDE_FROM_ABI _Tp& emplace(_Args&&... __args) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) { return __impl_.template __emplace<_Ip>(std::forward<_Args>(__args)...); } @@ -1254,7 +1270,7 @@ public: enable_if_t<(_Ip < sizeof...(_Types)), int> = 0, class _Tp = variant_alternative_t<_Ip, variant<_Types...>>, enable_if_t&, _Args...>, int> = 0> - _LIBCPP_HIDE_FROM_ABI _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) { return __impl_.template __emplace<_Ip>(__il, std::forward<_Args>(__args)...); } @@ -1262,7 +1278,7 @@ public: class... _Args, size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value, enable_if_t, int> = 0> - _LIBCPP_HIDE_FROM_ABI _Tp& emplace(_Args&&... __args) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) { return __impl_.template __emplace<_Ip>(std::forward<_Args>(__args)...); } @@ -1271,7 +1287,7 @@ public: class... _Args, size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value, enable_if_t&, _Args...>, int> = 0> - _LIBCPP_HIDE_FROM_ABI _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) { return __impl_.template __emplace<_Ip>(__il, std::forward<_Args>(__args)...); } @@ -1285,7 +1301,7 @@ public: enable_if_t< __all<(__dependent_type, _Dummy>::value && __dependent_type, _Dummy>::value)...>::value, int> = 0> - _LIBCPP_HIDE_FROM_ABI void swap(variant& __that) noexcept( + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(variant& __that) noexcept( __all<(is_nothrow_move_constructible_v<_Types> && is_nothrow_swappable_v<_Types>)...>::value) { __impl_.__swap(__that.__impl_); } @@ -1568,7 +1584,7 @@ visit(_Visitor&& __visitor, _Vs&&... __vs) { # endif template -_LIBCPP_HIDE_FROM_ABI auto +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs))) -> decltype(__lhs.swap(__rhs)) { return __lhs.swap(__rhs); diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp index 4b9eaba2d2ba8..98faf84fa52d4 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp @@ -33,17 +33,17 @@ struct Dummy { struct ThrowsCtorT { ThrowsCtorT(int) noexcept(false) {} - ThrowsCtorT &operator=(int) noexcept { return *this; } + ThrowsCtorT& operator=(int) noexcept { return *this; } }; struct ThrowsAssignT { ThrowsAssignT(int) noexcept {} - ThrowsAssignT &operator=(int) noexcept(false) { return *this; } + ThrowsAssignT& operator=(int) noexcept(false) { return *this; } }; struct NoThrowT { NoThrowT(int) noexcept {} - NoThrowT &operator=(int) noexcept { return *this; } + NoThrowT& operator=(int) noexcept { return *this; } }; } // namespace MetaHelpers @@ -55,7 +55,7 @@ struct ThrowsCtorT { int value; ThrowsCtorT() : value(0) {} ThrowsCtorT(int) noexcept(false) { throw 42; } - ThrowsCtorT &operator=(int v) noexcept { + ThrowsCtorT& operator=(int v) noexcept { value = v; return *this; } @@ -64,9 +64,12 @@ struct ThrowsCtorT { struct MoveCrashes { int value; MoveCrashes(int v = 0) noexcept : value{v} {} - MoveCrashes(MoveCrashes &&) noexcept { assert(false); } - MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; } - MoveCrashes &operator=(int v) noexcept { + MoveCrashes(MoveCrashes&&) noexcept { assert(false); } + MoveCrashes& operator=(MoveCrashes&&) noexcept { + assert(false); + return *this; + } + MoveCrashes& operator=(int v) noexcept { value = v; return *this; } @@ -76,8 +79,8 @@ struct ThrowsCtorTandMove { int value; ThrowsCtorTandMove() : value(0) {} ThrowsCtorTandMove(int) noexcept(false) { throw 42; } - ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); } - ThrowsCtorTandMove &operator=(int v) noexcept { + ThrowsCtorTandMove(ThrowsCtorTandMove&&) noexcept(false) { assert(false); } + ThrowsCtorTandMove& operator=(int v) noexcept { value = v; return *this; } @@ -87,14 +90,14 @@ struct ThrowsAssignT { int value; ThrowsAssignT() : value(0) {} ThrowsAssignT(int v) noexcept : value(v) {} - ThrowsAssignT &operator=(int) noexcept(false) { throw 42; } + ThrowsAssignT& operator=(int) noexcept(false) { throw 42; } }; struct NoThrowT { int value; NoThrowT() : value(0) {} NoThrowT(int v) noexcept : value(v) {} - NoThrowT &operator=(int v) noexcept { + NoThrowT& operator=(int v) noexcept { value = v; return *this; } @@ -103,7 +106,7 @@ struct NoThrowT { #endif // !defined(TEST_HAS_NO_EXCEPTIONS) } // namespace RuntimeHelpers -void test_T_assignment_noexcept() { +constexpr void test_T_assignment_noexcept() { using namespace MetaHelpers; { using V = std::variant; @@ -119,17 +122,17 @@ void test_T_assignment_noexcept() { } } -void test_T_assignment_sfinae() { +constexpr void test_T_assignment_sfinae() { { using V = std::variant; static_assert(!std::is_assignable::value, "ambiguous"); } { using V = std::variant; - static_assert(!std::is_assignable::value, "ambiguous"); + static_assert(!std::is_assignable::value, "ambiguous"); } { - using V = std::variant; + using V = std::variant; static_assert(!std::is_assignable::value, "no matching operator="); } { @@ -138,8 +141,7 @@ void test_T_assignment_sfinae() { } { using V = std::variant, bool>; - static_assert(!std::is_assignable>::value, - "no explicit bool in operator="); + static_assert(!std::is_assignable>::value, "no explicit bool in operator="); struct X { operator void*(); }; @@ -152,12 +154,11 @@ void test_T_assignment_sfinae() { operator X(); }; using V = std::variant; - static_assert(std::is_assignable::value, - "regression on user-defined conversions in operator="); + static_assert(std::is_assignable::value, "regression on user-defined conversions in operator="); } } -void test_T_assignment_basic() { +TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() { { std::variant v(43); v = 42; @@ -184,19 +185,146 @@ void test_T_assignment_basic() { } { std::variant v = true; - v = "bar"; + v = "bar"; assert(v.index() == 0); assert(std::get<0>(v) == "bar"); } +} + +void test_T_assignment_basic_no_constexpr() { + std::variant> v; + v = nullptr; + assert(v.index() == 1); + assert(std::get<1>(v) == nullptr); +} + +struct TraceStat { + int construct = 0; + int copy_construct = 0; + int copy_assign = 0; + int move_construct = 0; + int move_assign = 0; + int T_copy_assign = 0; + int T_move_assign = 0; + int destroy = 0; +}; + +template +struct Trace { + struct T {}; + + constexpr Trace(TraceStat* s) noexcept(CtorNoexcept) : stat(s) { ++s->construct; } + constexpr Trace(T) noexcept(CtorNoexcept) : stat(nullptr) {} + constexpr Trace(const Trace& o) : stat(o.stat) { ++stat->copy_construct; } + constexpr Trace(Trace&& o) noexcept(MoveCtorNoexcept) : stat(o.stat) { ++stat->move_construct; } + constexpr Trace& operator=(const Trace&) { + ++stat->copy_assign; + return *this; + } + constexpr Trace& operator=(Trace&&) noexcept { + ++stat->move_assign; + return *this; + } + + constexpr Trace& operator=(const T&) { + ++stat->T_copy_assign; + return *this; + } + constexpr Trace& operator=(T&&) noexcept { + ++stat->T_move_assign; + return *this; + } + TEST_CONSTEXPR_CXX20 ~Trace() { ++stat->destroy; } + + TraceStat* stat; +}; + +TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_construction() { { - std::variant> v; - v = nullptr; - assert(v.index() == 1); - assert(std::get<1>(v) == nullptr); + using V = std::variant>; + TraceStat stat; + V v{1}; + v = &stat; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.destroy == 0); + } + { + using V = std::variant>; + TraceStat stat; + V v{1}; + v = &stat; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 1); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.destroy == 1); + } + + { + using V = std::variant>; + TraceStat stat; + V v{1}; + v = &stat; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.destroy == 0); + } + + { + using V = std::variant>; + TraceStat stat; + V v{1}; + v = &stat; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.destroy == 0); } } -void test_T_assignment_performs_construction() { +TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_assignment() { + { + using V = std::variant>; + TraceStat stat; + V v{&stat}; + v = Trace::T{}; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.T_copy_assign == 0); + assert(stat.T_move_assign == 1); + assert(stat.destroy == 0); + } + { + using V = std::variant>; + TraceStat stat; + V v{&stat}; + Trace::T t; + v = t; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.T_copy_assign == 1); + assert(stat.T_move_assign == 0); + assert(stat.destroy == 0); + } +} + +void test_T_assignment_performs_construction_throw() { using namespace RuntimeHelpers; #ifndef TEST_HAS_NO_EXCEPTIONS { @@ -220,7 +348,7 @@ void test_T_assignment_performs_construction() { #endif // TEST_HAS_NO_EXCEPTIONS } -void test_T_assignment_performs_assignment() { +void test_T_assignment_performs_assignment_throw() { using namespace RuntimeHelpers; #ifndef TEST_HAS_NO_EXCEPTIONS { @@ -262,7 +390,7 @@ void test_T_assignment_performs_assignment() { #endif // TEST_HAS_NO_EXCEPTIONS } -void test_T_assignment_vector_bool() { +TEST_CONSTEXPR_CXX20 void test_T_assignment_vector_bool() { std::vector vec = {true}; std::variant v; v = vec[0]; @@ -270,7 +398,13 @@ void test_T_assignment_vector_bool() { assert(std::get<0>(v) == true); } -int main(int, char**) { +void non_constexpr_test() { + test_T_assignment_basic_no_constexpr(); + test_T_assignment_performs_construction_throw(); + test_T_assignment_performs_assignment_throw(); +} + +TEST_CONSTEXPR_CXX20 bool test() { test_T_assignment_basic(); test_T_assignment_performs_construction(); test_T_assignment_performs_assignment(); @@ -278,5 +412,15 @@ int main(int, char**) { test_T_assignment_sfinae(); test_T_assignment_vector_bool(); + return true; +} + +int main(int, char**) { + test(); + non_constexpr_test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp index 096d365d2d752..a6d3f34114eb7 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp @@ -22,88 +22,108 @@ #include "test_macros.h" struct NoCopy { - NoCopy(const NoCopy &) = delete; - NoCopy &operator=(const NoCopy &) = default; + NoCopy(const NoCopy&) = delete; + NoCopy& operator=(const NoCopy&) = default; }; struct CopyOnly { - CopyOnly(const CopyOnly &) = default; - CopyOnly(CopyOnly &&) = delete; - CopyOnly &operator=(const CopyOnly &) = default; - CopyOnly &operator=(CopyOnly &&) = delete; + CopyOnly(const CopyOnly&) = default; + CopyOnly(CopyOnly&&) = delete; + CopyOnly& operator=(const CopyOnly&) = default; + CopyOnly& operator=(CopyOnly&&) = delete; }; struct MoveOnly { - MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly &&) = default; - MoveOnly &operator=(const MoveOnly &) = default; + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(const MoveOnly&) = default; }; struct MoveOnlyNT { - MoveOnlyNT(const MoveOnlyNT &) = delete; - MoveOnlyNT(MoveOnlyNT &&) {} - MoveOnlyNT &operator=(const MoveOnlyNT &) = default; + MoveOnlyNT(const MoveOnlyNT&) = delete; + MoveOnlyNT(MoveOnlyNT&&) {} + MoveOnlyNT& operator=(const MoveOnlyNT&) = default; }; struct CopyAssign { - static int alive; - static int copy_construct; - static int copy_assign; - static int move_construct; - static int move_assign; - static void reset() { - copy_construct = copy_assign = move_construct = move_assign = alive = 0; - } - CopyAssign(int v) : value(v) { ++alive; } - CopyAssign(const CopyAssign &o) : value(o.value) { - ++alive; - ++copy_construct; - } - CopyAssign(CopyAssign &&o) noexcept : value(o.value) { + constexpr CopyAssign(int v, int* alv, int* cpy_ctr, int* cpy_assi, int* move_ctr, int* move_assi) + : value(v), + alive(alv), + copy_construct(cpy_ctr), + copy_assign(cpy_assi), + move_construct(move_ctr), + move_assign(move_assi) { + ++*alive; + } + constexpr CopyAssign(const CopyAssign& o) + : value(o.value), + alive(o.alive), + copy_construct(o.copy_construct), + copy_assign(o.copy_assign), + move_construct(o.move_construct), + move_assign(o.move_assign) { + ++*alive; + ++*copy_construct; + } + constexpr CopyAssign(CopyAssign&& o) noexcept + : value(o.value), + alive(o.alive), + copy_construct(o.copy_construct), + copy_assign(o.copy_assign), + move_construct(o.move_construct), + move_assign(o.move_assign) { o.value = -1; - ++alive; - ++move_construct; - } - CopyAssign &operator=(const CopyAssign &o) { - value = o.value; - ++copy_assign; + ++*alive; + ++*move_construct; + } + constexpr CopyAssign& operator=(const CopyAssign& o) { + value = o.value; + alive = o.alive; + copy_construct = o.copy_construct; + copy_assign = o.copy_assign; + move_construct = o.move_construct; + move_assign = o.move_assign; + ++*copy_assign; return *this; } - CopyAssign &operator=(CopyAssign &&o) noexcept { - value = o.value; - o.value = -1; - ++move_assign; + constexpr CopyAssign& operator=(CopyAssign&& o) noexcept { + value = o.value; + alive = o.alive; + copy_construct = o.copy_construct; + copy_assign = o.copy_assign; + move_construct = o.move_construct; + move_assign = o.move_assign; + o.value = -1; + ++*move_assign; return *this; } - ~CopyAssign() { --alive; } + TEST_CONSTEXPR_CXX20 ~CopyAssign() { --*alive; } int value; + int* alive; + int* copy_construct; + int* copy_assign; + int* move_construct; + int* move_assign; }; -int CopyAssign::alive = 0; -int CopyAssign::copy_construct = 0; -int CopyAssign::copy_assign = 0; -int CopyAssign::move_construct = 0; -int CopyAssign::move_assign = 0; - struct CopyMaybeThrows { - CopyMaybeThrows(const CopyMaybeThrows &); - CopyMaybeThrows &operator=(const CopyMaybeThrows &); + CopyMaybeThrows(const CopyMaybeThrows&); + CopyMaybeThrows& operator=(const CopyMaybeThrows&); }; struct CopyDoesThrow { - CopyDoesThrow(const CopyDoesThrow &) noexcept(false); - CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false); + CopyDoesThrow(const CopyDoesThrow&) noexcept(false); + CopyDoesThrow& operator=(const CopyDoesThrow&) noexcept(false); }; - struct NTCopyAssign { constexpr NTCopyAssign(int v) : value(v) {} - NTCopyAssign(const NTCopyAssign &) = default; - NTCopyAssign(NTCopyAssign &&) = default; - NTCopyAssign &operator=(const NTCopyAssign &that) { + NTCopyAssign(const NTCopyAssign&) = default; + NTCopyAssign(NTCopyAssign&&) = default; + NTCopyAssign& operator=(const NTCopyAssign& that) { value = that.value; return *this; }; - NTCopyAssign &operator=(NTCopyAssign &&) = delete; + NTCopyAssign& operator=(NTCopyAssign&&) = delete; int value; }; @@ -112,10 +132,10 @@ static_assert(std::is_copy_assignable::value, ""); struct TCopyAssign { constexpr TCopyAssign(int v) : value(v) {} - TCopyAssign(const TCopyAssign &) = default; - TCopyAssign(TCopyAssign &&) = default; - TCopyAssign &operator=(const TCopyAssign &) = default; - TCopyAssign &operator=(TCopyAssign &&) = delete; + TCopyAssign(const TCopyAssign&) = default; + TCopyAssign(TCopyAssign&&) = default; + TCopyAssign& operator=(const TCopyAssign&) = default; + TCopyAssign& operator=(TCopyAssign&&) = delete; int value; }; @@ -123,11 +143,11 @@ static_assert(std::is_trivially_copy_assignable::value, ""); struct TCopyAssignNTMoveAssign { constexpr TCopyAssignNTMoveAssign(int v) : value(v) {} - TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default; - TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default; - TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default; - TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) { - value = that.value; + TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign&) = default; + TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign&&) = default; + TCopyAssignNTMoveAssign& operator=(const TCopyAssignNTMoveAssign&) = default; + TCopyAssignNTMoveAssign& operator=(TCopyAssignNTMoveAssign&& that) { + value = that.value; that.value = -1; return *this; } @@ -139,17 +159,20 @@ static_assert(std::is_trivially_copy_assignable_v, ""); #ifndef TEST_HAS_NO_EXCEPTIONS struct CopyThrows { CopyThrows() = default; - CopyThrows(const CopyThrows &) { throw 42; } - CopyThrows &operator=(const CopyThrows &) { throw 42; } + CopyThrows(const CopyThrows&) { throw 42; } + CopyThrows& operator=(const CopyThrows&) { throw 42; } }; struct CopyCannotThrow { static int alive; CopyCannotThrow() { ++alive; } - CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; } - CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); } - CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default; - CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; } + CopyCannotThrow(const CopyCannotThrow&) noexcept { ++alive; } + CopyCannotThrow(CopyCannotThrow&&) noexcept { assert(false); } + CopyCannotThrow& operator=(const CopyCannotThrow&) noexcept = default; + CopyCannotThrow& operator=(CopyCannotThrow&&) noexcept { + assert(false); + return *this; + } }; int CopyCannotThrow::alive = 0; @@ -157,10 +180,10 @@ int CopyCannotThrow::alive = 0; struct MoveThrows { static int alive; MoveThrows() { ++alive; } - MoveThrows(const MoveThrows &) { ++alive; } - MoveThrows(MoveThrows &&) { throw 42; } - MoveThrows &operator=(const MoveThrows &) { return *this; } - MoveThrows &operator=(MoveThrows &&) { throw 42; } + MoveThrows(const MoveThrows&) { ++alive; } + MoveThrows(MoveThrows&&) { throw 42; } + MoveThrows& operator=(const MoveThrows&) { return *this; } + MoveThrows& operator=(MoveThrows&&) { throw 42; } ~MoveThrows() { --alive; } }; @@ -169,20 +192,21 @@ int MoveThrows::alive = 0; struct MakeEmptyT { static int alive; MakeEmptyT() { ++alive; } - MakeEmptyT(const MakeEmptyT &) { + MakeEmptyT(const MakeEmptyT&) { ++alive; // Don't throw from the copy constructor since variant's assignment // operator performs a copy before committing to the assignment. } - MakeEmptyT(MakeEmptyT &&) { throw 42; } - MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; } - MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + MakeEmptyT(MakeEmptyT&&) { throw 42; } + MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; } + MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; } ~MakeEmptyT() { --alive; } }; int MakeEmptyT::alive = 0; -template void makeEmpty(Variant &v) { +template +void makeEmpty(Variant& v) { Variant v2(std::in_place_type); try { v = std::move(v2); @@ -193,7 +217,7 @@ template void makeEmpty(Variant &v) { } #endif // TEST_HAS_NO_EXCEPTIONS -void test_copy_assignment_not_noexcept() { +constexpr void test_copy_assignment_not_noexcept() { { using V = std::variant; static_assert(!std::is_nothrow_copy_assignable::value, ""); @@ -204,7 +228,7 @@ void test_copy_assignment_not_noexcept() { } } -void test_copy_assignment_sfinae() { +constexpr void test_copy_assignment_sfinae() { { using V = std::variant; static_assert(std::is_copy_assignable::value, ""); @@ -259,7 +283,7 @@ void test_copy_assignment_empty_empty() { makeEmpty(v1); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -275,7 +299,7 @@ void test_copy_assignment_non_empty_empty() { V v1(std::in_place_index<0>, 42); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -285,7 +309,7 @@ void test_copy_assignment_non_empty_empty() { V v1(std::in_place_index<2>, "hello"); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -301,7 +325,7 @@ void test_copy_assignment_empty_non_empty() { V v1(std::in_place_index<0>); makeEmpty(v1); V v2(std::in_place_index<0>, 42); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 0); assert(std::get<0>(v1) == 42); @@ -311,7 +335,7 @@ void test_copy_assignment_empty_non_empty() { V v1(std::in_place_index<0>); makeEmpty(v1); V v2(std::in_place_type, "hello"); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); @@ -319,14 +343,18 @@ void test_copy_assignment_empty_non_empty() { #endif // TEST_HAS_NO_EXCEPTIONS } -template struct Result { std::size_t index; T value; }; +template +struct Result { + std::size_t index; + T value; +}; -void test_copy_assignment_same_index() { +TEST_CONSTEXPR_CXX20 void test_copy_assignment_same_index() { { using V = std::variant; V v1(43); V v2(42); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 0); assert(std::get<0>(v1) == 42); @@ -335,40 +363,28 @@ void test_copy_assignment_same_index() { using V = std::variant; V v1(43l); V v2(42l); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1) == 42); } { - using V = std::variant; - V v1(std::in_place_type, 43); - V v2(std::in_place_type, 42); - CopyAssign::reset(); - V &vref = (v1 = v2); + using V = std::variant; + int alive = 0; + int copy_construct = 0; + int copy_assign = 0; + int move_construct = 0; + int move_assign = 0; + V v1(std::in_place_type, 43, &alive, ©_construct, ©_assign, &move_construct, &move_assign); + V v2(std::in_place_type, 42, &alive, ©_construct, ©_assign, &move_construct, &move_assign); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1).value == 42); - assert(CopyAssign::copy_construct == 0); - assert(CopyAssign::move_construct == 0); - assert(CopyAssign::copy_assign == 1); + assert(copy_construct == 0); + assert(move_construct == 0); + assert(copy_assign == 1); } -#ifndef TEST_HAS_NO_EXCEPTIONS - using MET = MakeEmptyT; - { - using V = std::variant; - V v1(std::in_place_type); - MET &mref = std::get<1>(v1); - V v2(std::in_place_type); - try { - v1 = v2; - assert(false); - } catch (...) { - } - assert(v1.index() == 1); - assert(&std::get<1>(v1) == &mref); - } -#endif // TEST_HAS_NO_EXCEPTIONS // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). { @@ -429,34 +445,88 @@ void test_copy_assignment_same_index() { } } -void test_copy_assignment_different_index() { +TEST_CONSTEXPR_CXX20 void test_copy_assignment_different_index() { { using V = std::variant; V v1(43); V v2(42l); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1) == 42); } { - using V = std::variant; - CopyAssign::reset(); + using V = std::variant; + int alive = 0; + int copy_construct = 0; + int copy_assign = 0; + int move_construct = 0; + int move_assign = 0; V v1(std::in_place_type, 43u); - V v2(std::in_place_type, 42); - assert(CopyAssign::copy_construct == 0); - assert(CopyAssign::move_construct == 0); - assert(CopyAssign::alive == 1); - V &vref = (v1 = v2); + V v2(std::in_place_type, 42, &alive, ©_construct, ©_assign, &move_construct, &move_assign); + assert(copy_construct == 0); + assert(move_construct == 0); + assert(alive == 1); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1).value == 42); - assert(CopyAssign::alive == 2); - assert(CopyAssign::copy_construct == 1); - assert(CopyAssign::move_construct == 1); - assert(CopyAssign::copy_assign == 0); + assert(alive == 2); + assert(copy_construct == 1); + assert(move_construct == 1); + assert(copy_assign == 0); + } + + // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). + { + struct { + constexpr Result operator()() const { + using V = std::variant; + V v(43); + V v2(42l); + v = v2; + return {v.index(), std::get<1>(v)}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42l, ""); } + { + struct { + constexpr Result operator()() const { + using V = std::variant; + V v(std::in_place_type, 43u); + V v2(std::in_place_type, 42); + v = v2; + return {v.index(), std::get<1>(v).value}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42, ""); + } +} + +void test_assignment_throw() { #ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + // same index + { + using V = std::variant; + V v1(std::in_place_type); + MET& mref = std::get<1>(v1); + V v2(std::in_place_type); + try { + v1 = v2; + assert(false); + } catch (...) { + } + assert(v1.index() == 1); + assert(&std::get<1>(v1) == &mref); + } + + // difference indices { using V = std::variant; V v1(std::in_place_type, "hello"); @@ -496,7 +566,7 @@ void test_copy_assignment_different_index() { using V = std::variant; V v1(std::in_place_type); V v2(std::in_place_type, "hello"); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); @@ -507,7 +577,7 @@ void test_copy_assignment_different_index() { using V = std::variant; V v1(std::in_place_type); V v2(std::in_place_type, "hello"); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); @@ -515,69 +585,83 @@ void test_copy_assignment_different_index() { assert(std::get<2>(v2) == "hello"); } #endif // TEST_HAS_NO_EXCEPTIONS - - // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). - { - struct { - constexpr Result operator()() const { - using V = std::variant; - V v(43); - V v2(42l); - v = v2; - return {v.index(), std::get<1>(v)}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42l, ""); - } - { - struct { - constexpr Result operator()() const { - using V = std::variant; - V v(std::in_place_type, 43u); - V v2(std::in_place_type, 42); - v = v2; - return {v.index(), std::get<1>(v).value}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42, ""); - } } -template -constexpr bool test_constexpr_assign_imp( - std::variant&& v, ValueType&& new_value) -{ - const std::variant cp( - std::forward(new_value)); +template +constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) { + using Variant = std::decay_t; + const Variant cp(std::forward(new_value)); v = cp; - return v.index() == NewIdx && - std::get(v) == std::get(cp); + assert(v.index() == NewIdx); + assert(std::get(v) == std::get(cp)); } -void test_constexpr_copy_assignment() { +constexpr void test_constexpr_copy_assignment_trivial() { // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). using V = std::variant; static_assert(std::is_trivially_copyable::value, ""); static_assert(std::is_trivially_copy_assignable::value, ""); - static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), ""); - static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), ""); - static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), ""); - static_assert(test_constexpr_assign_imp<2>(V(42l), 101), ""); + test_constexpr_assign_imp<0>(V(42l), 101l); + test_constexpr_assign_imp<0>(V(nullptr), 101l); + test_constexpr_assign_imp<1>(V(42l), nullptr); + test_constexpr_assign_imp<2>(V(42l), 101); } -int main(int, char**) { +struct NonTrivialCopyAssign { + int i = 0; + constexpr NonTrivialCopyAssign(int ii) : i(ii) {} + constexpr NonTrivialCopyAssign(const NonTrivialCopyAssign& other) : i(other.i) {} + constexpr NonTrivialCopyAssign& operator=(const NonTrivialCopyAssign& o) { + i = o.i; + return *this; + } + TEST_CONSTEXPR_CXX20 ~NonTrivialCopyAssign() = default; + friend constexpr bool operator==(const NonTrivialCopyAssign& x, const NonTrivialCopyAssign& y) { return x.i == y.i; } +}; + +constexpr void test_constexpr_copy_assignment_non_trivial() { + // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). + using V = std::variant; + static_assert(!std::is_trivially_copyable::value, ""); + static_assert(!std::is_trivially_copy_assignable::value, ""); + test_constexpr_assign_imp<0>(V(42l), 101l); + test_constexpr_assign_imp<0>(V(nullptr), 101l); + test_constexpr_assign_imp<1>(V(42l), nullptr); + test_constexpr_assign_imp<2>(V(42l), NonTrivialCopyAssign(5)); + test_constexpr_assign_imp<2>(V(NonTrivialCopyAssign(3)), NonTrivialCopyAssign(5)); +} + +void non_constexpr_test() { test_copy_assignment_empty_empty(); test_copy_assignment_non_empty_empty(); test_copy_assignment_empty_non_empty(); - test_copy_assignment_same_index(); - test_copy_assignment_different_index(); + test_assignment_throw(); +} + +constexpr bool cxx17_constexpr_test() { test_copy_assignment_sfinae(); test_copy_assignment_not_noexcept(); - test_constexpr_copy_assignment(); + test_constexpr_copy_assignment_trivial(); + return true; +} + +TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() { + test_copy_assignment_same_index(); + test_copy_assignment_different_index(); + test_constexpr_copy_assignment_non_trivial(); + + return true; +} + +int main(int, char**) { + non_constexpr_test(); + cxx17_constexpr_test(); + cxx20_constexpr_test(); + + static_assert(cxx17_constexpr_test()); +#if TEST_STD_VER >= 20 + static_assert(cxx20_constexpr_test()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp index 84094347aed3a..157ff68f37482 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp @@ -24,71 +24,70 @@ #include "variant_test_helpers.h" struct NoCopy { - NoCopy(const NoCopy &) = delete; - NoCopy &operator=(const NoCopy &) = default; + NoCopy(const NoCopy&) = delete; + NoCopy& operator=(const NoCopy&) = default; }; struct CopyOnly { - CopyOnly(const CopyOnly &) = default; - CopyOnly(CopyOnly &&) = delete; - CopyOnly &operator=(const CopyOnly &) = default; - CopyOnly &operator=(CopyOnly &&) = delete; + CopyOnly(const CopyOnly&) = default; + CopyOnly(CopyOnly&&) = delete; + CopyOnly& operator=(const CopyOnly&) = default; + CopyOnly& operator=(CopyOnly&&) = delete; }; struct MoveOnly { - MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly &&) = default; - MoveOnly &operator=(const MoveOnly &) = delete; - MoveOnly &operator=(MoveOnly &&) = default; + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(const MoveOnly&) = delete; + MoveOnly& operator=(MoveOnly&&) = default; }; struct MoveOnlyNT { - MoveOnlyNT(const MoveOnlyNT &) = delete; - MoveOnlyNT(MoveOnlyNT &&) {} - MoveOnlyNT &operator=(const MoveOnlyNT &) = delete; - MoveOnlyNT &operator=(MoveOnlyNT &&) = default; + MoveOnlyNT(const MoveOnlyNT&) = delete; + MoveOnlyNT(MoveOnlyNT&&) {} + MoveOnlyNT& operator=(const MoveOnlyNT&) = delete; + MoveOnlyNT& operator=(MoveOnlyNT&&) = default; }; struct MoveOnlyOddNothrow { - MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {} - MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete; - MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default; - MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete; + MoveOnlyOddNothrow(MoveOnlyOddNothrow&&) noexcept(false) {} + MoveOnlyOddNothrow(const MoveOnlyOddNothrow&) = delete; + MoveOnlyOddNothrow& operator=(MoveOnlyOddNothrow&&) noexcept = default; + MoveOnlyOddNothrow& operator=(const MoveOnlyOddNothrow&) = delete; }; struct MoveAssignOnly { - MoveAssignOnly(MoveAssignOnly &&) = delete; - MoveAssignOnly &operator=(MoveAssignOnly &&) = default; + MoveAssignOnly(MoveAssignOnly&&) = delete; + MoveAssignOnly& operator=(MoveAssignOnly&&) = default; }; struct MoveAssign { - static int move_construct; - static int move_assign; - static void reset() { move_construct = move_assign = 0; } - MoveAssign(int v) : value(v) {} - MoveAssign(MoveAssign &&o) : value(o.value) { - ++move_construct; + constexpr MoveAssign(int v, int* move_ctor, int* move_assi) + : value(v), move_construct(move_ctor), move_assign(move_assi) {} + constexpr MoveAssign(MoveAssign&& o) : value(o.value), move_construct(o.move_construct), move_assign(o.move_assign) { + ++*move_construct; o.value = -1; } - MoveAssign &operator=(MoveAssign &&o) { - value = o.value; - ++move_assign; + constexpr MoveAssign& operator=(MoveAssign&& o) { + value = o.value; + move_construct = o.move_construct; + move_assign = o.move_assign; + ++*move_assign; o.value = -1; return *this; } int value; + int* move_construct; + int* move_assign; }; -int MoveAssign::move_construct = 0; -int MoveAssign::move_assign = 0; - struct NTMoveAssign { constexpr NTMoveAssign(int v) : value(v) {} - NTMoveAssign(const NTMoveAssign &) = default; - NTMoveAssign(NTMoveAssign &&) = default; - NTMoveAssign &operator=(const NTMoveAssign &that) = default; - NTMoveAssign &operator=(NTMoveAssign &&that) { - value = that.value; + NTMoveAssign(const NTMoveAssign&) = default; + NTMoveAssign(NTMoveAssign&&) = default; + NTMoveAssign& operator=(const NTMoveAssign& that) = default; + NTMoveAssign& operator=(NTMoveAssign&& that) { + value = that.value; that.value = -1; return *this; }; @@ -100,10 +99,10 @@ static_assert(std::is_move_assignable::value, ""); struct TMoveAssign { constexpr TMoveAssign(int v) : value(v) {} - TMoveAssign(const TMoveAssign &) = delete; - TMoveAssign(TMoveAssign &&) = default; - TMoveAssign &operator=(const TMoveAssign &) = delete; - TMoveAssign &operator=(TMoveAssign &&) = default; + TMoveAssign(const TMoveAssign&) = delete; + TMoveAssign(TMoveAssign&&) = default; + TMoveAssign& operator=(const TMoveAssign&) = delete; + TMoveAssign& operator=(TMoveAssign&&) = default; int value; }; @@ -111,13 +110,13 @@ static_assert(std::is_trivially_move_assignable::value, ""); struct TMoveAssignNTCopyAssign { constexpr TMoveAssignNTCopyAssign(int v) : value(v) {} - TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default; - TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default; - TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) { + TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign&) = default; + TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign&&) = default; + TMoveAssignNTCopyAssign& operator=(const TMoveAssignNTCopyAssign& that) { value = that.value; return *this; } - TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default; + TMoveAssignNTCopyAssign& operator=(TMoveAssignNTCopyAssign&&) = default; int value; }; @@ -127,16 +126,13 @@ struct TrivialCopyNontrivialMove { TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default; TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {} TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default; - TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { - return *this; - } + TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { return *this; } }; static_assert(std::is_trivially_copy_assignable_v, ""); static_assert(!std::is_trivially_move_assignable_v, ""); - -void test_move_assignment_noexcept() { +constexpr void test_move_assignment_noexcept() { { using V = std::variant; static_assert(std::is_nothrow_move_assignable::value, ""); @@ -163,7 +159,7 @@ void test_move_assignment_noexcept() { } } -void test_move_assignment_sfinae() { +constexpr void test_move_assignment_sfinae() { { using V = std::variant; static_assert(std::is_move_assignable::value, ""); @@ -228,7 +224,7 @@ void test_move_assignment_empty_empty() { makeEmpty(v1); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -244,7 +240,7 @@ void test_move_assignment_non_empty_empty() { V v1(std::in_place_index<0>, 42); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -254,7 +250,7 @@ void test_move_assignment_non_empty_empty() { V v1(std::in_place_index<2>, "hello"); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -270,7 +266,7 @@ void test_move_assignment_empty_non_empty() { V v1(std::in_place_index<0>); makeEmpty(v1); V v2(std::in_place_index<0>, 42); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 0); assert(std::get<0>(v1) == 42); @@ -280,7 +276,7 @@ void test_move_assignment_empty_non_empty() { V v1(std::in_place_index<0>); makeEmpty(v1); V v2(std::in_place_type, "hello"); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); @@ -288,14 +284,18 @@ void test_move_assignment_empty_non_empty() { #endif // TEST_HAS_NO_EXCEPTIONS } -template struct Result { std::size_t index; T value; }; +template +struct Result { + std::size_t index; + T value; +}; -void test_move_assignment_same_index() { +TEST_CONSTEXPR_CXX20 void test_move_assignment_same_index() { { using V = std::variant; V v1(43); V v2(42); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 0); assert(std::get<0>(v1) == 42); @@ -304,39 +304,24 @@ void test_move_assignment_same_index() { using V = std::variant; V v1(43l); V v2(42l); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1) == 42); } { - using V = std::variant; - V v1(std::in_place_type, 43); - V v2(std::in_place_type, 42); - MoveAssign::reset(); - V &vref = (v1 = std::move(v2)); + using V = std::variant; + int move_construct = 0; + int move_assign = 0; + V v1(std::in_place_type, 43, &move_construct, &move_assign); + V v2(std::in_place_type, 42, &move_construct, &move_assign); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1).value == 42); - assert(MoveAssign::move_construct == 0); - assert(MoveAssign::move_assign == 1); - } -#ifndef TEST_HAS_NO_EXCEPTIONS - using MET = MakeEmptyT; - { - using V = std::variant; - V v1(std::in_place_type); - MET &mref = std::get<1>(v1); - V v2(std::in_place_type); - try { - v1 = std::move(v2); - assert(false); - } catch (...) { - } - assert(v1.index() == 1); - assert(&std::get<1>(v1) == &mref); + assert(move_construct == 0); + assert(move_assign == 1); } -#endif // TEST_HAS_NO_EXCEPTIONS // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). { @@ -383,52 +368,29 @@ void test_move_assignment_same_index() { } } -void test_move_assignment_different_index() { +TEST_CONSTEXPR_CXX20 void test_move_assignment_different_index() { { using V = std::variant; V v1(43); V v2(42l); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1) == 42); } { - using V = std::variant; + using V = std::variant; + int move_construct = 0; + int move_assign = 0; V v1(std::in_place_type, 43u); - V v2(std::in_place_type, 42); - MoveAssign::reset(); - V &vref = (v1 = std::move(v2)); + V v2(std::in_place_type, 42, &move_construct, &move_assign); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1).value == 42); - assert(MoveAssign::move_construct == 1); - assert(MoveAssign::move_assign == 0); + assert(move_construct == 1); + assert(move_assign == 0); } -#ifndef TEST_HAS_NO_EXCEPTIONS - using MET = MakeEmptyT; - { - using V = std::variant; - V v1(std::in_place_type); - V v2(std::in_place_type); - try { - v1 = std::move(v2); - assert(false); - } catch (...) { - } - assert(v1.valueless_by_exception()); - assert(v1.index() == std::variant_npos); - } - { - using V = std::variant; - V v1(std::in_place_type); - V v2(std::in_place_type, "hello"); - V &vref = (v1 = std::move(v2)); - assert(&vref == &v1); - assert(v1.index() == 2); - assert(std::get<2>(v1) == "hello"); - } -#endif // TEST_HAS_NO_EXCEPTIONS // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). { @@ -461,38 +423,126 @@ void test_move_assignment_different_index() { } } -template -constexpr bool test_constexpr_assign_imp( - std::variant&& v, ValueType&& new_value) -{ - std::variant v2( - std::forward(new_value)); +void test_assignment_throw() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + // same index + { + using V = std::variant; + V v1(std::in_place_type); + MET& mref = std::get<1>(v1); + V v2(std::in_place_type); + try { + v1 = std::move(v2); + assert(false); + } catch (...) { + } + assert(v1.index() == 1); + assert(&std::get<1>(v1) == &mref); + } + + // different indices + { + using V = std::variant; + V v1(std::in_place_type); + V v2(std::in_place_type); + try { + v1 = std::move(v2); + assert(false); + } catch (...) { + } + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } + { + using V = std::variant; + V v1(std::in_place_type); + V v2(std::in_place_type, "hello"); + V& vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 2); + assert(std::get<2>(v1) == "hello"); + } +#endif // TEST_HAS_NO_EXCEPTIONS +} + +template +constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) { + using Variant = std::decay_t; + Variant v2(std::forward(new_value)); const auto cp = v2; - v = std::move(v2); - return v.index() == NewIdx && - std::get(v) == std::get(cp); + v = std::move(v2); + assert(v.index() == NewIdx); + assert(std::get(v) == std::get(cp)); } -void test_constexpr_move_assignment() { +constexpr void test_constexpr_move_assignment_trivial() { // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). using V = std::variant; static_assert(std::is_trivially_copyable::value, ""); static_assert(std::is_trivially_move_assignable::value, ""); - static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), ""); - static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), ""); - static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), ""); - static_assert(test_constexpr_assign_imp<2>(V(42l), 101), ""); + test_constexpr_assign_imp<0>(V(42l), 101l); + test_constexpr_assign_imp<0>(V(nullptr), 101l); + test_constexpr_assign_imp<1>(V(42l), nullptr); + test_constexpr_assign_imp<2>(V(42l), 101); } -int main(int, char**) { +struct NonTrivialMoveAssign { + int i = 0; + constexpr NonTrivialMoveAssign(int ii) : i(ii) {} + constexpr NonTrivialMoveAssign(const NonTrivialMoveAssign& other) = default; + constexpr NonTrivialMoveAssign(NonTrivialMoveAssign&& other) : i(other.i) {} + constexpr NonTrivialMoveAssign& operator=(const NonTrivialMoveAssign&) = default; + constexpr NonTrivialMoveAssign& operator=(NonTrivialMoveAssign&& o) { + i = o.i; + return *this; + } + TEST_CONSTEXPR_CXX20 ~NonTrivialMoveAssign() = default; + friend constexpr bool operator==(const NonTrivialMoveAssign& x, const NonTrivialMoveAssign& y) { return x.i == y.i; } +}; + +TEST_CONSTEXPR_CXX20 void test_constexpr_move_assignment_non_trivial() { + using V = std::variant; + static_assert(!std::is_trivially_copyable::value); + static_assert(!std::is_trivially_move_assignable::value); + test_constexpr_assign_imp<0>(V(42l), 101l); + test_constexpr_assign_imp<0>(V(nullptr), 101l); + test_constexpr_assign_imp<1>(V(42l), nullptr); + test_constexpr_assign_imp<2>(V(42l), NonTrivialMoveAssign(5)); + test_constexpr_assign_imp<2>(V(NonTrivialMoveAssign(3)), NonTrivialMoveAssign(5)); +} + +void non_constexpr_test() { test_move_assignment_empty_empty(); test_move_assignment_non_empty_empty(); test_move_assignment_empty_non_empty(); - test_move_assignment_same_index(); - test_move_assignment_different_index(); + test_assignment_throw(); +} + +constexpr bool cxx17_constexpr_test() { test_move_assignment_sfinae(); test_move_assignment_noexcept(); - test_constexpr_move_assignment(); + test_constexpr_move_assignment_trivial(); + + return true; +} +TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() { + test_move_assignment_same_index(); + test_move_assignment_different_index(); + test_constexpr_move_assignment_non_trivial(); + + return true; +} + +int main(int, char**) { + non_constexpr_test(); + cxx17_constexpr_test(); + cxx20_constexpr_test(); + + static_assert(cxx17_constexpr_test()); +#if TEST_STD_VER >= 20 + static_assert(cxx20_constexpr_test()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp index d1e5768f58d2b..820ff9e0d1a9d 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp @@ -22,30 +22,30 @@ #include "test_workarounds.h" struct NonT { - NonT(int v) : value(v) {} - NonT(const NonT &o) : value(o.value) {} + constexpr NonT(int v) : value(v) {} + constexpr NonT(const NonT& o) : value(o.value) {} int value; }; static_assert(!std::is_trivially_copy_constructible::value, ""); struct NoCopy { - NoCopy(const NoCopy &) = delete; + NoCopy(const NoCopy&) = delete; }; struct MoveOnly { - MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly &&) = default; + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; }; struct MoveOnlyNT { - MoveOnlyNT(const MoveOnlyNT &) = delete; - MoveOnlyNT(MoveOnlyNT &&) {} + MoveOnlyNT(const MoveOnlyNT&) = delete; + MoveOnlyNT(MoveOnlyNT&&) {} }; struct NTCopy { constexpr NTCopy(int v) : value(v) {} - NTCopy(const NTCopy &that) : value(that.value) {} - NTCopy(NTCopy &&) = delete; + NTCopy(const NTCopy& that) : value(that.value) {} + NTCopy(NTCopy&&) = delete; int value; }; @@ -54,8 +54,8 @@ static_assert(std::is_copy_constructible::value, ""); struct TCopy { constexpr TCopy(int v) : value(v) {} - TCopy(TCopy const &) = default; - TCopy(TCopy &&) = delete; + TCopy(TCopy const&) = default; + TCopy(TCopy&&) = delete; int value; }; @@ -74,20 +74,21 @@ static_assert(std::is_trivially_copy_constructible::value, ""); struct MakeEmptyT { static int alive; MakeEmptyT() { ++alive; } - MakeEmptyT(const MakeEmptyT &) { + MakeEmptyT(const MakeEmptyT&) { ++alive; // Don't throw from the copy constructor since variant's assignment // operator performs a copy before committing to the assignment. } - MakeEmptyT(MakeEmptyT &&) { throw 42; } - MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; } - MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + MakeEmptyT(MakeEmptyT&&) { throw 42; } + MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; } + MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; } ~MakeEmptyT() { --alive; } }; int MakeEmptyT::alive = 0; -template void makeEmpty(Variant &v) { +template +void makeEmpty(Variant& v) { Variant v2(std::in_place_type); try { v = std::move(v2); @@ -98,7 +99,7 @@ template void makeEmpty(Variant &v) { } #endif // TEST_HAS_NO_EXCEPTIONS -void test_copy_ctor_sfinae() { +constexpr void test_copy_ctor_sfinae() { { using V = std::variant; static_assert(std::is_copy_constructible::value, ""); @@ -136,7 +137,7 @@ void test_copy_ctor_sfinae() { } } -void test_copy_ctor_basic() { +TEST_CONSTEXPR_CXX20 void test_copy_ctor_basic() { { std::variant v(std::in_place_index<0>, 42); std::variant v2 = v; @@ -214,21 +215,21 @@ void test_copy_ctor_valueless_by_exception() { using V = std::variant; V v1; makeEmpty(v1); - const V &cv1 = v1; + const V& cv1 = v1; V v(cv1); assert(v.valueless_by_exception()); #endif // TEST_HAS_NO_EXCEPTIONS } -template -constexpr bool test_constexpr_copy_ctor_imp(std::variant const& v) { +template +constexpr void test_constexpr_copy_ctor_imp(const T& v) { auto v2 = v; - return v2.index() == v.index() && - v2.index() == Idx && - std::get(v2) == std::get(v); + assert(v2.index() == v.index()); + assert(v2.index() == Idx); + assert(std::get(v2) == std::get(v)); } -void test_constexpr_copy_ctor() { +constexpr void test_constexpr_copy_ctor_trivial() { // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). using V = std::variant; #ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE @@ -237,18 +238,57 @@ void test_constexpr_copy_ctor() { static_assert(std::is_trivially_move_constructible::value, ""); static_assert(!std::is_copy_assignable::value, ""); static_assert(!std::is_move_assignable::value, ""); -#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE +#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_copyable::value, ""); #endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE - static_assert(test_constexpr_copy_ctor_imp<0>(V(42l)), ""); - static_assert(test_constexpr_copy_ctor_imp<1>(V(nullptr)), ""); - static_assert(test_constexpr_copy_ctor_imp<2>(V(101)), ""); + static_assert(std::is_trivially_copy_constructible::value, ""); + test_constexpr_copy_ctor_imp<0>(V(42l)); + test_constexpr_copy_ctor_imp<1>(V(nullptr)); + test_constexpr_copy_ctor_imp<2>(V(101)); } -int main(int, char**) { - test_copy_ctor_basic(); - test_copy_ctor_valueless_by_exception(); +struct NonTrivialCopyCtor { + int i = 0; + constexpr NonTrivialCopyCtor(int ii) : i(ii) {} + constexpr NonTrivialCopyCtor(const NonTrivialCopyCtor& other) : i(other.i) {} + constexpr NonTrivialCopyCtor(NonTrivialCopyCtor&& other) = default; + TEST_CONSTEXPR_CXX20 ~NonTrivialCopyCtor() = default; + friend constexpr bool operator==(const NonTrivialCopyCtor& x, const NonTrivialCopyCtor& y) { return x.i == y.i; } +}; + +TEST_CONSTEXPR_CXX20 void test_constexpr_copy_ctor_non_trivial() { + // Test !is_trivially_move_constructible + using V = std::variant; + static_assert(!std::is_trivially_copy_constructible::value, ""); + test_constexpr_copy_ctor_imp<0>(V(42l)); + test_constexpr_copy_ctor_imp<1>(V(NonTrivialCopyCtor(5))); + test_constexpr_copy_ctor_imp<2>(V(nullptr)); +} + +void non_constexpr_test() { test_copy_ctor_valueless_by_exception(); } + +constexpr bool cxx17_constexpr_test() { test_copy_ctor_sfinae(); - test_constexpr_copy_ctor(); + test_constexpr_copy_ctor_trivial(); + + return true; +} + +TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() { + test_copy_ctor_basic(); + test_constexpr_copy_ctor_non_trivial(); + + return true; +} + +int main(int, char**) { + non_constexpr_test(); + cxx17_constexpr_test(); + cxx20_constexpr_test(); + + static_assert(cxx17_constexpr_test()); +#if TEST_STD_VER >= 20 + static_assert(cxx20_constexpr_test()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/default.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/default.pass.cpp index 40db038a00336..9abf4d758d841 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/default.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/default.pass.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "test_macros.h" #include "variant_test_helpers.h" @@ -35,7 +36,7 @@ struct DefaultCtorThrows { }; #endif -void test_default_ctor_sfinae() { +constexpr void test_default_ctor_sfinae() { { using V = std::variant; static_assert(std::is_default_constructible::value, ""); @@ -46,7 +47,7 @@ void test_default_ctor_sfinae() { } } -void test_default_ctor_noexcept() { +constexpr void test_default_ctor_noexcept() { { using V = std::variant; static_assert(std::is_nothrow_default_constructible::value, ""); @@ -63,7 +64,7 @@ void test_default_ctor_throws() { try { V v; assert(false); - } catch (const int &ex) { + } catch (const int& ex) { assert(ex == 42); } catch (...) { assert(false); @@ -71,7 +72,7 @@ void test_default_ctor_throws() { #endif } -void test_default_ctor_basic() { +constexpr void test_default_ctor_basic() { { std::variant v; assert(v.index() == 0); @@ -107,11 +108,24 @@ void test_default_ctor_basic() { } } -int main(int, char**) { +constexpr void issue_86686() { +#if TEST_STD_VER >= 20 + static_assert(std::variant{}.index() == 0); +#endif +} + +constexpr bool test() { test_default_ctor_basic(); test_default_ctor_sfinae(); test_default_ctor_noexcept(); - test_default_ctor_throws(); + issue_86686(); + return true; +} + +int main(int, char**) { + test(); + test_default_ctor_throws(); + static_assert(test()); return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp index e2518fe29caf7..4e8453c23cf55 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp @@ -23,31 +23,31 @@ #include "test_workarounds.h" struct ThrowsMove { - ThrowsMove(ThrowsMove &&) noexcept(false) {} + ThrowsMove(ThrowsMove&&) noexcept(false) {} }; struct NoCopy { - NoCopy(const NoCopy &) = delete; + NoCopy(const NoCopy&) = delete; }; struct MoveOnly { int value; - MoveOnly(int v) : value(v) {} - MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly &&) = default; + constexpr MoveOnly(int v) : value(v) {} + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; }; struct MoveOnlyNT { int value; - MoveOnlyNT(int v) : value(v) {} - MoveOnlyNT(const MoveOnlyNT &) = delete; - MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; } + constexpr MoveOnlyNT(int v) : value(v) {} + MoveOnlyNT(const MoveOnlyNT&) = delete; + constexpr MoveOnlyNT(MoveOnlyNT&& other) : value(other.value) { other.value = -1; } }; struct NTMove { constexpr NTMove(int v) : value(v) {} - NTMove(const NTMove &) = delete; - NTMove(NTMove &&that) : value(that.value) { that.value = -1; } + NTMove(const NTMove&) = delete; + NTMove(NTMove&& that) : value(that.value) { that.value = -1; } int value; }; @@ -56,8 +56,8 @@ static_assert(std::is_move_constructible::value, ""); struct TMove { constexpr TMove(int v) : value(v) {} - TMove(const TMove &) = delete; - TMove(TMove &&) = default; + TMove(const TMove&) = delete; + TMove(TMove&&) = default; int value; }; @@ -76,20 +76,21 @@ static_assert(std::is_trivially_move_constructible::value, ""); struct MakeEmptyT { static int alive; MakeEmptyT() { ++alive; } - MakeEmptyT(const MakeEmptyT &) { + MakeEmptyT(const MakeEmptyT&) { ++alive; // Don't throw from the copy constructor since variant's assignment // operator performs a copy before committing to the assignment. } - MakeEmptyT(MakeEmptyT &&) { throw 42; } - MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; } - MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + MakeEmptyT(MakeEmptyT&&) { throw 42; } + MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; } + MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; } ~MakeEmptyT() { --alive; } }; int MakeEmptyT::alive = 0; -template void makeEmpty(Variant &v) { +template +void makeEmpty(Variant& v) { Variant v2(std::in_place_type); try { v = std::move(v2); @@ -100,7 +101,7 @@ template void makeEmpty(Variant &v) { } #endif // TEST_HAS_NO_EXCEPTIONS -void test_move_noexcept() { +constexpr void test_move_noexcept() { { using V = std::variant; static_assert(std::is_nothrow_move_constructible::value, ""); @@ -119,7 +120,7 @@ void test_move_noexcept() { } } -void test_move_ctor_sfinae() { +constexpr void test_move_ctor_sfinae() { { using V = std::variant; static_assert(std::is_move_constructible::value, ""); @@ -158,9 +159,12 @@ void test_move_ctor_sfinae() { } template -struct Result { std::size_t index; T value; }; +struct Result { + std::size_t index; + T value; +}; -void test_move_ctor_basic() { +TEST_CONSTEXPR_CXX20 void test_move_ctor_basic() { { std::variant v(std::in_place_index<0>, 42); std::variant v2 = std::move(v); @@ -289,16 +293,16 @@ void test_move_ctor_valueless_by_exception() { #endif // TEST_HAS_NO_EXCEPTIONS } -template -constexpr bool test_constexpr_ctor_imp(std::variant const& v) { +template +constexpr void test_constexpr_ctor_imp(const T& v) { auto copy = v; - auto v2 = std::move(copy); - return v2.index() == v.index() && - v2.index() == Idx && - std::get(v2) == std::get(v); + auto v2 = std::move(copy); + assert(v2.index() == v.index()); + assert(v2.index() == Idx); + assert(std::get(v2) == std::get(v)); } -void test_constexpr_move_ctor() { +constexpr void test_constexpr_move_ctor_trivial() { // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). using V = std::variant; #ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE @@ -307,21 +311,58 @@ void test_constexpr_move_ctor() { static_assert(std::is_trivially_move_constructible::value, ""); static_assert(!std::is_copy_assignable::value, ""); static_assert(!std::is_move_assignable::value, ""); -#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE +#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_copyable::value, ""); #endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_move_constructible::value, ""); - static_assert(test_constexpr_ctor_imp<0>(V(42l)), ""); - static_assert(test_constexpr_ctor_imp<1>(V(nullptr)), ""); - static_assert(test_constexpr_ctor_imp<2>(V(101)), ""); + test_constexpr_ctor_imp<0>(V(42l)); + test_constexpr_ctor_imp<1>(V(nullptr)); + test_constexpr_ctor_imp<2>(V(101)); } -int main(int, char**) { - test_move_ctor_basic(); - test_move_ctor_valueless_by_exception(); +struct NonTrivialMoveCtor { + int i = 0; + constexpr NonTrivialMoveCtor(int ii) : i(ii) {} + constexpr NonTrivialMoveCtor(const NonTrivialMoveCtor& other) = default; + constexpr NonTrivialMoveCtor(NonTrivialMoveCtor&& other) : i(other.i) {} + TEST_CONSTEXPR_CXX20 ~NonTrivialMoveCtor() = default; + friend constexpr bool operator==(const NonTrivialMoveCtor& x, const NonTrivialMoveCtor& y) { return x.i == y.i; } +}; + +TEST_CONSTEXPR_CXX20 void test_constexpr_move_ctor_non_trivial() { + using V = std::variant; + static_assert(!std::is_trivially_move_constructible::value, ""); + test_constexpr_ctor_imp<0>(V(42l)); + test_constexpr_ctor_imp<1>(V(NonTrivialMoveCtor(5))); + test_constexpr_ctor_imp<2>(V(nullptr)); +} + +void non_constexpr_test() { test_move_ctor_valueless_by_exception(); } + +constexpr bool cxx17_constexpr_test() { test_move_noexcept(); test_move_ctor_sfinae(); - test_constexpr_move_ctor(); + test_constexpr_move_ctor_trivial(); + + return true; +} + +TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() { + test_move_ctor_basic(); + test_constexpr_move_ctor_non_trivial(); + + return true; +} + +int main(int, char**) { + non_constexpr_test(); + cxx17_constexpr_test(); + cxx20_constexpr_test(); + + static_assert(cxx17_constexpr_test()); +#if TEST_STD_VER >= 20 + static_assert(cxx20_constexpr_test()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.dtor/dtor.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.dtor/dtor.pass.cpp index 2e026038c97a3..53c5283b2edc6 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.dtor/dtor.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.dtor/dtor.pass.cpp @@ -21,55 +21,76 @@ #include "test_macros.h" struct NonTDtor { - static int count; - NonTDtor() = default; - ~NonTDtor() { ++count; } + int* count; + constexpr NonTDtor(int* a, int*) : count(a) {} + TEST_CONSTEXPR_CXX20 ~NonTDtor() { ++*count; } }; -int NonTDtor::count = 0; static_assert(!std::is_trivially_destructible::value, ""); struct NonTDtor1 { - static int count; - NonTDtor1() = default; - ~NonTDtor1() { ++count; } + int* count; + constexpr NonTDtor1(int*, int* b) : count(b) {} + TEST_CONSTEXPR_CXX20 ~NonTDtor1() { ++*count; } }; -int NonTDtor1::count = 0; static_assert(!std::is_trivially_destructible::value, ""); struct TDtor { - TDtor(const TDtor &) {} // non-trivial copy - ~TDtor() = default; + constexpr TDtor() = default; + constexpr TDtor(const TDtor&) {} // non-trivial copy + TEST_CONSTEXPR_CXX20 ~TDtor() = default; }; static_assert(!std::is_trivially_copy_constructible::value, ""); static_assert(std::is_trivially_destructible::value, ""); -int main(int, char**) { +TEST_CONSTEXPR_CXX20 bool test() { { using V = std::variant; static_assert(std::is_trivially_destructible::value, ""); + [[maybe_unused]] V v(std::in_place_index<2>); } { using V = std::variant; static_assert(!std::is_trivially_destructible::value, ""); { - V v(std::in_place_index<0>); - assert(NonTDtor::count == 0); - assert(NonTDtor1::count == 0); + int count0 = 0; + int count1 = 0; + { + V v(std::in_place_index<0>, &count0, &count1); + assert(count0 == 0); + assert(count1 == 0); + } + assert(count0 == 1); + assert(count1 == 0); + } + { + int count0 = 0; + int count1 = 0; + { V v(std::in_place_index<1>); } + assert(count0 == 0); + assert(count1 == 0); } - assert(NonTDtor::count == 1); - assert(NonTDtor1::count == 0); - NonTDtor::count = 0; - { V v(std::in_place_index<1>); } - assert(NonTDtor::count == 0); - assert(NonTDtor1::count == 0); { - V v(std::in_place_index<2>); - assert(NonTDtor::count == 0); - assert(NonTDtor1::count == 0); + int count0 = 0; + int count1 = 0; + { + V v(std::in_place_index<2>, &count0, &count1); + assert(count0 == 0); + assert(count1 == 0); + } + assert(count0 == 0); + assert(count1 == 1); } - assert(NonTDtor::count == 0); - assert(NonTDtor1::count == 1); } + return true; +} + +int main(int, char**) { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_args.pass.cpp index 2fe9033dd8166..f98d968f0eae0 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_args.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_args.pass.cpp @@ -26,8 +26,8 @@ #include "variant_test_helpers.h" template -constexpr auto test_emplace_exists_imp(int) -> decltype( - std::declval().template emplace(std::declval()...), true) { +constexpr auto test_emplace_exists_imp(int) + -> decltype(std::declval().template emplace(std::declval()...), true) { return true; } @@ -36,28 +36,32 @@ constexpr auto test_emplace_exists_imp(long) -> bool { return false; } -template constexpr bool emplace_exists() { +template +constexpr bool emplace_exists() { return test_emplace_exists_imp(0); } -void test_emplace_sfinae() { +constexpr void test_emplace_sfinae() { { - using V = std::variant; + using V = std::variant; static_assert(emplace_exists(), ""); static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), - "cannot construct"); + static_assert(!emplace_exists(), "cannot construct"); static_assert(emplace_exists(), ""); - static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), ""); + static_assert(emplace_exists(), ""); + static_assert(!emplace_exists(), ""); static_assert(!emplace_exists(), "cannot construct"); - static_assert(emplace_exists(), ""); - static_assert(emplace_exists(), ""); + static_assert(emplace_exists(), ""); + static_assert(emplace_exists(), ""); static_assert(!emplace_exists(), "cannot construct"); } } -void test_basic() { +struct NoCtor { + NoCtor() = delete; +}; + +TEST_CONSTEXPR_CXX20 void test_basic() { { using V = std::variant; V v(42); @@ -70,9 +74,9 @@ void test_basic() { assert(std::get<0>(v) == 42); assert(&ref2 == &std::get<0>(v)); } + { - using V = - std::variant; + using V = std::variant; const int x = 100; V v(std::in_place_index<0>, -1); // default emplace a value @@ -92,9 +96,19 @@ void test_basic() { } } -int main(int, char**) { +TEST_CONSTEXPR_CXX20 bool test() { test_basic(); test_emplace_sfinae(); + return true; +} + +int main(int, char**) { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_init_list_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_init_list_args.pass.cpp index 9068aacc43592..4c635570bd562 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_init_list_args.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_init_list_args.pass.cpp @@ -32,13 +32,12 @@ struct InitList { struct InitListArg { std::size_t size; int value; - constexpr InitListArg(std::initializer_list il, int v) - : size(il.size()), value(v) {} + constexpr InitListArg(std::initializer_list il, int v) : size(il.size()), value(v) {} }; template -constexpr auto test_emplace_exists_imp(int) -> decltype( - std::declval().template emplace(std::declval()...), true) { +constexpr auto test_emplace_exists_imp(int) + -> decltype(std::declval().template emplace(std::declval()...), true) { return true; } @@ -47,13 +46,13 @@ constexpr auto test_emplace_exists_imp(long) -> bool { return false; } -template constexpr bool emplace_exists() { +template +constexpr bool emplace_exists() { return test_emplace_exists_imp(0); } -void test_emplace_sfinae() { - using V = - std::variant; +constexpr void test_emplace_sfinae() { + using V = std::variant; using IL = std::initializer_list; static_assert(!emplace_exists(), "no such constructor"); static_assert(emplace_exists(), ""); @@ -65,8 +64,12 @@ void test_emplace_sfinae() { static_assert(!emplace_exists(), "too many args"); } -void test_basic() { - using V = std::variant; +struct NoCtor { + NoCtor() = delete; +}; + +TEST_CONSTEXPR_CXX20 void test_basic() { + using V = std::variant; V v; auto& ref1 = v.emplace<1>({1, 2, 3}); static_assert(std::is_same_v, ""); @@ -83,9 +86,19 @@ void test_basic() { assert(&ref3 == &std::get<1>(v)); } -int main(int, char**) { +TEST_CONSTEXPR_CXX20 bool test() { test_basic(); test_emplace_sfinae(); + return true; +} + +int main(int, char**) { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_args.pass.cpp index 4e9f67775d10c..c2ed54d8a6257 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_args.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_args.pass.cpp @@ -25,8 +25,8 @@ #include "variant_test_helpers.h" template -constexpr auto test_emplace_exists_imp(int) -> decltype( - std::declval().template emplace(std::declval()...), true) { +constexpr auto test_emplace_exists_imp(int) + -> decltype(std::declval().template emplace(std::declval()...), true) { return true; } @@ -35,28 +35,32 @@ constexpr auto test_emplace_exists_imp(long) -> bool { return false; } -template constexpr bool emplace_exists() { +template +constexpr bool emplace_exists() { return test_emplace_exists_imp(0); } -void test_emplace_sfinae() { +constexpr void test_emplace_sfinae() { { - using V = std::variant; + using V = std::variant; static_assert(emplace_exists(), ""); static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), - "cannot construct"); - static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), "cannot construct"); - static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), ""); - static_assert(emplace_exists(), ""); - static_assert(emplace_exists(), ""); + static_assert(!emplace_exists(), "cannot construct"); + static_assert(emplace_exists(), ""); + static_assert(!emplace_exists(), "cannot construct"); + static_assert(emplace_exists(), ""); + static_assert(!emplace_exists(), ""); + static_assert(emplace_exists(), ""); + static_assert(emplace_exists(), ""); static_assert(!emplace_exists(), "cannot construct"); } } -void test_basic() { +struct NoCtor { + NoCtor() = delete; +}; + +TEST_CONSTEXPR_CXX20 void test_basic() { { using V = std::variant; V v(42); @@ -70,8 +74,7 @@ void test_basic() { assert(&ref2 == &std::get<0>(v)); } { - using V = - std::variant; + using V = std::variant; const int x = 100; V v(std::in_place_type, -1); // default emplace a value @@ -79,8 +82,8 @@ void test_basic() { static_assert(std::is_same_v, ""); assert(std::get<1>(v) == 0); assert(&ref1 == &std::get<1>(v)); - auto& ref2 = v.emplace(&x); - static_assert(std::is_same_v, ""); + auto& ref2 = v.emplace(&x); + static_assert(std::is_same_v, ""); assert(std::get<2>(v) == &x); assert(&ref2 == &std::get<2>(v)); // emplace with multiple args @@ -91,9 +94,19 @@ void test_basic() { } } -int main(int, char**) { +TEST_CONSTEXPR_CXX20 bool test() { test_basic(); test_emplace_sfinae(); + return true; +} + +int main(int, char**) { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_init_list_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_init_list_args.pass.cpp index 74d834b9b3457..644f2418b9255 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_init_list_args.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_init_list_args.pass.cpp @@ -32,13 +32,12 @@ struct InitList { struct InitListArg { std::size_t size; int value; - constexpr InitListArg(std::initializer_list il, int v) - : size(il.size()), value(v) {} + constexpr InitListArg(std::initializer_list il, int v) : size(il.size()), value(v) {} }; template -constexpr auto test_emplace_exists_imp(int) -> decltype( - std::declval().template emplace(std::declval()...), true) { +constexpr auto test_emplace_exists_imp(int) + -> decltype(std::declval().template emplace(std::declval()...), true) { return true; } @@ -47,13 +46,13 @@ constexpr auto test_emplace_exists_imp(long) -> bool { return false; } -template constexpr bool emplace_exists() { +template +constexpr bool emplace_exists() { return test_emplace_exists_imp(0); } -void test_emplace_sfinae() { - using V = - std::variant; +constexpr void test_emplace_sfinae() { + using V = std::variant; using IL = std::initializer_list; static_assert(emplace_exists(), ""); static_assert(!emplace_exists(), "args don't match"); @@ -61,31 +60,44 @@ void test_emplace_sfinae() { static_assert(emplace_exists(), ""); static_assert(!emplace_exists(), "args don't match"); static_assert(!emplace_exists(), "too few args"); - static_assert(!emplace_exists(), - "too many args"); + static_assert(!emplace_exists(), "too many args"); } -void test_basic() { - using V = std::variant; +struct NoCtor { + NoCtor() = delete; +}; + +TEST_CONSTEXPR_CXX20 void test_basic() { + using V = std::variant; V v; auto& ref1 = v.emplace({1, 2, 3}); - static_assert(std::is_same_v, ""); + static_assert(std::is_same_v, ""); assert(std::get(v).size == 3); assert(&ref1 == &std::get(v)); auto& ref2 = v.emplace({1, 2, 3, 4}, 42); - static_assert(std::is_same_v, ""); + static_assert(std::is_same_v, ""); assert(std::get(v).size == 4); assert(std::get(v).value == 42); assert(&ref2 == &std::get(v)); auto& ref3 = v.emplace({1}); - static_assert(std::is_same_v, ""); + static_assert(std::is_same_v, ""); assert(std::get(v).size == 1); assert(&ref3 == &std::get(v)); } -int main(int, char**) { +TEST_CONSTEXPR_CXX20 bool test() { test_basic(); test_emplace_sfinae(); + return true; +} + +int main(int, char**) { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp index 1802bc4670bba..db05691c55818 100644 --- a/libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp @@ -25,37 +25,39 @@ #include "variant_test_helpers.h" struct NotSwappable {}; -void swap(NotSwappable &, NotSwappable &) = delete; +void swap(NotSwappable&, NotSwappable&) = delete; struct NotCopyable { - NotCopyable() = default; - NotCopyable(const NotCopyable &) = delete; - NotCopyable &operator=(const NotCopyable &) = delete; + NotCopyable() = default; + NotCopyable(const NotCopyable&) = delete; + NotCopyable& operator=(const NotCopyable&) = delete; }; struct NotCopyableWithSwap { - NotCopyableWithSwap() = default; - NotCopyableWithSwap(const NotCopyableWithSwap &) = delete; - NotCopyableWithSwap &operator=(const NotCopyableWithSwap &) = delete; + NotCopyableWithSwap() = default; + NotCopyableWithSwap(const NotCopyableWithSwap&) = delete; + NotCopyableWithSwap& operator=(const NotCopyableWithSwap&) = delete; }; -void swap(NotCopyableWithSwap &, NotCopyableWithSwap) {} +constexpr void swap(NotCopyableWithSwap&, NotCopyableWithSwap) {} struct NotMoveAssignable { - NotMoveAssignable() = default; - NotMoveAssignable(NotMoveAssignable &&) = default; - NotMoveAssignable &operator=(NotMoveAssignable &&) = delete; + NotMoveAssignable() = default; + NotMoveAssignable(NotMoveAssignable&&) = default; + NotMoveAssignable& operator=(NotMoveAssignable&&) = delete; }; struct NotMoveAssignableWithSwap { - NotMoveAssignableWithSwap() = default; - NotMoveAssignableWithSwap(NotMoveAssignableWithSwap &&) = default; - NotMoveAssignableWithSwap &operator=(NotMoveAssignableWithSwap &&) = delete; + NotMoveAssignableWithSwap() = default; + NotMoveAssignableWithSwap(NotMoveAssignableWithSwap&&) = default; + NotMoveAssignableWithSwap& operator=(NotMoveAssignableWithSwap&&) = delete; }; -void swap(NotMoveAssignableWithSwap &, NotMoveAssignableWithSwap &) noexcept {} +constexpr void swap(NotMoveAssignableWithSwap&, NotMoveAssignableWithSwap&) noexcept {} -template void do_throw() {} +template +constexpr void do_throw() {} -template <> void do_throw() { +template <> +void do_throw() { #ifndef TEST_HAS_NO_EXCEPTIONS throw 42; #else @@ -63,60 +65,49 @@ template <> void do_throw() { #endif } -template +template struct NothrowTypeImp { - static int move_called; - static int move_assign_called; - static int swap_called; - static void reset() { move_called = move_assign_called = swap_called = 0; } - NothrowTypeImp() = default; - explicit NothrowTypeImp(int v) : value(v) {} - NothrowTypeImp(const NothrowTypeImp &o) noexcept(NT_Copy) : value(o.value) { - assert(false); - } // never called by test - NothrowTypeImp(NothrowTypeImp &&o) noexcept(NT_Move) : value(o.value) { - ++move_called; + int value; + int* move_called; + int* move_assign_called; + int* swap_called; + + constexpr NothrowTypeImp(int v, int* mv_ctr, int* mv_assign, int* swap) + : value(v), move_called(mv_ctr), move_assign_called(mv_assign), swap_called(swap) {} + + NothrowTypeImp(const NothrowTypeImp& o) noexcept(NT_Copy) : value(o.value) { assert(false); } // never called by test + + constexpr NothrowTypeImp(NothrowTypeImp&& o) noexcept(NT_Move) + : value(o.value), + move_called(o.move_called), + move_assign_called(o.move_assign_called), + swap_called(o.swap_called) { + ++*move_called; do_throw(); o.value = -1; } - NothrowTypeImp &operator=(const NothrowTypeImp &) noexcept(NT_CopyAssign) { + + NothrowTypeImp& operator=(const NothrowTypeImp&) noexcept(NT_CopyAssign) { assert(false); return *this; } // never called by the tests - NothrowTypeImp &operator=(NothrowTypeImp &&o) noexcept(NT_MoveAssign) { - ++move_assign_called; + + constexpr NothrowTypeImp& operator=(NothrowTypeImp&& o) noexcept(NT_MoveAssign) { + ++*move_assign_called; do_throw(); - value = o.value; + value = o.value; o.value = -1; return *this; } - int value; }; -template -int NothrowTypeImp::move_called = 0; -template -int NothrowTypeImp::move_assign_called = 0; -template -int NothrowTypeImp::swap_called = 0; - -template -void swap(NothrowTypeImp &lhs, - NothrowTypeImp &rhs) noexcept(NT_Swap) { - lhs.swap_called++; + +template +constexpr void +swap(NothrowTypeImp& lhs, + NothrowTypeImp& rhs) noexcept(NT_Swap) { + ++*lhs.swap_called; do_throw(); - int tmp = lhs.value; - lhs.value = rhs.value; - rhs.value = tmp; + std::swap(lhs.value, rhs.value); } // throwing copy, nothrow move ctor/assign, no swap provided @@ -124,53 +115,42 @@ using NothrowMoveable = NothrowTypeImp; // throwing copy and move assign, nothrow move ctor, no swap provided using NothrowMoveCtor = NothrowTypeImp; // nothrow move ctor, throwing move assignment, swap provided -using NothrowMoveCtorWithThrowingSwap = - NothrowTypeImp; +using NothrowMoveCtorWithThrowingSwap = NothrowTypeImp; // throwing move ctor, nothrow move assignment, no swap provided -using ThrowingMoveCtor = - NothrowTypeImp; +using ThrowingMoveCtor = NothrowTypeImp; // throwing special members, nothrowing swap -using ThrowingTypeWithNothrowSwap = - NothrowTypeImp; -using NothrowTypeWithThrowingSwap = - NothrowTypeImp; +using ThrowingTypeWithNothrowSwap = NothrowTypeImp; +using NothrowTypeWithThrowingSwap = NothrowTypeImp; // throwing move assign with nothrow move and nothrow swap -using ThrowingMoveAssignNothrowMoveCtorWithSwap = - NothrowTypeImp; +using ThrowingMoveAssignNothrowMoveCtorWithSwap = NothrowTypeImp; // throwing move assign with nothrow move but no swap. -using ThrowingMoveAssignNothrowMoveCtor = - NothrowTypeImp; +using ThrowingMoveAssignNothrowMoveCtor = NothrowTypeImp; struct NonThrowingNonNoexceptType { - static int move_called; - static void reset() { move_called = 0; } - NonThrowingNonNoexceptType() = default; - NonThrowingNonNoexceptType(int v) : value(v) {} - NonThrowingNonNoexceptType(NonThrowingNonNoexceptType &&o) noexcept(false) - : value(o.value) { - ++move_called; + int value; + int* move_called; + constexpr NonThrowingNonNoexceptType(int v, int* mv_called) : value(v), move_called(mv_called) {} + constexpr NonThrowingNonNoexceptType(NonThrowingNonNoexceptType&& o) noexcept(false) + : value(o.value), move_called(o.move_called) { + ++*move_called; o.value = -1; } - NonThrowingNonNoexceptType & - operator=(NonThrowingNonNoexceptType &&) noexcept(false) { + NonThrowingNonNoexceptType& operator=(NonThrowingNonNoexceptType&&) noexcept(false) { assert(false); // never called by the tests. return *this; } - int value; }; -int NonThrowingNonNoexceptType::move_called = 0; struct ThrowsOnSecondMove { int value; int move_count; ThrowsOnSecondMove(int v) : value(v), move_count(0) {} - ThrowsOnSecondMove(ThrowsOnSecondMove &&o) noexcept(false) - : value(o.value), move_count(o.move_count + 1) { + ThrowsOnSecondMove(ThrowsOnSecondMove&& o) noexcept(false) : value(o.value), move_count(o.move_count + 1) { if (move_count == 2) do_throw(); o.value = -1; } - ThrowsOnSecondMove &operator=(ThrowsOnSecondMove &&) { + ThrowsOnSecondMove& operator=(ThrowsOnSecondMove&&) { assert(false); // not called by test return *this; } @@ -224,265 +204,293 @@ void test_swap_valueless_by_exception() { #endif } -void test_swap_same_alternative() { +TEST_CONSTEXPR_CXX20 void test_swap_same_alternative() { { - using T = ThrowingTypeWithNothrowSwap; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<0>, 100); + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); + V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); v1.swap(v2); - assert(T::swap_called == 1); + assert(swap_called == 1); assert(std::get<0>(v1).value == 100); assert(std::get<0>(v2).value == 42); swap(v1, v2); - assert(T::swap_called == 2); + assert(swap_called == 2); assert(std::get<0>(v1).value == 42); assert(std::get<0>(v2).value == 100); + + assert(move_called == 0); + assert(move_assign_called == 0); } { - using T = NothrowMoveable; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<0>, 100); + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); + V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); v1.swap(v2); - assert(T::swap_called == 0); - assert(T::move_called == 1); - assert(T::move_assign_called == 2); + assert(swap_called == 0); + assert(move_called == 1); + assert(move_assign_called == 2); assert(std::get<0>(v1).value == 100); assert(std::get<0>(v2).value == 42); - T::reset(); + + move_called = 0; + move_assign_called = 0; + swap_called = 0; + swap(v1, v2); - assert(T::swap_called == 0); - assert(T::move_called == 1); - assert(T::move_assign_called == 2); + assert(swap_called == 0); + assert(move_called == 1); + assert(move_assign_called == 2); assert(std::get<0>(v1).value == 42); assert(std::get<0>(v2).value == 100); } +} + +void test_swap_same_alternative_throws(){ #ifndef TEST_HAS_NO_EXCEPTIONS - { - using T = NothrowTypeWithThrowingSwap; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<0>, 100); - try { - v1.swap(v2); - assert(false); - } catch (int) { - } - assert(T::swap_called == 1); - assert(T::move_called == 0); - assert(T::move_assign_called == 0); - assert(std::get<0>(v1).value == 42); - assert(std::get<0>(v2).value == 100); - } - { - using T = ThrowingMoveCtor; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<0>, 100); - try { - v1.swap(v2); - assert(false); - } catch (int) { - } - assert(T::move_called == 1); // call threw - assert(T::move_assign_called == 0); - assert(std::get<0>(v1).value == - 42); // throw happened before v1 was moved from - assert(std::get<0>(v2).value == 100); + {using V = std::variant; +int move_called = 0; +int move_assign_called = 0; +int swap_called = 0; +V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); +V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); +try { + v1.swap(v2); + assert(false); +} catch (int) { +} +assert(swap_called == 1); +assert(move_called == 0); +assert(move_assign_called == 0); +assert(std::get<0>(v1).value == 42); +assert(std::get<0>(v2).value == 100); +} + +{ + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); + V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); + try { + v1.swap(v2); + assert(false); + } catch (int) { } - { - using T = ThrowingMoveAssignNothrowMoveCtor; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<0>, 100); - try { - v1.swap(v2); - assert(false); - } catch (int) { - } - assert(T::move_called == 1); - assert(T::move_assign_called == 1); // call threw and didn't complete - assert(std::get<0>(v1).value == -1); // v1 was moved from - assert(std::get<0>(v2).value == 100); + assert(move_called == 1); // call threw + assert(move_assign_called == 0); + assert(swap_called == 0); + assert(std::get<0>(v1).value == 42); // throw happened before v1 was moved from + assert(std::get<0>(v2).value == 100); +} +{ + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); + V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); + try { + v1.swap(v2); + assert(false); + } catch (int) { } + assert(move_called == 1); + assert(move_assign_called == 1); // call threw and didn't complete + assert(swap_called == 0); + assert(std::get<0>(v1).value == -1); // v1 was moved from + assert(std::get<0>(v2).value == 100); +} #endif } -void test_swap_different_alternatives() { +TEST_CONSTEXPR_CXX20 void test_swap_different_alternatives() { { - using T = NothrowMoveCtorWithThrowingSwap; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); V v2(std::in_place_index<1>, 100); v1.swap(v2); - assert(T::swap_called == 0); + assert(swap_called == 0); // The libc++ implementation double copies the argument, and not // the variant swap is called on. - LIBCPP_ASSERT(T::move_called == 1); - assert(T::move_called <= 2); - assert(T::move_assign_called == 0); + LIBCPP_ASSERT(move_called == 1); + assert(move_called <= 2); + assert(move_assign_called == 0); assert(std::get<1>(v1) == 100); assert(std::get<0>(v2).value == 42); - T::reset(); + + move_called = 0; + move_assign_called = 0; + swap_called = 0; + swap(v1, v2); - assert(T::swap_called == 0); - LIBCPP_ASSERT(T::move_called == 2); - assert(T::move_called <= 2); - assert(T::move_assign_called == 0); + assert(swap_called == 0); + LIBCPP_ASSERT(move_called == 2); + assert(move_called <= 2); + assert(move_assign_called == 0); assert(std::get<0>(v1).value == 42); assert(std::get<1>(v2) == 100); } +} + +void test_swap_different_alternatives_throws() { #ifndef TEST_HAS_NO_EXCEPTIONS { - using T1 = ThrowingTypeWithNothrowSwap; - using T2 = NonThrowingNonNoexceptType; - using V = std::variant; - T1::reset(); - T2::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<1>, 100); + using V = std::variant; + int move_called1 = 0; + int move_assign_called1 = 0; + int swap_called1 = 0; + int move_called2 = 0; + V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1); + V v2(std::in_place_index<1>, 100, &move_called2); try { v1.swap(v2); assert(false); } catch (int) { } - assert(T1::swap_called == 0); - assert(T1::move_called == 1); // throws - assert(T1::move_assign_called == 0); + assert(swap_called1 == 0); + assert(move_called1 == 1); // throws + assert(move_assign_called1 == 0); // FIXME: libc++ shouldn't move from T2 here. - LIBCPP_ASSERT(T2::move_called == 1); - assert(T2::move_called <= 1); + LIBCPP_ASSERT(move_called2 == 1); + assert(move_called2 <= 1); assert(std::get<0>(v1).value == 42); - if (T2::move_called != 0) + if (move_called2 != 0) assert(v2.valueless_by_exception()); else assert(std::get<1>(v2).value == 100); } { - using T1 = NonThrowingNonNoexceptType; - using T2 = ThrowingTypeWithNothrowSwap; - using V = std::variant; - T1::reset(); - T2::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<1>, 100); + using V = std::variant; + int move_called1 = 0; + int move_called2 = 0; + int move_assign_called2 = 0; + int swap_called2 = 0; + V v1(std::in_place_index<0>, 42, &move_called1); + V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2); try { v1.swap(v2); assert(false); } catch (int) { } - LIBCPP_ASSERT(T1::move_called == 0); - assert(T1::move_called <= 1); - assert(T2::swap_called == 0); - assert(T2::move_called == 1); // throws - assert(T2::move_assign_called == 0); - if (T1::move_called != 0) + LIBCPP_ASSERT(move_called1 == 0); + assert(move_called1 <= 1); + assert(swap_called2 == 0); + assert(move_called2 == 1); // throws + assert(move_assign_called2 == 0); + if (move_called1 != 0) assert(v1.valueless_by_exception()); else assert(std::get<0>(v1).value == 42); assert(std::get<1>(v2).value == 100); } // FIXME: The tests below are just very libc++ specific -#ifdef _LIBCPP_VERSION +# ifdef _LIBCPP_VERSION { - using T1 = ThrowsOnSecondMove; - using T2 = NonThrowingNonNoexceptType; - using V = std::variant; - T2::reset(); + using V = std::variant; + int move_called = 0; V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<1>, 100); + V v2(std::in_place_index<1>, 100, &move_called); v1.swap(v2); - assert(T2::move_called == 2); + assert(move_called == 2); assert(std::get<1>(v1).value == 100); assert(std::get<0>(v2).value == 42); assert(std::get<0>(v2).move_count == 1); } { - using T1 = NonThrowingNonNoexceptType; - using T2 = ThrowsOnSecondMove; - using V = std::variant; - T1::reset(); - V v1(std::in_place_index<0>, 42); + using V = std::variant; + int move_called = 0; + V v1(std::in_place_index<0>, 42, &move_called); V v2(std::in_place_index<1>, 100); try { v1.swap(v2); assert(false); } catch (int) { } - assert(T1::move_called == 1); + assert(move_called == 1); assert(v1.valueless_by_exception()); assert(std::get<0>(v2).value == 42); } -#endif -// testing libc++ extension. If either variant stores a nothrow move -// constructible type v1.swap(v2) provides the strong exception safety -// guarantee. -#ifdef _LIBCPP_VERSION +# endif + // testing libc++ extension. If either variant stores a nothrow move + // constructible type v1.swap(v2) provides the strong exception safety + // guarantee. +# ifdef _LIBCPP_VERSION { - - using T1 = ThrowingTypeWithNothrowSwap; - using T2 = NothrowMoveable; - using V = std::variant; - T1::reset(); - T2::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<1>, 100); + using V = std::variant; + int move_called1 = 0; + int move_assign_called1 = 0; + int swap_called1 = 0; + int move_called2 = 0; + int move_assign_called2 = 0; + int swap_called2 = 0; + V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1); + V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2); try { v1.swap(v2); assert(false); } catch (int) { } - assert(T1::swap_called == 0); - assert(T1::move_called == 1); - assert(T1::move_assign_called == 0); - assert(T2::swap_called == 0); - assert(T2::move_called == 2); - assert(T2::move_assign_called == 0); + assert(swap_called1 == 0); + assert(move_called1 == 1); + assert(move_assign_called1 == 0); + assert(swap_called2 == 0); + assert(move_called2 == 2); + assert(move_assign_called2 == 0); assert(std::get<0>(v1).value == 42); assert(std::get<1>(v2).value == 100); // swap again, but call v2's swap. - T1::reset(); - T2::reset(); + + move_called1 = 0; + move_assign_called1 = 0; + swap_called1 = 0; + move_called2 = 0; + move_assign_called2 = 0; + swap_called2 = 0; + try { v2.swap(v1); assert(false); } catch (int) { } - assert(T1::swap_called == 0); - assert(T1::move_called == 1); - assert(T1::move_assign_called == 0); - assert(T2::swap_called == 0); - assert(T2::move_called == 2); - assert(T2::move_assign_called == 0); + assert(swap_called1 == 0); + assert(move_called1 == 1); + assert(move_assign_called1 == 0); + assert(swap_called2 == 0); + assert(move_called2 == 2); + assert(move_assign_called2 == 0); assert(std::get<0>(v1).value == 42); assert(std::get<1>(v2).value == 100); } -#endif // _LIBCPP_VERSION +# endif // _LIBCPP_VERSION #endif } template -constexpr auto has_swap_member_imp(int) - -> decltype(std::declval().swap(std::declval()), true) { +constexpr auto has_swap_member_imp(int) -> decltype(std::declval().swap(std::declval()), true) { return true; } -template constexpr auto has_swap_member_imp(long) -> bool { +template +constexpr auto has_swap_member_imp(long) -> bool { return false; } -template constexpr bool has_swap_member() { +template +constexpr bool has_swap_member() { return has_swap_member_imp(0); } -void test_swap_sfinae() { +constexpr void test_swap_sfinae() { { // This variant type does not provide either a member or non-member swap // but is still swappable via the generic swap algorithm, since the @@ -508,7 +516,7 @@ void test_swap_sfinae() { } } -void test_swap_noexcept() { +_LIBCPP_CONSTEXPR_SINCE_CXX20 void test_swap_noexcept() { { using V = std::variant; static_assert(std::is_swappable_v && has_swap_member(), ""); @@ -581,12 +589,28 @@ void test_swap_noexcept() { template class std::variant; #endif -int main(int, char**) { +void non_constexpr_test() { test_swap_valueless_by_exception(); + test_swap_same_alternative_throws(); + test_swap_different_alternatives_throws(); +} + +TEST_CONSTEXPR_CXX20 bool test() { test_swap_same_alternative(); test_swap_different_alternatives(); test_swap_sfinae(); test_swap_noexcept(); + return true; +} + +int main(int, char**) { + non_constexpr_test(); + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } From 0466fbd0bcbcf59f8d7860f4a593fb6c62a34b2d Mon Sep 17 00:00:00 2001 From: Hui Date: Fri, 10 May 2024 09:41:03 +0100 Subject: [PATCH 2/2] remove inline --- libcxx/include/variant | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/variant b/libcxx/include/variant index 953ffea5dea88..631ffceab5f68 100644 --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -780,14 +780,14 @@ class _LIBCPP_TEMPLATE_VIS __dtor; _LIBCPP_VARIANT_DESTRUCTOR( _Trait::_TriviallyAvailable, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() = default, - inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept { this->__index = __variant_npos<__index_t>; } _LIBCPP_EAT_SEMICOLON); _LIBCPP_VARIANT_DESTRUCTOR( _Trait::_Available, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() { __destroy(); } _LIBCPP_EAT_SEMICOLON, - inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept { if (!this->valueless_by_exception()) { __visitation::__base::__visit_alt( [](auto& __alt) noexcept {