@@ -2787,37 +2787,56 @@ impl<T> [T] {
2787
2787
where
2788
2788
F : FnMut ( & ' a T ) -> Ordering ,
2789
2789
{
2790
+ // If T is a ZST, we assume that f is a constant function.
2791
+ // We do so because:
2792
+ // 1. ZST can only have one inhabitant
2793
+ // 2. We assume that f doesn't compare the address of the reference
2794
+ // passed, but only the value pointed to.
2795
+ // 3. We assume f's output to be entirely determined by its input
2796
+ if T :: IS_ZST {
2797
+ let res = if self . len ( ) == 0 {
2798
+ Err ( 0 )
2799
+ } else {
2800
+ match f ( & self [ 0 ] ) {
2801
+ Less => Err ( self . len ( ) ) ,
2802
+ Equal => Ok ( 0 ) ,
2803
+ Greater => Err ( 0 ) ,
2804
+ }
2805
+ } ;
2806
+ return res;
2807
+ }
2808
+ // Now we can assume that T is not a ZST, so self.len() <= isize::MAX
2790
2809
// INVARIANTS:
2791
- // - 0 <= left <= left + size = right <= self.len()
2810
+ // - 0 <= left <= right <= self.len() <= isize::MAX
2792
2811
// - f returns Less for everything in self[..left]
2793
2812
// - f returns Greater for everything in self[right..]
2794
- let mut size = self . len ( ) ;
2813
+ let mut right = self . len ( ) ;
2795
2814
let mut left = 0 ;
2796
- let mut right = size;
2797
2815
while left < right {
2798
- let mid = left + size / 2 ;
2816
+ // left + right <= 2*isize::MAX < usize::MAX
2817
+ // so the addition won't overflow
2818
+ let mid = ( left + right) / 2 ;
2799
2819
2800
- // SAFETY: the while condition means `size` is strictly positive, so
2801
- // `size/2 < size`. Thus `left + size/2 < left + size`, which
2802
- // coupled with the `left + size <= self.len()` invariant means
2803
- // we have `left + size/2 < self.len()`, and this is in-bounds.
2820
+ // SAFETY: We have that left < right, so
2821
+ // 0 <= left <= mid < right <= self.len()
2822
+ // and the indexing is in-bounds.
2804
2823
let cmp = f ( unsafe { self . get_unchecked ( mid) } ) ;
2805
2824
2806
2825
// This control flow produces conditional moves, which results in
2807
2826
// fewer branches and instructions than if/else or matching on
2808
2827
// cmp::Ordering.
2809
2828
// This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx.
2829
+ // (Note: the code as slightly changed since this comment but the
2830
+ // reasoning remains the same.)
2831
+
2810
2832
left = if cmp == Less { mid + 1 } else { left } ;
2811
2833
right = if cmp == Greater { mid } else { right } ;
2812
2834
if cmp == Equal {
2813
2835
// SAFETY: same as the `get_unchecked` above
2814
2836
unsafe { hint:: assert_unchecked ( mid < self . len ( ) ) } ;
2815
2837
return Ok ( mid) ;
2816
2838
}
2817
-
2818
- size = right - left;
2819
2839
}
2820
-
2821
2840
// SAFETY: directly true from the overall invariant.
2822
2841
// Note that this is `<=`, unlike the assume in the `Ok` path.
2823
2842
unsafe { hint:: assert_unchecked ( left <= self . len ( ) ) } ;
0 commit comments