Skip to content

Commit 8e85afd

Browse files
committed
slice.get(i) should use a slice projection in MIR, like slice[i] does
1 parent 6e23095 commit 8e85afd

15 files changed

+382
-86
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ pub(crate) fn check_intrinsic_type(
256256
vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.isize],
257257
Ty::new_imm_ptr(tcx, param(0)),
258258
),
259+
sym::slice_get_unchecked => (3, 0, vec![param(1), tcx.types.usize], param(0)),
259260
sym::ptr_mask => (
260261
1,
261262
0,

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 46 additions & 0 deletions
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 {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,6 +1982,7 @@ symbols! {
19821982
slice,
19831983
slice_from_raw_parts,
19841984
slice_from_raw_parts_mut,
1985+
slice_get_unchecked,
19851986
slice_into_vec,
19861987
slice_iter,
19871988
slice_len_fn,

library/core/src/intrinsics/bounds.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 Pointee: ?Sized;
7+
}
8+
9+
impl<T: ?Sized> BuiltinDeref for &mut T {
10+
type Pointee = T;
11+
}
12+
impl<T: ?Sized> BuiltinDeref for &T {
13+
type Pointee = T;
14+
}
15+
impl<T: ?Sized> BuiltinDeref for *mut T {
16+
type Pointee = T;
17+
}
18+
impl<T: ?Sized> BuiltinDeref for *const T {
19+
type Pointee = T;
20+
}
21+
22+
pub trait ChangePointee<U: ?Sized>: BuiltinDeref {
23+
type Output;
24+
}
25+
impl<'a, T: ?Sized + 'a, U: ?Sized + 'a> ChangePointee<U> for &'a mut T {
26+
type Output = &'a mut U;
27+
}
28+
impl<'a, T: ?Sized + 'a, U: ?Sized + 'a> ChangePointee<U> for &'a T {
29+
type Output = &'a U;
30+
}
31+
impl<T: ?Sized, U: ?Sized> ChangePointee<U> for *mut T {
32+
type Output = *mut U;
33+
}
34+
impl<T: ?Sized, U: ?Sized> ChangePointee<U> for *const T {
35+
type Output = *const U;
36+
}

library/core/src/intrinsics/mod.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ use crate::marker::{DiscriminantKind, Tuple};
6565
use crate::mem::SizedTypeProperties;
6666
use crate::{ptr, ub_checks};
6767

68+
mod bounds;
6869
pub mod fallback;
6970
pub mod mir;
7071
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,33 @@ 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+
ItemPtr: bounds::ChangePointee<[T], Pointee = T, Output = SlicePtr>,
1782+
SlicePtr,
1783+
T,
1784+
>(
1785+
slice_ptr: SlicePtr,
1786+
index: usize,
1787+
) -> ItemPtr;
1788+
17611789
/// Masks out bits of the pointer according to a mask.
17621790
///
17631791
/// Note that, unlike most intrinsics, this is safe to call;
@@ -3606,18 +3634,9 @@ pub const fn type_id<T: ?Sized + 'static>() -> u128;
36063634
#[unstable(feature = "core_intrinsics", issue = "none")]
36073635
#[rustc_intrinsic_const_stable_indirect]
36083636
#[rustc_intrinsic]
3609-
pub const fn aggregate_raw_ptr<P: AggregateRawPtr<D, Metadata = M>, D, M>(data: D, meta: M) -> P;
3610-
3611-
#[unstable(feature = "core_intrinsics", issue = "none")]
3612-
pub trait AggregateRawPtr<D> {
3613-
type Metadata: Copy;
3614-
}
3615-
impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*const T> for *const P {
3616-
type Metadata = <P as ptr::Pointee>::Metadata;
3617-
}
3618-
impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
3619-
type Metadata = <P as ptr::Pointee>::Metadata;
3620-
}
3637+
pub const fn aggregate_raw_ptr<P: bounds::BuiltinDeref, D, M>(data: D, meta: M) -> P
3638+
where
3639+
<P as bounds::BuiltinDeref>::Pointee: ptr::Pointee<Metadata = M>;
36213640

36223641
/// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`.
36233642
///

library/core/src/slice/index.rs

Lines changed: 49 additions & 7 deletions
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]

tests/mir-opt/lower_intrinsics.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,24 @@ pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug
266266
let _usize = ptr_metadata(b);
267267
let _vtable = ptr_metadata(c);
268268
}
269+
270+
// EMIT_MIR lower_intrinsics.slice_get.LowerIntrinsics.diff
271+
pub unsafe fn slice_get<'a, 'b>(
272+
r: &'a [i8],
273+
rm: &'b mut [i16],
274+
p: *const [i32],
275+
pm: *mut [i64],
276+
i: usize,
277+
) -> (&'a i8, &'b mut i16, *const i32, *mut i64) {
278+
use std::intrinsics::slice_get_unchecked;
279+
// CHECK: = &(*_{{[0-9]+}})[_{{[0-9]+}}]
280+
// CHECK: = &mut (*_{{[0-9]+}})[_{{[0-9]+}}]
281+
// CHECK: = &raw const (*_{{[0-9]+}})[_{{[0-9]+}}]
282+
// CHECK: = &raw mut (*_{{[0-9]+}})[_{{[0-9]+}}]
283+
(
284+
slice_get_unchecked(r, i),
285+
slice_get_unchecked(rm, i),
286+
slice_get_unchecked(p, i),
287+
slice_get_unchecked(pm, i),
288+
)
289+
}

0 commit comments

Comments
 (0)