Skip to content

Commit 99480fc

Browse files
committed
stabilize ptr::swap_nonoverlapping in const
1 parent ed49386 commit 99480fc

File tree

6 files changed

+44
-11
lines changed

6 files changed

+44
-11
lines changed

library/core/src/intrinsics/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -4000,7 +4000,6 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
40004000
#[inline]
40014001
#[rustc_intrinsic]
40024002
#[rustc_intrinsic_const_stable_indirect]
4003-
#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic
40044003
pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
40054004
// SAFETY: The caller provided single non-overlapping items behind
40064005
// pointers, so swapping them with `count: 1` is fine.

library/core/src/ptr/mod.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -1064,10 +1064,43 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
10641064
/// assert_eq!(x, [7, 8, 3, 4]);
10651065
/// assert_eq!(y, [1, 2, 9]);
10661066
/// ```
1067+
///
1068+
/// # Const evaluation limitations
1069+
///
1070+
/// If this function is invoked during const-evaluation, the current implementation has a small (and
1071+
/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y`
1072+
/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function
1073+
/// may fail.
1074+
/// This is illustrated by the following example:
1075+
///
1076+
/// ```
1077+
/// use std::mem::size_of;
1078+
/// use std::ptr;
1079+
///
1080+
/// const { unsafe {
1081+
/// const PTR_SIZE: usize = size_of::<*const i32>();
1082+
/// let mut data1 = [0u8; PTR_SIZE];
1083+
/// let mut data2 = [0u8; PTR_SIZE];
1084+
/// // Store a pointer in `data1`.
1085+
/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42);
1086+
/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks.
1087+
/// // This call will fail, because the pointer in `data1` crosses the boundary
1088+
/// // between several of the 1-byte chunks that are being swapped here.
1089+
/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE);
1090+
/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size
1091+
/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between
1092+
/// // two chunks.
1093+
/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1);
1094+
/// // Read the pointer from `data2` and dereference it.
1095+
/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned();
1096+
/// assert!(*ptr == 42);
1097+
/// } }
1098+
/// ```
10671099
#[inline]
10681100
#[stable(feature = "swap_nonoverlapping", since = "1.27.0")]
1069-
#[rustc_const_unstable(feature = "const_swap_nonoverlapping", issue = "133668")]
1101+
#[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "CURRENT_RUSTC_VERSION")]
10701102
#[rustc_diagnostic_item = "ptr_swap_nonoverlapping"]
1103+
#[rustc_allow_const_fn_unstable(const_eval_select)] // both implementations behave the same
10711104
pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
10721105
ub_checks::assert_unsafe_precondition!(
10731106
check_library_ub,

library/coretests/tests/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#![feature(cell_update)]
1616
#![feature(clone_to_uninit)]
1717
#![feature(const_eval_select)]
18-
#![feature(const_swap_nonoverlapping)]
1918
#![feature(const_trait_impl)]
2019
#![feature(core_intrinsics)]
2120
#![feature(core_intrinsics_fallbacks)]

library/coretests/tests/ptr.rs

+4
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,10 @@ fn test_const_swap_ptr() {
957957
// Make sure they still work.
958958
assert!(*s1.0.ptr == 1);
959959
assert!(*s2.0.ptr == 666);
960+
961+
// This is where we'd swap again using a `u8` type and a `count` of `size_of::<T>()` if it
962+
// were not for the limitation of `swap_nonoverlapping` around pointers crossing multiple
963+
// elements.
960964
};
961965
}
962966

tests/ui/consts/missing_span_in_backtrace.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//@ compile-flags: -Z ui-testing=no
22

3-
4-
#![feature(const_swap_nonoverlapping)]
53
use std::{
64
mem::{self, MaybeUninit},
75
ptr,

tests/ui/consts/missing_span_in_backtrace.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ note: inside `swap_nonoverlapping::compiletime::<MaybeUninit<u8>>`
1212
note: inside `swap_nonoverlapping::<MaybeUninit<u8>>`
1313
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
1414
note: inside `X`
15-
--> $DIR/missing_span_in_backtrace.rs:16:9
15+
--> $DIR/missing_span_in_backtrace.rs:14:9
1616
|
17-
16 | / ptr::swap_nonoverlapping(
18-
17 | | &mut ptr1 as *mut _ as *mut MaybeUninit<u8>,
19-
18 | | &mut ptr2 as *mut _ as *mut MaybeUninit<u8>,
20-
19 | | mem::size_of::<&i32>(),
21-
20 | | );
17+
14 | / ptr::swap_nonoverlapping(
18+
15 | | &mut ptr1 as *mut _ as *mut MaybeUninit<u8>,
19+
16 | | &mut ptr2 as *mut _ as *mut MaybeUninit<u8>,
20+
17 | | mem::size_of::<&i32>(),
21+
18 | | );
2222
| |_________^
2323
= help: this code performed an operation that depends on the underlying bytes representing a pointer
2424
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported

0 commit comments

Comments
 (0)