@@ -5,6 +5,7 @@ use crate::ascii;
5
5
use crate::cmp::{self, BytewiseEq, Ordering};
6
6
use crate::intrinsics::compare_bytes;
7
7
use crate::num::NonZero;
8
+ use crate::ops::ControlFlow;
8
9
9
10
#[stable(feature = "rust1", since = "1.0.0")]
10
11
impl<T, U> PartialEq<[U]> for [T]
@@ -31,12 +32,64 @@ impl<T: Ord> Ord for [T] {
31
32
}
32
33
}
33
34
35
+ #[inline]
36
+ fn as_underlying(x: ControlFlow<bool>) -> u8 {
37
+ // SAFETY: This will only compile if `bool` and `ControlFlow<bool>` have the same
38
+ // size (which isn't guaranteed but this is libcore). Because they have the same
39
+ // size, it's a niched implementation, which in one byte means there can't be
40
+ // any uninitialized memory. The callers then only check for `0` or `1` from this,
41
+ // which must necessarily match the `Break` variant, and we're fine no matter
42
+ // what ends up getting picked as the value representing `Continue(())`.
43
+ unsafe { crate::mem::transmute(x) }
44
+ }
45
+
34
46
/// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison).
35
47
#[stable(feature = "rust1", since = "1.0.0")]
36
48
impl<T: PartialOrd> PartialOrd for [T] {
49
+ #[inline]
37
50
fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {
38
51
SlicePartialOrd::partial_compare(self, other)
39
52
}
53
+ #[inline]
54
+ fn lt(&self, other: &Self) -> bool {
55
+ // This is certainly not the obvious way to implement these methods.
56
+ // Unfortunately, using anything that looks at the discriminant means that
57
+ // LLVM sees a check for `2` (aka `ControlFlow<bool>::Continue(())`) and
58
+ // gets very distracted by that, ending up generating extraneous code.
59
+ // This should be changed to something simpler once either LLVM is smarter,
60
+ // see <https://github.com/llvm/llvm-project/issues/132678>, or we generate
61
+ // niche discriminant checks in a way that doesn't trigger it.
62
+
63
+ as_underlying(self.__chaining_lt(other)) == 1
64
+ }
65
+ #[inline]
66
+ fn le(&self, other: &Self) -> bool {
67
+ as_underlying(self.__chaining_le(other)) != 0
68
+ }
69
+ #[inline]
70
+ fn gt(&self, other: &Self) -> bool {
71
+ as_underlying(self.__chaining_gt(other)) == 1
72
+ }
73
+ #[inline]
74
+ fn ge(&self, other: &Self) -> bool {
75
+ as_underlying(self.__chaining_ge(other)) != 0
76
+ }
77
+ #[inline]
78
+ fn __chaining_lt(&self, other: &Self) -> ControlFlow<bool> {
79
+ SliceChain::chaining_lt(self, other)
80
+ }
81
+ #[inline]
82
+ fn __chaining_le(&self, other: &Self) -> ControlFlow<bool> {
83
+ SliceChain::chaining_le(self, other)
84
+ }
85
+ #[inline]
86
+ fn __chaining_gt(&self, other: &Self) -> ControlFlow<bool> {
87
+ SliceChain::chaining_gt(self, other)
88
+ }
89
+ #[inline]
90
+ fn __chaining_ge(&self, other: &Self) -> ControlFlow<bool> {
91
+ SliceChain::chaining_ge(self, other)
92
+ }
40
93
}
41
94
42
95
#[doc(hidden)]
@@ -99,24 +152,63 @@ trait SlicePartialOrd: Sized {
99
152
fn partial_compare(left: &[Self], right: &[Self]) -> Option<Ordering>;
100
153
}
101
154
155
+ #[doc(hidden)]
156
+ // intermediate trait for specialization of slice's PartialOrd chaining methods
157
+ trait SliceChain: Sized {
158
+ fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow<bool>;
159
+ fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow<bool>;
160
+ fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow<bool>;
161
+ fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow<bool>;
162
+ }
163
+
164
+ type AlwaysBreak<B> = ControlFlow<B, crate::convert::Infallible>;
165
+
102
166
impl<A: PartialOrd> SlicePartialOrd for A {
103
167
default fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> {
104
- let l = cmp::min(left.len(), right.len());
105
-
106
- // Slice to the loop iteration range to enable bound check
107
- // elimination in the compiler
108
- let lhs = &left[..l];
109
- let rhs = &right[..l];
168
+ let elem_chain = |a, b| match PartialOrd::partial_cmp(a, b) {
169
+ Some(Ordering::Equal) => ControlFlow::Continue(()),
170
+ non_eq => ControlFlow::Break(non_eq),
171
+ };
172
+ let len_chain = |a: &_, b: &_| ControlFlow::Break(usize::partial_cmp(a, b));
173
+ let AlwaysBreak::Break(b) = chaining_impl(left, right, elem_chain, len_chain);
174
+ b
175
+ }
176
+ }
110
177
111
- for i in 0..l {
112
- match lhs[i].partial_cmp(&rhs[i]) {
113
- Some(Ordering::Equal) => (),
114
- non_eq => return non_eq,
115
- }
116
- }
178
+ impl<A: PartialOrd> SliceChain for A {
179
+ default fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow<bool> {
180
+ chaining_impl(left, right, PartialOrd::__chaining_lt, usize::__chaining_lt)
181
+ }
182
+ default fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow<bool> {
183
+ chaining_impl(left, right, PartialOrd::__chaining_le, usize::__chaining_le)
184
+ }
185
+ default fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow<bool> {
186
+ chaining_impl(left, right, PartialOrd::__chaining_gt, usize::__chaining_gt)
187
+ }
188
+ default fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow<bool> {
189
+ chaining_impl(left, right, PartialOrd::__chaining_ge, usize::__chaining_ge)
190
+ }
191
+ }
117
192
118
- left.len().partial_cmp(&right.len())
193
+ #[inline]
194
+ fn chaining_impl<'l, 'r, A: PartialOrd, B, C>(
195
+ left: &'l [A],
196
+ right: &'r [A],
197
+ elem_chain: impl Fn(&'l A, &'r A) -> ControlFlow<B>,
198
+ len_chain: impl for<'a> FnOnce(&'a usize, &'a usize) -> ControlFlow<B, C>,
199
+ ) -> ControlFlow<B, C> {
200
+ let l = cmp::min(left.len(), right.len());
201
+
202
+ // Slice to the loop iteration range to enable bound check
203
+ // elimination in the compiler
204
+ let lhs = &left[..l];
205
+ let rhs = &right[..l];
206
+
207
+ for i in 0..l {
208
+ elem_chain(&lhs[i], &rhs[i])?;
119
209
}
210
+
211
+ len_chain(&left.len(), &right.len())
120
212
}
121
213
122
214
// This is the impl that we would like to have. Unfortunately it's not sound.
@@ -165,21 +257,13 @@ trait SliceOrd: Sized {
165
257
166
258
impl<A: Ord> SliceOrd for A {
167
259
default fn compare(left: &[Self], right: &[Self]) -> Ordering {
168
- let l = cmp::min(left.len(), right.len());
169
-
170
- // Slice to the loop iteration range to enable bound check
171
- // elimination in the compiler
172
- let lhs = &left[..l];
173
- let rhs = &right[..l];
174
-
175
- for i in 0..l {
176
- match lhs[i].cmp(&rhs[i]) {
177
- Ordering::Equal => (),
178
- non_eq => return non_eq,
179
- }
180
- }
181
-
182
- left.len().cmp(&right.len())
260
+ let elem_chain = |a, b| match Ord::cmp(a, b) {
261
+ Ordering::Equal => ControlFlow::Continue(()),
262
+ non_eq => ControlFlow::Break(non_eq),
263
+ };
264
+ let len_chain = |a: &_, b: &_| ControlFlow::Break(usize::cmp(a, b));
265
+ let AlwaysBreak::Break(b) = chaining_impl(left, right, elem_chain, len_chain);
266
+ b
183
267
}
184
268
}
185
269
@@ -191,7 +275,7 @@ impl<A: Ord> SliceOrd for A {
191
275
/// * For every `x` and `y` of this type, `Ord(x, y)` must return the same
192
276
/// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`.
193
277
#[rustc_specialization_trait]
194
- unsafe trait UnsignedBytewiseOrd {}
278
+ unsafe trait UnsignedBytewiseOrd: Ord {}
195
279
196
280
unsafe impl UnsignedBytewiseOrd for bool {}
197
281
unsafe impl UnsignedBytewiseOrd for u8 {}
@@ -225,6 +309,38 @@ impl<A: Ord + UnsignedBytewiseOrd> SliceOrd for A {
225
309
}
226
310
}
227
311
312
+ // Don't generate our own chaining loops for `memcmp`-able things either.
313
+ impl<A: PartialOrd + UnsignedBytewiseOrd> SliceChain for A {
314
+ #[inline]
315
+ fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow<bool> {
316
+ match SliceOrd::compare(left, right) {
317
+ Ordering::Equal => ControlFlow::Continue(()),
318
+ ne => ControlFlow::Break(ne.is_lt()),
319
+ }
320
+ }
321
+ #[inline]
322
+ fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow<bool> {
323
+ match SliceOrd::compare(left, right) {
324
+ Ordering::Equal => ControlFlow::Continue(()),
325
+ ne => ControlFlow::Break(ne.is_le()),
326
+ }
327
+ }
328
+ #[inline]
329
+ fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow<bool> {
330
+ match SliceOrd::compare(left, right) {
331
+ Ordering::Equal => ControlFlow::Continue(()),
332
+ ne => ControlFlow::Break(ne.is_gt()),
333
+ }
334
+ }
335
+ #[inline]
336
+ fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow<bool> {
337
+ match SliceOrd::compare(left, right) {
338
+ Ordering::Equal => ControlFlow::Continue(()),
339
+ ne => ControlFlow::Break(ne.is_ge()),
340
+ }
341
+ }
342
+ }
343
+
228
344
pub(super) trait SliceContains: Sized {
229
345
fn slice_contains(&self, x: &[Self]) -> bool;
230
346
}
0 commit comments