11
11
// REQUIRES: has-unix-headers
12
12
// UNSUPPORTED: c++03, c++11, c++14, c++17
13
13
// XFAIL: availability-verbose_abort-missing
14
- // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
14
+ // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 -D_LIBCPP_DEBUG_STRICT_WEAK_ORDERING_CHECK
15
15
16
16
// This test uses a specific combination of an invalid comparator and sequence of values to
17
- // ensure that our sorting functions do not go out-of-bounds in that case. Instead, we should
18
- // fail loud with an assertion. The specific issue we're looking for here is when the comparator
19
- // does not satisfy the following property :
17
+ // ensure that our sorting functions do not go out-of-bounds and satisfy strict weak ordering in that case.
18
+ // Instead, we should fail loud with an assertion. The specific issue we're looking for here is when the comparator
19
+ // does not satisfy the strict weak ordering :
20
20
//
21
- // comp(a, b) implies that !comp(b, a)
22
- //
23
- // In other words,
24
- //
25
- // a < b implies that !(b < a)
21
+ // Irreflexivity: comp(a, a) is false
22
+ // Antisymmetry: comp(a, b) implies that !comp(b, a)
23
+ // Transitivity: comp(a, b), comp(b, c) imply comp(a, c)
24
+ // Transitivity of equivalence: !comp(a, b), !comp(b, a), !comp(b, c), !comp(c, b) imply !comp(a, c), !comp(c, a)
26
25
//
27
26
// If this is not satisfied, we have seen issues in the past where the std::sort implementation
28
- // would proceed to do OOB reads (rdar://106897934).
27
+ // would proceed to do OOB reads. (rdar://106897934).
28
+ // Other algorithms like std::stable_sort, std::sort_heap do not go out of bounds but can produce
29
+ // incorrect results, we also want to assert on that.
30
+ // Sometimes std::sort does not go out of bounds as well, for example, right now if transitivity
31
+ // of equivalence is not met, std::sort can only produce incorrect result but would not fail.
29
32
30
- // When the debug mode is enabled, this test fails because we actually catch that the comparator
33
+ // When the debug mode is enabled, this test fails because we actually catch on the fly that the comparator
31
34
// is not a strict-weak ordering before we catch that we'd dereference out-of-bounds inside std::sort,
32
35
// which leads to different errors than the ones tested below.
33
36
// XFAIL: libcpp-has-debug-mode
34
37
35
38
#include < algorithm>
36
39
#include < cassert>
37
40
#include < cstddef>
41
+ #include < limits>
38
42
#include < map>
39
43
#include < memory>
40
44
#include < ranges>
45
+ #include < random>
41
46
#include < set>
42
47
#include < string>
43
48
#include < vector>
44
49
45
50
#include " bad_comparator_values.h"
46
51
#include " check_assertion.h"
47
52
48
- int main ( int , char ** ) {
53
+ void check_oob_sort_read ( ) {
49
54
std::map<std::size_t , std::map<std::size_t , bool >> comparison_results; // terrible for performance, but really convenient
50
55
for (auto line : std::views::split (DATA, ' \n ' ) | std::views::filter ([](auto const & line) { return !line.empty (); })) {
51
56
auto values = std::views::split (line, ' ' );
@@ -90,20 +95,27 @@ int main(int, char**) {
90
95
std::vector<std::size_t *> copy;
91
96
for (auto const & e : elements)
92
97
copy.push_back (e.get ());
93
- std::stable_sort (copy.begin (), copy.end (), checked_predicate); // doesn't go OOB even with invalid comparator
98
+ TEST_LIBCPP_ASSERT_FAILURE (std::stable_sort (copy.begin (), copy.end (), checked_predicate), " not a valid strict-weak ordering" );
99
+ }
100
+ {
101
+ std::vector<std::size_t *> copy;
102
+ for (auto const & e : elements)
103
+ copy.push_back (e.get ());
104
+ std::make_heap (copy.begin (), copy.end (), checked_predicate); // doesn't go OOB even with invalid comparator
105
+ TEST_LIBCPP_ASSERT_FAILURE (std::sort_heap (copy.begin (), copy.end (), checked_predicate), " not a valid strict-weak ordering" );
94
106
}
95
107
{
96
108
std::vector<std::size_t *> copy;
97
109
for (auto const & e : elements)
98
110
copy.push_back (e.get ());
99
- std::partial_sort (copy.begin (), copy.begin (), copy.end (), checked_predicate); // doesn't go OOB even with invalid comparator
111
+ TEST_LIBCPP_ASSERT_FAILURE ( std::partial_sort (copy.begin (), copy.end (), copy.end (), checked_predicate), " not a valid strict-weak ordering " );
100
112
}
101
113
{
102
114
std::vector<std::size_t *> copy;
103
115
for (auto const & e : elements)
104
116
copy.push_back (e.get ());
105
117
std::vector<std::size_t *> results (copy.size (), nullptr );
106
- std::partial_sort_copy (copy.begin (), copy.end (), results.begin (), results.end (), checked_predicate); // doesn't go OOB even with invalid comparator
118
+ TEST_LIBCPP_ASSERT_FAILURE ( std::partial_sort_copy (copy.begin (), copy.end (), results.begin (), results.end (), checked_predicate), " not a valid strict-weak ordering " );
107
119
}
108
120
{
109
121
std::vector<std::size_t *> copy;
@@ -123,27 +135,88 @@ int main(int, char**) {
123
135
std::vector<std::size_t *> copy;
124
136
for (auto const & e : elements)
125
137
copy.push_back (e.get ());
126
- std::ranges::stable_sort (copy, checked_predicate); // doesn't go OOB even with invalid comparator
138
+ TEST_LIBCPP_ASSERT_FAILURE ( std::ranges::stable_sort (copy, checked_predicate), " not a valid strict-weak ordering " );
127
139
}
128
140
{
129
141
std::vector<std::size_t *> copy;
130
142
for (auto const & e : elements)
131
143
copy.push_back (e.get ());
132
- std::ranges::partial_sort (copy, copy.begin (), checked_predicate); // doesn't go OOB even with invalid comparator
144
+ std::ranges::make_heap (copy, checked_predicate); // doesn't go OOB even with invalid comparator
145
+ TEST_LIBCPP_ASSERT_FAILURE (std::ranges::sort_heap (copy, checked_predicate), " not a valid strict-weak ordering" );
146
+ }
147
+ {
148
+ std::vector<std::size_t *> copy;
149
+ for (auto const & e : elements)
150
+ copy.push_back (e.get ());
151
+ TEST_LIBCPP_ASSERT_FAILURE (std::ranges::partial_sort (copy, copy.end (), checked_predicate), " not a valid strict-weak ordering" );
133
152
}
134
153
{
135
154
std::vector<std::size_t *> copy;
136
155
for (auto const & e : elements)
137
156
copy.push_back (e.get ());
138
157
std::vector<std::size_t *> results (copy.size (), nullptr );
139
- std::ranges::partial_sort_copy (copy, results, checked_predicate); // doesn't go OOB even with invalid comparator
158
+ TEST_LIBCPP_ASSERT_FAILURE ( std::ranges::partial_sort_copy (copy, results, checked_predicate), " not a valid strict-weak ordering " );
140
159
}
141
160
{
142
161
std::vector<std::size_t *> copy;
143
162
for (auto const & e : elements)
144
163
copy.push_back (e.get ());
145
164
std::ranges::nth_element (copy, copy.end (), checked_predicate); // doesn't go OOB even with invalid comparator
146
165
}
166
+ }
167
+
168
+ struct FloatContainer {
169
+ float value;
170
+ bool operator <(const FloatContainer& other) const {
171
+ return value < other.value ;
172
+ }
173
+ };
174
+
175
+ // Nans in floats do not satisfy strict weak ordering by breaking transitivity of equivalence.
176
+ std::vector<FloatContainer> generate_float_data () {
177
+ std::vector<FloatContainer> floats (50 );
178
+ for (int i = 0 ; i < 50 ; ++i) {
179
+ floats[i].value = static_cast <float >(i);
180
+ }
181
+ floats.push_back (FloatContainer{std::numeric_limits<float >::quiet_NaN ()});
182
+ std::shuffle (floats.begin (), floats.end (), std::default_random_engine ());
183
+ return floats;
184
+ }
185
+
186
+ void check_nan_floats () {
187
+ auto floats = generate_float_data ();
188
+ TEST_LIBCPP_ASSERT_FAILURE (std::sort (floats.begin (), floats.end ()), " not a valid strict-weak ordering" );
189
+ floats = generate_float_data ();
190
+ TEST_LIBCPP_ASSERT_FAILURE (std::stable_sort (floats.begin (), floats.end ()), " not a valid strict-weak ordering" );
191
+ floats = generate_float_data ();
192
+ std::make_heap (floats.begin (), floats.end ());
193
+ TEST_LIBCPP_ASSERT_FAILURE (std::sort_heap (floats.begin (), floats.end ()), " not a valid strict-weak ordering" );
194
+ TEST_LIBCPP_ASSERT_FAILURE (std::ranges::sort (generate_float_data (), std::less ()), " not a valid strict-weak ordering" );
195
+ TEST_LIBCPP_ASSERT_FAILURE (std::ranges::stable_sort (generate_float_data (), std::less ()), " not a valid strict-weak ordering" );
196
+ floats = generate_float_data ();
197
+ std::ranges::make_heap (floats, std::less ());
198
+ TEST_LIBCPP_ASSERT_FAILURE (std::ranges::sort_heap (floats, std::less ()), " not a valid strict-weak ordering" );
199
+ }
200
+
201
+ void check_irreflexive () {
202
+ std::vector<int > v (1 );
203
+ TEST_LIBCPP_ASSERT_FAILURE (std::sort (v.begin (), v.end (), std::greater_equal<int >()), " not a valid strict-weak ordering" );
204
+ TEST_LIBCPP_ASSERT_FAILURE (std::stable_sort (v.begin (), v.end (), std::greater_equal<int >()), " not a valid strict-weak ordering" );
205
+ std::make_heap (v.begin (), v.end (), std::greater_equal<int >());
206
+ TEST_LIBCPP_ASSERT_FAILURE (std::sort_heap (v.begin (), v.end (), std::greater_equal<int >()), " not a valid strict-weak ordering" );
207
+ TEST_LIBCPP_ASSERT_FAILURE (std::ranges::sort (v, std::greater_equal<int >()), " not a valid strict-weak ordering" );
208
+ TEST_LIBCPP_ASSERT_FAILURE (std::ranges::stable_sort (v, std::greater_equal<int >()), " not a valid strict-weak ordering" );
209
+ std::ranges::make_heap (v, std::greater_equal<int >());
210
+ TEST_LIBCPP_ASSERT_FAILURE (std::ranges::sort_heap (v, std::greater_equal<int >()), " not a valid strict-weak ordering" );
211
+ }
212
+
213
+ int main (int , char **) {
214
+
215
+ check_oob_sort_read ();
216
+
217
+ check_nan_floats ();
218
+
219
+ check_irreflexive ();
147
220
148
221
return 0 ;
149
222
}
0 commit comments