dungeon_cell/lifetime_type_id.rs
1//! [`TypeId`] for types with a lifetime.
2//!
3//! [`TypeId`] only exists for `'static` types. This restricts the types
4//! a [`DungeonCore`][crate::DungeonCore] can hold to '`static` types by default.
5//! However, this module implements a technique inspired by the [`better_any`](https://docs.rs/better_any/latest/better_any/index.html) crate.
6//! With this technique, we can give a `T`, with a single lifetime parameter, a `'static`
7//! marker type and take the [`TypeId`] of that marker. The [`LifetimeTypeId`] type
8//! also wraps around the [`TypeId`] this gives to have the borrow checker verify the
9//! lifetime.
10
11use core::any::TypeId;
12use core::cmp::Ordering;
13use core::marker::PhantomData;
14
15/// Unique identifier for a type with zero or one lifetimes.
16///
17/// If two [`LifetimeTypeId`] are equal with `==` then the type they represent
18/// is identical including the lifetime. This is possible because we use the borrow
19/// checker to prove that `'a` is the same `'a` for both the left and right hand side
20/// of the `==`. If the borrow checker can't prove they are the same then it won't let
21/// the code compile. This is needed because [`TypeId`] can't encode lifetime information
22/// for inspection at runtime.
23///
24/// This type stores the [`TypeId`] for the static form of a type given by [`StaticForm`].
25#[derive(Copy, Clone, Debug, Eq, Hash)]
26pub struct LifetimeTypeId<'a> {
27 /// Type ID of the static form of the type.
28 static_type_id: TypeId,
29
30 /// Marker to hold the lifetime.
31 ///
32 /// This must be invariant over `'a` to make equality correct.
33 ///
34 /// The marker used is taken from:
35 /// <https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns>
36 _marker: PhantomData<core::cell::Cell<&'a ()>>,
37}
38
39impl<'a> LifetimeTypeId<'a> {
40 /// Get the [`LifetimeTypeId`] for a `T`.
41 ///
42 /// The `'a` lifetime will be connected to the single lifetime of `T` if it has one.
43 ///
44 /// Types must enroll into this system by implementing [`StaticForm`].
45 ///
46 /// # Examples
47 ///
48 /// ```
49 /// use dungeon_cell::lifetime_type_id::{LifetimeTypeId};
50 ///
51 /// assert_eq!(LifetimeTypeId::of::<&i32>(), LifetimeTypeId::of::<&i32>());
52 /// assert_ne!(LifetimeTypeId::of::<&i32>(), LifetimeTypeId::of::<i32>());
53 /// ```
54 ///
55 /// ```compile_fail
56 /// use dungeon_cell::lifetime_type_id::{LifetimeTypeId, StaticForm};
57 ///
58 /// let foo = 123i32;
59 /// let mut foo_borrow = &foo;
60 ///
61 /// {
62 /// let bar = 456i32;
63 /// let mut bar_borrow = &bar;
64 ///
65 /// // forces foo_borrow and bar_borrow to have the same lifetime
66 /// assert_eq!(of_value(&mut foo_borrow), of_value(&mut bar_borrow));
67 /// }
68 ///
69 /// foo_borrow.set(42);
70 ///
71 /// // helper to get LifetimeTypeId from value
72 /// fn of_value<'a, T: StaticForm<'a>>(_: &'_ mut T) -> LifetimeTypeId<'a> {
73 /// LifetimeTypeId::of::<T>()
74 /// }
75 /// ```
76 pub fn of<T: StaticForm<'a>>() -> Self {
77 Self {
78 static_type_id: TypeId::of::<T::Static>(),
79 _marker: PhantomData,
80 }
81 }
82}
83
84impl<'a> PartialEq for LifetimeTypeId<'a> {
85 fn eq(&self, other: &Self) -> bool {
86 // Because `LifetimeTypeId` is invariant over `'a` we know that both
87 // `self` and `other` have the same lifetime parameter and we know that
88 // all types that implement `StaticForm<'a>` are unique except for the
89 // lifetime parameter.
90 //
91 // Because of these two things we know the types must be the same including
92 // the lifetime.
93
94 self.static_type_id == other.static_type_id
95 }
96}
97
98impl<'a> PartialOrd for LifetimeTypeId<'a> {
99 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
100 Some(self.cmp(other))
101 }
102}
103
104impl<'a> Ord for LifetimeTypeId<'a> {
105 fn cmp(&self, other: &Self) -> Ordering {
106 // See `PartialEq::eq` for why equality works.
107
108 self.static_type_id.cmp(&other.static_type_id)
109 }
110}
111
112/// Static form of a type with zero or one lifetimes.
113///
114/// The [`Self::Static`] type will be a `'static` marker type unique to [`Self`] minus the
115/// lifetime. [`Self`] must live for the lifetime `'a` for this trait to be implemented
116/// on it. This allows for implementations to tie `'a` to the lifetime parameter of [`Self`] if it
117/// has one.
118///
119/// The [`has_static_form`][crate::has_static_form] macro can implement this trait for most types in a safe way.
120///
121/// # Limitations
122///
123/// [`StaticForm`] cannot be implemented automatically for `T: 'static`. Instead you must
124/// use [`has_static_form`][crate::has_static_form] to implement [`StaticForm`] for the type or a wrapper around the type.
125///
126/// # Safety
127/// [`Self::Static`] must be unique for a given [`Self`] type minus lifetimes.
128pub unsafe trait StaticForm<'a>: 'a {
129 /// Static marker type for [`Self`].
130 type Static: 'static;
131}
132
133/// Implement [`StaticForm`] for type.
134///
135/// This macro uses a subset of the `impl` syntax.
136/// However, you do not need to name the [`StaticForm`] trait in the macro call or provide a body.
137/// Additionally, a `'a` lifetime (it doesn't have to be literally `'a`) is required as the first
138/// generic.
139///
140/// All generics will automatically be bounded by `StaticForm<'a>`. To opt out of this
141/// use `T: 'static` in the `impl` generic definitions. Additionally, extra trait bounds
142/// can be placed in a `where` clause at the end.
143///
144/// See the examples below for the different ways to invoke the macro.
145///
146/// # Limitations
147///
148/// This macro can only implement [`StaticForm`] for nameable types. For opaque types
149/// you must use the [`give_opaque_static_form`][crate::give_opaque_static_form] macro to wrap a specific value.
150///
151/// # Examples
152/// ```
153/// use dungeon_cell::has_static_form;
154/// use dungeon_cell::lifetime_type_id::LifetimeTypeId;
155///
156/// // no lifetimes and no generics
157/// struct BasicType;
158/// has_static_form!(impl<'a> BasicType);
159/// # let _ = LifetimeTypeId::of::<BasicType>();
160///
161/// // one generic
162/// struct OneGeneric<T>(T);
163/// has_static_form!(impl<'a, T> OneGeneric<T>);
164/// # let _ = LifetimeTypeId::of::<OneGeneric<()>>();
165///
166/// // multiple generics
167/// struct MultipleGeneric<A, B, C>(A, B, C);
168/// has_static_form!(impl<'a, A, B, C> MultipleGeneric<A, B, C>);
169/// # let _ = LifetimeTypeId::of::<MultipleGeneric<(), (), ()>>();
170///
171/// // a lifetime
172/// struct Lifetime<'a>(&'a str);
173/// has_static_form!(impl<'a> Lifetime<'a>);
174/// # let _ = LifetimeTypeId::of::<Lifetime<'static>>();
175///
176/// // a lifetime and a generic
177/// struct LifetimeGeneric<'a, T>(&'a T);
178/// has_static_form!(impl<'a, T> LifetimeGeneric<'a, T>);
179/// # let _ = LifetimeTypeId::of::<LifetimeGeneric<'static, ()>>();
180///
181/// // a lifetime and a static generic
182/// struct StaticGeneric<'a, T>(&'a T);
183/// has_static_form!(impl<'a, T: 'static> StaticGeneric<'a, T>);
184///
185/// struct WithoutStaticForm;
186/// let _ = LifetimeTypeId::of::<StaticGeneric<WithoutStaticForm>>();
187///
188/// // extra trait bounds
189/// struct AlwaysCopy<T: Copy>(T);
190/// has_static_form!(impl<'a, T> AlwaysCopy<T> where T: Copy);
191/// # let _ = LifetimeTypeId::of::<AlwaysCopy<i32>>();
192/// ```
193#[macro_export]
194macro_rules! has_static_form {
195 (impl $($tail:tt)+) => {
196 $crate::has_static_form_helper!(
197 @filter_static
198 static
199 impl $($tail)+
200 );
201 };
202}
203
204/// `tt` muncher for the [`has_static_form`] macro.
205///
206/// This helper will filter out `: 'static` generics then find
207/// the `where` clause if it exists.
208#[doc(hidden)]
209#[macro_export]
210macro_rules! has_static_form_helper {
211 (impl $($tail:tt)+) => {
212 $crate::has_static_form_helper!(
213 @filter_static
214 static
215 impl $($tail)+
216 );
217 };
218 (
219 @filter_static
220 $(,$generic:ident)*
221 static $(,$static_generic:ident)*
222 impl<$lt:lifetime, $token:ident : 'static $($tail:tt)+
223 ) => {
224 $crate::has_static_form_helper!(
225 @filter_static
226 $(,$generic)*
227 static $(,$static_generic)* ,$token
228 impl<$lt $($tail)+
229 );
230 };
231 (
232 @filter_static
233 $(,$generic:ident)*
234 static $(,$static_generic:ident)*
235 impl<$lt:lifetime, $token:ident $($tail:tt)+
236 ) => {
237 $crate::has_static_form_helper!(
238 @filter_static
239 $(,$generic)* ,$token
240 static $(,$static_generic)*
241 impl<$lt $($tail)+
242 );
243 };
244 (
245 @filter_static
246 $(,$generic:ident)*
247 static $(,$static_generic:ident)*
248 impl<$lt:lifetime> $($tail:tt)+
249 ) => {
250 $crate::has_static_form_helper!(
251 @filter_where
252 $(,$generic)*
253 static $(,$static_generic)*
254 impl<$lt> {} $($tail)+
255 );
256 };
257 (
258 @filter_where
259 $(,$generic:ident)*
260 static $(,$static_generic:ident)*
261 impl<$lt:lifetime> { $($type:tt)* } where $($tail:tt)*
262 ) => {
263 const _: () = {
264 use ::core::marker::PhantomData;
265
266 pub struct __Marker<$lt $(,$generic: ?Sized)* $(,$static_generic: ?Sized)*>(PhantomData<&$lt()> $(,PhantomData<$generic>)* $(,PhantomData<$static_generic>)*);
267
268 // SAFETY:
269 unsafe impl<
270 $lt
271 $(,$generic: $crate::lifetime_type_id::StaticForm<$lt>)*
272 $(,$static_generic: 'static)*
273 > $crate::lifetime_type_id::StaticForm<$lt> for $($type)*
274 where
275 $($tail)*
276 {
277 type Static = __Marker<'static $(,$generic::Static)* $(,$static_generic)*>;
278 }
279 };
280 };
281 (
282 @filter_where
283 $(,$generic:ident)*
284 static $(,$static_generic:ident)*
285 impl<$lt:lifetime> { $($type:tt)* } $token:tt $($tail:tt)*
286 ) => {
287 $crate::has_static_form_helper!(
288 @filter_where
289 $(,$generic)*
290 static $(,$static_generic)*
291 impl<$lt> { $($type)* $token } $($tail)*
292 );
293 };
294 (
295 @filter_where
296 $(,$generic:ident)*
297 static $(,$static_generic:ident)*
298 impl<$lt:lifetime> { $($type:tt)* }
299 ) => {
300 const _: () = {
301 use ::core::marker::PhantomData;
302
303 pub struct __Marker<$lt $(,$generic: ?Sized)* $(,$static_generic: ?Sized)*>(PhantomData<&$lt()> $(,PhantomData<$generic>)* $(,PhantomData<$static_generic>)*);
304
305 // SAFETY: The static is unique because we used __marker.
306 unsafe impl<
307 $lt
308 $(,$generic: $crate::lifetime_type_id::StaticForm<$lt>)*
309 $(,$static_generic: 'static)*
310 > $crate::lifetime_type_id::StaticForm<$lt> for $($type)* {
311 type Static = __Marker<'static $(,$generic::Static)* $(,$static_generic)*>;
312 }
313 };
314 };
315}
316
317/// Wrap opaque typed value to implement [`StaticForm`].
318///
319/// This macro generates a `#[repr(transparent)]` wrapper struct
320/// that implements [`StaticForm`]. The resulting wrapper is also
321/// an opaque type.
322///
323/// Use the `value.0` accessor to get the original value.
324///
325/// # Examples
326/// ```
327/// use dungeon_cell::give_opaque_static_form;
328/// use dungeon_cell::lifetime_type_id::{LifetimeTypeId, StaticForm};
329///
330/// let mut a = 42;
331/// let b = &mut a;
332///
333/// // closures have an opaque type
334/// let x = || *b += 1;
335///
336/// // wrap the closure so it works with of_value
337/// let mut z = give_opaque_static_form!(x);
338///
339/// // we can still access the closure.
340/// z();
341///
342/// // the type ID of the closure isn't the same as a i32
343/// assert_ne!(of_value(&z), of_value(&0));
344///
345/// // calling the closure changed the variable so it has a lifetime
346/// assert_eq!(a, 43);
347///
348/// fn of_value<'a, T: StaticForm<'a>>(_: &T) -> LifetimeTypeId<'a> {
349/// LifetimeTypeId::of::<T>()
350/// }
351/// ```
352#[macro_export]
353macro_rules! give_opaque_static_form {
354 ($value:expr) => {{
355 // This type will act as a opaque type from the outside of the macro scope.
356 #[repr(transparent)]
357 struct __Wrapper<T>(pub T);
358
359 // SAFETY: The type `Wrapper` cannot be named outside of the macro scope.
360 // Because of this no other uses of __Wrapper can exist.
361 unsafe impl<'a, T: 'a> $crate::lifetime_type_id::StaticForm<'a> for __Wrapper<T> {
362 // This will be unique to the type of `$value`.
363 type Static = __Wrapper<()>;
364 }
365
366 impl<T> ::core::ops::Deref for __Wrapper<T> {
367 type Target = T;
368
369 fn deref(&self) -> &Self::Target {
370 &self.0
371 }
372 }
373
374 impl<T> ::core::ops::DerefMut for __Wrapper<T> {
375 fn deref_mut(&mut self) -> &mut Self::Target {
376 &mut self.0
377 }
378 }
379
380 // Have type inference construct a new opaque type for us.
381 __Wrapper($value)
382 }}
383}
384
385// SAFETY: The static is unique minus the lifetime.
386unsafe impl<'a, T: StaticForm<'a>, const N: usize> StaticForm<'a> for [T; N] {
387 type Static = [T::Static; N];
388}
389
390has_static_form!(impl<'a> bool);
391has_static_form!(impl<'a> char);
392has_static_form!(impl<'a> f32);
393has_static_form!(impl<'a> f64);
394
395has_static_form!(impl<'a, R> fn() -> R);
396has_static_form!(impl<'a, A1, R> fn(A1) -> R);
397has_static_form!(impl<'a, A1, A2, R> fn(A1, A2) -> R);
398
399has_static_form!(impl<'a> i8);
400has_static_form!(impl<'a> i16);
401has_static_form!(impl<'a> i32);
402has_static_form!(impl<'a> i64);
403has_static_form!(impl<'a> i128);
404has_static_form!(impl<'a> isize);
405
406has_static_form!(impl<'a, T> *const T where T: ?Sized);
407has_static_form!(impl<'a, T> *mut T where T: ?Sized);
408
409has_static_form!(impl<'a, T> &'a T where T: ?Sized);
410has_static_form!(impl<'a, T> &'a mut T where T: ?Sized);
411
412has_static_form!(impl<'a, T> [T]);
413has_static_form!(impl<'a> str);
414
415has_static_form!(impl<'a> ());
416has_static_form!(impl<'a, A> (A,));
417has_static_form!(impl<'a, A, B> (A, B));
418
419has_static_form!(impl<'a> u8);
420has_static_form!(impl<'a> u16);
421has_static_form!(impl<'a> u32);
422has_static_form!(impl<'a> u64);
423has_static_form!(impl<'a> u128);
424has_static_form!(impl<'a> usize);