xref: /llvm-project/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/three_way.pass.cpp (revision d2baefae6846765eef6a6dd69d4fdf1082ce29ad)
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // <tuple>
10 
11 // template <class... Types> class tuple;
12 
13 // template<class... TTypes, class... UTypes>
14 //   auto
15 //   operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
16 
17 // UNSUPPORTED: c++03, c++11, c++14, c++17
18 
19 #include "test_macros.h"
20 
21 #if defined(TEST_COMPILER_CLANG) || defined(TEST_COMPILER_GCC)
22 #pragma GCC diagnostic ignored "-Wsign-compare"
23 #elif defined(TEST_COMPILER_MSVC)
24 #pragma warning(disable: 4242 4244) // Various truncation warnings
25 #endif
26 
27 #include <cassert>
28 #include <compare>
29 #include <limits>
30 #include <tuple>
31 #include <type_traits> // std::is_constant_evaluated
32 
33 // A custom three-way result type
34 struct CustomEquality {
35   friend constexpr bool operator==(const CustomEquality&, int) noexcept { return true; }
36   friend constexpr bool operator<(const CustomEquality&, int) noexcept { return false; }
37   friend constexpr bool operator<(int, const CustomEquality&) noexcept { return false; }
38 };
39 
40 constexpr bool test() {
41   // Empty tuple
42   {
43     typedef std::tuple<> T0;
44     // No member types yields strong ordering (all are equal).
45     ASSERT_SAME_TYPE(decltype(T0() <=> T0()), std::strong_ordering);
46     assert((T0() <=> T0()) == std::strong_ordering::equal);
47   }
48   // Mixed types with integers, which compare strongly ordered
49   {
50     typedef std::tuple<long> T1;
51     typedef std::tuple<short> T2;
52     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering);
53     assert((T1(1) <=> T2(1)) == std::strong_ordering::equal);
54     assert((T1(1) <=> T2(0)) == std::strong_ordering::greater);
55     assert((T1(1) <=> T2(2)) == std::strong_ordering::less);
56   }
57   {
58     typedef std::tuple<long, unsigned int> T1;
59     typedef std::tuple<short, unsigned long> T2;
60     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering);
61     assert((T1(1, 2) <=> T2(1, 2)) == std::strong_ordering::equal);
62     assert((T1(1, 2) <=> T2(0, 2)) == std::strong_ordering::greater);
63     assert((T1(1, 2) <=> T2(2, 2)) == std::strong_ordering::less);
64     assert((T1(1, 2) <=> T2(1, 1)) == std::strong_ordering::greater);
65     assert((T1(1, 2) <=> T2(1, 3)) == std::strong_ordering::less);
66   }
67   {
68     typedef std::tuple<long, int, unsigned short> T1;
69     typedef std::tuple<short, long, unsigned int> T2;
70     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering);
71     assert((T1(1, 2, 3) <=> T2(1, 2, 3)) == std::strong_ordering::equal);
72     assert((T1(1, 2, 3) <=> T2(0, 2, 3)) == std::strong_ordering::greater);
73     assert((T1(1, 2, 3) <=> T2(2, 2, 3)) == std::strong_ordering::less);
74     assert((T1(1, 2, 3) <=> T2(1, 1, 3)) == std::strong_ordering::greater);
75     assert((T1(1, 2, 3) <=> T2(1, 3, 3)) == std::strong_ordering::less);
76     assert((T1(1, 2, 3) <=> T2(1, 2, 2)) == std::strong_ordering::greater);
77     assert((T1(1, 2, 3) <=> T2(1, 2, 4)) == std::strong_ordering::less);
78   }
79   // Mixed types with floating point, which compare partially ordered
80   {
81     typedef std::tuple<long> T1;
82     typedef std::tuple<double> T2;
83     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
84     assert((T1(1) <=> T2(1)) == std::partial_ordering::equivalent);
85     assert((T1(1) <=> T2(0.9)) == std::partial_ordering::greater);
86     assert((T1(1) <=> T2(1.1)) == std::partial_ordering::less);
87   }
88   {
89     typedef std::tuple<long, float> T1;
90     typedef std::tuple<double, unsigned int> T2;
91     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
92     assert((T1(1, 2) <=> T2(1, 2)) == std::partial_ordering::equivalent);
93     assert((T1(1, 2) <=> T2(0.9, 2)) == std::partial_ordering::greater);
94     assert((T1(1, 2) <=> T2(1.1, 2)) == std::partial_ordering::less);
95     assert((T1(1, 2) <=> T2(1, 1)) == std::partial_ordering::greater);
96     assert((T1(1, 2) <=> T2(1, 3)) == std::partial_ordering::less);
97   }
98   {
99     typedef std::tuple<short, float, double> T1;
100     typedef std::tuple<double, long, unsigned int> T2;
101     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
102     assert((T1(1, 2, 3) <=> T2(1, 2, 3)) == std::partial_ordering::equivalent);
103     assert((T1(1, 2, 3) <=> T2(0.9, 2, 3)) == std::partial_ordering::greater);
104     assert((T1(1, 2, 3) <=> T2(1.1, 2, 3)) == std::partial_ordering::less);
105     assert((T1(1, 2, 3) <=> T2(1, 1, 3)) == std::partial_ordering::greater);
106     assert((T1(1, 2, 3) <=> T2(1, 3, 3)) == std::partial_ordering::less);
107     assert((T1(1, 2, 3) <=> T2(1, 2, 2)) == std::partial_ordering::greater);
108     assert((T1(1, 2, 3) <=> T2(1, 2, 4)) == std::partial_ordering::less);
109   }
110   {
111     typedef std::tuple<float> T1;
112     typedef std::tuple<double> T2;
113     constexpr double nan = std::numeric_limits<double>::quiet_NaN();
114     // Comparisons with NaN and non-NaN are non-constexpr in GCC, so both sides must be NaN
115     assert((T1(nan) <=> T2(nan)) == std::partial_ordering::unordered);
116   }
117   {
118     typedef std::tuple<double, double> T1;
119     typedef std::tuple<float, float> T2;
120     constexpr double nan = std::numeric_limits<double>::quiet_NaN();
121     assert((T1(nan, 2) <=> T2(nan, 2)) == std::partial_ordering::unordered);
122     assert((T1(1, nan) <=> T2(1, nan)) == std::partial_ordering::unordered);
123   }
124   {
125     typedef std::tuple<double, float, float> T1;
126     typedef std::tuple<double, double, float> T2;
127     constexpr double nan = std::numeric_limits<double>::quiet_NaN();
128     assert((T1(nan, 2, 3) <=> T2(nan, 2, 3)) == std::partial_ordering::unordered);
129     assert((T1(1, nan, 3) <=> T2(1, nan, 3)) == std::partial_ordering::unordered);
130     assert((T1(1, 2, nan) <=> T2(1, 2, nan)) == std::partial_ordering::unordered);
131   }
132   // Ordering classes and synthesized three way comparison
133   {
134     typedef std::tuple<long, int, unsigned int> T1;
135     typedef std::tuple<int, long, unsigned short> T2;
136     // All strongly ordered members yields strong ordering.
137     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering);
138   }
139   {
140     struct WeakSpaceship {
141       constexpr bool operator==(const WeakSpaceship&) const { return true; }
142       constexpr std::weak_ordering operator<=>(const WeakSpaceship&) const { return std::weak_ordering::equivalent; }
143     };
144     {
145       typedef std::tuple<int, unsigned int, WeakSpaceship> T1;
146       typedef std::tuple<int, unsigned long, WeakSpaceship> T2;
147       // Strongly ordered members and a weakly ordered member yields weak ordering.
148       ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
149     }
150     {
151       typedef std::tuple<unsigned int, int, WeakSpaceship> T1;
152       typedef std::tuple<double, long, WeakSpaceship> T2;
153       // Doubles are partially ordered, so one partial, one strong, and one weak ordering
154       // yields partial ordering.
155       ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
156     }
157   }
158   {
159     struct NoSpaceship {
160       constexpr bool operator==(const NoSpaceship&) const { return true; }
161       constexpr bool operator<(const NoSpaceship&) const { return false; }
162     };
163     typedef std::tuple<int, unsigned int, NoSpaceship> T1;
164     typedef std::tuple<int, unsigned long, NoSpaceship> T2;
165     // Strongly ordered members and a weakly ordered member (synthesized) yields weak ordering.
166     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
167   }
168   {
169     struct SpaceshipNoEquals {
170       constexpr std::strong_ordering operator<=>(const SpaceshipNoEquals&) const { return std::strong_ordering::equal; }
171       constexpr bool operator<(const SpaceshipNoEquals&) const { return false; }
172     };
173     typedef std::tuple<int, unsigned int, SpaceshipNoEquals> T1;
174     typedef std::tuple<int, unsigned long, SpaceshipNoEquals> T2;
175     // Spaceship operator with no == operator falls back on the < operator and weak ordering.
176     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
177   }
178   {
179     struct CustomSpaceship {
180       constexpr CustomEquality operator<=>(const CustomSpaceship&) const { return CustomEquality(); }
181     };
182     typedef std::tuple<int, unsigned int, CustomSpaceship> T1;
183     typedef std::tuple<short, unsigned long, CustomSpaceship> T2;
184     typedef std::tuple<CustomSpaceship> T3;
185     // Custom three way return types cannot be used in synthesized three way comparison,
186     // but they can be used for (rewritten) operator< when synthesizing a weak ordering.
187     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
188     ASSERT_SAME_TYPE(decltype(T3() <=> T3()), std::weak_ordering);
189   }
190   {
191     typedef std::tuple<long, int> T1;
192     typedef std::tuple<long, unsigned int> T2;
193     // Even with the warning suppressed (-Wno-sign-compare) there should still be no <=> operator
194     // between signed and unsigned types, so we should end up with a synthesized weak ordering.
195     ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
196   }
197 
198 #ifdef TEST_COMPILER_GCC
199   // GCC cannot evaluate NaN @ non-NaN constexpr, so test that runtime-only.
200   if (!std::is_constant_evaluated())
201 #endif
202   {
203     {
204       typedef std::tuple<double> T1;
205       typedef std::tuple<int> T2;
206       constexpr double nan = std::numeric_limits<double>::quiet_NaN();
207       ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
208       assert((T1(nan) <=> T2(1)) == std::partial_ordering::unordered);
209     }
210     {
211       typedef std::tuple<double, double> T1;
212       typedef std::tuple<int, int> T2;
213       constexpr double nan = std::numeric_limits<double>::quiet_NaN();
214       ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
215       assert((T1(nan, 2) <=> T2(1, 2)) == std::partial_ordering::unordered);
216       assert((T1(1, nan) <=> T2(1, 2)) == std::partial_ordering::unordered);
217     }
218     {
219       typedef std::tuple<double, double, double> T1;
220       typedef std::tuple<int, int, int> T2;
221       constexpr double nan = std::numeric_limits<double>::quiet_NaN();
222       ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
223       assert((T1(nan, 2, 3) <=> T2(1, 2, 3)) == std::partial_ordering::unordered);
224       assert((T1(1, nan, 3) <=> T2(1, 2, 3)) == std::partial_ordering::unordered);
225       assert((T1(1, 2, nan) <=> T2(1, 2, 3)) == std::partial_ordering::unordered);
226     }
227   }
228 
229   return true;
230 }
231 
232 int main(int, char**) {
233   test();
234   static_assert(test());
235 
236   return 0;
237 }
238