Skip to content

Commit 5d9cc7a

Browse files
committed
slice.get(i) should use a slice projection in MIR, like slice[i] does
1 parent c211076 commit 5d9cc7a

15 files changed

+370
-86
lines changed

Diff for: compiler/rustc_hir_analysis/src/check/intrinsic.rs

+1
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ pub fn check_intrinsic_type(
277277
vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.isize],
278278
Ty::new_imm_ptr(tcx, param(0)),
279279
),
280+
sym::slice_get_unchecked => (2, 0, vec![param(0), tcx.types.usize], param(1)),
280281
sym::ptr_mask => (
281282
1,
282283
0,

Diff for: compiler/rustc_mir_transform/src/lower_intrinsics.rs

+46
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,52 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
262262
});
263263
terminator.kind = TerminatorKind::Goto { target };
264264
}
265+
sym::slice_get_unchecked => {
266+
let target = target.unwrap();
267+
let Ok([ptrish, index]) = take_array(args) else {
268+
span_bug!(
269+
terminator.source_info.span,
270+
"Wrong number of arguments for {intrinsic:?}",
271+
);
272+
};
273+
274+
let place = ptrish.node.place().unwrap();
275+
assert!(!place.is_indirect());
276+
let updated_place = place.project_deeper(
277+
&[
278+
ProjectionElem::Deref,
279+
ProjectionElem::Index(
280+
index.node.place().unwrap().as_local().unwrap(),
281+
),
282+
],
283+
tcx,
284+
);
285+
286+
let ret_ty = generic_args.type_at(0);
287+
let rvalue = match *ret_ty.kind() {
288+
ty::RawPtr(_, Mutability::Not) => {
289+
Rvalue::RawPtr(RawPtrKind::Const, updated_place)
290+
}
291+
ty::RawPtr(_, Mutability::Mut) => {
292+
Rvalue::RawPtr(RawPtrKind::Mut, updated_place)
293+
}
294+
ty::Ref(region, _, Mutability::Not) => {
295+
Rvalue::Ref(region, BorrowKind::Shared, updated_place)
296+
}
297+
ty::Ref(region, _, Mutability::Mut) => Rvalue::Ref(
298+
region,
299+
BorrowKind::Mut { kind: MutBorrowKind::Default },
300+
updated_place,
301+
),
302+
_ => bug!("Unknown return type {ret_ty:?}"),
303+
};
304+
305+
block.statements.push(Statement {
306+
source_info: terminator.source_info,
307+
kind: StatementKind::Assign(Box::new((*destination, rvalue))),
308+
});
309+
terminator.kind = TerminatorKind::Goto { target };
310+
}
265311
sym::transmute | sym::transmute_unchecked => {
266312
let dst_ty = destination.ty(local_decls, tcx).ty;
267313
let Ok([arg]) = take_array(args) else {

Diff for: compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1979,6 +1979,7 @@ symbols! {
19791979
slice,
19801980
slice_from_raw_parts,
19811981
slice_from_raw_parts_mut,
1982+
slice_get_unchecked,
19821983
slice_into_vec,
19831984
slice_iter,
19841985
slice_len_fn,

Diff for: library/core/src/intrinsics/bounds.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//! Various traits used to restrict intrinsics to not-completely-wrong types.
2+
3+
/// Types with a built-in dereference operator in runtime MIR,
4+
/// aka references and raw pointers.
5+
pub trait BuiltinDeref: Sized {
6+
type Kind;
7+
type Pointee: ?Sized;
8+
}
9+
10+
impl<'a, T: ?Sized> BuiltinDeref for &'a mut T {
11+
type Kind = &'a mut ();
12+
type Pointee = T;
13+
}
14+
impl<'a, T: ?Sized> BuiltinDeref for &'a T {
15+
type Kind = &'a ();
16+
type Pointee = T;
17+
}
18+
impl<T: ?Sized> BuiltinDeref for *mut T {
19+
type Kind = *mut ();
20+
type Pointee = T;
21+
}
22+
impl<T: ?Sized> BuiltinDeref for *const T {
23+
type Kind = *const ();
24+
type Pointee = T;
25+
}

Diff for: library/core/src/intrinsics/mod.rs

+31-13
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ use crate::marker::{DiscriminantKind, Tuple};
6868
use crate::mem::SizedTypeProperties;
6969
use crate::{ptr, ub_checks};
7070

71+
mod bounds;
7172
pub mod fallback;
7273
pub mod mir;
7374
pub mod simd;
@@ -1737,7 +1738,7 @@ pub const fn needs_drop<T: ?Sized>() -> bool;
17371738
#[rustc_intrinsic_const_stable_indirect]
17381739
#[rustc_nounwind]
17391740
#[rustc_intrinsic]
1740-
pub const unsafe fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
1741+
pub const unsafe fn offset<Ptr: bounds::BuiltinDeref, Delta>(dst: Ptr, offset: Delta) -> Ptr;
17411742

17421743
/// Calculates the offset from a pointer, potentially wrapping.
17431744
///
@@ -1758,6 +1759,32 @@ pub const unsafe fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
17581759
#[rustc_intrinsic]
17591760
pub const unsafe fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
17601761

1762+
/// Projects to the `index`-th element of `slice_ptr`, as the same kind of pointer
1763+
/// as the slice was provided -- so `&mut [T] → &mut T`, `&[T] → &T`,
1764+
/// `*mut [T] → *mut T`, or `*const [T] → *const T` -- without a bounds check.
1765+
///
1766+
/// This is exposed via `<usize as SliceIndex>::get(_unchecked)(_mut)`,
1767+
/// and isn't intended to be used elsewhere.
1768+
///
1769+
/// Expands in MIR to `{&, &mut, &raw const, &raw mut} (*slice_ptr)[index]`,
1770+
/// depending on the types involved, so no backend support is needed.
1771+
///
1772+
/// # Safety
1773+
///
1774+
/// - `index < PtrMetadata(slice_ptr)`, so the indexing is in-bounds for the slice
1775+
/// - the resulting offsetting is in-bounds of the allocated object, which is
1776+
/// always the case for references, but needs to be upheld manually for pointers
1777+
#[cfg(not(bootstrap))]
1778+
#[rustc_nounwind]
1779+
#[rustc_intrinsic]
1780+
pub const unsafe fn slice_get_unchecked<
1781+
SlicePtr: bounds::BuiltinDeref<Pointee = [<ItemPtr as bounds::BuiltinDeref>::Pointee]>,
1782+
ItemPtr: bounds::BuiltinDeref<Kind = SlicePtr::Kind, Pointee: Sized>,
1783+
>(
1784+
slice_ptr: SlicePtr,
1785+
index: usize,
1786+
) -> ItemPtr;
1787+
17611788
/// Masks out bits of the pointer according to a mask.
17621789
///
17631790
/// Note that, unlike most intrinsics, this is safe to call;
@@ -3609,18 +3636,9 @@ pub const fn type_id<T: ?Sized + 'static>() -> u128;
36093636
#[unstable(feature = "core_intrinsics", issue = "none")]
36103637
#[rustc_intrinsic_const_stable_indirect]
36113638
#[rustc_intrinsic]
3612-
pub const fn aggregate_raw_ptr<P: AggregateRawPtr<D, Metadata = M>, D, M>(data: D, meta: M) -> P;
3613-
3614-
#[unstable(feature = "core_intrinsics", issue = "none")]
3615-
pub trait AggregateRawPtr<D> {
3616-
type Metadata: Copy;
3617-
}
3618-
impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*const T> for *const P {
3619-
type Metadata = <P as ptr::Pointee>::Metadata;
3620-
}
3621-
impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
3622-
type Metadata = <P as ptr::Pointee>::Metadata;
3623-
}
3639+
pub const fn aggregate_raw_ptr<P: bounds::BuiltinDeref, D, M>(data: D, meta: M) -> P
3640+
where
3641+
<P as bounds::BuiltinDeref>::Pointee: ptr::Pointee<Metadata = M>;
36243642

36253643
/// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`.
36263644
///

Diff for: library/core/src/slice/index.rs

+49-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Indexing implementations for `[T]`.
22
3+
#[cfg(not(bootstrap))]
4+
use crate::intrinsics::slice_get_unchecked;
35
use crate::panic::const_panic;
46
use crate::ub_checks::assert_unsafe_precondition;
57
use crate::{ops, range};
@@ -83,13 +85,15 @@ const fn slice_end_index_overflow_fail() -> ! {
8385
// Both the safe and unsafe public methods share these helpers,
8486
// which use intrinsics directly to get *no* extra checks.
8587

88+
#[cfg(bootstrap)]
8689
#[inline(always)]
8790
const unsafe fn get_noubcheck<T>(ptr: *const [T], index: usize) -> *const T {
8891
let ptr = ptr as *const T;
8992
// SAFETY: The caller already checked these preconditions
9093
unsafe { crate::intrinsics::offset(ptr, index) }
9194
}
9295

96+
#[cfg(bootstrap)]
9397
#[inline(always)]
9498
const unsafe fn get_mut_noubcheck<T>(ptr: *mut [T], index: usize) -> *mut T {
9599
let ptr = ptr as *mut T;
@@ -103,8 +107,9 @@ const unsafe fn get_offset_len_noubcheck<T>(
103107
offset: usize,
104108
len: usize,
105109
) -> *const [T] {
110+
let ptr = ptr as *const T;
106111
// SAFETY: The caller already checked these preconditions
107-
let ptr = unsafe { get_noubcheck(ptr, offset) };
112+
let ptr = unsafe { crate::intrinsics::offset(ptr, offset) };
108113
crate::intrinsics::aggregate_raw_ptr(ptr, len)
109114
}
110115

@@ -114,8 +119,9 @@ const unsafe fn get_offset_len_mut_noubcheck<T>(
114119
offset: usize,
115120
len: usize,
116121
) -> *mut [T] {
122+
let ptr = ptr as *mut T;
117123
// SAFETY: The caller already checked these preconditions
118-
let ptr = unsafe { get_mut_noubcheck(ptr, offset) };
124+
let ptr = unsafe { crate::intrinsics::offset(ptr, offset) };
119125
crate::intrinsics::aggregate_raw_ptr(ptr, len)
120126
}
121127

@@ -224,15 +230,35 @@ unsafe impl<T> SliceIndex<[T]> for usize {
224230

225231
#[inline]
226232
fn get(self, slice: &[T]) -> Option<&T> {
227-
// SAFETY: `self` is checked to be in bounds.
228-
if self < slice.len() { unsafe { Some(&*get_noubcheck(slice, self)) } } else { None }
233+
if self < slice.len() {
234+
#[cfg(bootstrap)]
235+
// SAFETY: `self` is checked to be in bounds.
236+
unsafe {
237+
Some(&*get_noubcheck(slice, self))
238+
}
239+
#[cfg(not(bootstrap))]
240+
// SAFETY: `self` is checked to be in bounds.
241+
unsafe {
242+
Some(slice_get_unchecked(slice, self))
243+
}
244+
} else {
245+
None
246+
}
229247
}
230248

231249
#[inline]
232250
fn get_mut(self, slice: &mut [T]) -> Option<&mut T> {
233251
if self < slice.len() {
252+
#[cfg(bootstrap)]
253+
// SAFETY: `self` is checked to be in bounds.
254+
unsafe {
255+
Some(&mut *get_mut_noubcheck(slice, self))
256+
}
257+
#[cfg(not(bootstrap))]
234258
// SAFETY: `self` is checked to be in bounds.
235-
unsafe { Some(&mut *get_mut_noubcheck(slice, self)) }
259+
unsafe {
260+
Some(slice_get_unchecked(slice, self))
261+
}
236262
} else {
237263
None
238264
}
@@ -253,7 +279,14 @@ unsafe impl<T> SliceIndex<[T]> for usize {
253279
// Use intrinsics::assume instead of hint::assert_unchecked so that we don't check the
254280
// precondition of this function twice.
255281
crate::intrinsics::assume(self < slice.len());
256-
get_noubcheck(slice, self)
282+
#[cfg(bootstrap)]
283+
{
284+
get_noubcheck(slice, self)
285+
}
286+
#[cfg(not(bootstrap))]
287+
{
288+
slice_get_unchecked(slice, self)
289+
}
257290
}
258291
}
259292

@@ -265,7 +298,16 @@ unsafe impl<T> SliceIndex<[T]> for usize {
265298
(this: usize = self, len: usize = slice.len()) => this < len
266299
);
267300
// SAFETY: see comments for `get_unchecked` above.
268-
unsafe { get_mut_noubcheck(slice, self) }
301+
unsafe {
302+
#[cfg(bootstrap)]
303+
{
304+
get_mut_noubcheck(slice, self)
305+
}
306+
#[cfg(not(bootstrap))]
307+
{
308+
slice_get_unchecked(slice, self)
309+
}
310+
}
269311
}
270312

271313
#[inline]

Diff for: tests/mir-opt/lower_intrinsics.rs

+21
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,24 @@ pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug
267267
let _usize = ptr_metadata(b);
268268
let _vtable = ptr_metadata(c);
269269
}
270+
271+
// EMIT_MIR lower_intrinsics.slice_get.LowerIntrinsics.diff
272+
pub unsafe fn slice_get<'a, 'b>(
273+
r: &'a [i8],
274+
rm: &'b mut [i16],
275+
p: *const [i32],
276+
pm: *mut [i64],
277+
i: usize,
278+
) -> (&'a i8, &'b mut i16, *const i32, *mut i64) {
279+
use std::intrinsics::slice_get_unchecked;
280+
// CHECK: = &(*_{{[0-9]+}})[_{{[0-9]+}}]
281+
// CHECK: = &mut (*_{{[0-9]+}})[_{{[0-9]+}}]
282+
// CHECK: = &raw const (*_{{[0-9]+}})[_{{[0-9]+}}]
283+
// CHECK: = &raw mut (*_{{[0-9]+}})[_{{[0-9]+}}]
284+
(
285+
slice_get_unchecked(r, i),
286+
slice_get_unchecked(rm, i),
287+
slice_get_unchecked(p, i),
288+
slice_get_unchecked(pm, i),
289+
)
290+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
- // MIR for `slice_get` before LowerIntrinsics
2+
+ // MIR for `slice_get` after LowerIntrinsics
3+
4+
fn slice_get(_1: &[i8], _2: &mut [i16], _3: *const [i32], _4: *mut [i64], _5: usize) -> (&i8, &mut i16, *const i32, *mut i64) {
5+
debug r => _1;
6+
debug rm => _2;
7+
debug p => _3;
8+
debug pm => _4;
9+
debug i => _5;
10+
let mut _0: (&i8, &mut i16, *const i32, *mut i64);
11+
let mut _6: &i8;
12+
let mut _7: &[i8];
13+
let mut _8: usize;
14+
let mut _9: &mut i16;
15+
let mut _10: &mut [i16];
16+
let mut _11: usize;
17+
let mut _12: *const i32;
18+
let mut _13: *const [i32];
19+
let mut _14: usize;
20+
let mut _15: *mut i64;
21+
let mut _16: *mut [i64];
22+
let mut _17: usize;
23+
24+
bb0: {
25+
StorageLive(_6);
26+
StorageLive(_7);
27+
_7 = copy _1;
28+
StorageLive(_8);
29+
_8 = copy _5;
30+
- _6 = slice_get_unchecked::<&[i8], &i8>(move _7, move _8) -> [return: bb1, unwind unreachable];
31+
+ _6 = &(*_7)[_8];
32+
+ goto -> bb1;
33+
}
34+
35+
bb1: {
36+
StorageDead(_8);
37+
StorageDead(_7);
38+
StorageLive(_9);
39+
StorageLive(_10);
40+
_10 = move _2;
41+
StorageLive(_11);
42+
_11 = copy _5;
43+
- _9 = slice_get_unchecked::<&mut [i16], &mut i16>(move _10, move _11) -> [return: bb2, unwind unreachable];
44+
+ _9 = &mut (*_10)[_11];
45+
+ goto -> bb2;
46+
}
47+
48+
bb2: {
49+
StorageDead(_11);
50+
StorageDead(_10);
51+
StorageLive(_12);
52+
StorageLive(_13);
53+
_13 = copy _3;
54+
StorageLive(_14);
55+
_14 = copy _5;
56+
- _12 = slice_get_unchecked::<*const [i32], *const i32>(move _13, move _14) -> [return: bb3, unwind unreachable];
57+
+ _12 = &raw const (*_13)[_14];
58+
+ goto -> bb3;
59+
}
60+
61+
bb3: {
62+
StorageDead(_14);
63+
StorageDead(_13);
64+
StorageLive(_15);
65+
StorageLive(_16);
66+
_16 = copy _4;
67+
StorageLive(_17);
68+
_17 = copy _5;
69+
- _15 = slice_get_unchecked::<*mut [i64], *mut i64>(move _16, move _17) -> [return: bb4, unwind unreachable];
70+
+ _15 = &raw mut (*_16)[_17];
71+
+ goto -> bb4;
72+
}
73+
74+
bb4: {
75+
StorageDead(_17);
76+
StorageDead(_16);
77+
_0 = (move _6, move _9, move _12, move _15);
78+
StorageDead(_15);
79+
StorageDead(_12);
80+
StorageDead(_9);
81+
StorageDead(_6);
82+
return;
83+
}
84+
}
85+

0 commit comments

Comments
 (0)