Skip to content

Commit 60a0782

Browse files
committed
fix and extend dropck documentation
1 parent 2d91939 commit 60a0782

File tree

2 files changed

+71
-11
lines changed

2 files changed

+71
-11
lines changed

library/core/src/marker.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -668,24 +668,17 @@ impl<T: ?Sized> !Sync for *mut T {}
668668
///
669669
/// ## Ownership and the drop check
670670
///
671-
/// Adding a field of type `PhantomData<T>` indicates that your
672-
/// type owns data of type `T`. This in turn implies that when your
673-
/// type is dropped, it may drop one or more instances of the type
674-
/// `T`. This has bearing on the Rust compiler's [drop check]
675-
/// analysis.
676-
///
677-
/// If your struct does not in fact *own* the data of type `T`, it is
678-
/// better to use a reference type, like `PhantomData<&'a T>`
679-
/// (ideally) or `PhantomData<*const T>` (if no lifetime applies), so
680-
/// as not to indicate ownership.
671+
/// Adding a field of type `PhantomData<T>` indicates that your type *owns* data of type `T`. This
672+
/// in turn has effects on the Rust compiler's [drop check] analysis, but that only matters in very
673+
/// specific circumstances. For the exact rules, see the [drop check] documentation.
681674
///
682675
/// ## Layout
683676
///
684677
/// For all `T`, the following are guaranteed:
685678
/// * `size_of::<PhantomData<T>>() == 0`
686679
/// * `align_of::<PhantomData<T>>() == 1`
687680
///
688-
/// [drop check]: ../../nomicon/dropck.html
681+
/// [drop check]: Drop#drop-check
689682
#[lang = "phantom_data"]
690683
#[stable(feature = "rust1", since = "1.0.0")]
691684
pub struct PhantomData<T: ?Sized>;

library/core/src/ops/drop.rs

+67
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,73 @@
132132
/// are `Copy` get implicitly duplicated by the compiler, making it very
133133
/// hard to predict when, and how often destructors will be executed. As such,
134134
/// these types cannot have destructors.
135+
///
136+
/// ## Drop check
137+
///
138+
/// Dropping interacts with the borrow checker in subtle ways: when a type `T` is being implicitly
139+
/// dropped as some variable of this type goes out of scope, the borrow checker needs to ensure that
140+
/// calling `T`'s destructor at this moment is safe. In particular, it also needs to be safe to
141+
/// recursively drop all the fields of `T`. For example, it is crucial that code like the following
142+
/// is being rejected:
143+
///
144+
/// ```compile_fail,E0597
145+
/// use std::cell::Cell;
146+
///
147+
/// struct S<'a>(Cell<Option<&'a S<'a>>>, Box<i32>);
148+
/// impl Drop for S<'_> {
149+
/// fn drop(&mut self) {
150+
/// if let Some(r) = self.0.get() {
151+
/// // Print the contents of the `Box` in `r`.
152+
/// println!("{}", r.1);
153+
/// }
154+
/// }
155+
/// }
156+
///
157+
/// fn main() {
158+
/// // Set up two `S` that point to each other.
159+
/// let s1 = S(Cell::new(None), Box::new(42));
160+
/// let s2 = S(Cell::new(Some(&s1)), Box::new(42));
161+
/// s1.0.set(Some(&s2));
162+
/// // Now they both get dropped. But whichever is the 2nd one
163+
/// // to be dropped will access the `Box` in the first one,
164+
/// // which is a use-after-free!
165+
/// }
166+
/// ```
167+
///
168+
/// The Nomicon discusses the need for [drop check in more detail][drop check].
169+
///
170+
/// To reject such code, the "drop check" analysis determines which types and lifetimes need to
171+
/// still be live when `T` gets dropped:
172+
/// - If `T` has no drop glue, then trivially nothing is required to be live. This is the case if
173+
/// neither `T` nor any of its (recursive) fields have a destructor (`impl Drop`). [`PhantomData`]
174+
/// and [`ManuallyDrop`] are considered to never have a destructor, no matter their field type.
175+
/// - If `T` has drop glue, then, for all types `U` that are *owned* by any field of `T`,
176+
/// recursively add the types and lifetimes that need to be live when `U` gets dropped. The set of
177+
/// owned types is determined by recursively traversing `T`:
178+
/// - Recursively descend through `PhantomData`, `Box`, tuples, and arrays (including arrays of
179+
/// length 0).
180+
/// - Stop at reference and raw pointer types as well as function pointers and function items;
181+
/// they do not own anything.
182+
/// - Stop at non-composite types (type parameters that remain generic in the current context and
183+
/// base types such as integers and `bool`); these types are owned.
184+
/// - When hitting an ADT with `impl Drop`, stop there; this type is owned.
185+
/// - When hitting an ADT without `impl Drop`, recursively descend to its fields. (For an `enum`,
186+
/// consider all fields of all variants.)
187+
/// - Furthermore, if `T` implements `Drop`, then all generic (lifetime and type) parameters of `T`
188+
/// must be live.
189+
///
190+
/// In the above example, the last clause implies that `'a` must be live when `S<'a>` is dropped,
191+
/// and hence the example is rejected. If we remove the `impl Drop`, the liveness requirement
192+
/// disappears and the example is accepted.
193+
///
194+
/// There exists an unstable way for a type to opt-out of the last clause; this is called "drop
195+
/// check eyepatch" or `may_dangle`. For more details on this nightly-only feature, see the
196+
/// [discussion in the Nomicon][nomicon].
197+
///
198+
/// [`ManuallyDrop`]: crate::mem::ManuallyDrop
199+
/// [`PhantomData`]: crate::marker::PhantomData
200+
/// [drop check]: ../../nomicon/dropck.html
201+
/// [nomicon]: ../../nomicon/phantom-data.html#an-exception-the-special-case-of-the-standard-library-and-its-unstable-may_dangle
135202
#[lang = "drop"]
136203
#[stable(feature = "rust1", since = "1.0.0")]
137204
#[const_trait]

0 commit comments

Comments
 (0)