Skip to content
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

Implement Manually Drop #40559

Merged
merged 5 commits into from
Apr 12, 2017
Merged

Implement Manually Drop #40559

merged 5 commits into from
Apr 12, 2017

Conversation

nagisa
Copy link
Member

@nagisa nagisa commented Mar 15, 2017

As the RFC has been from approx a week in FCP without any major comments, I’m taking the opportunity to submit the PR early.

@rust-highfive
Copy link
Collaborator

r? @brson

(rust_highfive has picked a reviewer for you, use r? to override)

impl<T: ::fmt::Debug> ::fmt::Debug for ManuallyDrop<T> {
fn fmt(&self, fmt: &mut ::fmt::Formatter) -> ::fmt::Result {
unsafe {
fmt.debug_tuple("ManuallyDrop").field(&self.value).finish()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably recommend just printing ManuallyDrop { .. } here (but retain the T: Debug bound) to be maximally conservative.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? The data inside ManuallyDrop is guaranteed to be valid, just like any other wrapper.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of disagree. Since the value can be logically uninhabited, I think we either need to state up front that this will access the inner value and cannot be called after drop, or that it will not. While we could leave the door open to going from not printing to printing, that seems like something I would not be comfortable actually doing in practice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gereeter Isn't the entire point of this type that that is not the case?

Copy link
Contributor

@gereeter gereeter Mar 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sfackler No - the point of this type is that it modifies the behavior on drop - instead of dropping the wrapped value, it just doesn't do anything. However, aside from behavior on drop, ManuallyDrop has no implications on accessing the data.

This is why ManuallyDrop implements Deref and DerefMut.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

~Every usage of ManuallyDrop will involve calling ManuallyDrop::drop at some point. It's then up to the developer to ensure that the inner value is never accessed past that call. If Debug does not access that value, and then all of the sudden starts doing so, that seems bad.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not so certain that most uses of ManuallyDrop will involve calling ManuallyDrop::drop. The one use I know of currently in the standard library, for example, never drops the contained value. Looking at the three commonly-downloaded users of nodrop, arrayvec doesn't drop the inside of the wrapper (since it only wants to drop some elements), quickersort just replaces the elements instead of dropping them, and generic-array doesn't drop the array inside NoDrop because it, like arrayvec, needs to only drop some of the elements.

That said, I completely agree that changing the behavior of Debug is not a good idea. I just think there isn't a good reason to not print the contained value.

Copy link
Contributor

@codyps codyps Mar 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ManuallyDrop::drop() is unsafe with a warning that it is the responsibility of the user not to access the field after it is dropped. Is there a reason to look at Debug as different from the other methods/traits provided by ManuallyDrop which expect it's contents to be valid?

The user has already been warned about use-after-drop, I'd consider that enough to allow the default Debug impl for ManuallyDrop to remain useful (and print the value).

@est31
Copy link
Member

est31 commented Mar 16, 2017

Cross linking rust-lang/rfcs#1860


/// Extract the value from the ManuallyDrop container.
#[unstable(feature = "manually_drop", issue = "0")]
pub fn into_inner(self) -> T {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a static method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This belongs to the RFC.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, commented over there as well.

/// }
/// }
/// ```
pub union ManuallyDrop<T>{ value: T }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Union fields are private by default, right?

impl<T: ::fmt::Debug> ::fmt::Debug for ManuallyDrop<T> {
fn fmt(&self, fmt: &mut ::fmt::Formatter) -> ::fmt::Result {
unsafe {
fmt.debug_tuple("ManuallyDrop").field(&self.value).finish()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of disagree. Since the value can be logically uninhabited, I think we either need to state up front that this will access the inner value and cannot be called after drop, or that it will not. While we could leave the door open to going from not printing to printing, that seems like something I would not be comfortable actually doing in practice.

@@ -736,3 +736,99 @@ pub fn discriminant<T>(v: &T) -> Discriminant<T> {
}
}


#[unstable(feature = "manually_drop", issue = "0")]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can you move these attributes below documentation? The established ordering convention is "docs, attributes, implementation".

@scottmcm
Copy link
Member

Would it make sense to have another union field to "mark" the value as dropped? Something like:

pub union ManuallyDrop<T>{ value: T, dropped: () }
    pub unsafe fn drop(slot: &mut ManuallyDrop<T>) {
        ptr::drop_in_place(&mut slot.value)
        *slot = ManuallyDrop { dropped: () };
    }

Then a hypothetical "union sanitizer" could flag any future uses of .value as errors post-drop since it knows that .dropped is the only active fragment. It might also communicate stuff to the optimizer that could potentially be helpful, like "those sizeof(T) bytes are definitely dead now".

(But of course that depends on future events or uncertainties, so may well not be worth doing now.)

@alexcrichton
Copy link
Member

@rust-lang/libs thoughts on the ManuallyDrop::into_inner question here?

It was brought up on the RFC that the likelihood of shadowing a legitimate method is very small because deref traits don't allow movement. @sfackler, however, brings up that trying to be clever here may mean it's just more difficult to understand in general.

I personally am sympathetic to what @sfackler is saying and would lean a bit towards a static method instead of an inherent method myself at this point.

/// }
/// }
/// ```
pub union ManuallyDrop<T>{ value: T }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space before {, and value should likely be on its own line (although I don't mind it like this).

@eddyb
Copy link
Member

eddyb commented Mar 21, 2017

@scottmcm I believe that might actually be needed in some interpretations of union.
cc @petrochenkov

@alexcrichton alexcrichton added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Mar 21, 2017
@petrochenkov
Copy link
Contributor

Would it make sense to have another union field to "mark" the value as dropped?

Seems reasonable if this will help some future sanitizer. This is an implementation detail though and can be changed later.

I believe that might actually be needed in some interpretations of union.

ptr::drop_in_place is an unsafe action orthogonal to unions, so it can break union guarantees and bring unions into invalid state in the same way like, e.g. writes through unsafe pointers can break any guarantees. In this case ptr::drop_in_place does bring the union into invalid state - its active field value no longer contains a valid value.
*slot = ManuallyDrop { dropped: () }; returns the union into valid state again, but I don't see what practical benefits it gives without sanitizers, given that the accessor methods (deref, into_inner etc) still cannot be used.

@bors
Copy link
Contributor

bors commented Mar 21, 2017

☔ The latest upstream changes (presumably #40601) made this pull request unmergeable. Please resolve the merge conflicts.

@ghost
Copy link

ghost commented Mar 22, 2017

Good job getting rid of NoDrop from our sorting routines!

I see there is one more similar struct left, namely in src/librustc_data_structures/array_vec.rs - incidentally it's called ManuallyDrop, too. Perhaps we could also replace that one?

@nagisa
Copy link
Member Author

nagisa commented Mar 22, 2017

Perhaps we could also replace that one?

Sure!

@scottmcm
Copy link
Member

Another random thought: forget(foo) can now be spelled ManuallyDrop::new(foo). Thus,

  1. Should std::mem::forget be implemented using this instead of the intrinsic?
  2. Should ManuallyDrop be must_use to encourage forget when there's no path that might use it later?

@nagisa
Copy link
Member Author

nagisa commented Apr 1, 2017

Intrinsic replaced.

The PR has been open for a while. I haven’t seen any opinions on into_inner so I’m inclined to just land this and (re-)consider the option during stabilisation.

@alexcrichton
Copy link
Member

@nagisa want to switch to a static method instead of an inherent function? I'll r+ w/ that

@nagisa nagisa force-pushed the manually-drop branch 2 times, most recently from 87a5096 to 4090a97 Compare April 2, 2017 10:26
@alexcrichton
Copy link
Member

r=me, but I'd recommend squashing the commits as well

Looks like travis is also failing?

@scottmcm
Copy link
Member

scottmcm commented Apr 4, 2017

@nagisa looks like compile-fail/forget-init-unsafe.rs is using the intrinsic that 4f494c9 removed.

@nagisa nagisa force-pushed the manually-drop branch 2 times, most recently from 23b8527 to 99f995b Compare April 5, 2017 14:50
@bors
Copy link
Contributor

bors commented Apr 7, 2017

☔ The latest upstream changes (presumably #41121) made this pull request unmergeable. Please resolve the merge conflicts.

@alexcrichton
Copy link
Member

@bors: r+

@bors
Copy link
Contributor

bors commented Apr 11, 2017

📌 Commit c337b99 has been approved by alexcrichton

@bors
Copy link
Contributor

bors commented Apr 11, 2017

⌛ Testing commit c337b99 with merge 5ad9e49...

@bors
Copy link
Contributor

bors commented Apr 11, 2017

💔 Test failed - status-appveyor

@TimNN
Copy link
Contributor

TimNN commented Apr 11, 2017

@alexcrichton
Copy link
Member

alexcrichton commented Apr 11, 2017 via email

frewsxcv added a commit to frewsxcv/rust that referenced this pull request Apr 11, 2017
Implement Manually Drop

As the RFC has been from approx a week in FCP without any major comments, I’m taking the opportunity to submit the PR early.
frewsxcv added a commit to frewsxcv/rust that referenced this pull request Apr 11, 2017
Implement Manually Drop

As the RFC has been from approx a week in FCP without any major comments, I’m taking the opportunity to submit the PR early.
bors added a commit that referenced this pull request Apr 12, 2017
Rollup of 8 pull requests

- Successful merges: #40377, #40559, #41173, #41202, #41204, #41209, #41216, #41231
- Failed merges:
@bors bors merged commit c337b99 into rust-lang:master Apr 12, 2017
@bors
Copy link
Contributor

bors commented Apr 12, 2017

⌛ Testing commit c337b99 with merge da32752...

@bors
Copy link
Contributor

bors commented Apr 12, 2017

☔ The latest upstream changes (presumably #41237) made this pull request unmergeable. Please resolve the merge conflicts.

pietroalbini added a commit to pietroalbini/rust that referenced this pull request Nov 12, 2018
…xcrichton

Add mem::forget_unsized() for forgetting unsized values

~~Allows passing values of `T: ?Sized` types to `mem::drop` and `mem::forget`.~~

Adds `mem::forget_unsized()` that accepts `T: ?Sized`.

I had to revert the PR that removed the `forget` intrinsic and replaced it with `ManuallyDrop`: rust-lang#40559
We can't use `ManuallyDrop::new()` here because it needs `T: Sized` and we don't have support for unsized return values yet (will we ever?).

r? @eddyb
pietroalbini added a commit to pietroalbini/rust that referenced this pull request Nov 15, 2018
…xcrichton

Add mem::forget_unsized() for forgetting unsized values

~~Allows passing values of `T: ?Sized` types to `mem::drop` and `mem::forget`.~~

Adds `mem::forget_unsized()` that accepts `T: ?Sized`.

I had to revert the PR that removed the `forget` intrinsic and replaced it with `ManuallyDrop`: rust-lang#40559
We can't use `ManuallyDrop::new()` here because it needs `T: Sized` and we don't have support for unsized return values yet (will we ever?).

r? @eddyb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.