maybe_dangling/maybe_dangling.rs
1// This used to be `crate::ManuallyDrop` (i.e., with `MaybeDangling` semantics in it),
2// but we can directly use the one from stdlib
3use ::core::mem::ManuallyDrop;
4
5/// Like [`crate::ManuallyDrop`] but for having `drop` glue.
6/// This wrapper is 0-cost.
7///
8/// In other words, a <code>[MaybeDangling]\<T\></code> is just like `T`, but
9/// for having been stripped of aliasing/`dereferenceable`-ity properties.
10///
11/// Its usage should be quite rare and advanced: if you are intending to keep
12/// hold of a potentially dangling / exhausted value, chances are you won't
13/// want implicit/automatic drop glue of it without having previously checked
14/// for lack of exhaustion ⚠️.
15///
16/// That is, it is strongly advisable to be using
17/// <code>[crate::ManuallyDrop]\<T\></code> instead!
18///
19/// ### Opting into unstable `#[may_dangle]` and the `dropck_eyepatch`
20///
21/// Ironically, for this drop glue to be as smooth as it should be, the unstable
22/// `#[may_dangle]` feature is needed.
23///
24/// But by virtue of being unstable, it cannot be offered by this crate on
25/// stable Rust.
26///
27/// For the adventurous `nightly` users, you can enable the
28/// `nightly-dropck_eyepatch` Cargo feature to opt into the usage of the
29/// [eponymous `rustc` feature][RFC-1327] so as to get the `Drop` implementation
30/// amended accordingly.
31///
32/// Explanation:
33///
34/// <details class="custom"><summary><span class="summary-box"><span>Click to show</span></span></summary>
35///
36/// #### What does it mean to have a "dangling `T`"?
37///
38/// Note that the terminology of a "dangling `T`" can be a bit hard to
39/// visualize. The idea is to consider some `'dangling` lifetime (_e.g._,
40/// some `&'dangling` borrow), to then imagine different type definitions
41/// involving it.
42///
43/// For instance:
44///
45/// 1. `T = &'dangling str`
46/// 2. `T = PrintOnDrop<&'dangling str>`,
47/// 3. `T = Box<&'dangling str>`,
48/// 4. `T = (u8, Box<PrintOnDrop<&'dangling str>>)`,
49/// 5. `T = &mut PrintOnDrop<&'dangling str>`,
50///
51/// The key idea is that there are three categories of types here:
52///
53/// 1. The types with no drop glue at all, _i.e._, types for which
54/// `mem::needs_drop::<T>()` returns `false`: types 1. and 5.
55///
56/// Such types _should be allowed_ to go out of scope at a point
57/// where their lifetime may be `'dangling`.
58///
59/// 2. The types with drop glue known not to involve a dereference of
60/// the `&'dangling` reference: type 3.
61///
62/// Such types _can be allowed_ to go out of scope (and thus, run
63/// their drop glue) at a point where their lifetime may be
64/// `'dangling`.
65///
66/// 3. The types with drop glue (potentially) involving a dereference
67/// of the `&'dangling` reference: types 2. and 4.
68///
69/// Such types _should never be allowed_ to go out of scope at a
70/// point where their lifetime may be `'dangling`.
71///
72/// Notice how a useful distinction thus revolves around the presence
73/// of drop glue or lack thereof, to determine whether we are in the
74/// first category, or the other two. On the other hand, whether a type
75/// _directly_ implements `Drop`, such as `Box` or `PrintOnDrop`, or
76/// does not (wrapper types containing it, such as `String` w.r.t. the
77/// `Drop` implementation of the underlying `Vec<u8>`, or `(u8, Box<...>)`
78/// in the fourth example type above), is not enough information to
79/// distinguish between the two, as
80///
81/// - types 2. and 3. both implement `Drop`, and yet belong to different
82/// categories,
83///
84/// - type 4. does not implement `Drop`, and yet belongs to the same
85/// category as type 2.
86///
87/// See the [`drop_bounds` lint] for more info.
88///
89/// The distinction between the second and third category is whether a generic
90/// type, when dropped,
91///
92/// 1. merely drops its inner `T` (like `Box<T>` does) and
93///
94/// 2. makes it known to the [drop checker] that it does so.
95///
96/// If a type violates either restriction, either by unconditionally using any
97/// other API of `T`, like `PrintOnDrop<T: Debug>` does, or by not making
98/// it known to the drop checker that it merely drops its inner `T`, it will
99/// belong to category 3, which can't be allowed to compile.
100///
101/// Making it known to the drop checker that `T` is merely dropped requires
102/// the unstable [`#[may_dangle]`][RFC-1327] attribute.
103/// The drop checker does not know the implementation details of any
104/// `Drop` implementation.
105/// It can't statically analyse how `T` is used in the destructor.
106/// Instead, drop check requires every generic argument to strictly
107/// outlive the wrapper type to guarantee soundness.
108/// This can be overly restrictive when merely dropping `T`, making it
109/// impossible to have `Drop` implementations where `T` might be dangling,
110/// even if dropping a dangling `T` would be sound in the given context.
111/// Hence the `#[may_dangle]` attribute is required to manually and _unsafely_
112/// tell drop check that `T` is merely dropped in the generic type's
113/// destructor, relaxing the drop checker in situations where its soundness
114/// requirements are overly restrictive.
115/// With the `nightly-dropck_eyepatch` feature enabled, <code>[MaybeDangling]\<T\></code>
116/// uses `#[may_dangle]` under the hood to let drop check know that it won't
117/// access the potentially dangling `T` (_e.g._, the `str` behind
118/// `T = &'dangling str`) in its destructor, [*unless*][dropck-generics] `T`'s
119/// `'dangling` lifetime is involved in transitive drop glue, _i.e._:
120/// - whenever `T` implements `Drop` (without `#[may_dangle]`);
121/// - or whenever `T` transitively owns some field with drop glue involving
122/// `'dangling`.
123///
124/// With that context in mind, let's look at examples for the three categories:
125///
126/// #### Category 1: `T` has no drop glue (_e.g._, `T = &'dangling str`)
127///
128/// Since `T` does not have drop glue (`mem::needs_drop::<T>()` returns `false`),
129/// the drop checker will allow this to compile, even though the reference
130/// dangles when `v` gets dropped:
131///
132/// ```
133/// # #[cfg(feature = "nightly-dropck_eyepatch")]
134/// # {
135/// use ::maybe_dangling::MaybeDangling;
136///
137/// fn main() {
138/// let s: String = "I will dangle".into();
139/// let v = MaybeDangling::new(&s);
140/// drop(s); // <- makes `&s` dangle
141/// } // <- `v` dropped here, despite containing a `&'dangling s` reference!
142/// # }
143/// ```
144///
145/// #### Category 2: `T` has drop glue known not to involve `'dangling` (_e.g._, `T = Box<&'dangling str>`)
146///
147/// Now that `T` is has drop glue, it must be executed when `v` is dropped.
148/// `Box<&'dangling str>`'s `Drop` implementation is known not to involve
149/// `'dangling`, so it is safe for `&'dangling str` to dangle when the `Box`
150/// is dropped:
151///
152/// ```
153/// # #![cfg_attr(feature = "nightly-dropck_eyepatch", feature(dropck_eyepatch))]
154/// # #[cfg(feature = "nightly-dropck_eyepatch")]
155/// # {
156/// use ::maybe_dangling::MaybeDangling;
157///
158/// fn main() {
159/// let s: String = "I will dangle".into();
160/// let v = MaybeDangling::new(Box::new(&s));
161/// drop(s); // <- makes `&s` dangle
162/// } // <- `v`, and thus `Box(&s)` dropped here
163/// # }
164/// ```
165///
166/// #### Category 3: `T` has drop glue (potentially) involving `'dangling` (_e.g._, `T = PrintOnDrop<&'dangling str>`)
167///
168/// Like the second category, `T` now has drop glue.
169/// But unlike category 2., `T` now has drop glue either involving `'dangling`
170/// or not informing the drop checker that `'dangling` is unused.
171/// Let's look at an example where `'dangling` is involved in drop glue:
172///
173/// ```compile_fail
174/// use ::maybe_dangling::MaybeDangling;
175///
176/// use ::std::fmt::Debug;
177///
178/// struct PrintOnDrop<T: Debug>(T);
179///
180/// impl<T: Debug> Drop for PrintOnDrop<T> {
181/// fn drop(&mut self) {
182/// println!("Using the potentially dangling `T` in our destructor: {:?}", self.0);
183/// }
184/// }
185///
186/// fn main() {
187/// let s: String = "I will dangle".into();
188/// let v = MaybeDangling::new(PrintOnDrop(&s));
189/// drop(s); // <- makes `&s` dangle
190/// } // <- `v`, and thus `PrintOnDrop(&s)` dropped here, causing a use-after-free ! ⚠️
191/// ```
192///
193/// The example above should never be allowed to compile as `PrintOnDrop`
194/// will dereference `&'dangling str`, which points to a `str` that already
195/// got dropped and invalidated, causing undefined behavior.
196///
197/// An example for a type where `'dangling` is not involved in any drop glue
198/// but does not relax the drop checker with `#[may_dangle]` would be:
199///
200/// ```compile_fail
201/// use ::maybe_dangling::MaybeDangling;
202///
203/// struct MerelyDrop<T>(T);
204///
205/// impl<T> Drop for MerelyDrop<T> {
206/// fn drop(&mut self) {
207/// println!("Not using the potentially dangling `T` in our destructor");
208/// }
209/// }
210///
211/// fn main() {
212/// let s: String = "I will dangle".into();
213/// let v = MaybeDangling::new(MerelyDrop(&s));
214/// drop(s); // <- makes `&s` dangle
215/// } // <- `v`, and thus `MerelyDrop(&s)` dropped here
216/// ```
217///
218/// To amend the example above and move from category 3. to category 2. and
219/// make it compile, `#[may_dangle]` can be applied to `T` in `MerelyDrop`'s
220/// `Drop` implementation:
221///
222/// ```
223/// # #![cfg_attr(feature = "nightly-dropck_eyepatch", feature(dropck_eyepatch))]
224/// # #[cfg(feature = "nightly-dropck_eyepatch")]
225/// # {
226/// #![feature(dropck_eyepatch)]
227///
228/// use ::maybe_dangling::MaybeDangling;
229///
230/// struct MerelyDrop<T>(T);
231///
232/// unsafe impl<#[may_dangle] T> Drop for MerelyDrop<T> {
233/// fn drop(&mut self) {
234/// println!("Not using the potentially dangling `T` in our destructor");
235/// }
236/// }
237///
238/// fn main() {
239/// let s: String = "I will dangle".into();
240/// let v = MaybeDangling::new(MerelyDrop(&s));
241/// drop(s); // <- makes `&s` dangle
242/// } // <- `v`, and thus `MerelyDrop(&s)` dropped here
243/// # }
244/// ```
245///
246/// Note that the `Drop` implementation is _unsafe_ now, as we are still free
247/// to use the dangling `T` in the destructor.
248/// We only pinky-swear to the drop checker that we won't.
249///
250/// </details>
251///
252/// #### Summary: when is a `MaybeDangling<...'dangling...>` allowed to go out of scope
253///
254/// This table summarises which of the categories shown above can be compiled, with
255/// or without the `nightly-dropck_eyepatch` feature enabled:
256///
257/// | `MaybeDangling<T>`<br/><br/>`where T` | With `nightly-dropck_eyepatch` | Without `nightly-dropck_eyepatch` |
258/// | --- | --- | --- |
259/// | has no drop glue<br/>_e.g._<br/>`T = &'dangling str` | ✅ | ❌ |
260/// | has drop glue known not to involve `'dangling`<br/>_e.g._<br/>`T = Box<&'dangling str>` | ✅ | ❌ |
261/// | has drop glue (potentially) involving `'dangling`<br/>_e.g._<br/>`T = PrintOnDrop<&'dangling str>` | ❌ | ❌ |
262///
263/// [RFC-1327]: https://rust-lang.github.io/rfcs/1327-dropck-param-eyepatch.html
264/// [`drop_bounds` lint]: https://doc.rust-lang.org/1.71.0/nightly-rustc/rustc_lint/traits/static.DROP_BOUNDS.html#explanation
265/// [drop checker]: https://doc.rust-lang.org/1.71.0/nomicon/dropck.html
266/// [dropck-generics]: https://doc.rust-lang.org/1.71.0/nomicon/phantom-data.html#generic-parameters-and-drop-checking
267#[repr(transparent)]
268pub struct MaybeDangling<T> {
269 value: ManuallyDrop<T>,
270 #[cfg(feature = "nightly-dropck_eyepatch")]
271 #[allow(nonstandard_style)]
272 // disables `#[may_dangle]` for `T` invovled in transitive drop glue
273 _owns_T: ::core::marker::PhantomData<T>,
274}
275
276impl<T> MaybeDangling<T> {
277 pub const fn new(value: T) -> MaybeDangling<T> {
278 Self {
279 value: ManuallyDrop::new(value),
280 #[cfg(feature = "nightly-dropck_eyepatch")]
281 _owns_T: ::core::marker::PhantomData,
282 }
283 }
284
285 /// Extracts the value from the `MaybeDangling` container.
286 ///
287 /// See [`::core::mem::ManuallyDrop::into_inner()`] for more info.
288 #[inline]
289 pub fn into_inner(slot: MaybeDangling<T>) -> T {
290 #![allow(unsafe_code)]
291 // Safety: this is the defuse inherent drop glue pattern.
292 unsafe { ManuallyDrop::take(&mut ManuallyDrop::new(slot).value) }
293 }
294
295 /// Akin to [`ManuallyDrop::drop()`]: it drops the inner value **in-place**. Raw & `unsafe`
296 /// version of [`drop_in_place!`].
297 ///
298 /// [`drop_in_place!`]: `crate::drop_in_place!`
299 ///
300 /// `Pin` code can rely on this guarantee: [an example](https://docs.rs/droppable_pin).
301 ///
302 /// # Safety
303 ///
304 /// This API is `unsafe` and wildly dangerous. It is very strongly advisable to use
305 /// [`drop_in_place!`] instead.
306 ///
307 /// Indeed, since [`MaybeDangling`] does have embedded drop glue, the moment this function
308 /// returns the only thing that ought to be done is immediately [`::core::mem::forget()`]ting it
309 /// (or wrapping it in a [`ManuallyDrop`]), lest it be dropped implicitly (_e.g._, because of
310 /// some panic), resulting un double-drop unsoundness 😱.
311 ///
312 /// As a matter of fact, this very function needs to feature an abort-on-panic guard to
313 /// handle this problem internally.
314 #[allow(unsafe_code)]
315 pub unsafe fn drop_in_place(this: &mut MaybeDangling<T>) {
316 struct PanicOnDrop();
317 impl Drop for PanicOnDrop {
318 fn drop(&mut self) {
319 panic!("aborting for soundness");
320 }
321 }
322 let _guard = PanicOnDrop();
323 // Unwind-safety: if this unwinds, the `_guard` is dropped, which panics, resulting in a
324 // nested panic which causes the process to abort.
325 unsafe {
326 ManuallyDrop::drop(&mut this.value);
327 }
328 ::core::mem::forget(_guard);
329 }
330}
331
332// The main difference with `ManuallyDrop`: automatic drop glue!
333cfg_select! {
334 feature = "nightly-dropck_eyepatch" => {
335 #[allow(unsafe_code)]
336 unsafe impl<#[may_dangle] T> Drop for MaybeDangling<T> {
337 fn drop(&mut self) {
338 unsafe {
339 ManuallyDrop::drop(&mut self.value)
340 }
341 }
342 }
343 },
344
345 _ => {
346 impl<T> Drop for MaybeDangling<T> {
347 fn drop(&mut self) {
348 #![allow(unsafe_code)]
349 unsafe {
350 ManuallyDrop::drop(&mut self.value)
351 }
352 }
353 }
354 },
355}
356
357impl<T> ::core::ops::DerefMut for MaybeDangling<T> {
358 #[inline]
359 fn deref_mut(&mut self) -> &mut T {
360 #[expect(non_local_definitions)]
361 impl<T> ::core::ops::Deref for MaybeDangling<T> {
362 type Target = T;
363
364 #[inline]
365 fn deref(self: &Self) -> &T {
366 let Self { value, .. } = self;
367 value
368 }
369 }
370
371 let Self { value, .. } = self;
372 value
373 }
374}
375
376impl<T: Default> Default for MaybeDangling<T> {
377 #[inline]
378 fn default() -> Self {
379 Self::new(T::default())
380 }
381}
382
383impl<T: Clone> Clone for MaybeDangling<T> {
384 fn clone(self: &Self) -> Self {
385 Self::new(T::clone(self))
386 }
387
388 fn clone_from(self: &mut Self, source: &Self) {
389 T::clone_from(self, source)
390 }
391}
392
393/// Safe API around [`MaybeDangling::drop_in_place()`], which performs the mandatory
394/// [`::core::mem::forget()`] on the given var.
395///
396/// Equivalent to doing <code>[drop]::\<[MaybeDangling]\>\($var\)</code>, but for not moving the
397/// given `$var` before doing so (important with, for instance, `Pin` stuff).
398///
399/// - Using [`MaybeDangling::drop_in_place()`] directly is so wildly dangerous that it is
400/// discouraged.
401///
402/// - Using [`ManuallyDrop`] alongside [`ManuallyDrop::drop()`] is significantly less dangerous
403/// w.r.t. double-dropping, but alas just as dangerous w.r.t. leaking when dealing with the
404/// `Pin` contract for which a lack of drop in certain cases is just as unsound.
405///
406/// Remark: this macro requires the given `$var` binding to have been declared `mut`able.
407///
408/// # Example
409///
410/// Imagine, as a library author, wanting to offer the following kind of API:
411///
412/// ```rust
413/// # fn stuff() {}
414/// pub use ::core;
415/// # pub extern crate maybe_dangling; /*
416/// pub use ::maybe_dangling;
417/// # */
418///
419/// macro_rules! my_droppable_pin {(
420/// let mut $var:ident = pin!($value:expr);
421/// ) => (
422/// let mut pinned_value = $crate::maybe_dangling::MaybeDangling::new($value);
423/// macro_rules! drop_it {() => (
424/// $crate::maybe_dangling::drop_in_place!(pinned_value);
425/// )}
426/// #[allow(unused_mut)]
427/// let mut $var = unsafe {
428/// $crate::core::pin::Pin::new_unchecked(&mut *pinned_value)
429/// };
430/// )}
431///
432/// fn main() {
433/// use ::core::{marker::PhantomPinned, pin::*};
434///
435/// my_droppable_pin! {
436/// let mut p = pin!(PhantomPinned);
437/// }
438/// let _: Pin<&mut PhantomPinned> = p.as_mut(); // properly pinned!
439/// for i in 0.. {
440/// if i == 5 {
441/// drop_it!(); // drops the `PhantomPinned` in-place, abiding by the drop guarantee.
442/// // stuff runs after `PhantomPinned` has been dropped, rather than before.
443/// stuff();
444/// break;
445/// }
446/// }
447/// }
448/// ```
449#[macro_export]
450macro_rules! drop_in_place {
451 ( $var:ident $(,)? ) => {
452 // guard against `$var` not being a place (e.g., some `const` or `static mut` or whatnot).
453 _ = &raw const $var;
454 unsafe {
455 $crate::MaybeDangling::drop_in_place(&mut $var);
456 }
457 $crate::ඞ::core::mem::forget($var);
458 };
459}