gazebo/
any.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under both the MIT license found in the
5 * LICENSE-MIT file in the root directory of this source tree and the Apache
6 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
7 * of this source tree.
8 */
9
10//! Methods that build upon the [`Any` trait](std::any::Any).
11
12use std::any::TypeId;
13use std::cell::Cell;
14use std::cell::RefCell;
15use std::cell::UnsafeCell;
16use std::collections::BTreeMap;
17use std::collections::HashMap;
18use std::rc::Rc;
19use std::sync::Arc;
20
21pub use gazebo_derive::ProvidesStaticType;
22
23/// Provides access to the same type as `Self` but with all lifetimes dropped to `'static`
24/// (including lifetimes of parameters).
25///
26/// This type is usually implemented with `#[derive(ProvidesStaticType)]`.
27pub unsafe trait ProvidesStaticType {
28    /// Same type as `Self` but with lifetimes dropped to `'static`.
29    ///
30    /// The trait is unsafe because if this is implemented incorrectly,
31    /// the program might not work correctly.
32    type StaticType: 'static + ?Sized;
33}
34
35/// Any `ProvidesStaticType` can implement `AnyLifetime`.
36///
37/// Note `ProvidesStaticType` and `AnyLifetime` cannot be the same type,
38/// because `AnyLifetime` need to be object safe,
39/// and `ProvidesStaticType` has type member.
40unsafe impl<'a, T: ProvidesStaticType + 'a + ?Sized> AnyLifetime<'a> for T {
41    fn static_type_id() -> TypeId
42    where
43        Self: Sized,
44    {
45        TypeId::of::<T::StaticType>()
46    }
47
48    fn static_type_of(&self) -> TypeId {
49        TypeId::of::<T::StaticType>()
50    }
51}
52
53/// Like [`Any`](std::any::Any), but while [`Any`](std::any::Any) requires `'static`,
54/// this version allows a lifetime parameter.
55///
56/// Code using this trait is _unsafe_ if your implementation of the inner
57/// methods do not meet the invariants listed. Therefore, it is recommended you
58/// use one of the helper macros.
59///
60/// If your data type is of the form `Foo` or `Foo<'v>` you can derive
61/// `AnyLifetime`:
62///
63/// ```
64/// use gazebo::any::ProvidesStaticType;
65/// #[derive(ProvidesStaticType)]
66/// struct Foo1();
67/// #[derive(ProvidesStaticType)]
68/// struct Foo2<'a>(&'a ());
69/// ```
70///
71/// For more complicated context or constraints, you can implement `ProvidesStaticType`
72/// directly.
73///
74/// ```
75/// use gazebo::any::ProvidesStaticType;
76/// # fn main() {
77/// # use std::fmt::Display;
78/// struct Baz<T: Display>(T);
79/// # // TODO: `#[derive(ProvidesStaticType)]` should learn to handle this case too.
80/// unsafe impl<T> ProvidesStaticType for Baz<T>
81///     where
82///         T: ProvidesStaticType + Display,
83///         T::StaticType: Display + Sized,
84/// {
85///     type StaticType = Baz<T::StaticType>;
86/// }
87/// # }
88/// ```
89pub unsafe trait AnyLifetime<'a>: 'a {
90    /// Must return the `TypeId` of `Self` but where the lifetimes are changed
91    /// to `'static`. Must be consistent with `static_type_of`.
92    fn static_type_id() -> TypeId
93    where
94        Self: Sized;
95
96    /// Must return the `TypeId` of `Self` but where the lifetimes are changed
97    /// to `'static`. Must be consistent with `static_type_id`. Must not
98    /// consult the `self` parameter in any way.
99    fn static_type_of(&self) -> TypeId;
100    // Required so we can have a `dyn AnyLifetime`.
101}
102
103impl<'a> dyn AnyLifetime<'a> {
104    /// Is the value of type `T`.
105    pub fn is<T: AnyLifetime<'a>>(&self) -> bool {
106        self.static_type_of() == T::static_type_id()
107    }
108
109    /// Downcast a reference to type `T`, or return [`None`](None) if it is not the
110    /// right type.
111    pub fn downcast_ref<T: AnyLifetime<'a>>(&self) -> Option<&T> {
112        if self.is::<T>() {
113            // SAFETY: just checked whether we are pointing to the correct type.
114            unsafe { Some(&*(self as *const Self as *const T)) }
115        } else {
116            None
117        }
118    }
119
120    /// Downcast a mutable reference to type `T`, or return [`None`](None) if it is not
121    /// the right type.
122    pub fn downcast_mut<T: AnyLifetime<'a>>(&mut self) -> Option<&mut T> {
123        if self.is::<T>() {
124            // SAFETY: just checked whether we are pointing to the correct type.
125            unsafe { Some(&mut *(self as *mut Self as *mut T)) }
126        } else {
127            None
128        }
129    }
130}
131
132macro_rules! any_lifetime {
133    ( $t:ty ) => {
134        unsafe impl $crate::any::ProvidesStaticType for $t {
135            type StaticType = $t;
136        }
137    };
138}
139
140// One of the disadvantages of AnyLifetime is there is no finite covering set of
141// types so we predeclare instances for things that seem useful, but the list is
142// pretty adhoc
143any_lifetime!(());
144any_lifetime!(bool);
145any_lifetime!(u8);
146any_lifetime!(u16);
147any_lifetime!(u32);
148any_lifetime!(u64);
149any_lifetime!(u128);
150any_lifetime!(usize);
151any_lifetime!(i8);
152any_lifetime!(i16);
153any_lifetime!(i32);
154any_lifetime!(i64);
155any_lifetime!(i128);
156any_lifetime!(isize);
157any_lifetime!(f32);
158any_lifetime!(f64);
159any_lifetime!(String);
160any_lifetime!(str);
161
162unsafe impl<'a, T: ProvidesStaticType + ?Sized> ProvidesStaticType for &'a T {
163    type StaticType = &'static T::StaticType;
164}
165unsafe impl<'a, T: ProvidesStaticType + ?Sized> ProvidesStaticType for &'a mut T {
166    type StaticType = &'static mut T::StaticType;
167}
168unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for *const T {
169    type StaticType = *const T::StaticType;
170}
171unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for *mut T {
172    type StaticType = *mut T::StaticType;
173}
174unsafe impl<T> ProvidesStaticType for [T]
175where
176    T: ProvidesStaticType,
177    T::StaticType: Sized,
178{
179    type StaticType = [T::StaticType];
180}
181unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for Box<T> {
182    type StaticType = Box<T::StaticType>;
183}
184unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for Rc<T> {
185    type StaticType = Rc<T::StaticType>;
186}
187unsafe impl<T: ProvidesStaticType + ?Sized> ProvidesStaticType for Arc<T> {
188    type StaticType = Arc<T::StaticType>;
189}
190unsafe impl<T: ProvidesStaticType> ProvidesStaticType for Cell<T> {
191    type StaticType = Cell<T::StaticType>;
192}
193unsafe impl<T: ProvidesStaticType> ProvidesStaticType for UnsafeCell<T> {
194    type StaticType = UnsafeCell<T::StaticType>;
195}
196unsafe impl<T: ProvidesStaticType> ProvidesStaticType for RefCell<T> {
197    type StaticType = RefCell<T::StaticType>;
198}
199unsafe impl<T> ProvidesStaticType for Option<T>
200where
201    T: ProvidesStaticType,
202    T::StaticType: Sized,
203{
204    type StaticType = Option<T::StaticType>;
205}
206unsafe impl<T, E> ProvidesStaticType for Result<T, E>
207where
208    T: ProvidesStaticType,
209    T::StaticType: Sized,
210    E: ProvidesStaticType,
211    E::StaticType: Sized,
212{
213    type StaticType = Result<T::StaticType, E::StaticType>;
214}
215unsafe impl<T> ProvidesStaticType for Vec<T>
216where
217    T: ProvidesStaticType,
218    T::StaticType: Sized,
219{
220    type StaticType = Vec<T::StaticType>;
221}
222unsafe impl<K, V> ProvidesStaticType for HashMap<K, V>
223where
224    K: ProvidesStaticType,
225    K::StaticType: Sized,
226    V: ProvidesStaticType,
227    V::StaticType: Sized,
228{
229    type StaticType = HashMap<K::StaticType, V::StaticType>;
230}
231unsafe impl<K, V> ProvidesStaticType for BTreeMap<K, V>
232where
233    K: ProvidesStaticType,
234    K::StaticType: Sized,
235    V: ProvidesStaticType,
236    V::StaticType: Sized,
237{
238    type StaticType = BTreeMap<K::StaticType, V::StaticType>;
239}
240
241#[cfg(test)]
242mod tests {
243    use std::fmt::Display;
244
245    use super::*;
246    #[allow(unused_imports)] // Not actually unused, this makes testing the derive macro work
247    use crate as gazebo;
248
249    #[test]
250    fn test_can_convert() {
251        #[derive(Debug, PartialEq, ProvidesStaticType)]
252        struct Value<'a>(&'a str);
253
254        #[derive(ProvidesStaticType)]
255        struct Value2<'a>(&'a str);
256
257        // Changing the return type too `Value<'static>` causes a compile error.
258        fn convert_value<'a>(x: &'a Value<'a>) -> Option<&'a Value<'a>> {
259            <dyn AnyLifetime>::downcast_ref(x)
260        }
261
262        fn convert_any<'p, 'a>(x: &'p dyn AnyLifetime<'a>) -> Option<&'p Value<'a>> {
263            x.downcast_ref()
264        }
265
266        let v = Value("test");
267        let v2 = Value2("test");
268        assert_eq!(convert_value(&v), Some(&v));
269        assert_eq!(convert_any(&v), Some(&v));
270        assert_eq!(convert_any(&v2), None);
271    }
272
273    #[test]
274    fn test_any_lifetime() {
275        fn test<'a, A: AnyLifetime<'a>>(expected: TypeId) {
276            assert_eq!(expected, A::static_type_id());
277        }
278
279        test::<&str>(TypeId::of::<&str>());
280        test::<&String>(TypeId::of::<&String>());
281        test::<Box<str>>(TypeId::of::<Box<str>>());
282    }
283
284    #[test]
285    fn test_provides_static_type_id() {
286        fn test<'a, A: AnyLifetime<'a>>(expected: TypeId) {
287            assert_eq!(expected, A::static_type_id());
288        }
289
290        #[derive(ProvidesStaticType)]
291        struct Aaa;
292        test::<Aaa>(TypeId::of::<Aaa>());
293
294        #[derive(ProvidesStaticType)]
295        struct Bbb<'a>(&'a str);
296        test::<Bbb>(TypeId::of::<Bbb<'static>>());
297
298        #[derive(ProvidesStaticType)]
299        struct Bbb2<'a, 'b>(&'a str, &'b str);
300        test::<Bbb2>(TypeId::of::<Bbb2<'static, 'static>>());
301
302        #[derive(ProvidesStaticType)]
303        struct Ccc<X>(X);
304        test::<Ccc<String>>(TypeId::of::<Ccc<String>>());
305
306        #[derive(ProvidesStaticType)]
307        struct LifetimeTypeConst<'a, T, const N: usize>([&'a T; N]);
308        test::<LifetimeTypeConst<i32, 3>>(TypeId::of::<LifetimeTypeConst<'static, i32, 3>>());
309
310        #[derive(ProvidesStaticType)]
311        struct TypeWithConstraint<T: Display>(T);
312        test::<TypeWithConstraint<String>>(TypeId::of::<TypeWithConstraint<String>>());
313
314        struct TypeWhichDoesNotImplementAnyLifetime;
315
316        #[derive(ProvidesStaticType)]
317        struct TypeWithStaticLifetime<T: 'static>(T);
318        test::<TypeWithStaticLifetime<TypeWhichDoesNotImplementAnyLifetime>>(TypeId::of::<
319            TypeWithStaticLifetime<TypeWhichDoesNotImplementAnyLifetime>,
320        >());
321    }
322
323    #[test]
324    fn test_provides_static_type_when_type_parameter_has_bound_with_lifetime() {
325        trait My<'a> {}
326
327        #[derive(ProvidesStaticType)]
328        struct FooBar<'x, P: My<'x>>(&'x P);
329    }
330}