about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/intrinsics/mod.rs1
-rw-r--r--library/core/src/ptr/mod.rs37
-rw-r--r--library/coretests/tests/lib.rs1
-rw-r--r--library/coretests/tests/ptr.rs4
-rw-r--r--tests/ui/consts/missing_span_in_backtrace.rs2
-rw-r--r--tests/ui/consts/missing_span_in_backtrace.stderr12
6 files changed, 46 insertions, 11 deletions
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index c5d5bc000f1..5649736e404 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -3320,7 +3320,6 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
 #[inline]
 #[rustc_intrinsic]
 #[rustc_intrinsic_const_stable_indirect]
-#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic
 pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
     // SAFETY: The caller provided single non-overlapping items behind
     // pointers, so swapping them with `count: 1` is fine.
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 445c789a7de..aa103af93ff 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -1065,10 +1065,45 @@ 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_unstable(feature = "const_swap_nonoverlapping", issue = "133668")]
+#[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "CURRENT_RUSTC_VERSION")]
 #[rustc_diagnostic_item = "ptr_swap_nonoverlapping"]
+#[rustc_allow_const_fn_unstable(const_eval_select)] // both implementations behave the same
 pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
     ub_checks::assert_unsafe_precondition!(
         check_library_ub,
diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs
index ef548971aaf..195fe3890db 100644
--- a/library/coretests/tests/lib.rs
+++ b/library/coretests/tests/lib.rs
@@ -15,7 +15,6 @@
 #![feature(char_max_len)]
 #![feature(clone_to_uninit)]
 #![feature(const_eval_select)]
-#![feature(const_swap_nonoverlapping)]
 #![feature(const_trait_impl)]
 #![feature(core_intrinsics)]
 #![feature(core_intrinsics_fallbacks)]
diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs
index 7d6e4eac1e2..bb60fb07468 100644
--- a/library/coretests/tests/ptr.rs
+++ b/library/coretests/tests/ptr.rs
@@ -949,6 +949,10 @@ fn test_const_swap_ptr() {
         // Make sure they still work.
         assert!(*s1.0.ptr == 1);
         assert!(*s2.0.ptr == 666);
+
+        // This is where we'd swap again using a `u8` type and a `count` of `size_of::<T>()` if it
+        // were not for the limitation of `swap_nonoverlapping` around pointers crossing multiple
+        // elements.
     };
 }
 
diff --git a/tests/ui/consts/missing_span_in_backtrace.rs b/tests/ui/consts/missing_span_in_backtrace.rs
index c8c7453daa1..490eb57c24d 100644
--- a/tests/ui/consts/missing_span_in_backtrace.rs
+++ b/tests/ui/consts/missing_span_in_backtrace.rs
@@ -1,7 +1,5 @@
 //@ compile-flags: -Z ui-testing=no
 
-
-#![feature(const_swap_nonoverlapping)]
 use std::{
     mem::{self, MaybeUninit},
     ptr,
diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr
index aad3d76dd26..88b3e37bb84 100644
--- a/tests/ui/consts/missing_span_in_backtrace.stderr
+++ b/tests/ui/consts/missing_span_in_backtrace.stderr
@@ -1,11 +1,11 @@
 error[E0080]: evaluation of constant value failed
-  --> $DIR/missing_span_in_backtrace.rs:16:9
+  --> $DIR/missing_span_in_backtrace.rs:14:9
    |
-16 | /         ptr::swap_nonoverlapping(
-17 | |             &mut ptr1 as *mut _ as *mut MaybeUninit<u8>,
-18 | |             &mut ptr2 as *mut _ as *mut MaybeUninit<u8>,
-19 | |             mem::size_of::<&i32>(),
-20 | |         );
+14 | /         ptr::swap_nonoverlapping(
+15 | |             &mut ptr1 as *mut _ as *mut MaybeUninit<u8>,
+16 | |             &mut ptr2 as *mut _ as *mut MaybeUninit<u8>,
+17 | |             mem::size_of::<&i32>(),
+18 | |         );
    | |_________^ unable to copy parts of a pointer from memory at ALLOC0
    |
 note: inside `swap_nonoverlapping::<MaybeUninit<u8>>`