generativity/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3//! Create a trusted carrier with a new lifetime that is guaranteed to be
4//! unique among other trusted carriers. When you call [`make_guard!`] to make a
5//! unique lifetime, the macro creates a [`Guard`] to hold it. This guard can be
6//! converted `into` an [`Id`], which can be stored in structures to uniquely
7//! "brand" them. A different invocation of the macro will produce a new
8//! lifetime that cannot be unified. The only way to construct these types is
9//! with [`make_guard!`] or `unsafe` code.
10//!
11//! ```rust
12//! use generativity::{Id, make_guard};
13//! struct Struct<'id>(Id<'id>);
14//! make_guard!(a);
15//! Struct(a.into());
16//! ```
17//!
18//! This is the concept of "generative" lifetime brands. `Guard` and `Id` are
19//! [invariant](https://doc.rust-lang.org/nomicon/subtyping.html#variance) over
20//! their lifetime parameter, meaning that it is never valid to substitute or
21//! otherwise coerce `Id<'a>` into `Id<'b>`, for *any* concrete `'a` or `'b`,
22//! *including* the `'static` lifetime.
23//!
24//! Any invariant lifetime can be "trusted" to carry a brand, so long as they
25//! are known to be restricted to carrying a brand, and haven't been derived
26//! from some untrusted lifetime (or are completely unbound). When using this
27//! library, it is recommended to always use `Id<'id>` to carry the brand, as
28//! this reduces the risk of accidentally trusting an untrusted lifetime.
29//! Importantly, non-invariant lifetimes *cannot* be trusted, as the variance
30//! allows lifetimes to be contracted to match and copy the brand lifetime.
31//!
32//! To achieve lifetime invariance without `Id`, there are two standard ways:
33//! `PhantomData<&'a mut &'a ()>` and `PhantomData<fn(&'a ()) -> &'a ()>`. The
34//! former works because `&mut T` is invariant over `T`, and the latter works
35//! because `fn(T)` is *contra*variant over `T` and `fn() -> T` is *co*variant
36//! over `T`, which combines to *in*variance. Both are equivalent in this case
37//! with `T = ()`, but `fn(T) -> T` is generally preferred if the only purpose
38//! is to indicate invariance, as function pointers are a perfect cover for all
39//! auto traits (e.g. `Send`, `Sync`, `Unpin`, `UnwindSafe`, etc.) and thus
40//! only indicates invariance, whereas `&mut T` can carry further implication
41//! of "by example" use of `PhantomData`.
42
43use core_::fmt;
44use core_::marker::PhantomData;
45
46#[doc(hidden)]
47/// NOT STABLE PUBLIC API. Previously Used by the expansion of [`make_guard!`].
48pub extern crate core as core_;
49
50/// A phantomdata-like type taking a single invariant lifetime.
51///
52/// Used to manipulate and store the unique invariant lifetime obtained from
53/// [`Guard`]. Use `guard.into()` to create a new `Id`.
54///
55/// Holding `Id<'id>` indicates that the lifetime `'id` is a trusted brand.
56/// `'id` will not unify with another trusted brand lifetime unless it comes
57/// from the same original brand (i.e. the same invocation of [`make_guard!`]).
58#[repr(transparent)]
59#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
60pub struct Id<'id> {
61    phantom: PhantomData<fn(&'id ()) -> &'id ()>,
62}
63
64impl<'id> Id<'id> {
65    /// Construct an `Id` with an unbounded lifetime.
66    ///
67    /// You should not need to use this function; use [`make_guard!`] instead.
68    ///
69    /// # Safety
70    ///
71    /// `Id` holds an invariant lifetime that must be derived from a generative
72    /// brand. Using this function directly is the "I know what I'm doing"
73    /// button; restrict the lifetime to a known brand immediately to avoid
74    /// introducing potential unsoundness.
75    pub unsafe fn new() -> Self {
76        Id {
77            phantom: PhantomData,
78        }
79    }
80}
81
82impl<'id> fmt::Debug for Id<'id> {
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        f.debug_struct("#[invariant] 'id").finish()
85    }
86}
87
88impl<'id> From<Guard<'id>> for Id<'id> {
89    fn from(guard: Guard<'id>) -> Self {
90        guard.id
91    }
92}
93
94/// An invariant lifetime phantomdata-alike that is guaranteed to be unique
95/// with respect to other trusted invariant lifetimes.
96///
97/// In effect, this means that `'id` is a "generative brand". Use [`make_guard`]
98/// to obtain a new `Guard`.
99#[repr(transparent)]
100#[derive(Eq, PartialEq)]
101pub struct Guard<'id> {
102    #[allow(unused)]
103    id: Id<'id>,
104}
105
106impl<'id> Guard<'id> {
107    /// Construct a `Guard` with an unbound lifetime.
108    ///
109    /// You should not need to use this function; use [`make_guard!`] instead.
110    ///
111    /// # Safety
112    ///
113    /// `Guard` holds an invariant lifetime that must be an unused generative
114    /// brand. Using this function directly is the "I know what I'm doing"
115    /// button; restrict the lifetime to a known brand immediately to avoid
116    /// introducing potential unsoundness.
117    pub unsafe fn new(id: Id<'id>) -> Guard<'id> {
118        Guard { id }
119    }
120}
121
122impl<'id> fmt::Debug for Guard<'id> {
123    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124        f.debug_struct("#[unique] 'id").finish()
125    }
126}
127
128#[doc(hidden)]
129/// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`].
130pub struct LifetimeBrand<'id> {
131    phantom: PhantomData<&'id Id<'id>>,
132}
133
134impl<'id> Drop for LifetimeBrand<'id> {
135    #[inline(always)]
136    fn drop(&mut self) {
137        // This impl purposefully left blank. The presence of a Drop impl gives
138        // the `make_guard` type drop glue, dropping it at the end of scope.
139        // Importantly, this ensures that the compiler has to consider `'id`
140        // live at the point that this type is dropped, because this impl could
141        // potentially use data borrowed that lifetime. #[inline(always)] just
142        // serves to make it easier to optimize out the noop function call.
143    }
144}
145
146#[doc(hidden)]
147/// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`].
148impl<'id> LifetimeBrand<'id> {
149    #[doc(hidden)]
150    #[inline(always)]
151    /// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`].
152    pub unsafe fn new(_: &'id Id<'id>) -> LifetimeBrand<'id> {
153        // This function serves to entangle the `'id` lifetime, making it into
154        // a proper lifetime brand. The `'id` region may open at any point, but
155        // it must end in-between the drop timing of this `LifetimeBrand` and
156        // the `Id` binding used to create it.
157        LifetimeBrand {
158            phantom: PhantomData,
159        }
160    }
161}
162
163/// Create a `Guard` with a unique invariant lifetime (with respect to other
164/// trusted/invariant lifetime brands).
165///
166/// Multiple `make_guard` lifetimes will always fail to unify:
167///
168/// ```rust,compile_fail,E0597
169/// # // trybuild ui test tests/ui/crossed_streams.rs
170/// # use generativity::make_guard;
171/// make_guard!(a);
172/// make_guard!(b);
173/// dbg!(a == b); // ERROR (here == is a static check)
174/// ```
175#[macro_export]
176macro_rules! make_guard {
177    ($name:ident) => {
178        // SAFETY: The lifetime given to `$name` is unique among trusted brands.
179        // We know this because of how we carefully control drop timing here.
180        // The branded lifetime's end is bound to be no later than when the
181        // `branded_place` is invalidated at the end of scope, but also must be
182        // no sooner than `lifetime_brand` is dropped, also at the end of scope.
183        // Some other variant lifetime could be constrained to be equal to the
184        // brand lifetime, but no other lifetime branded by `make_guard!` can,
185        // as its brand lifetime has a distinct drop time from this one. QED
186        let branded_place = unsafe { $crate::Id::new() };
187        #[allow(unused)]
188        let lifetime_brand = unsafe { $crate::LifetimeBrand::new(&branded_place) };
189        let $name = unsafe { $crate::Guard::new(branded_place) };
190    };
191}
192
193#[cfg(test)]
194mod test {
195    use super::*;
196    use std::panic::{RefUnwindSafe, UnwindSafe};
197
198    #[test]
199    fn dont_error_in_general() {
200        make_guard!(a);
201        make_guard!(b);
202        assert_eq!(a, a);
203        assert_eq!(b, b);
204    }
205
206    #[test]
207    fn test_oibits() {
208        fn assert_oibits<T>(_: &T)
209        where
210            T: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe,
211        {
212        }
213
214        make_guard!(a);
215        assert_oibits(&a);
216        let id: Id<'_> = a.into();
217        assert_oibits(&id);
218
219        // const compatible (e.g. const_refs_to_cell, const destructor)
220        const fn _const_id(_: Id<'_>) {}
221        const fn _const_ref_id(_: &'_ Id<'_>) {}
222        const fn _const_guard(_: Guard<'_>) {}
223        const fn _const_ref_guard(_: &'_ Guard<'_>) {}
224    }
225}