diff options
| author | Ralf Jung <post@ralfj.de> | 2025-09-08 10:40:18 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2025-09-08 13:22:52 +0200 |
| commit | aed0ed4c93d661fc7b66dc4a39690948476e8a4a (patch) | |
| tree | defedf24675aa706b949f66950d41ac2ffa1fbc0 /library | |
| parent | ebdf2abea4b46fcbba959eee1207c979c77dc95c (diff) | |
| download | rust-aed0ed4c93d661fc7b66dc4a39690948476e8a4a.tar.gz rust-aed0ed4c93d661fc7b66dc4a39690948476e8a4a.zip | |
const-eval: disable pointer fragment support
Diffstat (limited to 'library')
| -rw-r--r-- | library/core/src/ptr/mod.rs | 38 | ||||
| -rw-r--r-- | library/coretests/tests/ptr.rs | 9 |
2 files changed, 42 insertions, 5 deletions
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 6b94088cb56..625024373ef 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1348,6 +1348,40 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) { /// assert_eq!(x, [7, 8, 3, 4]); /// assert_eq!(y, [1, 2, 9]); /// ``` +/// +/// # Const evaluation limitations +/// +/// If this function is invoked during const-evaluation, the current implementation has a small (and +/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y` +/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may +/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the +/// future. +/// +/// The limitation is illustrated by the following example: +/// +/// ``` +/// use std::mem::size_of; +/// use std::ptr; +/// +/// const { unsafe { +/// const PTR_SIZE: usize = size_of::<*const i32>(); +/// let mut data1 = [0u8; PTR_SIZE]; +/// let mut data2 = [0u8; PTR_SIZE]; +/// // Store a pointer in `data1`. +/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42); +/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks. +/// // This call will fail, because the pointer in `data1` crosses the boundary +/// // between several of the 1-byte chunks that are being swapped here. +/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE); +/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size +/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between +/// // two chunks. +/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1); +/// // Read the pointer from `data2` and dereference it. +/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned(); +/// assert!(*ptr == 42); +/// } } +/// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] #[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) { const_eval_select!( @capture[T] { x: *mut T, y: *mut T, count: usize }: if const { - // At compile-time we don't need all the special code below. + // At compile-time we want to always copy this in chunks of `T`, to ensure that if there + // are pointers inside `T` we will copy them in one go rather than trying to copy a part + // of a pointer (which would not work). // SAFETY: Same preconditions as this function unsafe { swap_nonoverlapping_const(x, y, count) } } else { diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index c13fb96a67f..f8774833d0a 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -936,12 +936,13 @@ fn test_const_swap_ptr() { assert!(*s1.0.ptr == 666); assert!(*s2.0.ptr == 1); - // Swap them back, byte-for-byte + // Swap them back, again as an array. + // FIXME(#146291): we should be swapping back at type `u8` but that currently does not work. unsafe { ptr::swap_nonoverlapping( - ptr::from_mut(&mut s1).cast::<u8>(), - ptr::from_mut(&mut s2).cast::<u8>(), - size_of::<A>(), + ptr::from_mut(&mut s1).cast::<T>(), + ptr::from_mut(&mut s2).cast::<T>(), + 1, ); } |
