-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Clarify drop_in_place safety #108684
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
base: master
Are you sure you want to change the base?
Clarify drop_in_place safety #108684
Changes from 1 commit
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 | ||||
---|---|---|---|---|---|---|
|
@@ -430,6 +430,10 @@ mod mut_ptr; | |||||
/// done automatically by the compiler. This means the fields of packed structs | ||||||
/// are not dropped in-place. | ||||||
/// | ||||||
/// [`drop_in_place()`] does not modify the pointed-to value beyond any changes | ||||||
/// performed by [`Drop::drop()`]. As far as the compiler is concerned, the value | ||||||
/// will still contain a valid bit pattern for type `T`. | ||||||
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
I feel like "will" is a little odd here, this reads better to me -- but in general I find the sentence strange; typically we don't I think say things like "As far as the compiler ...", rather that something is UB 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. You'd say that except that party of the wording was lifted straight from the MaybeUninit docs :) As Jakob says it doesn't make much sense though, so we should probably tighten up the wording on both 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. You mean the ManuallyDrop docs, I assume. But yeah that wording doesn't make a ton of sense. I don't know what that sentence is even trying to achieve, can we just remove it? |
||||||
/// | ||||||
/// [`ptr::read`]: self::read | ||||||
/// [`ptr::read_unaligned`]: self::read_unaligned | ||||||
/// [pinned]: crate::pin | ||||||
|
@@ -446,10 +450,15 @@ mod mut_ptr; | |||||
/// additional invariants - this is type-dependent. | ||||||
/// | ||||||
/// Additionally, if `T` is not [`Copy`], using the pointed-to value after | ||||||
/// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop = | ||||||
/// calling `drop_in_place` may cause undefined behavior. Note that `*to_drop = | ||||||
/// foo` counts as a use because it will cause the value to be dropped | ||||||
/// again. [`write()`] can be used to overwrite data without causing it to be | ||||||
/// dropped. | ||||||
/// dropped. Read operations may be UB based on library invariants of that type, | ||||||
/// for example reading the value pointed to by a dropped `Box<T>` is a use-after-free. | ||||||
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 sentence seems useful, but can you clarify: Read operations on what? Ie, I have code like: let p: *mut T = some_pointer_from_somewhere;
drop_in_place(p); I initially interpreted read operations as being about |
||||||
/// | ||||||
/// Having an `&` or `&mut` reference to the pointed-to value after calling [`drop_in_place()`] | ||||||
/// is still sound as long as it is not read from (in which case the soundness of the operation | ||||||
/// depends on the specific type). | ||||||
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'm not so sure that we should say 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.
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 there definitely is. The issue is that "holding" is a really imprecise term. Specifically, consider: struct S(u8);
impl Drop for S {
fn drop(&mut self) {
let p = self as *mut S as *mut MaybeUninit<u8>;
*p = MaybeUninit::uninit();
// In the world I'm talking about, this would be UB, if it was actually written:
// let r = self;
}
}
let mut s = S;
let r = &mut s;
drop_in_place(r);
// But the move about was not actually included, and so no UB. However, we include it here:
let r_immut = &*r; // or let r2 = r, either is possible
// Now we have UB This world isn't particularly likely, but it's definitely not unimaginable 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. It would be nice to just get a ruling on this because as it stands this leads to the API being super weird and annoying. Note that 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. Actually! The guarantee on 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 statement about 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. From the docs of ManuallyDrop::drop():
Perhaps there's ambiguity on how the "other than changes made by the destructor itself" groups with the rest of the statement, but to me this is saying that one can rely on 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, ok, so it's an open question whether 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. Yeah; it's an open question but I think it's not actually needed to be answered to reach my conclusion. 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've opened rust-lang/unsafe-code-guidelines#394 and will change the docs to use the same squirrely wording that ManuallyDrop uses |
||||||
/// | ||||||
/// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
What is the point of this guarantee?
drop
can still mutate the value pretty much arbitrarily, right?Or is it intended to say something about the auto-generated drop glue, in case I control the
drop
being called? That seems odd to state like this though, instead we should give a somewhat operational description of the auto-generated drop glue:drop
if it exists, and then call the drop glue of each fielddrop
if it exists, and then determine the active variant and call the drop glue for all fields of that variantAlso IMO we shouldn't duplicate the same thing between ManuallyDrop::drop and here.
drop_in_place
is probably the more canonical location, but then ManuallyDrop::drop should just reference this.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.