@@ -6,10 +6,10 @@ use crate::mem::ManuallyDrop;
6
6
#[ cfg( not( feature = "optimize_for_size" ) ) ]
7
7
use crate :: slice:: sort:: shared:: pivot:: choose_pivot;
8
8
#[ cfg( not( feature = "optimize_for_size" ) ) ]
9
- use crate :: slice:: sort:: shared:: smallsort:: UnstableSmallSortTypeImpl ;
9
+ use crate :: slice:: sort:: shared:: smallsort:: { UnstableSmallSortTypeImpl , panic_on_ord_violation } ;
10
10
#[ cfg( not( feature = "optimize_for_size" ) ) ]
11
11
use crate :: slice:: sort:: unstable:: heapsort;
12
- use crate :: { cfg_select, intrinsics , ptr} ;
12
+ use crate :: { cfg_select, ptr} ;
13
13
14
14
/// Sorts `v` recursively.
15
15
///
@@ -53,16 +53,18 @@ pub(crate) fn quicksort<'a, T, F>(
53
53
54
54
// Continue sorting elements greater than the pivot. We know that `num_lt` contains
55
55
// the pivot. So we can continue after `num_lt`.
56
- v = & mut v[ ( num_lt + 1 ) ..] ;
56
+ v = num_lt
57
+ . and_then ( |num_lt| v. get_mut ( num_lt + 1 ..) )
58
+ . unwrap_or_else ( || panic_on_ord_violation ( ) ) ;
57
59
ancestor_pivot = None ;
58
60
continue ;
59
61
}
60
62
}
61
63
62
64
// Partition the slice.
63
- let num_lt = partition ( v, pivot_pos, is_less) ;
64
- // SAFETY: partition ensures that `num_lt` will be in-bounds.
65
- unsafe { intrinsics :: assume ( num_lt < v . len ( ) ) } ;
65
+ let Some ( num_lt) = partition ( v, pivot_pos, is_less) else {
66
+ panic_on_ord_violation ( ) ;
67
+ } ;
66
68
67
69
// Split the slice into `left`, `pivot`, and `right`.
68
70
let ( left, right) = v. split_at_mut ( num_lt) ;
@@ -84,56 +86,46 @@ pub(crate) fn quicksort<'a, T, F>(
84
86
/// on the left side of `v` followed by the other elements, notionally considered greater or
85
87
/// equal to `pivot`.
86
88
///
87
- /// Returns the number of elements that are compared true for `is_less(elem, pivot)`.
89
+ /// Returns the number of elements that are compared true for `is_less(elem, pivot)`
90
+ /// if `is_less` implements a total order.
88
91
///
89
92
/// If `is_less` does not implement a total order the resulting order and return value are
90
- /// unspecified. All original elements will remain in `v` and any possible modifications via
93
+ /// unspecified, except that if `Some` is returned, the value will be in bounds of `v`.
94
+ /// All original elements will remain in `v` and any possible modifications via
91
95
/// interior mutability will be observable. Same is true if `is_less` panics or `v.len()`
92
96
/// exceeds `scratch.len()`.
93
- pub ( crate ) fn partition < T , F > ( v : & mut [ T ] , pivot : usize , is_less : & mut F ) -> usize
97
+ pub ( crate ) fn partition < T , F > ( v : & mut [ T ] , pivot : usize , is_less : & mut F ) -> Option < usize >
94
98
where
95
99
F : FnMut ( & T , & T ) -> bool ,
96
100
{
97
101
let len = v. len ( ) ;
98
102
99
103
// Allows for panic-free code-gen by proving this property to the compiler.
100
- if len == 0 {
101
- return 0 ;
104
+ if len == 0 || pivot >= len {
105
+ return None ;
102
106
}
103
107
104
- if pivot >= len {
105
- intrinsics:: abort ( ) ;
106
- }
107
-
108
- // SAFETY: We checked that `pivot` is in-bounds.
109
- unsafe {
110
- // Place the pivot at the beginning of slice.
111
- v. swap_unchecked ( 0 , pivot) ;
112
- }
113
- let ( pivot, v_without_pivot) = v. split_at_mut ( 1 ) ;
108
+ v. swap ( 0 , pivot) ;
109
+ let ( pivot, v_without_pivot) = v. split_first_mut ( ) ?;
114
110
115
111
// Assuming that Rust generates noalias LLVM IR we can be sure that a partition function
116
112
// signature of the form `(v: &mut [T], pivot: &T)` guarantees that pivot and v can't alias.
117
113
// Having this guarantee is crucial for optimizations. It's possible to copy the pivot value
118
114
// into a stack value, but this creates issues for types with interior mutability mandating
119
115
// a drop guard.
120
- let pivot = & mut pivot[ 0 ] ;
121
116
122
117
// This construct is used to limit the LLVM IR generated, which saves large amounts of
123
118
// compile-time by only instantiating the code that is needed. Idea by Frank Steffahn.
124
119
let num_lt = ( const { inst_partition :: < T , F > ( ) } ) ( v_without_pivot, pivot, is_less) ;
125
120
126
121
if num_lt >= len {
127
- intrinsics :: abort ( ) ;
122
+ return None ;
128
123
}
129
124
130
- // SAFETY: We checked that `num_lt` is in-bounds.
131
- unsafe {
132
- // Place the pivot between the two partitions.
133
- v. swap_unchecked ( 0 , num_lt) ;
134
- }
125
+ // Place the pivot between the two partitions.
126
+ v. swap ( 0 , num_lt) ;
135
127
136
- num_lt
128
+ Some ( num_lt)
137
129
}
138
130
139
131
const fn inst_partition < T , F : FnMut ( & T , & T ) -> bool > ( ) -> fn ( & mut [ T ] , & T , & mut F ) -> usize {
0 commit comments