diff --git a/core/src/intrinsics.rs b/core/src/intrinsics.rs index d230c06a2..2895c923a 100644 --- a/core/src/intrinsics.rs +++ b/core/src/intrinsics.rs @@ -2356,6 +2356,9 @@ pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) - /// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but /// with the argument order swapped. /// +/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// /// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy /// /// # Safety @@ -2461,6 +2464,9 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// order swapped. Copying takes place as if the bytes were copied from `src` /// to a temporary array and then copied from the array to `dst`. /// +/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// /// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove /// /// # Safety diff --git a/core/src/ptr/mod.rs b/core/src/ptr/mod.rs index 970636c0c..4f257f9de 100644 --- a/core/src/ptr/mod.rs +++ b/core/src/ptr/mod.rs @@ -730,7 +730,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// Swaps the values at two mutable locations of the same type, without /// deinitializing either. /// -/// But for the following two exceptions, this function is semantically +/// But for the following exceptions, this function is semantically /// equivalent to [`mem::swap`]: /// /// * It operates on raw pointers instead of references. When references are @@ -740,6 +740,9 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// overlapping region of memory from `x` will be used. This is demonstrated /// in the second example below. /// +/// * The operation is "untyped" in the sense that data may be uninitialized or otherwise violate +/// the requirements of `T`. The initialization state is preserved exactly. +/// /// # Safety /// /// Behavior is undefined if any of the following conditions are violated: @@ -816,6 +819,9 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// Swaps `count * size_of::()` bytes between the two regions of memory /// beginning at `x` and `y`. The two regions must *not* overlap. /// +/// The operation is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// /// # Safety /// /// Behavior is undefined if any of the following conditions are violated: @@ -861,15 +867,15 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { if mem::align_of::() >= mem::align_of::<$ChunkTy>() && mem::size_of::() % mem::size_of::<$ChunkTy>() == 0 { - let x: *mut MaybeUninit<$ChunkTy> = x.cast(); - let y: *mut MaybeUninit<$ChunkTy> = y.cast(); + let x: *mut $ChunkTy = x.cast(); + let y: *mut $ChunkTy = y.cast(); let count = count * (mem::size_of::() / mem::size_of::<$ChunkTy>()); // SAFETY: these are the same bytes that the caller promised were // ok, just typed as `MaybeUninit`s instead of as `T`s. // The `if` condition above ensures that we're not violating // alignment requirements, and that the division is exact so // that we don't lose any bytes off the end. - return unsafe { swap_nonoverlapping_simple(x, y, count) }; + return unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }; } }; } @@ -902,7 +908,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { } // SAFETY: Same preconditions as this function - unsafe { swap_nonoverlapping_simple(x, y, count) } + unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } } /// Same behaviour and safety conditions as [`swap_nonoverlapping`] @@ -911,17 +917,17 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { /// `swap_nonoverlapping` tries to use) so no need to manually SIMD it. #[inline] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] -const unsafe fn swap_nonoverlapping_simple(x: *mut T, y: *mut T, count: usize) { +const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, count: usize) { + let x = x.cast::>(); + let y = y.cast::>(); let mut i = 0; while i < count { - let x: &mut T = - // SAFETY: By precondition, `i` is in-bounds because it's below `n` - unsafe { &mut *x.add(i) }; - let y: &mut T = - // SAFETY: By precondition, `i` is in-bounds because it's below `n` - // and it's distinct from `x` since the ranges are non-overlapping - unsafe { &mut *y.add(i) }; - mem::swap_simple(x, y); + // SAFETY: By precondition, `i` is in-bounds because it's below `n` + let x = unsafe { &mut *x.add(i) }; + // SAFETY: By precondition, `i` is in-bounds because it's below `n` + // and it's distinct from `x` since the ranges are non-overlapping + let y = unsafe { &mut *y.add(i) }; + mem::swap_simple::>(x, y); i += 1; } diff --git a/core/tests/ptr.rs b/core/tests/ptr.rs index e49595c25..bab2b1792 100644 --- a/core/tests/ptr.rs +++ b/core/tests/ptr.rs @@ -781,6 +781,31 @@ fn nonnull_tagged_pointer_with_provenance() { } } +#[test] +fn swap_copy_untyped() { + // We call `{swap,copy}{,_nonoverlapping}` at `bool` type on data that is not a valid bool. + // These should all do untyped copies, so this should work fine. + let mut x = 5u8; + let mut y = 6u8; + + let ptr1 = &mut x as *mut u8 as *mut bool; + let ptr2 = &mut y as *mut u8 as *mut bool; + + unsafe { + ptr::swap(ptr1, ptr2); + ptr::swap_nonoverlapping(ptr1, ptr2, 1); + } + assert_eq!(x, 5); + assert_eq!(y, 6); + + unsafe { + ptr::copy(ptr1, ptr2, 1); + ptr::copy_nonoverlapping(ptr1, ptr2, 1); + } + assert_eq!(x, 5); + assert_eq!(y, 5); +} + #[test] fn test_const_copy() { const {