-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Deprecate Read::initializer in favor of ptr::freeze #58363
Changes from 3 commits
5e0fb23
c21e79e
e4d316e
73133ee
1671e90
0529921
695feb1
9d4f07e
055bcb6
78e2233
ec682d1
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 | ||||
---|---|---|---|---|---|---|
|
@@ -946,6 +946,58 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) { | |||||
intrinsics::volatile_store(dst, src); | ||||||
} | ||||||
|
||||||
/// Freezes `count * size_of::<T>()` bytes of memory, converting undefined data into | ||||||
/// arbitrary but fixed data. | ||||||
sfackler marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
/// | ||||||
/// Uninitialized memory has undefined contents, and interation with that data | ||||||
/// can easily cause undefined behavior. This function "freezes" memory | ||||||
/// contents, converting uninitialized memory to initialized memory with | ||||||
/// arbitrary contents so that use of it is well defined. | ||||||
/// | ||||||
/// This function has no runtime effect; it is purely an instruction to the | ||||||
/// compiler. In particular, it does not actually write anything to the memory. | ||||||
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. This function does have an effect in the "Rust abstract machine" though, not just in the compiler. And of course it has a run-time effect by inhibiting optimizations. Maybe a comparison with a compiler fence helps? Those also clearly have an effect on runtime behavior even though they do not have a runtime effect themselves. 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. Oh, also I think we should be clear about this counting as a write access as far as mutability and data races are concerned. Doing this on a shared reference is UB. |
||||||
/// | ||||||
/// # Safety | ||||||
/// | ||||||
/// Behavior is undefined if any of the following conditions are violated: | ||||||
/// | ||||||
/// * `dst` must be [valid] for writes. | ||||||
/// | ||||||
/// * Every bit representation of `T` must be a valid value. | ||||||
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. Why is that a precondition to
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. I thought that what you meant in this comment: #58363 (comment). If the validity requirements are less strict I can move this to be a more general warning about use of the function rather than a hard UB constraint. 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. The phrasing here seems congruent with the behavior of 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. The validity requirements come in once you actually operate on or a make a copy of a value at some type. So the following is UB: let x = MaybeUninit::<bool>::uninitialized();
ptr::freeze(x.as_mut_ptr());
let x = x.into_initialized(); // UB But it's not the freeze that is UB, it's the 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. @RalfJung Oh... you're talking about |
||||||
/// | ||||||
/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. | ||||||
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.
Suggested change
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. Note that this formulation is used consistently throughout this file, so I'd prefer that if it gets changed that happens in a separate PR. 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. Ah; that's a good idea; I'll see if I can remember to make such a PR... :) |
||||||
/// | ||||||
/// [valid]: ../ptr/index.html#safety | ||||||
/// | ||||||
/// # Examples | ||||||
/// | ||||||
/// ```ignore (io-is-in-std) | ||||||
/// use std::io::{self, Read}; | ||||||
/// use std::mem; | ||||||
/// use std::ptr; | ||||||
/// | ||||||
/// pub fn read_le_u32<R>(reader: &mut R) -> io::Result<u32> | ||||||
/// where | ||||||
/// R: Read, | ||||||
/// { | ||||||
/// unsafe { | ||||||
/// // We're passing this buffer to an arbitrary reader and aren't | ||||||
/// // guaranteed they won't read from it, so freeze to avoid UB. | ||||||
/// let mut buf: [u8; 4] = mem::uninitialized(); | ||||||
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. Could you add a FIXME somewhere about porting this to 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. (This is still open, from what I can see) |
||||||
/// ptr::freeze(&mut buf, 1); | ||||||
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. Should this use 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. I don't think it really matters either way - we're either freezing a single 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. Oh sure, I just figured it was a bit odd compared to how we'd expect it to idiomatically be used |
||||||
/// reader.read_exact(&mut buf)?; | ||||||
/// | ||||||
/// Ok(u32::from_le_bytes(buf)) | ||||||
/// } | ||||||
/// } | ||||||
/// ``` | ||||||
#[inline] | ||||||
#[unstable(feature = "ptr_freeze", issue = "0")] | ||||||
pub unsafe fn freeze<T>(dst: *mut T, count: usize) { | ||||||
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. Is this the right interface? It's currently a bit weird in that we don't actually use the count. It could alternatively just take the pointer, and say that it freezes all memory reachable through it? 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. Also, should this be unsafe in the first place? Since it's not actually modifying any of the pointed-to data, does it matter if it's valid or not? 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. Since it's already "basically stable", I wonder if this should take I'd naively think that it could be safe and probably should be, but I'm not an expert! We also briefly discussed maybe only taking 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. I think it should take raw pointers so people don't have to create references to uninitialized data.
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. Could we change this to 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. AFAIK there's currently no way to form a |
||||||
let _ = count; | ||||||
asm!("" : "=*m"(dst) : ); | ||||||
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. The constraints here are incorrect since they only tell LLVM that the inline asm is writing to one element of What you want is something like this: 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. Also I just noticed that the current constraints indicate that the inline asm is overwriting the value in 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. Why would someone be using this function if they've already initialized the memory? 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.
The 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.
The docs for this function say that this function has no runtime effect. I would assume that this means that already-initialized data passed to this function would remain untouched.
The 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. "Clobbers all memory" does not mean literally all memory in naive source language semantics, e.g. certainly in 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.
First of all, you called it "freeze", and that is what LLVM's proposed "freeze" does: pick non-deterministic values for uninitialized data but keep initialized data intact. Second, we have a use-case for that in crossbeam-rs/crossbeam#315. |
||||||
} | ||||||
|
||||||
#[lang = "const_ptr"] | ||||||
impl<T: ?Sized> *const T { | ||||||
/// Returns `true` if the pointer is null. | ||||||
|
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 think this is the first time we talk about this kind of data in the docs. I usually call it "uninitialized data" as I feel that is easier to understand. It also is further away from LLVM's
undef
, which is good -- Rust's "uninitialized data" is much more likepoision
thanundef
.