diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv --- a/libcxx/docs/Status/SpaceshipProjects.csv +++ b/libcxx/docs/Status/SpaceshipProjects.csv @@ -25,7 +25,7 @@ | nullopt",None,Kent Ross,|In Progress| "| `[variant.relops] `_ | `[variant.monostate.relops] `_","| `monostate `_ -| `variant `_",None,Kent Ross,|In Progress| +| `variant `_",None,Kent Ross,|Complete| | `[unique.ptr.special] `_,| `unique_ptr `_,[comparisons.three.way],Adrian Vogelsgesang,|Complete| | `[util.smartptr.shared.cmp] `_,| `shared_ptr `_,[comparisons.three.way],Adrian Vogelsgesang,|Complete| | `[type.index.members] `_,| `type_index `_,None,Adrian Vogelsgesang,|In Progress| diff --git a/libcxx/include/__variant/monostate.h b/libcxx/include/__variant/monostate.h --- a/libcxx/include/__variant/monostate.h +++ b/libcxx/include/__variant/monostate.h @@ -10,6 +10,7 @@ #ifndef _LIBCPP___VARIANT_MONOSTATE_H #define _LIBCPP___VARIANT_MONOSTATE_H +#include <__compare/ordering.h> #include <__config> #include <__functional/hash.h> #include @@ -24,6 +25,19 @@ struct _LIBCPP_TEMPLATE_VIS monostate {}; +inline _LIBCPP_INLINE_VISIBILITY +constexpr bool operator==(monostate, monostate) noexcept { return true; } + +#if _LIBCPP_STD_VER > 17 + +inline _LIBCPP_INLINE_VISIBILITY +constexpr strong_ordering operator<=>(monostate, monostate) noexcept { return strong_ordering::equal; } + +#else // _LIBCPP_STD_VER > 17 + +inline _LIBCPP_INLINE_VISIBILITY +constexpr bool operator!=(monostate, monostate) noexcept { return false; } + inline _LIBCPP_INLINE_VISIBILITY constexpr bool operator<(monostate, monostate) noexcept { return false; } @@ -36,11 +50,7 @@ inline _LIBCPP_INLINE_VISIBILITY constexpr bool operator>=(monostate, monostate) noexcept { return true; } -inline _LIBCPP_INLINE_VISIBILITY -constexpr bool operator==(monostate, monostate) noexcept { return true; } - -inline _LIBCPP_INLINE_VISIBILITY -constexpr bool operator!=(monostate, monostate) noexcept { return false; } +#endif // _LIBCPP_STD_VER > 17 template <> struct _LIBCPP_TEMPLATE_VIS hash { diff --git a/libcxx/include/variant b/libcxx/include/variant --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -165,6 +165,10 @@ template constexpr bool operator>=(const variant&, const variant&); + template + constexpr common_comparison_category_t...> + operator<=>(const variant&, const variant&); // since C++20 + // 20.7.6, visitation template constexpr see below visit(Visitor&&, Variants&&...); @@ -176,12 +180,13 @@ struct monostate; // 20.7.8, monostate relational operators - constexpr bool operator<(monostate, monostate) noexcept; - constexpr bool operator>(monostate, monostate) noexcept; - constexpr bool operator<=(monostate, monostate) noexcept; - constexpr bool operator>=(monostate, monostate) noexcept; constexpr bool operator==(monostate, monostate) noexcept; - constexpr bool operator!=(monostate, monostate) noexcept; + constexpr bool operator!=(monostate, monostate) noexcept; // until C++20 + constexpr bool operator<(monostate, monostate) noexcept; // until C++20 + constexpr bool operator>(monostate, monostate) noexcept; // until C++20 + constexpr bool operator<=(monostate, monostate) noexcept; // until C++20 + constexpr bool operator>=(monostate, monostate) noexcept; // until C++20 + constexpr strong_ordering operator<=>(monostate, monostate) noexcept; // since C++20 // 20.7.9, specialized algorithms template @@ -201,6 +206,9 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__availability> +#include <__compare/common_comparison_category.h> +#include <__compare/compare_three_way_result.h> +#include <__compare/three_way_comparable.h> #include <__config> #include <__functional/hash.h> #include <__functional/operations.h> @@ -1630,6 +1638,27 @@ return __variant::__visit_value_at(__lhs.index(), __convert_to_bool>{}, __lhs, __rhs); } +#if _LIBCPP_STD_VER > 17 + +template requires (three_way_comparable<_Types> && ...) +inline _LIBCPP_INLINE_VISIBILITY constexpr +common_comparison_category_t...> +operator<=>(const variant<_Types...>& __lhs, + const variant<_Types...>& __rhs) { + using __variant_detail::__visitation::__variant; + using __result_t = common_comparison_category_t...>; + if (__lhs.valueless_by_exception() && __rhs.valueless_by_exception()) return strong_ordering::equal; + if (__lhs.valueless_by_exception()) return strong_ordering::less; + if (__rhs.valueless_by_exception()) return strong_ordering::greater; + if (auto __c = __lhs.index() <=> __rhs.index(); __c != 0) return __c; + auto __three_way = [](const _Type& __v, const _Type& __w) -> __result_t { + return __v <=> __w; + }; + return __variant::__visit_value_at(__lhs.index(), __three_way, __lhs, __rhs); +} + +#endif // _LIBCPP_STD_VER > 17 + template inline _LIBCPP_INLINE_VISIBILITY constexpr bool operator!=(const variant<_Types...>& __lhs, diff --git a/libcxx/test/std/utilities/variant/variant.monostate.relops/relops.pass.cpp b/libcxx/test/std/utilities/variant/variant.monostate.relops/relops.pass.cpp --- a/libcxx/test/std/utilities/variant/variant.monostate.relops/relops.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.monostate.relops/relops.pass.cpp @@ -16,40 +16,31 @@ // constexpr bool operator>=(monostate, monostate) noexcept { return true; } // constexpr bool operator==(monostate, monostate) noexcept { return true; } // constexpr bool operator!=(monostate, monostate) noexcept { return false; } +// constexpr strong_ordering operator<=>(monostate, monostate) noexcept { return strong_ordering::equal; } // since C++20 -#include "test_macros.h" -#include -#include #include -int main(int, char**) { +#include "test_comparisons.h" +#include "test_macros.h" + +constexpr bool test() { using M = std::monostate; constexpr M m1{}; constexpr M m2{}; - { - static_assert((m1 < m2) == false, ""); - ASSERT_NOEXCEPT(m1 < m2); - } - { - static_assert((m1 > m2) == false, ""); - ASSERT_NOEXCEPT(m1 > m2); - } - { - static_assert((m1 <= m2) == true, ""); - ASSERT_NOEXCEPT(m1 <= m2); - } - { - static_assert((m1 >= m2) == true, ""); - ASSERT_NOEXCEPT(m1 >= m2); - } - { - static_assert((m1 == m2) == true, ""); - ASSERT_NOEXCEPT(m1 == m2); - } - { - static_assert((m1 != m2) == false, ""); - ASSERT_NOEXCEPT(m1 != m2); - } + testComparisons(m1, m2, /*isEqual*/ true, /*isLess*/ false); + AssertComparisonsAreNoexcept(); + +#if TEST_STD_VER > 17 + testOrder(m1, m2, std::strong_ordering::equal); + AssertOrderAreNoexcept(); +#endif // TEST_STD_VER > 17 + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); return 0; } diff --git a/libcxx/test/std/utilities/variant/variant.relops/three_way.pass.cpp b/libcxx/test/std/utilities/variant/variant.relops/three_way.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.relops/three_way.pass.cpp @@ -0,0 +1,127 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template class variant; + +// template +// constexpr std::common_comparison_category_t< +// std::compare_three_way_result_t...> +// operator<=>(const variant& t, const variant& u); + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_comparisons.h" + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct MakeEmptyT { + MakeEmptyT() = default; + MakeEmptyT(MakeEmptyT &&) { throw 42; } + MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } +}; +inline bool operator==(const MakeEmptyT &, const MakeEmptyT &) { + assert(false); + return false; +} +inline std::weak_ordering operator<=>(const MakeEmptyT&, const MakeEmptyT&) { + assert(false); + return std::weak_ordering::equivalent; +} + +template void makeEmpty(Variant &v) { + Variant v2(std::in_place_type); + try { + v = std::move(v2); + assert(false); + } catch (...) { + assert(v.valueless_by_exception()); + } +} +#endif // TEST_HAS_NO_EXCEPTIONS + +template +void test_with_types() { + using V = std::variant; + using Order = std::compare_three_way_result_t; + { // same index, same value + constexpr V v1(std::in_place_index<0>, T1{1}); + constexpr V v2(std::in_place_index<0>, T1{1}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::equivalent))); + } + { // same index, value < other_value + constexpr V v1(std::in_place_index<0>, T1{0}); + constexpr V v2(std::in_place_index<0>, T1{1}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::less))); + } + { // same index, value > other_value + constexpr V v1(std::in_place_index<0>, T1{1}); + constexpr V v2(std::in_place_index<0>, T1{0}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::greater))); + } + { // LHS.index() < RHS.index() + constexpr V v1(std::in_place_index<0>, T1{0}); + constexpr V v2(std::in_place_index<1>, T2{0}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::less))); + } + { // LHS.index() > RHS.index() + constexpr V v1(std::in_place_index<1>, T2{0}); + constexpr V v2(std::in_place_index<0>, T1{0}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::greater))); + } +} + +void test_three_way() { + { + using V = std::variant; + ASSERT_SAME_TYPE(std::partial_ordering, std::compare_three_way_result_t); + test_with_types(); + } + { + using V = std::variant; + ASSERT_SAME_TYPE(std::strong_ordering, std::compare_three_way_result_t); + test_with_types(); + } + +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using V = std::variant; + V v1; + V v2; + makeEmpty(v2); + assert(testOrder(v1, v2, std::weak_ordering::greater)); + } + { + using V = std::variant; + V v1; + makeEmpty(v1); + V v2; + assert(testOrder(v1, v2, std::weak_ordering::less)); + } + { + using V = std::variant; + V v1; + makeEmpty(v1); + V v2; + makeEmpty(v2); + assert(testOrder(v1, v2, std::weak_ordering::equivalent)); + } +#endif // TEST_HAS_NO_EXCEPTIONS +} + +int main(int, char**) { + test_three_way(); + + return 0; +}