-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stabilize ptr::swap_nonoverlapping in const #137280
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1064,10 +1064,43 @@ 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. | ||
/// This 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); | ||
/// } } | ||
/// ``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explicitly state that this may change in the future somewhere in the docs? I know the "current" wording seemingly implies that, but I'd rather do some very clear CYA here. Also, the docs here seem to leave it a bit mysterious as to what "fail" means. Is it a panic? Silent logical error? Something else? Could that be clarified as well? |
||
#[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, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it's a "current" thing not a promise, but specifically things like "at least 2" seem oddly specific.
Are we sure it's impossible to, say, create a failing version using a big packed struct that arranges to have an unaligned pointer that carries provenance but is unaligned so that it's copied in multiple parts by the implementation?
(I don't consider this an FCP blocker.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we are. The const implementation will always swap entire
T
at once (this got fixed in #134689). We even have a test covering this:rust/library/coretests/tests/ptr.rs
Lines 905 to 963 in 749c3e6
"at least 2" is technically redundant since the text already says a pointer must span the boundary between two chunks being swapped, and if there's only one chunk there is no boundary. But I felt it helped make the point more clear.