Skip to content

Commit fd600c2

Browse files
committed
const-eval: disable pointer fragment support
1 parent e6e32f1 commit fd600c2

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

core/src/ptr/mod.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,40 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
13481348
/// assert_eq!(x, [7, 8, 3, 4]);
13491349
/// assert_eq!(y, [1, 2, 9]);
13501350
/// ```
1351+
///
1352+
/// # Const evaluation limitations
1353+
///
1354+
/// If this function is invoked during const-evaluation, the current implementation has a small (and
1355+
/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y`
1356+
/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may
1357+
/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the
1358+
/// future.
1359+
///
1360+
/// The limitation is illustrated by the following example:
1361+
///
1362+
/// ```
1363+
/// use std::mem::size_of;
1364+
/// use std::ptr;
1365+
///
1366+
/// const { unsafe {
1367+
/// const PTR_SIZE: usize = size_of::<*const i32>();
1368+
/// let mut data1 = [0u8; PTR_SIZE];
1369+
/// let mut data2 = [0u8; PTR_SIZE];
1370+
/// // Store a pointer in `data1`.
1371+
/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42);
1372+
/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks.
1373+
/// // This call will fail, because the pointer in `data1` crosses the boundary
1374+
/// // between several of the 1-byte chunks that are being swapped here.
1375+
/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE);
1376+
/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size
1377+
/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between
1378+
/// // two chunks.
1379+
/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1);
1380+
/// // Read the pointer from `data2` and dereference it.
1381+
/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned();
1382+
/// assert!(*ptr == 42);
1383+
/// } }
1384+
/// ```
13511385
#[inline]
13521386
#[stable(feature = "swap_nonoverlapping", since = "1.27.0")]
13531387
#[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")]
@@ -1376,7 +1410,9 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
13761410
const_eval_select!(
13771411
@capture[T] { x: *mut T, y: *mut T, count: usize }:
13781412
if const {
1379-
// At compile-time we don't need all the special code below.
1413+
// At compile-time we want to always copy this in chunks of `T`, to ensure that if there
1414+
// are pointers inside `T` we will copy them in one go rather than trying to copy a part
1415+
// of a pointer (which would not work).
13801416
// SAFETY: Same preconditions as this function
13811417
unsafe { swap_nonoverlapping_const(x, y, count) }
13821418
} else {

coretests/tests/ptr.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -936,12 +936,13 @@ fn test_const_swap_ptr() {
936936
assert!(*s1.0.ptr == 666);
937937
assert!(*s2.0.ptr == 1);
938938

939-
// Swap them back, byte-for-byte
939+
// Swap them back, again as an array.
940+
// FIXME(#146291): we should be swapping back at type `u8` but that currently does not work.
940941
unsafe {
941942
ptr::swap_nonoverlapping(
942-
ptr::from_mut(&mut s1).cast::<u8>(),
943-
ptr::from_mut(&mut s2).cast::<u8>(),
944-
size_of::<A>(),
943+
ptr::from_mut(&mut s1).cast::<T>(),
944+
ptr::from_mut(&mut s2).cast::<T>(),
945+
1,
945946
);
946947
}
947948

0 commit comments

Comments
 (0)