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