Skip to content

Commit 3554036

Browse files
committed
Use nuw when calculating slice lengths from Ranges
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>)
1 parent 7820b62 commit 3554036

File tree

4 files changed

+68
-3
lines changed

4 files changed

+68
-3
lines changed

library/core/src/slice/index.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use crate::intrinsics::assert_unsafe_precondition;
44
use crate::intrinsics::const_eval_select;
5+
use crate::intrinsics::unchecked_sub;
56
use crate::ops;
67
use crate::ptr;
78

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

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

tests/codegen/slice-indexing.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// compile-flags: -O
2+
// only-64bit (because the LLVM type of i64 for usize shows up)
3+
// ignore-debug: the debug assertions get in the way
4+
5+
#![crate_type = "lib"]
6+
7+
use std::ops::Range;
8+
9+
// CHECK-LABEL: @index_by_range(
10+
#[no_mangle]
11+
pub fn index_by_range(x: &[u16], r: Range<usize>) -> &[u16] {
12+
// CHECK: sub nuw i64
13+
&x[r]
14+
}
15+
16+
// CHECK-LABEL: @get_unchecked_by_range(
17+
#[no_mangle]
18+
pub unsafe fn get_unchecked_by_range(x: &[u16], r: Range<usize>) -> &[u16] {
19+
// CHECK: sub nuw i64
20+
x.get_unchecked(r)
21+
}
22+
23+
// CHECK-LABEL: @index_mut_by_range(
24+
#[no_mangle]
25+
pub fn index_mut_by_range(x: &mut [i32], r: Range<usize>) -> &mut [i32] {
26+
// CHECK: sub nuw i64
27+
&mut x[r]
28+
}
29+
30+
// CHECK-LABEL: @get_unchecked_mut_by_range(
31+
#[no_mangle]
32+
pub unsafe fn get_unchecked_mut_by_range(x: &mut [i32], r: Range<usize>) -> &mut [i32] {
33+
// CHECK: sub nuw i64
34+
x.get_unchecked_mut(r)
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![feature(const_slice_index)]
2+
3+
const A: [(); 5] = [(), (), (), (), ()];
4+
5+
// Since the indexing is on a ZST, the addresses are all fine,
6+
// but we should still catch the bad range.
7+
const B: &[()] = unsafe { A.get_unchecked(3..1) };
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $SRC_DIR/core/src/slice/index.rs:LL:COL
3+
|
4+
= note: overflow executing `unchecked_sub`
5+
|
6+
note: inside `<std::ops::Range<usize> as SliceIndex<[()]>>::get_unchecked`
7+
--> $SRC_DIR/core/src/slice/index.rs:LL:COL
8+
note: inside `core::slice::<impl [()]>::get_unchecked::<std::ops::Range<usize>>`
9+
--> $SRC_DIR/core/src/slice/mod.rs:LL:COL
10+
note: inside `B`
11+
--> $DIR/ub-slice-get-unchecked.rs:7:27
12+
|
13+
LL | const B: &[()] = unsafe { A.get_unchecked(3..1) };
14+
| ^^^^^^^^^^^^^^^^^^^^^
15+
16+
error: aborting due to previous error
17+
18+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)