-
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?
Conversation
Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter cc @rust-lang/miri, @rust-lang/wg-const-eval |
c820135
to
749c3e6
Compare
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
/// # 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` |
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
fn test_const_swap_ptr() { | |
// The `swap` functions are implemented in the library, they are not primitives. | |
// Only `swap_nonoverlapping` takes a count; pointers that cross multiple elements | |
// are *not* supported. | |
// We put the pointer at an odd offset in the type and copy them as an array of bytes, | |
// which should catch most of the ways that the library implementation can get it wrong. | |
#[cfg(target_pointer_width = "32")] | |
type HalfPtr = i16; | |
#[cfg(target_pointer_width = "64")] | |
type HalfPtr = i32; | |
#[repr(C, packed)] | |
#[allow(unused)] | |
struct S { | |
f1: HalfPtr, | |
// Crucially this field is at an offset that is not a multiple of the pointer size. | |
ptr: &'static i32, | |
// Make sure the entire type does not have a power-of-2 size: | |
// make it 3 pointers in size. This used to hit a bug in `swap_nonoverlapping`. | |
f2: [HalfPtr; 3], | |
} | |
// Ensure the entire thing is usize-aligned, so in principle this | |
// looks like it could be eligible for a `usize` copying loop. | |
#[cfg_attr(target_pointer_width = "32", repr(align(4)))] | |
#[cfg_attr(target_pointer_width = "64", repr(align(8)))] | |
struct A(S); | |
const { | |
let mut s1 = A(S { ptr: &1, f1: 0, f2: [0; 3] }); | |
let mut s2 = A(S { ptr: &666, f1: 0, f2: [0; 3] }); | |
// Swap ptr1 and ptr2, as an array. | |
type T = [u8; mem::size_of::<A>()]; | |
unsafe { | |
ptr::swap(ptr::from_mut(&mut s1).cast::<T>(), ptr::from_mut(&mut s2).cast::<T>()); | |
} | |
// Make sure they still work. | |
assert!(*s1.0.ptr == 666); | |
assert!(*s2.0.ptr == 1); | |
// Swap them back, again as an array. | |
// (This is where we'd use 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.) | |
unsafe { | |
ptr::swap_nonoverlapping( | |
ptr::from_mut(&mut s1).cast::<T>(), | |
ptr::from_mut(&mut s2).cast::<T>(), | |
1, | |
); | |
} | |
// Make sure they still work. | |
assert!(*s1.0.ptr == 1); | |
assert!(*s2.0.ptr == 666); | |
}; |
"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.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
@rfcbot fcp merge |
Team member @traviscross has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. |
749c3e6
to
99480fc
Compare
☔ The latest upstream changes (presumably #137848) made this pull request unmergeable. Please resolve the merge conflicts. |
All reactions
Sorry, something went wrong.
@BurntSushi @joshtriplett @nikomatsakis @tmandry friendly FCP checkbox reminder :) |
All reactions
Sorry, something went wrong.
99480fc
to
168267d
Compare
/// 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 comment
The 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?
Sorry, something went wrong.
All reactions
BurntSushi
scottmcm
Successfully merging this pull request may close these issues.
Tracking Issue for const_swap_nonoverlapping
Closes #133668
The blocking issue mentioned there is resolved by documentation. We may in the future actually support such code, but that is blocked on rust-lang/const-eval#72 which is non-trivial to implement. Meanwhile, this completes stabilization of all
const fn
inptr
. :)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 insideswap_nonoverlapping
feels a bit different.)