Skip to content

Commit 0f65371

Browse files
committed
Auto merge of #117035 - scottmcm:slice-index-intrinsic, r=<try>
Use MIR slice indexing for get(_unchecked)(_mut) on slices Rather than having the MIR also need to drop down to pointers and `offset`. Inspired by #116915 r? `@ghost`
2 parents 97a2894 + b812f16 commit 0f65371

14 files changed

+367
-130
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
216216

217217
sym::type_name => (1, Vec::new(), Ty::new_static_str(tcx)),
218218
sym::type_id => (1, Vec::new(), tcx.types.u128),
219+
sym::slice_index => (2, vec![param(0), tcx.types.usize], param(1)),
219220
sym::offset => (2, vec![param(0), param(1)], param(0)),
220221
sym::arith_offset => (
221222
1,

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,43 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
234234
terminator.kind = TerminatorKind::Goto { target };
235235
}
236236
}
237+
sym::slice_index => {
238+
let target = target.unwrap();
239+
240+
let elem =
241+
if let Some(slice_ptr) = args[0].place()
242+
&& let Some(index_place) = args[1].place()
243+
&& let Some(index) = index_place.as_local()
244+
{
245+
let slice = tcx.mk_place_deref(slice_ptr);
246+
tcx.mk_place_index(slice, index)
247+
} else {
248+
span_bug!(
249+
terminator.source_info.span,
250+
"Invalid call to slice_index"
251+
);
252+
};
253+
254+
let elem_ptr =
255+
match generic_args.type_at(1).kind() {
256+
ty::Ref(region, _, Mutability::Mut) =>
257+
Rvalue::Ref(*region, BorrowKind::Mut { kind: MutBorrowKind::Default }, elem),
258+
ty::Ref(region, _, Mutability::Not) =>
259+
Rvalue::Ref(*region, BorrowKind::Shared, elem),
260+
ty::RawPtr(ty_and_mut) =>
261+
Rvalue::AddressOf(ty_and_mut.mutbl, elem),
262+
x => bug!("Invalid return type for slice_index: {x:?}"),
263+
};
264+
265+
block.statements.push(Statement {
266+
source_info: terminator.source_info,
267+
kind: StatementKind::Assign(Box::new((
268+
*destination,
269+
elem_ptr,
270+
))),
271+
});
272+
terminator.kind = TerminatorKind::Goto { target };
273+
}
237274
sym::offset => {
238275
let target = target.unwrap();
239276
let Ok([ptr, delta]) = <[_; 2]>::try_from(std::mem::take(args)) else {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,7 @@ symbols! {
15281528
slice,
15291529
slice_from_raw_parts,
15301530
slice_from_raw_parts_mut,
1531+
slice_index,
15311532
slice_len_fn,
15321533
slice_patterns,
15331534
slicing_syntax,

library/core/src/intrinsics.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
7474
unsafe { crate::ptr::drop_in_place(to_drop) }
7575
}
7676

77+
#[allow(private_bounds)]
7778
extern "rust-intrinsic" {
7879
// N.B., these intrinsics take raw pointers because they mutate aliased
7980
// memory, which is not valid for either `&` or `&mut`.
@@ -1410,6 +1411,39 @@ extern "rust-intrinsic" {
14101411
#[rustc_nounwind]
14111412
pub fn needs_drop<T: ?Sized>() -> bool;
14121413

1414+
/// Indexes into a slice using `ProjectionElem::Index`
1415+
///
1416+
/// Depending on the return type, this lowers to one of the following in MIR:
1417+
/// - `&raw const (*slice)[index]`, for `*const _`
1418+
/// - `&raw mut (*slice)[index]`, for `*mut _`
1419+
/// - `&(*slice)[index]`, for `&_`
1420+
/// - `&mut (*slice)[index]`, for `&mut _`
1421+
///
1422+
/// This is intended for use in implementing `[T]::get_unchecked_mut`,
1423+
/// `[T]::get`, and friends. It should not be used elsewhere; ICEs from
1424+
/// misuse in callers of intrinsics are *not* considered compiler bugs.
1425+
///
1426+
/// While semantically unnecessary, as it could be done via `offset`, this
1427+
/// reduces and simplifies the MIR produced for common operations.
1428+
///
1429+
/// # Safety
1430+
///
1431+
/// Requires `index < slice.len()` and that `slice` points to a valid slice.
1432+
///
1433+
/// If the return type is a reference (mutable or shared), then you must
1434+
/// ensure that its lifetime is no longer than the input's.
1435+
#[cfg(not(bootstrap))]
1436+
#[must_use = "returns a new pointer rather than modifying its argument"]
1437+
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
1438+
#[rustc_nounwind]
1439+
pub fn slice_index<
1440+
Slice: PointerOrReference<To = [Item::To]>,
1441+
Item: PointerOrReference<To: Sized>,
1442+
>(
1443+
slice: Slice,
1444+
index: usize,
1445+
) -> Item;
1446+
14131447
/// Calculates the offset from a pointer.
14141448
///
14151449
/// This is implemented as an intrinsic to avoid converting to and from an
@@ -1430,7 +1464,7 @@ extern "rust-intrinsic" {
14301464
#[must_use = "returns a new pointer rather than modifying its argument"]
14311465
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
14321466
#[rustc_nounwind]
1433-
pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
1467+
pub fn offset<Ptr: PointerOrReference<To: Sized>, Delta>(dst: Ptr, offset: Delta) -> Ptr;
14341468

14351469
/// Calculates the offset from a pointer, potentially wrapping.
14361470
///
@@ -2855,3 +2889,20 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
28552889
write_bytes(dst, val, count)
28562890
}
28572891
}
2892+
2893+
/// No semantic meaning; just to help reduce mistakes with generic intrinsics
2894+
trait PointerOrReference {
2895+
type To: ?Sized;
2896+
}
2897+
impl<T: ?Sized> PointerOrReference for &T {
2898+
type To = T;
2899+
}
2900+
impl<T: ?Sized> PointerOrReference for &mut T {
2901+
type To = T;
2902+
}
2903+
impl<T: ?Sized> PointerOrReference for *const T {
2904+
type To = T;
2905+
}
2906+
impl<T: ?Sized> PointerOrReference for *mut T {
2907+
type To = T;
2908+
}

library/core/src/slice/index.rs

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use crate::intrinsics::assert_unsafe_precondition;
44
use crate::intrinsics::const_eval_select;
5+
#[cfg(not(bootstrap))]
6+
use crate::intrinsics::slice_index;
57
use crate::intrinsics::unchecked_sub;
68
use crate::ops;
79
use crate::ptr;
@@ -213,14 +215,38 @@ unsafe impl<T> SliceIndex<[T]> for usize {
213215

214216
#[inline]
215217
fn get(self, slice: &[T]) -> Option<&T> {
216-
// SAFETY: `self` is checked to be in bounds.
217-
if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None }
218+
if self < slice.len() {
219+
#[cfg(bootstrap)]
220+
// SAFETY: `self` is checked to be in bounds.
221+
unsafe {
222+
Some(&*self.get_unchecked(slice))
223+
}
224+
#[cfg(not(bootstrap))]
225+
// SAFETY: `self` is checked to be in bounds.
226+
unsafe {
227+
Some(slice_index(slice, self))
228+
}
229+
} else {
230+
None
231+
}
218232
}
219233

220234
#[inline]
221235
fn get_mut(self, slice: &mut [T]) -> Option<&mut T> {
222-
// SAFETY: `self` is checked to be in bounds.
223-
if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None }
236+
if self < slice.len() {
237+
#[cfg(bootstrap)]
238+
// SAFETY: `self` is checked to be in bounds.
239+
unsafe {
240+
Some(&mut *self.get_unchecked_mut(slice))
241+
}
242+
#[cfg(not(bootstrap))]
243+
// SAFETY: `self` is checked to be in bounds.
244+
unsafe {
245+
Some(slice_index(slice, self))
246+
}
247+
} else {
248+
None
249+
}
224250
}
225251

226252
#[inline]
@@ -231,11 +257,18 @@ unsafe impl<T> SliceIndex<[T]> for usize {
231257
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
232258
// so the call to `add` is safe.
233259
unsafe {
234-
assert_unsafe_precondition!(
235-
"slice::get_unchecked requires that the index is within the slice",
236-
[T](this: usize, slice: *const [T]) => this < slice.len()
237-
);
238-
slice.as_ptr().add(self)
260+
#[cfg(bootstrap)]
261+
{
262+
assert_unsafe_precondition!(
263+
"slice::get_unchecked requires that the index is within the slice",
264+
[T](this: usize, slice: *const [T]) => this < slice.len()
265+
);
266+
slice.as_ptr().add(self)
267+
}
268+
#[cfg(not(bootstrap))]
269+
{
270+
slice_index(slice, this)
271+
}
239272
}
240273
}
241274

@@ -244,11 +277,18 @@ unsafe impl<T> SliceIndex<[T]> for usize {
244277
let this = self;
245278
// SAFETY: see comments for `get_unchecked` above.
246279
unsafe {
247-
assert_unsafe_precondition!(
248-
"slice::get_unchecked_mut requires that the index is within the slice",
249-
[T](this: usize, slice: *mut [T]) => this < slice.len()
250-
);
251-
slice.as_mut_ptr().add(self)
280+
#[cfg(bootstrap)]
281+
{
282+
assert_unsafe_precondition!(
283+
"slice::get_unchecked_mut requires that the index is within the slice",
284+
[T](this: usize, slice: *mut [T]) => this < slice.len()
285+
);
286+
slice.as_mut_ptr().add(self)
287+
}
288+
#[cfg(not(bootstrap))]
289+
{
290+
slice_index(slice, this)
291+
}
252292
}
253293
}
254294

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
//@compile-flags: -Zmiri-strict-provenance
2-
//@error-in-other-file: /retag .* tag does not exist in the borrow stack/
32

43
fn main() {
54
unsafe {
65
let a = [1, 2, 3];
76
let s = &a[0..0];
87
assert_eq!(s.len(), 0);
9-
assert_eq!(*s.get_unchecked(1), 2);
8+
assert_eq!(*s.as_ptr().add(1), 2); //~ ERROR: /retag .* tag does not exist in the borrow stack/
109
}
1110
}

src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
error: Undefined Behavior: trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
2-
--> RUSTLIB/core/src/slice/mod.rs:LL:CC
2+
--> $DIR/zst_slice.rs:LL:CC
33
|
4-
LL | unsafe { &*index.get_unchecked(self) }
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6-
| |
7-
| trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
8-
| this error occurs as part of retag at ALLOC[0x4..0x8]
4+
LL | assert_eq!(*s.as_ptr().add(1), 2);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
8+
| this error occurs as part of retag at ALLOC[0x4..0x8]
99
|
1010
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
1111
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
1212
help: <TAG> would have been created here, but this is a zero-size retag ([0x0..0x0]) so the tag in question does not exist anywhere
1313
--> $DIR/zst_slice.rs:LL:CC
1414
|
15-
LL | assert_eq!(*s.get_unchecked(1), 2);
16-
| ^^^^^^^^^^^^^^^^^^
15+
LL | assert_eq!(*s.as_ptr().add(1), 2);
16+
| ^^^^^^^^^^
1717
= note: BACKTRACE (of the first span):
18-
= note: inside `core::slice::<impl [i32]>::get_unchecked::<usize>` at RUSTLIB/core/src/slice/mod.rs:LL:CC
19-
note: inside `main`
20-
--> $DIR/zst_slice.rs:LL:CC
21-
|
22-
LL | assert_eq!(*s.get_unchecked(1), 2);
23-
| ^^^^^^^^^^^^^^^^^^
18+
= note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC
19+
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
2420

2521
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2622

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//@compile-flags: -Zmiri-disable-stacked-borrows
22
fn main() {
33
let v: Vec<u8> = Vec::with_capacity(10);
4-
let undef = unsafe { *v.get_unchecked(5) }; //~ ERROR: uninitialized
4+
let undef = unsafe { *v.as_ptr().add(5) }; //~ ERROR: uninitialized
55
let x = undef + 1;
66
panic!("this should never print: {}", x);
77
}

src/tools/miri/tests/fail/uninit_byte_read.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
22
--> $DIR/uninit_byte_read.rs:LL:CC
33
|
4-
LL | let undef = unsafe { *v.get_unchecked(5) };
5-
| ^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
4+
LL | let undef = unsafe { *v.as_ptr().add(5) };
5+
| ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

0 commit comments

Comments
 (0)