maybe_dangling/
maybe_dangling.rs

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