Skip to content

Commit 537aea0

Browse files
authored
Merge pull request #432 from rust-lang/KodrAus-patch-1
Add note about PhantomData and dropck
2 parents f65c544 + d81ddc8 commit 537aea0

File tree

1 file changed

+37
-1
lines changed

1 file changed

+37
-1
lines changed

src/libs/maintaining-std.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,41 @@ Changes to collection internals may affect the order their items are dropped in.
187187

188188
### Is there a manual `Drop` implementation?
189189

190-
Generic types that manually implement `Drop` should consider whether a `#[may_dangle]` attribute is appropriate. The [Nomicon][dropck] has some details on what `#[may_dangle]` is all about.
190+
A generic `Type<T>` that manually implements `Drop` should consider whether a `#[may_dangle]` attribute is appropriate on `T`. The [Nomicon][dropck] has some details on what `#[may_dangle]` is all about.
191+
192+
If a generic `Type<T>` has a manual drop implementation that may also involve dropping `T` then dropck needs to know about it. If `Type<T>`'s ownership of `T` is expressed through types that don't drop `T` themselves such as `ManuallyDrop<T>`, `*mut T`, or `MaybeUninit<T>` then `Type<T>` also [needs a `PhantomData<T>` field][RFC 0769 PhantomData] to tell dropck that `T` may be dropped. Types in the standard library that use the internal `Unique<T>` pointer type don't need a `PhantomData<T>` marker field. That's taken care of for them by `Unique<T>`.
193+
194+
As a real-world example of where this can go wrong, consider an `OptionCell<T>` that looks something like this:
195+
196+
```rust
197+
struct OptionCell<T> {
198+
is_init: bool,
199+
value: MaybeUninit<T>,
200+
}
201+
202+
impl<T> Drop for OptionCell<T> {
203+
fn drop(&mut self) {
204+
if self.is_init {
205+
// Safety: `value` is guaranteed to be fully initialized when `is_init` is true.
206+
// Safety: The cell is being dropped, so it can't be accessed again.
207+
drop(unsafe { self.value.read() });
208+
}
209+
}
210+
}
211+
```
212+
213+
Adding a `#[may_dangle]` attribute to this `OptionCell<T>` that didn't have a `PhantomData<T>` marker field opened up [a soundness hole][rust/issues/76367] for `T`'s that didn't strictly outlive the `OptionCell<T>`, and so could be accessed after being dropped in their own `Drop` implementations. The correct application of `#[may_dangle]` also required a `PhantomData<T>` field:
214+
215+
```diff
216+
struct OptionCell<T> {
217+
is_init: bool,
218+
value: MaybeUninit<T>,
219+
+ _marker: PhantomData<T>,
220+
}
221+
222+
- impl<T> Drop for OptionCell<T> {
223+
+ unsafe impl<#[may_dangle] T> Drop for OptionCell<T> {
224+
```
191225

192226
### How could `mem` break assumptions?
193227

@@ -260,7 +294,9 @@ Where `unsafe` and `const` is involved, e.g., for operations which are "unconst"
260294
[Forge]: https://forge.rust-lang.org/
261295
[RFC 1023]: https://rust-lang.github.io/rfcs/1023-rebalancing-coherence.html
262296
[RFC 1105]: https://rust-lang.github.io/rfcs/1105-api-evolution.html
297+
[RFC 0769 PhantomData]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
263298
[Everyone Poops]: http://cglab.ca/~abeinges/blah/everyone-poops
264299
[rust/pull/46799]: https://github.com/rust-lang/rust/pull/46799
300+
[rust/issues/76367]: https://github.com/rust-lang/rust/issues/76367
265301
[hashbrown/pull/119]: https://github.com/rust-lang/hashbrown/pull/119
266302
[rollup guidelines]: ../compiler/reviews.md#rollups

0 commit comments

Comments
 (0)