forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of rust-lang#101586 - ssbr:master, r=lcnr
Add the `#[manually_drop]` attribute. This attribute is equivalent to `#[lang = "manually_drop"]`, and the behavior of both are extended to allow the type to define `Drop`, in which case, that will still be run. Only the field drop glue is suppressed. This PR should essentially fully implement rust-lang#100344, and is gated behind the feature flag `manually_drop_attr`. Some additional notes: ### The meaning of `#[manually_drop]` `#[manually_drop]` means "do not destroy the fields automatically during destruction". `ManuallyDrop`, could "just", morally speaking, be `#[manually_drop] struct ManuallyDrop<T>(T);`. The intent is, per the MCP, to allow customization of the drop order, without making the interface for initializing or accessing the fields of the struct clumsier than a normal Rust type. It also -- and people did seem excited about this part -- lifts `ManuallyDrop` from being a special language supported feature to just another user of `#[manually_drop]`. ### Insignificant Drop There is the question of how this interacts with "insignificant" drop. I had trouble understanding the comments, but insignificant drop appears to just be a static analysis tool, and not something that changes behavior. (For example, it's used to detect if a language change will reorder drops in a meaningful way -- meaning, reorder the significant drops, not the insignificant ones.) Since it's unlikely to be used for `#[manually_drop]` types, I don't think it matters a whole lot. And where a destructor is defined, it would seem to make sense for `#[manually_drop]` types to match exactly the behavior of `union`, since they both have the shared property that field drop glue is suppressed. ### Users that expect `adt.is_manually_drop()` <-> "is `std::mem::ManuallyDrop`" I looked for all locations that queried for `is_manually_drop` in any form, and found two difficult cases which are hardcoded for `ManuallyDrop` in particular. The first is a clippy lint for redundant clones. I don't understand why it special-cases `ManuallyDrop`, and it's almost certainly wrong for it to special-case `#[manually_drop]` types in general. However, without understanding the original rationale, I have trouble figuring the right fix. Perhaps it should simply be deleted altogether. The second is unions -- specifically, the logic for disabling `DerefMut`. `my_union.x.y = z` will automatically dereference `my_union.x` if it implements `DerefMut`, unless it is `ManuallyDrop`, in which case it will not. This is because `ManuallyDrop` is a pointer back to its content, and so this will automatically call `drop` on a probably uninitialized field, and is unsafe. This is true of `ManuallyDrop`, but not necessarily any other `#[manually_drop]` type. I believe the correct fix would, instead, be a way to mark and detect types which are a smart pointer whose pointee is within `self`. See, for example, this playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=76fb22a6214ce453538fc18ec35a839d But that needs to wait for a separate RFC. For now, we apply exactly the same restriction for `ManuallyDrop` and for any other `#[manually_drop]` type, even though it may be confusing. ## To-do in future PRs 1. Delete `#[lang = "manually_drop"]`. I'm not sure if anything special needs to be done here other than replacing it with `#[manually_drop]` -- is there a compatibility guarantee that must be upheld? 2. (Optional) Fix the redundant clone check to correctly handle `#[manually_drop]` structs that aren't `ManuallyDrop`. 3. When there is more experience with the feature (e.g. in Crubit) create a full RFC based on the MCP, and go through the remainder of the stabilization process. (Also, do things like generalize the union error messages to not specifically mention `ManuallyDrop`, but also mention `#[manually_drop]`. For as long as the feature is unstable, the error messages would be confusing if they referenced it...)
- Loading branch information
Showing
28 changed files
with
385 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
src/doc/unstable-book/src/language-features/manually-drop-attr.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# `manually_drop_attr` | ||
|
||
The tracking issue for this feature is: [#100344] | ||
|
||
[#100344]: https://github.com/rust-lang/rust/issues/100344 | ||
|
||
The `manually_drop_attr` feature enables the `#[manually_drop]` attribute, which disables the drop glue for the type it is applied to. | ||
|
||
For example, `std::mem::ManuallyDrop` is implemented as follows: | ||
|
||
```rs | ||
#[manually_drop] | ||
struct ManuallyDrop<T>(T); | ||
``` | ||
|
||
But you can also use the attribute to change the order in which fields are dropped, by overriding `Drop`: | ||
|
||
```rs | ||
/// This struct changes the order in which `x` and `y` are dropped from the default. | ||
#[manually_drop] | ||
struct MyStruct { | ||
x: String, | ||
y: String, | ||
} | ||
|
||
impl Drop for MyStruct { | ||
fn drop(&mut self) { | ||
unsafe { | ||
std::ptr::drop_in_place(&mut self.y); | ||
std::ptr::drop_in_place(&mut self.x); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
This can be useful in combination with `repr(C)`, to decouple the layout from the destruction order. See MCP [#135](https://github.com/rust-lang/lang-team/issues/135). |
5 changes: 5 additions & 0 deletions
5
src/test/ui/manually_drop_attr/feature-gate-manually_drop_attr.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#[manually_drop] | ||
//~^ ERROR the `#[manually_drop]` attribute is an experimental feature | ||
struct Foo {} | ||
|
||
fn main() {} |
12 changes: 12 additions & 0 deletions
12
src/test/ui/manually_drop_attr/feature-gate-manually_drop_attr.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
error[E0658]: the `#[manually_drop]` attribute is an experimental feature | ||
--> $DIR/feature-gate-manually_drop_attr.rs:1:1 | ||
| | ||
LL | #[manually_drop] | ||
| ^^^^^^^^^^^^^^^^ | ||
| | ||
= note: see issue #100344 <https://github.com/rust-lang/rust/issues/100344> for more information | ||
= help: add `#![feature(manually_drop_attr)]` to the crate attributes to enable | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0658`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#![feature(manually_drop_attr)] | ||
#![forbid(unused_attributes)] | ||
#![manually_drop] | ||
//~^ ERROR attribute should be applied to a struct or enum | ||
|
||
#[manually_drop] | ||
//~^ ERROR attribute should be applied to a struct or enum | ||
fn foo() {} | ||
|
||
fn main() {} |
23 changes: 23 additions & 0 deletions
23
src/test/ui/manually_drop_attr/manually_drop-bad-item.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
error: attribute should be applied to a struct or enum | ||
--> $DIR/manually_drop-bad-item.rs:6:1 | ||
| | ||
LL | #[manually_drop] | ||
| ^^^^^^^^^^^^^^^^ | ||
LL | | ||
LL | fn foo() {} | ||
| ----------- not a struct or enum | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/manually_drop-bad-item.rs:2:11 | ||
| | ||
LL | #![forbid(unused_attributes)] | ||
| ^^^^^^^^^^^^^^^^^ | ||
|
||
error: attribute should be applied to a struct or enum | ||
--> $DIR/manually_drop-bad-item.rs:3:1 | ||
| | ||
LL | #![manually_drop] | ||
| ^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to 2 previous errors | ||
|
83 changes: 83 additions & 0 deletions
83
src/test/ui/manually_drop_attr/manually_drop-destructor.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
//! A test of `#[manually_drop]` on a type that *does* have a `Drop` impl. | ||
//! | ||
//! The mirror image of `manually_drop-nodestructor.rs` | ||
#![feature(manually_drop_attr)] | ||
// run-pass | ||
extern crate core; | ||
use core::cell::Cell; | ||
|
||
struct DropCounter<'a>(&'a Cell<isize>); | ||
impl<'a> Drop for DropCounter<'a> { | ||
fn drop(&mut self) { | ||
self.0.set(self.0.get() + 1); | ||
} | ||
} | ||
|
||
#[manually_drop] | ||
struct ManuallyDropped<'a> { | ||
field_1: DropCounter<'a>, | ||
field_2: DropCounter<'a>, | ||
} | ||
|
||
impl<'a> Drop for ManuallyDropped<'a> { | ||
fn drop(&mut self) { | ||
// just do a LITTLE dropping. | ||
unsafe { | ||
core::ptr::drop_in_place(&mut self.field_1) | ||
} | ||
} | ||
} | ||
|
||
#[manually_drop] | ||
enum ManuallyDroppedEnum<'a> { | ||
_A, | ||
B(DropCounter<'a>, DropCounter<'a>), | ||
} | ||
|
||
impl<'a> Drop for ManuallyDroppedEnum<'a> { | ||
fn drop(&mut self) { | ||
// just do a LITTLE dropping. | ||
if let ManuallyDroppedEnum::B(a, _) = self { | ||
unsafe { | ||
core::ptr::drop_in_place(a); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Dropping a `#[manually_drop]` struct does not implicitly drop its fields. | ||
/// | ||
/// (Though it does run `Drop`, which can choose to drop them explicitly.) | ||
fn test_destruction() { | ||
let counter = Cell::new(0); | ||
core::mem::drop(ManuallyDropped { | ||
field_1: DropCounter(&counter), | ||
field_2: DropCounter(&counter), | ||
}); | ||
// We only run the drop specifically requested in the Drop impl. | ||
assert_eq!(counter.get(), 1); | ||
assert!(core::mem::needs_drop::<ManuallyDropped>()); | ||
|
||
core::mem::drop(ManuallyDroppedEnum::B(DropCounter(&counter), DropCounter(&counter))); | ||
assert_eq!(counter.get(), 2); | ||
assert!(core::mem::needs_drop::<ManuallyDroppedEnum>()); | ||
|
||
} | ||
|
||
/// Assignment does still drop the fields. | ||
fn test_assignment() { | ||
let counter = Cell::new(0); | ||
let mut manually_dropped = ManuallyDropped { | ||
field_1: DropCounter(&counter), | ||
field_2: DropCounter(&counter), | ||
}; | ||
assert_eq!(counter.get(), 0); | ||
manually_dropped.field_1 = DropCounter(&counter); | ||
manually_dropped.field_2 = DropCounter(&counter); | ||
assert_eq!(counter.get(), 2); | ||
} | ||
|
||
fn main() { | ||
test_destruction(); | ||
test_assignment(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
//! The drop checker only complains about a `#[manually_drop]` type if it _itself_ defines `Drop`. | ||
|
||
// FIXME: this does test dropck, does it also test needs_drop? | ||
|
||
#![feature(manually_drop_attr)] | ||
|
||
|
||
// For example, this is absolutely fine: | ||
|
||
#[manually_drop] | ||
struct ManuallyDrop<T>(T); | ||
|
||
fn drop_out_of_order_ok<T>(x: T) { | ||
let mut manually_dropped = ManuallyDrop(None); | ||
// x will be dropped before manually_dropped. | ||
let x = x; | ||
// ... but this is still fine, because it doesn't have logic on Drop. | ||
manually_dropped.0 = Some(&x); | ||
} | ||
|
||
// ... but this is not: | ||
|
||
#[manually_drop] | ||
struct ManuallyDropWithDestructor<T>(T); | ||
impl<T> Drop for ManuallyDropWithDestructor<T> { | ||
fn drop(&mut self) { | ||
// maybe we read self.0 here! | ||
} | ||
} | ||
|
||
fn drop_out_of_order_not_ok<T>(x: T) { | ||
let mut manually_dropped_bad = ManuallyDropWithDestructor(None); | ||
let x = x; | ||
manually_dropped_bad.0 = Some(&x); | ||
//~^ ERROR `x` does not live long enough | ||
} | ||
|
||
fn main() {} |
Oops, something went wrong.