Skip to content

Tell LLVM that partition_point returns a valid fencepost #102535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2426,15 +2426,20 @@ impl<T> [T] {
where
F: FnMut(&'a T) -> Ordering,
{
// INVARIANTS:
// - 0 <= left <= left + size = right <= self.len()
// - f returns Less for everything in self[..left]
// - f returns Greater for everything in self[right..]
let mut size = self.len();
let mut left = 0;
let mut right = size;
while left < right {
let mid = left + size / 2;

// SAFETY: the call is made safe by the following invariants:
// - `mid >= 0`
// - `mid < size`: `mid` is limited by `[left; right)` bound.
// SAFETY: the while condition means `size` is strictly positive, so
// `size/2 < size`. Thus `left + size/2 < left + size`, which
// coupled with the `left + size <= self.len()` invariant means
// we have `left + size/2 < self.len()`, and this is in-bounds.
let cmp = f(unsafe { self.get_unchecked(mid) });

// The reason why we use if/else control flow rather than match
Expand All @@ -2452,6 +2457,10 @@ impl<T> [T] {

size = right - left;
}

// SAFETY: directly true from the overall invariant.
// Note that this is `<=`, unlike the assume in the `Ok` path.
unsafe { crate::intrinsics::assume(left <= self.len()) };
Err(left)
}

Expand Down
20 changes: 20 additions & 0 deletions src/test/codegen/binary-search-index-no-bound-check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,23 @@ pub fn binary_search_index_no_bounds_check(s: &[u8]) -> u8 {
42
}
}

// Similarly, check that `partition_point` is known to return a valid fencepost.

// CHECK-LABEL: @unknown_split
#[no_mangle]
pub fn unknown_split(x: &[i32], i: usize) -> (&[i32], &[i32]) {
// This just makes sure that the subsequent function is looking for the
// absence of something that might actually be there.

// CHECK: call core::panicking::panic
x.split_at(i)
}

// CHECK-LABEL: @partition_point_split_no_bounds_check
#[no_mangle]
pub fn partition_point_split_no_bounds_check(x: &[i32], needle: i32) -> (&[i32], &[i32]) {
// CHECK-NOT: call core::panicking::panic
let i = x.partition_point(|p| p < &needle);
x.split_at(i)
}