Rollup merge of #137280 - RalfJung:const_swap_nonoverlapping, r=lcnr
stabilize ptr::swap_nonoverlapping in const Closes https://github.com/rust-lang/rust/issues/133668 The blocking issue mentioned there is resolved by documentation. We may in the future actually support such code, but that is blocked on https://github.com/rust-lang/const-eval/issues/72 which is non-trivial to implement. Meanwhile, this completes stabilization of all `const fn` in `ptr`. :) Here's a version of the problematic example to play around with: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6c390452379fb593e109b8f8ee854d2a Should be FCP'd with both `@rust-lang/libs-api` and `@rust-lang/lang` since `swap_nonoverlapping` is documented to work as an "untyped" operation but due to the limitation mentioned above, that's not entirely true during const evaluation. I expect this limitation will only be hit in niche corner cases, so the benefits of having this function work most of the time outweigh the downsides of users running into this problem. (Note that unsafe code could already hit this limitation before this PR by doing cursed pointer casts, but having it hidden inside `swap_nonoverlapping` feels a bit different.)
This commit is contained in:
commit
fcb9da597a
6 changed files with 46 additions and 11 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
//@ compile-flags: -Z ui-testing=no
|
||||
|
||||
|
||||
#![feature(const_swap_nonoverlapping)]
|
||||
use std::{
|
||||
mem::{self, MaybeUninit},
|
||||
ptr,
|
||||
|
|
|
|||
|
|
@ -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>>`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue