195 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// <variant>12 13// template <class... Types> class variant;14 15// template <class... Types> requires (three_way_comparable<Types> && ...)16// constexpr std::common_comparison_category_t<17// std::compare_three_way_result_t<Types>...>18// operator<=>(const variant<Types...>& t, const variant<Types...>& u);19 20#include <cassert>21#include <limits>22#include <type_traits>23#include <utility>24#include <variant>25 26#include "test_macros.h"27#include "test_comparisons.h"28 29#ifndef TEST_HAS_NO_EXCEPTIONS30// MakeEmptyT throws in operator=(&&), so we can move to it to create valueless-by-exception variants.31struct MakeEmptyT {32 MakeEmptyT() = default;33 MakeEmptyT(MakeEmptyT&&) { throw 42; }34 MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }35};36inline bool operator==(const MakeEmptyT&, const MakeEmptyT&) {37 assert(false);38 return false;39}40inline std::weak_ordering operator<=>(const MakeEmptyT&, const MakeEmptyT&) {41 assert(false);42 return std::weak_ordering::equivalent;43}44 45template <class Variant>46void makeEmpty(Variant& v) {47 Variant v2(std::in_place_type<MakeEmptyT>);48 try {49 v = std::move(v2);50 assert(false);51 } catch (...) {52 assert(v.valueless_by_exception());53 }54}55 56void test_empty() {57 {58 using V = std::variant<int, MakeEmptyT>;59 V v1;60 V v2;61 makeEmpty(v2);62 assert(testOrder(v1, v2, std::weak_ordering::greater));63 }64 {65 using V = std::variant<int, MakeEmptyT>;66 V v1;67 makeEmpty(v1);68 V v2;69 assert(testOrder(v1, v2, std::weak_ordering::less));70 }71 {72 using V = std::variant<int, MakeEmptyT>;73 V v1;74 makeEmpty(v1);75 V v2;76 makeEmpty(v2);77 assert(testOrder(v1, v2, std::weak_ordering::equivalent));78 }79}80#endif // TEST_HAS_NO_EXCEPTIONS81 82template <class T1, class T2, class Order>83constexpr bool test_with_types() {84 using V = std::variant<T1, T2>;85 AssertOrderReturn<Order, V>();86 { // same index, same value87 constexpr V v1(std::in_place_index<0>, T1{1});88 constexpr V v2(std::in_place_index<0>, T1{1});89 assert(testOrder(v1, v2, Order::equivalent));90 }91 { // same index, value < other_value92 constexpr V v1(std::in_place_index<0>, T1{0});93 constexpr V v2(std::in_place_index<0>, T1{1});94 assert(testOrder(v1, v2, Order::less));95 }96 { // same index, value > other_value97 constexpr V v1(std::in_place_index<0>, T1{1});98 constexpr V v2(std::in_place_index<0>, T1{0});99 assert(testOrder(v1, v2, Order::greater));100 }101 { // LHS.index() < RHS.index()102 constexpr V v1(std::in_place_index<0>, T1{0});103 constexpr V v2(std::in_place_index<1>, T2{0});104 assert(testOrder(v1, v2, Order::less));105 }106 { // LHS.index() > RHS.index()107 constexpr V v1(std::in_place_index<1>, T2{0});108 constexpr V v2(std::in_place_index<0>, T1{0});109 assert(testOrder(v1, v2, Order::greater));110 }111 112 return true;113}114 115constexpr bool test_three_way() {116 assert((test_with_types<int, double, std::partial_ordering>()));117 assert((test_with_types<int, long, std::strong_ordering>()));118 119 {120 using V = std::variant<int, double>;121 constexpr double nan = std::numeric_limits<double>::quiet_NaN();122 {123 constexpr V v1(std::in_place_type<int>, 1);124 constexpr V v2(std::in_place_type<double>, nan);125 assert(testOrder(v1, v2, std::partial_ordering::less));126 }127 {128 constexpr V v1(std::in_place_type<double>, nan);129 constexpr V v2(std::in_place_type<int>, 2);130 assert(testOrder(v1, v2, std::partial_ordering::greater));131 }132 {133 constexpr V v1(std::in_place_type<double>, nan);134 constexpr V v2(std::in_place_type<double>, nan);135 assert(testOrder(v1, v2, std::partial_ordering::unordered));136 }137 }138 139 return true;140}141 142// SFINAE tests143template <class T, class U = T>144concept has_three_way_op = requires (T& t, U& u) { t <=> u; };145 146// std::three_way_comparable is a more stringent requirement that demands147// operator== and a few other things.148using std::three_way_comparable;149 150struct HasSimpleOrdering {151 constexpr bool operator==(const HasSimpleOrdering&) const;152 constexpr bool operator<(const HasSimpleOrdering&) const;153};154 155struct HasOnlySpaceship {156 constexpr bool operator==(const HasOnlySpaceship&) const = delete;157 constexpr std::weak_ordering operator<=>(const HasOnlySpaceship&) const;158};159 160struct HasFullOrdering {161 constexpr bool operator==(const HasFullOrdering&) const;162 constexpr std::weak_ordering operator<=>(const HasFullOrdering&) const;163};164 165// operator<=> must resolve the return types of all its union types'166// operator<=>s to determine its own return type, so it is detectable by SFINAE167static_assert(!has_three_way_op<HasSimpleOrdering>);168static_assert(!has_three_way_op<std::variant<int, HasSimpleOrdering>>);169 170static_assert(!three_way_comparable<HasSimpleOrdering>);171static_assert(!three_way_comparable<std::variant<int, HasSimpleOrdering>>);172 173static_assert(has_three_way_op<HasOnlySpaceship>);174static_assert(!has_three_way_op<std::variant<int, HasOnlySpaceship>>);175 176static_assert(!three_way_comparable<HasOnlySpaceship>);177static_assert(!three_way_comparable<std::variant<int, HasOnlySpaceship>>);178 179static_assert( has_three_way_op<HasFullOrdering>);180static_assert( has_three_way_op<std::variant<int, HasFullOrdering>>);181 182static_assert( three_way_comparable<HasFullOrdering>);183static_assert( three_way_comparable<std::variant<int, HasFullOrdering>>);184 185int main(int, char**) {186 test_three_way();187 static_assert(test_three_way());188 189#ifndef TEST_HAS_NO_EXCEPTIONS190 test_empty();191#endif // TEST_HAS_NO_EXCEPTIONS192 193 return 0;194}195