[libcxx-commits] [libcxx] [libc++] Make `constexpr std::variant`. Implement P2231R1 (PR #83335)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Mar 27 00:40:55 PDT 2024
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/83335
>From b54f8a1924cb7c8d7142d2cef75aab9716319c6c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Wed, 28 Feb 2024 21:06:51 +0000
Subject: [PATCH] [libc++] Make `constexpr std::variant`. Implement P2231R1
---
libcxx/docs/ReleaseNotes/19.rst | 2 +
libcxx/docs/Status/Cxx20.rst | 1 -
libcxx/docs/Status/Cxx20Papers.csv | 2 +-
libcxx/include/variant | 211 ++++----
.../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/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 +++++++++---------
15 files changed, 1304 insertions(+), 828 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index dd39c1bbbc78a3..ae92017ccffd3d 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -44,6 +44,8 @@ Implemented Papers
- P2495R3 - Interfacing ``stringstream``\s with ``string_view``
- P2302R4 - ``std::ranges::contains``
- P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
+- 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 2deb82547d6310..c00d6fb2372869 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 db6491419a5cc3..aa79c7b5d3d409 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -192,7 +192,7 @@
"`P2106R0 <https://wg21.link/P2106R0>`__","LWG","Alternative wording for GB315 and GB316","Prague","|Complete|","15.0","|ranges|"
"`P2116R0 <https://wg21.link/P2116R0>`__","LWG","Remove tuple-like protocol support from fixed-extent span","Prague","|Complete|","11.0"
"","","","","","",""
-"`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Partial| [#note-P2231]_","13.0"
+"`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Complete|","19.0"
"`P2325R3 <https://wg21.link/P2325R3>`__","LWG","Views should not be required to be default constructible","June 2021","|Complete|","16.0","|ranges|"
"`P2210R2 <https://wg21.link/P2210R2>`__","LWG","Superior String Splitting","June 2021","|Complete|","16.0","|ranges|"
"`P2216R3 <https://wg21.link/P2216R3>`__","LWG","std::format improvements","June 2021","|Complete|","15.0"
diff --git a/libcxx/include/variant b/libcxx/include/variant
index f5e99fc3239e84..339ba94753dca9 100644
--- a/libcxx/include/variant
+++ b/libcxx/include/variant
@@ -42,26 +42,28 @@ namespace std {
in_place_index_t<I>, initializer_list<U>, 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 <class T> variant& operator=(T&&) noexcept(see below);
+ template <class T>
+ constexpr variant& operator=(T&&) noexcept(see below); // constexpr since c++20
// 20.7.2.4, modifiers
template <class T, class... Args>
- T& emplace(Args&&...);
+ constexpr T& emplace(Args&&...); // constexpr since c++20
template <class T, class U, class... Args>
- T& emplace(initializer_list<U>, Args&&...);
+ constexpr T& emplace(initializer_list<U>, Args&&...); // constexpr since c++20
template <size_t I, class... Args>
- variant_alternative_t<I, variant>& emplace(Args&&...);
+ constexpr variant_alternative_t<I, variant>& emplace(Args&&...); // constexpr since c++20
template <size_t I, class U, class... Args>
- variant_alternative_t<I, variant>& emplace(initializer_list<U>, Args&&...);
+ constexpr variant_alternative_t<I, variant>&
+ emplace(initializer_list<U>, 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>
@@ -657,7 +660,8 @@ private:
template <size_t _Index, class _Tp>
struct _LIBCPP_TEMPLATE_VIS __alt {
- using __value_type = _Tp;
+ using __value_type = _Tp;
+ static constexpr size_t __index = _Index;
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI explicit constexpr __alt(in_place_t, _Args&&... __args)
@@ -672,7 +676,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 <size_t _Index, class _Tp, class... _Types> \
union _LIBCPP_TEMPLATE_VIS __union<destructible_trait, _Index, _Tp, _Types...> { \
public: \
@@ -686,26 +690,23 @@ 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; \
+ _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 \
\
- destructor \
- \
- __union& \
- operator=(const __union&) = default; \
- __union& operator=(__union&&) = default; \
- \
- private: \
- char __dummy; \
+ private : char __dummy; \
__alt<_Index, _Tp> __head; \
__union<destructible_trait, _Index + 1, _Types...> __tail; \
\
friend struct __access::__union; \
}
-_LIBCPP_VARIANT_UNION(_Trait::_TriviallyAvailable, ~__union() = default;);
-_LIBCPP_VARIANT_UNION(_Trait::_Available, ~__union(){});
-_LIBCPP_VARIANT_UNION(_Trait::_Unavailable, ~__union() = delete;);
+_LIBCPP_VARIANT_UNION(_Trait::_TriviallyAvailable,
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union() = default;);
+_LIBCPP_VARIANT_UNION(_Trait::_Available, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union(){});
+_LIBCPP_VARIANT_UNION(_Trait::_Unavailable, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union() = delete;);
# undef _LIBCPP_VARIANT_UNION
@@ -748,7 +749,7 @@ protected:
template <class _Traits, _Trait = _Traits::__destructible_trait>
class _LIBCPP_TEMPLATE_VIS __dtor;
-# define _LIBCPP_VARIANT_DESTRUCTOR(destructible_trait, destructor, destroy) \
+# define _LIBCPP_VARIANT_DESTRUCTOR(destructible_trait, destructor_definition, destroy) \
template <class... _Types> \
class _LIBCPP_TEMPLATE_VIS __dtor<__traits<_Types...>, destructible_trait> \
: public __base<destructible_trait, _Types...> { \
@@ -758,24 +759,25 @@ class _LIBCPP_TEMPLATE_VIS __dtor;
public: \
using __base_type::__base_type; \
using __base_type::operator=; \
+ _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 \
\
- __dtor(const __dtor&) = default; \
- __dtor(__dtor&&) = default; \
- destructor __dtor& operator=(const __dtor&) = default; \
- __dtor& operator=(__dtor&&) = default; \
- \
- protected: \
- inline _LIBCPP_HIDE_FROM_ABI destroy \
+ protected : destroy \
}
_LIBCPP_VARIANT_DESTRUCTOR(
- _Trait::_TriviallyAvailable, ~__dtor() = default;
- , void __destroy() noexcept { this->__index = __variant_npos<__index_t>; });
+ _Trait::_TriviallyAvailable, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() = default;
+ , _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept {
+ this->__index = __variant_npos<__index_t>;
+ });
_LIBCPP_VARIANT_DESTRUCTOR(
_Trait::_Available,
- ~__dtor() { __destroy(); },
- void __destroy() noexcept {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() { __destroy(); },
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept {
if (!this->valueless_by_exception()) {
__visitation::__base::__visit_alt(
[](auto& __alt) noexcept {
@@ -787,7 +789,8 @@ _LIBCPP_VARIANT_DESTRUCTOR(
this->__index = __variant_npos<__index_t>;
});
-_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
@@ -800,23 +803,18 @@ public:
using __base_type::operator=;
protected:
- template <size_t _Ip, class _Tp, class... _Args>
- _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 <class _Rhs>
- _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<decltype(__rhs_alt)>(__rhs_alt).__value);
+ [&__lhs](auto&& __rhs_alt) {
+ std::__construct_at(std::addressof(__lhs.__data),
+ in_place_index<__decay_t<decltype(__rhs_alt)>::__index>,
+ std::forward<decltype(__rhs_alt)>(__rhs_alt).__value);
},
- __lhs,
std::forward<_Rhs>(__rhs));
__lhs.__index = __rhs_index;
}
@@ -826,7 +824,7 @@ protected:
template <class _Traits, _Trait = _Traits::__move_constructible_trait>
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... _Types> \
class _LIBCPP_TEMPLATE_VIS __move_constructor<__traits<_Types...>, move_constructible_trait> \
: public __ctor<__traits<_Types...>> { \
@@ -836,28 +834,33 @@ class _LIBCPP_TEMPLATE_VIS __move_constructor;
using __base_type::__base_type; \
using __base_type::operator=; \
\
- __move_constructor(const __move_constructor&) = default; \
- move_constructor ~__move_constructor() = default; \
- __move_constructor& operator=(const __move_constructor&) = default; \
- __move_constructor& operator=(__move_constructor&&) = default; \
+ _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,
- __move_constructor(__move_constructor&& __that) noexcept(__all<is_nothrow_move_constructible_v<_Types>...>::value)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&& __that) noexcept(
+ __all<is_nothrow_move_constructible_v<_Types>...>::value)
: __move_constructor(__valueless_t{}) { this->__generic_construct(*this, std::move(__that)); });
-_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 _Traits, _Trait = _Traits::__copy_constructible_trait>
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... _Types> \
class _LIBCPP_TEMPLATE_VIS __copy_constructor<__traits<_Types...>, copy_constructible_trait> \
: public __move_constructor<__traits<_Types...>> { \
@@ -867,20 +870,25 @@ class _LIBCPP_TEMPLATE_VIS __copy_constructor;
using __base_type::__base_type; \
using __base_type::operator=; \
\
- copy_constructor __copy_constructor(__copy_constructor&&) = default; \
- ~__copy_constructor() = default; \
- __copy_constructor& operator=(const __copy_constructor&) = default; \
- __copy_constructor& operator=(__copy_constructor&&) = default; \
+ _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_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(
+ const __copy_constructor& __that) = default;);
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(
- _Trait::_Available, __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_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
@@ -893,22 +901,24 @@ public:
using __base_type::operator=;
template <size_t _Ip, class... _Args>
- _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 <size_t _Ip, class _Tp, class _Arg>
- _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_HIDE_FROM_ABI void operator()(true_type) const { __this->__emplace<_Ip>(std::forward<_Arg>(__arg)); }
- _LIBCPP_HIDE_FROM_ABI void operator()(false_type) const {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()(true_type) const {
+ __this->__emplace<_Ip>(std::forward<_Arg>(__arg));
+ }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()(false_type) const {
__this->__emplace<_Ip>(_Tp(std::forward<_Arg>(__arg)));
}
__assignment* __this;
@@ -919,7 +929,7 @@ protected:
}
template <class _That>
- _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()) {
@@ -939,7 +949,7 @@ protected:
template <class _Traits, _Trait = _Traits::__move_assignable_trait>
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... _Types> \
class _LIBCPP_TEMPLATE_VIS __move_assignment<__traits<_Types...>, move_assignable_trait> \
: public __assignment<__traits<_Types...>> { \
@@ -949,33 +959,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,
- __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_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 _Traits, _Trait = _Traits::__copy_assignable_trait>
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... _Types> \
class _LIBCPP_TEMPLATE_VIS __copy_assignment<__traits<_Types...>, copy_assignable_trait> \
: public __move_assignment<__traits<_Types...>> { \
@@ -985,22 +998,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 __copy_assignment& operator=(__copy_assignment&&) = default; \
+ _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, __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_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
@@ -1016,11 +1035,11 @@ public:
_LIBCPP_HIDE_FROM_ABI __impl& operator=(__impl&&) = default;
template <size_t _Ip, class _Arg>
- _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()) {
@@ -1065,7 +1084,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()];
}
@@ -1207,7 +1226,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;
@@ -1217,7 +1236,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_assignable_v<_Tp&, _Arg> && 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;
@@ -1228,7 +1247,7 @@ public:
enable_if_t<(_Ip < sizeof...(_Types)), int> = 0,
class _Tp = variant_alternative_t<_Ip, variant<_Types...>>,
enable_if_t<is_constructible_v<_Tp, _Args...>, 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)...);
}
@@ -1238,7 +1257,7 @@ public:
enable_if_t<(_Ip < sizeof...(_Types)), int> = 0,
class _Tp = variant_alternative_t<_Ip, variant<_Types...>>,
enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _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)...);
}
@@ -1246,7 +1265,7 @@ public:
class... _Args,
size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
enable_if_t<is_constructible_v<_Tp, _Args...>, 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)...);
}
@@ -1255,7 +1274,7 @@ public:
class... _Args,
size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _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)...);
}
@@ -1269,7 +1288,7 @@ public:
enable_if_t< __all<(__dependent_type<is_move_constructible<_Types>, _Dummy>::value &&
__dependent_type<is_swappable<_Types>, _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_);
}
@@ -1552,7 +1571,7 @@ visit(_Visitor&& __visitor, _Vs&&... __vs) {
# endif
template <class... _Types>
-_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 4b9eaba2d2ba81..98faf84fa52d4b 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<Dummy, NoThrowT>;
@@ -119,17 +122,17 @@ void test_T_assignment_noexcept() {
}
}
-void test_T_assignment_sfinae() {
+constexpr void test_T_assignment_sfinae() {
{
using V = std::variant<long, long long>;
static_assert(!std::is_assignable<V, int>::value, "ambiguous");
}
{
using V = std::variant<std::string, std::string>;
- static_assert(!std::is_assignable<V, const char *>::value, "ambiguous");
+ static_assert(!std::is_assignable<V, const char*>::value, "ambiguous");
}
{
- using V = std::variant<std::string, void *>;
+ using V = std::variant<std::string, void*>;
static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
}
{
@@ -138,8 +141,7 @@ void test_T_assignment_sfinae() {
}
{
using V = std::variant<std::unique_ptr<int>, bool>;
- static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value,
- "no explicit bool in operator=");
+ static_assert(!std::is_assignable<V, std::unique_ptr<char>>::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<X>;
- static_assert(std::is_assignable<V, Y>::value,
- "regression on user-defined conversions in operator=");
+ static_assert(std::is_assignable<V, Y>::value, "regression on user-defined conversions in operator=");
}
}
-void test_T_assignment_basic() {
+TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() {
{
std::variant<int> v(43);
v = 42;
@@ -184,19 +185,146 @@ void test_T_assignment_basic() {
}
{
std::variant<std::string, bool> 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<bool, std::unique_ptr<int>> 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 <bool CtorNoexcept, bool MoveCtorNoexcept>
+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<bool, std::unique_ptr<int>> v;
- v = nullptr;
- assert(v.index() == 1);
- assert(std::get<1>(v) == nullptr);
+ using V = std::variant<int, Trace<false, false>>;
+ 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<int, Trace<false, true>>;
+ 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<int, Trace<true, false>>;
+ 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<int, Trace<true, true>>;
+ 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<int, Trace<false, false>>;
+ TraceStat stat;
+ V v{&stat};
+ v = Trace<false, false>::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<int, Trace<false, false>>;
+ TraceStat stat;
+ V v{&stat};
+ Trace<false, false>::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<bool> vec = {true};
std::variant<bool, int> 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 096d365d2d7525..a6d3f34114eb71 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<NTCopyAssign>::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<TCopyAssign>::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<TCopyAssignNTMoveAssign>, "");
#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 <class Variant> void makeEmpty(Variant &v) {
+template <class Variant>
+void makeEmpty(Variant& v) {
Variant v2(std::in_place_type<MakeEmptyT>);
try {
v = std::move(v2);
@@ -193,7 +217,7 @@ template <class Variant> 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<CopyMaybeThrows>;
static_assert(!std::is_nothrow_copy_assignable<V>::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<int, long>;
static_assert(std::is_copy_assignable<V>::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<std::string>, "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 <typename T> struct Result { std::size_t index; T value; };
+template <typename T>
+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<int>;
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<int, long, unsigned>;
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<int, CopyAssign, unsigned>;
- V v1(std::in_place_type<CopyAssign>, 43);
- V v2(std::in_place_type<CopyAssign>, 42);
- CopyAssign::reset();
- V &vref = (v1 = v2);
+ using V = std::variant<int, CopyAssign, unsigned>;
+ 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<CopyAssign>, 43, &alive, ©_construct, ©_assign, &move_construct, &move_assign);
+ V v2(std::in_place_type<CopyAssign>, 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<int, MET, std::string>;
- V v1(std::in_place_type<MET>);
- MET &mref = std::get<1>(v1);
- V v2(std::in_place_type<MET>);
- 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<int, long, unsigned>;
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<int, CopyAssign, unsigned>;
- CopyAssign::reset();
+ using V = std::variant<int, CopyAssign, unsigned>;
+ 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<unsigned>, 43u);
- V v2(std::in_place_type<CopyAssign>, 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<CopyAssign>, 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<long> operator()() const {
+ using V = std::variant<int, long, unsigned>;
+ 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<int> operator()() const {
+ using V = std::variant<int, TCopyAssign, unsigned>;
+ V v(std::in_place_type<unsigned>, 43u);
+ V v2(std::in_place_type<TCopyAssign>, 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<int, MET, std::string>;
+ V v1(std::in_place_type<MET>);
+ MET& mref = std::get<1>(v1);
+ V v2(std::in_place_type<MET>);
+ try {
+ v1 = v2;
+ assert(false);
+ } catch (...) {
+ }
+ assert(v1.index() == 1);
+ assert(&std::get<1>(v1) == &mref);
+ }
+
+ // difference indices
{
using V = std::variant<int, CopyThrows, std::string>;
V v1(std::in_place_type<std::string>, "hello");
@@ -496,7 +566,7 @@ void test_copy_assignment_different_index() {
using V = std::variant<int, CopyThrows, std::string>;
V v1(std::in_place_type<CopyThrows>);
V v2(std::in_place_type<std::string>, "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<int, MoveThrows, std::string>;
V v1(std::in_place_type<MoveThrows>);
V v2(std::in_place_type<std::string>, "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<long> operator()() const {
- using V = std::variant<int, long, unsigned>;
- 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<int> operator()() const {
- using V = std::variant<int, TCopyAssign, unsigned>;
- V v(std::in_place_type<unsigned>, 43u);
- V v2(std::in_place_type<TCopyAssign>, 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 <std::size_t NewIdx, class ValueType>
-constexpr bool test_constexpr_assign_imp(
- std::variant<long, void*, int>&& v, ValueType&& new_value)
-{
- const std::variant<long, void*, int> cp(
- std::forward<ValueType>(new_value));
+template <std::size_t NewIdx, class T, class ValueType>
+constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {
+ using Variant = std::decay_t<T>;
+ const Variant cp(std::forward<ValueType>(new_value));
v = cp;
- return v.index() == NewIdx &&
- std::get<NewIdx>(v) == std::get<NewIdx>(cp);
+ assert(v.index() == NewIdx);
+ assert(std::get<NewIdx>(v) == std::get<NewIdx>(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<long, void*, int>;
static_assert(std::is_trivially_copyable<V>::value, "");
static_assert(std::is_trivially_copy_assignable<V>::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<long, void*, NonTrivialCopyAssign>;
+ static_assert(!std::is_trivially_copyable<V>::value, "");
+ static_assert(!std::is_trivially_copy_assignable<V>::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 84094347aed3a7..157ff68f374825 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<NTMoveAssign>::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<TMoveAssign>::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<TrivialCopyNontrivialMove>, "");
static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
-
-void test_move_assignment_noexcept() {
+constexpr void test_move_assignment_noexcept() {
{
using V = std::variant<int>;
static_assert(std::is_nothrow_move_assignable<V>::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<int, long>;
static_assert(std::is_move_assignable<V>::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<std::string>, "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 <typename T> struct Result { std::size_t index; T value; };
+template <typename T>
+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<int>;
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<int, long, unsigned>;
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<int, MoveAssign, unsigned>;
- V v1(std::in_place_type<MoveAssign>, 43);
- V v2(std::in_place_type<MoveAssign>, 42);
- MoveAssign::reset();
- V &vref = (v1 = std::move(v2));
+ using V = std::variant<int, MoveAssign, unsigned>;
+ int move_construct = 0;
+ int move_assign = 0;
+ V v1(std::in_place_type<MoveAssign>, 43, &move_construct, &move_assign);
+ V v2(std::in_place_type<MoveAssign>, 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<int, MET, std::string>;
- V v1(std::in_place_type<MET>);
- MET &mref = std::get<1>(v1);
- V v2(std::in_place_type<MET>);
- 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<int, long, unsigned>;
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<int, MoveAssign, unsigned>;
+ using V = std::variant<int, MoveAssign, unsigned>;
+ int move_construct = 0;
+ int move_assign = 0;
V v1(std::in_place_type<unsigned>, 43u);
- V v2(std::in_place_type<MoveAssign>, 42);
- MoveAssign::reset();
- V &vref = (v1 = std::move(v2));
+ V v2(std::in_place_type<MoveAssign>, 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<int, MET, std::string>;
- V v1(std::in_place_type<int>);
- V v2(std::in_place_type<MET>);
- try {
- v1 = std::move(v2);
- assert(false);
- } catch (...) {
- }
- assert(v1.valueless_by_exception());
- assert(v1.index() == std::variant_npos);
- }
- {
- using V = std::variant<int, MET, std::string>;
- V v1(std::in_place_type<MET>);
- V v2(std::in_place_type<std::string>, "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 <std::size_t NewIdx, class ValueType>
-constexpr bool test_constexpr_assign_imp(
- std::variant<long, void*, int>&& v, ValueType&& new_value)
-{
- std::variant<long, void*, int> v2(
- std::forward<ValueType>(new_value));
+void test_assignment_throw() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ using MET = MakeEmptyT;
+ // same index
+ {
+ using V = std::variant<int, MET, std::string>;
+ V v1(std::in_place_type<MET>);
+ MET& mref = std::get<1>(v1);
+ V v2(std::in_place_type<MET>);
+ try {
+ v1 = std::move(v2);
+ assert(false);
+ } catch (...) {
+ }
+ assert(v1.index() == 1);
+ assert(&std::get<1>(v1) == &mref);
+ }
+
+ // different indices
+ {
+ using V = std::variant<int, MET, std::string>;
+ V v1(std::in_place_type<int>);
+ V v2(std::in_place_type<MET>);
+ try {
+ v1 = std::move(v2);
+ assert(false);
+ } catch (...) {
+ }
+ assert(v1.valueless_by_exception());
+ assert(v1.index() == std::variant_npos);
+ }
+ {
+ using V = std::variant<int, MET, std::string>;
+ V v1(std::in_place_type<MET>);
+ V v2(std::in_place_type<std::string>, "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 <std::size_t NewIdx, class T, class ValueType>
+constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {
+ using Variant = std::decay_t<T>;
+ Variant v2(std::forward<ValueType>(new_value));
const auto cp = v2;
- v = std::move(v2);
- return v.index() == NewIdx &&
- std::get<NewIdx>(v) == std::get<NewIdx>(cp);
+ v = std::move(v2);
+ assert(v.index() == NewIdx);
+ assert(std::get<NewIdx>(v) == std::get<NewIdx>(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<long, void*, int>;
static_assert(std::is_trivially_copyable<V>::value, "");
static_assert(std::is_trivially_move_assignable<V>::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<long, void*, NonTrivialMoveAssign>;
+ static_assert(!std::is_trivially_copyable<V>::value);
+ static_assert(!std::is_trivially_move_assignable<V>::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 d1e5768f58d2b1..820ff9e0d1a9d0 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<NonT>::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<NTCopy>::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<TCopyNTMove>::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 <class Variant> void makeEmpty(Variant &v) {
+template <class Variant>
+void makeEmpty(Variant& v) {
Variant v2(std::in_place_type<MakeEmptyT>);
try {
v = std::move(v2);
@@ -98,7 +99,7 @@ template <class Variant> void makeEmpty(Variant &v) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
-void test_copy_ctor_sfinae() {
+constexpr void test_copy_ctor_sfinae() {
{
using V = std::variant<int, long>;
static_assert(std::is_copy_constructible<V>::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<int> v(std::in_place_index<0>, 42);
std::variant<int> v2 = v;
@@ -214,21 +215,21 @@ void test_copy_ctor_valueless_by_exception() {
using V = std::variant<int, MakeEmptyT>;
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 <std::size_t Idx>
-constexpr bool test_constexpr_copy_ctor_imp(std::variant<long, void*, const int> const& v) {
+template <std::size_t Idx, class T>
+constexpr void test_constexpr_copy_ctor_imp(const T& v) {
auto v2 = v;
- return v2.index() == v.index() &&
- v2.index() == Idx &&
- std::get<Idx>(v2) == std::get<Idx>(v);
+ assert(v2.index() == v.index());
+ assert(v2.index() == Idx);
+ assert(std::get<Idx>(v2) == std::get<Idx>(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<long, void*, const int>;
#ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
@@ -237,18 +238,57 @@ void test_constexpr_copy_ctor() {
static_assert(std::is_trivially_move_constructible<V>::value, "");
static_assert(!std::is_copy_assignable<V>::value, "");
static_assert(!std::is_move_assignable<V>::value, "");
-#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
+#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
static_assert(std::is_trivially_copyable<V>::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<V>::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<long, NonTrivialCopyCtor, void*>;
+ static_assert(!std::is_trivially_copy_constructible<V>::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/move.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp
index e2518fe29caf7e..4e8453c23cf550 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<NTMove>::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<TMoveNTCopy>::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 <class Variant> void makeEmpty(Variant &v) {
+template <class Variant>
+void makeEmpty(Variant& v) {
Variant v2(std::in_place_type<MakeEmptyT>);
try {
v = std::move(v2);
@@ -100,7 +101,7 @@ template <class Variant> void makeEmpty(Variant &v) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
-void test_move_noexcept() {
+constexpr void test_move_noexcept() {
{
using V = std::variant<int, long>;
static_assert(std::is_nothrow_move_constructible<V>::value, "");
@@ -119,7 +120,7 @@ void test_move_noexcept() {
}
}
-void test_move_ctor_sfinae() {
+constexpr void test_move_ctor_sfinae() {
{
using V = std::variant<int, long>;
static_assert(std::is_move_constructible<V>::value, "");
@@ -158,9 +159,12 @@ void test_move_ctor_sfinae() {
}
template <typename T>
-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<int> v(std::in_place_index<0>, 42);
std::variant<int> v2 = std::move(v);
@@ -289,16 +293,16 @@ void test_move_ctor_valueless_by_exception() {
#endif // TEST_HAS_NO_EXCEPTIONS
}
-template <std::size_t Idx>
-constexpr bool test_constexpr_ctor_imp(std::variant<long, void*, const int> const& v) {
+template <std::size_t Idx, class T>
+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<Idx>(v2) == std::get<Idx>(v);
+ auto v2 = std::move(copy);
+ assert(v2.index() == v.index());
+ assert(v2.index() == Idx);
+ assert(std::get<Idx>(v2) == std::get<Idx>(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<long, void*, const int>;
#ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
@@ -307,21 +311,58 @@ void test_constexpr_move_ctor() {
static_assert(std::is_trivially_move_constructible<V>::value, "");
static_assert(!std::is_copy_assignable<V>::value, "");
static_assert(!std::is_move_assignable<V>::value, "");
-#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
+#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
static_assert(std::is_trivially_copyable<V>::value, "");
#endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
static_assert(std::is_trivially_move_constructible<V>::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<long, NonTrivialMoveCtor, void*>;
+ static_assert(!std::is_trivially_move_constructible<V>::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 2e026038c97a3c..53c5283b2edc6f 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<NonTDtor>::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<NonTDtor1>::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<TDtor>::value, "");
static_assert(std::is_trivially_destructible<TDtor>::value, "");
-int main(int, char**) {
+TEST_CONSTEXPR_CXX20 bool test() {
{
using V = std::variant<int, long, TDtor>;
static_assert(std::is_trivially_destructible<V>::value, "");
+ [[maybe_unused]] V v(std::in_place_index<2>);
}
{
using V = std::variant<NonTDtor, int, NonTDtor1>;
static_assert(!std::is_trivially_destructible<V>::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 2fe9033dd8166f..f98d968f0eae04 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 <class Var, std::size_t I, class... Args>
-constexpr auto test_emplace_exists_imp(int) -> decltype(
- std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) {
+constexpr auto test_emplace_exists_imp(int)
+ -> decltype(std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) {
return true;
}
@@ -36,28 +36,32 @@ constexpr auto test_emplace_exists_imp(long) -> bool {
return false;
}
-template <class Var, std::size_t I, class... Args> constexpr bool emplace_exists() {
+template <class Var, std::size_t I, class... Args>
+constexpr bool emplace_exists() {
return test_emplace_exists_imp<Var, I, Args...>(0);
}
-void test_emplace_sfinae() {
+constexpr void test_emplace_sfinae() {
{
- using V = std::variant<int, void *, const void *, TestTypes::NoCtors>;
+ using V = std::variant<int, void*, const void*, TestTypes::NoCtors>;
static_assert(emplace_exists<V, 0>(), "");
static_assert(emplace_exists<V, 0, int>(), "");
- static_assert(!emplace_exists<V, 0, decltype(nullptr)>(),
- "cannot construct");
+ static_assert(!emplace_exists<V, 0, decltype(nullptr)>(), "cannot construct");
static_assert(emplace_exists<V, 1, decltype(nullptr)>(), "");
- static_assert(emplace_exists<V, 1, int *>(), "");
- static_assert(!emplace_exists<V, 1, const int *>(), "");
+ static_assert(emplace_exists<V, 1, int*>(), "");
+ static_assert(!emplace_exists<V, 1, const int*>(), "");
static_assert(!emplace_exists<V, 1, int>(), "cannot construct");
- static_assert(emplace_exists<V, 2, const int *>(), "");
- static_assert(emplace_exists<V, 2, int *>(), "");
+ static_assert(emplace_exists<V, 2, const int*>(), "");
+ static_assert(emplace_exists<V, 2, int*>(), "");
static_assert(!emplace_exists<V, 3>(), "cannot construct");
}
}
-void test_basic() {
+struct NoCtor {
+ NoCtor() = delete;
+};
+
+TEST_CONSTEXPR_CXX20 void test_basic() {
{
using V = std::variant<int>;
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<int, long, const void *, TestTypes::NoCtors, std::string>;
+ using V = std::variant<int, long, const void*, NoCtor, std::string>;
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 9068aacc435928..4c635570bd5622 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<int> il, int v)
- : size(il.size()), value(v) {}
+ constexpr InitListArg(std::initializer_list<int> il, int v) : size(il.size()), value(v) {}
};
template <class Var, std::size_t I, class... Args>
-constexpr auto test_emplace_exists_imp(int) -> decltype(
- std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) {
+constexpr auto test_emplace_exists_imp(int)
+ -> decltype(std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) {
return true;
}
@@ -47,13 +46,13 @@ constexpr auto test_emplace_exists_imp(long) -> bool {
return false;
}
-template <class Var, std::size_t I, class... Args> constexpr bool emplace_exists() {
+template <class Var, std::size_t I, class... Args>
+constexpr bool emplace_exists() {
return test_emplace_exists_imp<Var, I, Args...>(0);
}
-void test_emplace_sfinae() {
- using V =
- std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>;
+constexpr void test_emplace_sfinae() {
+ using V = std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>;
using IL = std::initializer_list<int>;
static_assert(!emplace_exists<V, 1, IL>(), "no such constructor");
static_assert(emplace_exists<V, 2, IL>(), "");
@@ -65,8 +64,12 @@ void test_emplace_sfinae() {
static_assert(!emplace_exists<V, 3, IL, int, int>(), "too many args");
}
-void test_basic() {
- using V = std::variant<int, InitList, InitListArg, TestTypes::NoCtors>;
+struct NoCtor {
+ NoCtor() = delete;
+};
+
+TEST_CONSTEXPR_CXX20 void test_basic() {
+ using V = std::variant<int, InitList, InitListArg, NoCtor>;
V v;
auto& ref1 = v.emplace<1>({1, 2, 3});
static_assert(std::is_same_v<InitList&, decltype(ref1)>, "");
@@ -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 4e9f67775d10c4..c2ed54d8a6257c 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 <class Var, class T, class... Args>
-constexpr auto test_emplace_exists_imp(int) -> decltype(
- std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) {
+constexpr auto test_emplace_exists_imp(int)
+ -> decltype(std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) {
return true;
}
@@ -35,28 +35,32 @@ constexpr auto test_emplace_exists_imp(long) -> bool {
return false;
}
-template <class... Args> constexpr bool emplace_exists() {
+template <class... Args>
+constexpr bool emplace_exists() {
return test_emplace_exists_imp<Args...>(0);
}
-void test_emplace_sfinae() {
+constexpr void test_emplace_sfinae() {
{
- using V = std::variant<int, void *, const void *, TestTypes::NoCtors>;
+ using V = std::variant<int, void*, const void*, TestTypes::NoCtors>;
static_assert(emplace_exists<V, int>(), "");
static_assert(emplace_exists<V, int, int>(), "");
- static_assert(!emplace_exists<V, int, decltype(nullptr)>(),
- "cannot construct");
- static_assert(emplace_exists<V, void *, decltype(nullptr)>(), "");
- static_assert(!emplace_exists<V, void *, int>(), "cannot construct");
- static_assert(emplace_exists<V, void *, int *>(), "");
- static_assert(!emplace_exists<V, void *, const int *>(), "");
- static_assert(emplace_exists<V, const void *, const int *>(), "");
- static_assert(emplace_exists<V, const void *, int *>(), "");
+ static_assert(!emplace_exists<V, int, decltype(nullptr)>(), "cannot construct");
+ static_assert(emplace_exists<V, void*, decltype(nullptr)>(), "");
+ static_assert(!emplace_exists<V, void*, int>(), "cannot construct");
+ static_assert(emplace_exists<V, void*, int*>(), "");
+ static_assert(!emplace_exists<V, void*, const int*>(), "");
+ static_assert(emplace_exists<V, const void*, const int*>(), "");
+ static_assert(emplace_exists<V, const void*, int*>(), "");
static_assert(!emplace_exists<V, TestTypes::NoCtors>(), "cannot construct");
}
}
-void test_basic() {
+struct NoCtor {
+ NoCtor() = delete;
+};
+
+TEST_CONSTEXPR_CXX20 void test_basic() {
{
using V = std::variant<int>;
V v(42);
@@ -70,8 +74,7 @@ void test_basic() {
assert(&ref2 == &std::get<0>(v));
}
{
- using V =
- std::variant<int, long, const void *, TestTypes::NoCtors, std::string>;
+ using V = std::variant<int, long, const void*, NoCtor, std::string>;
const int x = 100;
V v(std::in_place_type<int>, -1);
// default emplace a value
@@ -79,8 +82,8 @@ void test_basic() {
static_assert(std::is_same_v<long&, decltype(ref1)>, "");
assert(std::get<1>(v) == 0);
assert(&ref1 == &std::get<1>(v));
- auto& ref2 = v.emplace<const void *>(&x);
- static_assert(std::is_same_v<const void *&, decltype(ref2)>, "");
+ auto& ref2 = v.emplace<const void*>(&x);
+ static_assert(std::is_same_v<const void*&, decltype(ref2)>, "");
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 74d834b9b34575..644f2418b9255a 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<int> il, int v)
- : size(il.size()), value(v) {}
+ constexpr InitListArg(std::initializer_list<int> il, int v) : size(il.size()), value(v) {}
};
template <class Var, class T, class... Args>
-constexpr auto test_emplace_exists_imp(int) -> decltype(
- std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) {
+constexpr auto test_emplace_exists_imp(int)
+ -> decltype(std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) {
return true;
}
@@ -47,13 +46,13 @@ constexpr auto test_emplace_exists_imp(long) -> bool {
return false;
}
-template <class... Args> constexpr bool emplace_exists() {
+template <class... Args>
+constexpr bool emplace_exists() {
return test_emplace_exists_imp<Args...>(0);
}
-void test_emplace_sfinae() {
- using V =
- std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>;
+constexpr void test_emplace_sfinae() {
+ using V = std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>;
using IL = std::initializer_list<int>;
static_assert(emplace_exists<V, InitList, IL>(), "");
static_assert(!emplace_exists<V, InitList, int>(), "args don't match");
@@ -61,31 +60,44 @@ void test_emplace_sfinae() {
static_assert(emplace_exists<V, InitListArg, IL, int>(), "");
static_assert(!emplace_exists<V, InitListArg, int>(), "args don't match");
static_assert(!emplace_exists<V, InitListArg, IL>(), "too few args");
- static_assert(!emplace_exists<V, InitListArg, IL, int, int>(),
- "too many args");
+ static_assert(!emplace_exists<V, InitListArg, IL, int, int>(), "too many args");
}
-void test_basic() {
- using V = std::variant<int, InitList, InitListArg, TestTypes::NoCtors>;
+struct NoCtor {
+ NoCtor() = delete;
+};
+
+TEST_CONSTEXPR_CXX20 void test_basic() {
+ using V = std::variant<int, InitList, InitListArg, NoCtor>;
V v;
auto& ref1 = v.emplace<InitList>({1, 2, 3});
- static_assert(std::is_same_v<InitList&,decltype(ref1)>, "");
+ static_assert(std::is_same_v<InitList&, decltype(ref1)>, "");
assert(std::get<InitList>(v).size == 3);
assert(&ref1 == &std::get<InitList>(v));
auto& ref2 = v.emplace<InitListArg>({1, 2, 3, 4}, 42);
- static_assert(std::is_same_v<InitListArg&,decltype(ref2)>, "");
+ static_assert(std::is_same_v<InitListArg&, decltype(ref2)>, "");
assert(std::get<InitListArg>(v).size == 4);
assert(std::get<InitListArg>(v).value == 42);
assert(&ref2 == &std::get<InitListArg>(v));
auto& ref3 = v.emplace<InitList>({1});
- static_assert(std::is_same_v<InitList&,decltype(ref3)>, "");
+ static_assert(std::is_same_v<InitList&, decltype(ref3)>, "");
assert(std::get<InitList>(v).size == 1);
assert(&ref3 == &std::get<InitList>(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 1802bc4670bba9..db05691c55818c 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 <bool Throws> void do_throw() {}
+template <bool Throws>
+constexpr void do_throw() {}
-template <> void do_throw<true>() {
+template <>
+void do_throw<true>() {
#ifndef TEST_HAS_NO_EXCEPTIONS
throw 42;
#else
@@ -63,60 +65,49 @@ template <> void do_throw<true>() {
#endif
}
-template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
- bool NT_Swap, bool EnableSwap = true>
+template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap, bool EnableSwap = true>
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<!NT_Move>();
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<!NT_MoveAssign>();
- value = o.value;
+ value = o.value;
o.value = -1;
return *this;
}
- int value;
};
-template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
- bool NT_Swap, bool EnableSwap>
-int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
- EnableSwap>::move_called = 0;
-template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
- bool NT_Swap, bool EnableSwap>
-int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
- EnableSwap>::move_assign_called = 0;
-template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
- bool NT_Swap, bool EnableSwap>
-int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
- EnableSwap>::swap_called = 0;
-
-template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
- bool NT_Swap>
-void swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign,
- NT_Swap, true> &lhs,
- NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign,
- NT_Swap, true> &rhs) noexcept(NT_Swap) {
- lhs.swap_called++;
+
+template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap>
+constexpr void
+swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, true>& lhs,
+ NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, true>& rhs) noexcept(NT_Swap) {
+ ++*lhs.swap_called;
do_throw<!NT_Swap>();
- 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<false, true, false, true, false, false>;
// throwing copy and move assign, nothrow move ctor, no swap provided
using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
// nothrow move ctor, throwing move assignment, swap provided
-using NothrowMoveCtorWithThrowingSwap =
- NothrowTypeImp<false, true, false, false, false, true>;
+using NothrowMoveCtorWithThrowingSwap = NothrowTypeImp<false, true, false, false, false, true>;
// throwing move ctor, nothrow move assignment, no swap provided
-using ThrowingMoveCtor =
- NothrowTypeImp<false, false, false, true, false, false>;
+using ThrowingMoveCtor = NothrowTypeImp<false, false, false, true, false, false>;
// throwing special members, nothrowing swap
-using ThrowingTypeWithNothrowSwap =
- NothrowTypeImp<false, false, false, false, true, true>;
-using NothrowTypeWithThrowingSwap =
- NothrowTypeImp<true, true, true, true, false, true>;
+using ThrowingTypeWithNothrowSwap = NothrowTypeImp<false, false, false, false, true, true>;
+using NothrowTypeWithThrowingSwap = NothrowTypeImp<true, true, true, true, false, true>;
// throwing move assign with nothrow move and nothrow swap
-using ThrowingMoveAssignNothrowMoveCtorWithSwap =
- NothrowTypeImp<false, true, false, false, true, true>;
+using ThrowingMoveAssignNothrowMoveCtorWithSwap = NothrowTypeImp<false, true, false, false, true, true>;
// throwing move assign with nothrow move but no swap.
-using ThrowingMoveAssignNothrowMoveCtor =
- NothrowTypeImp<false, true, false, false, false, false>;
+using ThrowingMoveAssignNothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
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<true>();
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, int>;
- T::reset();
- V v1(std::in_place_index<0>, 42);
- V v2(std::in_place_index<0>, 100);
+ using V = std::variant<ThrowingTypeWithNothrowSwap, int>;
+ 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, int>;
- T::reset();
- V v1(std::in_place_index<0>, 42);
- V v2(std::in_place_index<0>, 100);
+ using V = std::variant<NothrowMoveable, int>;
+ 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, int>;
- 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, int>;
- 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<NothrowTypeWithThrowingSwap, int>;
+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<ThrowingMoveCtor, int>;
+ 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, int>;
- 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<ThrowingMoveAssignNothrowMoveCtor, int>;
+ 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, int>;
- T::reset();
- V v1(std::in_place_index<0>, 42);
+ using V = std::variant<NothrowMoveCtorWithThrowingSwap, int>;
+ 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, T2>;
- T1::reset();
- T2::reset();
- V v1(std::in_place_index<0>, 42);
- V v2(std::in_place_index<1>, 100);
+ using V = std::variant<ThrowingTypeWithNothrowSwap, NonThrowingNonNoexceptType>;
+ 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, T2>;
- T1::reset();
- T2::reset();
- V v1(std::in_place_index<0>, 42);
- V v2(std::in_place_index<1>, 100);
+ using V = std::variant<NonThrowingNonNoexceptType, ThrowingTypeWithNothrowSwap>;
+ 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<T1, T2>;
- T2::reset();
+ using V = std::variant<ThrowsOnSecondMove, NonThrowingNonNoexceptType>;
+ 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, T2>;
- T1::reset();
- V v1(std::in_place_index<0>, 42);
+ using V = std::variant<NonThrowingNonNoexceptType, ThrowsOnSecondMove>;
+ 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, T2>;
- T1::reset();
- T2::reset();
- V v1(std::in_place_index<0>, 42);
- V v2(std::in_place_index<1>, 100);
+ using V = std::variant<ThrowingTypeWithNothrowSwap, NothrowMoveable>;
+ 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 <class Var>
-constexpr auto has_swap_member_imp(int)
- -> decltype(std::declval<Var &>().swap(std::declval<Var &>()), true) {
+constexpr auto has_swap_member_imp(int) -> decltype(std::declval<Var&>().swap(std::declval<Var&>()), true) {
return true;
}
-template <class Var> constexpr auto has_swap_member_imp(long) -> bool {
+template <class Var>
+constexpr auto has_swap_member_imp(long) -> bool {
return false;
}
-template <class Var> constexpr bool has_swap_member() {
+template <class Var>
+constexpr bool has_swap_member() {
return has_swap_member_imp<Var>(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<int, NothrowMoveable>;
static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
@@ -581,12 +589,28 @@ void test_swap_noexcept() {
template class std::variant<int, NotSwappable>;
#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;
}
More information about the libcxx-commits
mailing list