@@ -6,10 +6,10 @@ use crate::mem::ManuallyDrop;
66#[ cfg( not( feature = "optimize_for_size" ) ) ]
77use crate :: slice:: sort:: shared:: pivot:: choose_pivot;
88#[ 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 } ;
1010#[ cfg( not( feature = "optimize_for_size" ) ) ]
1111use crate :: slice:: sort:: unstable:: heapsort;
12- use crate :: { cfg_select, intrinsics , ptr} ;
12+ use crate :: { cfg_select, ptr} ;
1313
1414/// Sorts `v` recursively.
1515///
@@ -53,16 +53,18 @@ pub(crate) fn quicksort<'a, T, F>(
5353
5454 // Continue sorting elements greater than the pivot. We know that `num_lt` contains
5555 // 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 ( ) ) ;
5759 ancestor_pivot = None ;
5860 continue ;
5961 }
6062 }
6163
6264 // 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+ } ;
6668
6769 // Split the slice into `left`, `pivot`, and `right`.
6870 let ( left, right) = v. split_at_mut ( num_lt) ;
@@ -84,56 +86,46 @@ pub(crate) fn quicksort<'a, T, F>(
8486/// on the left side of `v` followed by the other elements, notionally considered greater or
8587/// equal to `pivot`.
8688///
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.
8891///
8992/// 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
9195/// interior mutability will be observable. Same is true if `is_less` panics or `v.len()`
9296/// 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 >
9498where
9599 F : FnMut ( & T , & T ) -> bool ,
96100{
97101 let len = v. len ( ) ;
98102
99103 // 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 ;
102106 }
103107
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 ( ) ?;
114110
115111 // Assuming that Rust generates noalias LLVM IR we can be sure that a partition function
116112 // signature of the form `(v: &mut [T], pivot: &T)` guarantees that pivot and v can't alias.
117113 // Having this guarantee is crucial for optimizations. It's possible to copy the pivot value
118114 // into a stack value, but this creates issues for types with interior mutability mandating
119115 // a drop guard.
120- let pivot = & mut pivot[ 0 ] ;
121116
122117 // This construct is used to limit the LLVM IR generated, which saves large amounts of
123118 // compile-time by only instantiating the code that is needed. Idea by Frank Steffahn.
124119 let num_lt = ( const { inst_partition :: < T , F > ( ) } ) ( v_without_pivot, pivot, is_less) ;
125120
126121 if num_lt >= len {
127- intrinsics :: abort ( ) ;
122+ return None ;
128123 }
129124
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) ;
135127
136- num_lt
128+ Some ( num_lt)
137129}
138130
139131const fn inst_partition < T , F : FnMut ( & T , & T ) -> bool > ( ) -> fn ( & mut [ T ] , & T , & mut F ) -> usize {
0 commit comments