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 | |
43 | use core_::fmt; |
44 | use core_::marker::PhantomData; |
45 | |
46 | #[doc (hidden)] |
47 | /// NOT STABLE PUBLIC API. Previously Used by the expansion of [`make_guard!`]. |
48 | pub 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)] |
60 | pub struct Id<'id> { |
61 | phantom: PhantomData<fn(&'id ()) -> &'id ()>, |
62 | } |
63 | |
64 | impl<'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 | |
82 | impl<'id> fmt::Debug for Id<'id> { |
83 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
84 | f.debug_struct(name:"#[invariant] 'id" ).finish() |
85 | } |
86 | } |
87 | |
88 | impl<'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)] |
101 | pub struct Guard<'id> { |
102 | #[allow (unused)] |
103 | id: Id<'id>, |
104 | } |
105 | |
106 | impl<'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 | |
122 | impl<'id> fmt::Debug for Guard<'id> { |
123 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
124 | f.debug_struct(name:"#[unique] 'id" ).finish() |
125 | } |
126 | } |
127 | |
128 | #[doc (hidden)] |
129 | /// NOT STABLE PUBLIC API. Used by the expansion of [`make_guard!`]. |
130 | pub struct LifetimeBrand<'id> { |
131 | phantom: PhantomData<&'id Id<'id>>, |
132 | } |
133 | |
134 | impl<'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!`]. |
148 | impl<'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 ] |
176 | macro_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)] |
194 | mod 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 | } |
226 | |