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