Skip to content

Commit

Permalink
Use nuw when calculating slice lengths from Ranges
Browse files Browse the repository at this point in the history
An `assume` would definitely not be worth it, but since the flag is almost free we might as well tell LLVM this, especially on `_unchecked` calls where there's no obvious way for it to deduce it.

(Today neither safe nor unsafe indexing gets it: <https://rust.godbolt.org/z/G1jYT548s>)
  • Loading branch information
scottmcm committed Mar 5, 2023
1 parent 7820b62 commit 3554036
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 3 deletions.
9 changes: 6 additions & 3 deletions library/core/src/slice/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use crate::intrinsics::assert_unsafe_precondition;
use crate::intrinsics::const_eval_select;
use crate::intrinsics::unchecked_sub;
use crate::ops;
use crate::ptr;

Expand Down Expand Up @@ -375,14 +376,15 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
// SAFETY: the caller guarantees that `slice` is not dangling, so it
// cannot be longer than `isize::MAX`. They also guarantee that
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
// so the call to `add` is safe.
// so the call to `add` is safe and the length calculation cannot overflow.
unsafe {
assert_unsafe_precondition!(
"slice::get_unchecked requires that the range is within the slice",
[T](this: ops::Range<usize>, slice: *const [T]) =>
this.end >= this.start && this.end <= slice.len()
);
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
let new_len = unchecked_sub(self.end, self.start);
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len)
}
}

Expand All @@ -396,7 +398,8 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
[T](this: ops::Range<usize>, slice: *mut [T]) =>
this.end >= this.start && this.end <= slice.len()
);
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
let new_len = unchecked_sub(self.end, self.start);
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len)
}
}

Expand Down
35 changes: 35 additions & 0 deletions tests/codegen/slice-indexing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// compile-flags: -O
// only-64bit (because the LLVM type of i64 for usize shows up)
// ignore-debug: the debug assertions get in the way

#![crate_type = "lib"]

use std::ops::Range;

// CHECK-LABEL: @index_by_range(
#[no_mangle]
pub fn index_by_range(x: &[u16], r: Range<usize>) -> &[u16] {
// CHECK: sub nuw i64
&x[r]
}

// CHECK-LABEL: @get_unchecked_by_range(
#[no_mangle]
pub unsafe fn get_unchecked_by_range(x: &[u16], r: Range<usize>) -> &[u16] {
// CHECK: sub nuw i64
x.get_unchecked(r)
}

// CHECK-LABEL: @index_mut_by_range(
#[no_mangle]
pub fn index_mut_by_range(x: &mut [i32], r: Range<usize>) -> &mut [i32] {
// CHECK: sub nuw i64
&mut x[r]
}

// CHECK-LABEL: @get_unchecked_mut_by_range(
#[no_mangle]
pub unsafe fn get_unchecked_mut_by_range(x: &mut [i32], r: Range<usize>) -> &mut [i32] {
// CHECK: sub nuw i64
x.get_unchecked_mut(r)
}
9 changes: 9 additions & 0 deletions tests/ui/consts/const-eval/ub-slice-get-unchecked.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![feature(const_slice_index)]

const A: [(); 5] = [(), (), (), (), ()];

// Since the indexing is on a ZST, the addresses are all fine,
// but we should still catch the bad range.
const B: &[()] = unsafe { A.get_unchecked(3..1) };

fn main() {}
18 changes: 18 additions & 0 deletions tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/slice/index.rs:LL:COL
|
= note: overflow executing `unchecked_sub`
|
note: inside `<std::ops::Range<usize> as SliceIndex<[()]>>::get_unchecked`
--> $SRC_DIR/core/src/slice/index.rs:LL:COL
note: inside `core::slice::<impl [()]>::get_unchecked::<std::ops::Range<usize>>`
--> $SRC_DIR/core/src/slice/mod.rs:LL:COL
note: inside `B`
--> $DIR/ub-slice-get-unchecked.rs:7:27
|
LL | const B: &[()] = unsafe { A.get_unchecked(3..1) };
| ^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0080`.

0 comments on commit 3554036

Please sign in to comment.