aiscript_arena/
collect_impl.rs

1use alloc::boxed::Box;
2use alloc::collections::{BTreeMap, BTreeSet};
3use alloc::collections::{LinkedList, VecDeque};
4use alloc::rc::Rc;
5use alloc::string::String;
6use alloc::vec::Vec;
7use core::cell::{Cell, RefCell};
8use core::marker::PhantomData;
9#[cfg(feature = "std")]
10use std::collections::{HashMap, HashSet};
11
12use crate::collect::Collect;
13use crate::context::Collection;
14
15/// If a type is static, we know that it can never hold `Gc` pointers, so it is safe to provide a
16/// simple empty `Collect` implementation.
17#[macro_export]
18macro_rules! static_collect {
19    ($type:ty) => {
20        unsafe impl Collect for $type
21        where
22            $type: 'static,
23        {
24            #[inline]
25            fn needs_trace() -> bool {
26                false
27            }
28        }
29    };
30}
31
32static_collect!(bool);
33static_collect!(char);
34static_collect!(u8);
35static_collect!(u16);
36static_collect!(u32);
37static_collect!(u64);
38static_collect!(usize);
39static_collect!(i8);
40static_collect!(i16);
41static_collect!(i32);
42static_collect!(i64);
43static_collect!(isize);
44static_collect!(f32);
45static_collect!(f64);
46static_collect!(String);
47static_collect!(str);
48static_collect!(alloc::ffi::CString);
49static_collect!(core::ffi::CStr);
50static_collect!(core::any::TypeId);
51#[cfg(feature = "std")]
52static_collect!(std::path::Path);
53#[cfg(feature = "std")]
54static_collect!(std::path::PathBuf);
55#[cfg(feature = "std")]
56static_collect!(std::ffi::OsStr);
57#[cfg(feature = "std")]
58static_collect!(std::ffi::OsString);
59
60/// SAFETY: We know that a `&'static` reference cannot possibly point to `'gc` data, so it is safe
61/// to keep in a rooted objet and we do not have to trace through it.
62///
63/// HOWEVER, There is an extra bound here that seems superfluous. If we have a `&'static T`, why do
64/// we require `T: 'static`, shouldn't this be implied, otherwise a `&'static T` would not be well-
65/// formed? WELL, there are currently some neat compiler bugs, observe...
66///
67/// ```rust,compile_fail
68/// let arena = Arena::<Rootable![&'static Gc<'gc, i32>]>::new(Default::default(), |mc| {
69///     Box::leak(Box::new(Gc::new(mc, 4)))
70/// });
71/// ```
72///
73/// At the time of this writing, without the extra `T: static` bound, the above code compiles and
74/// produces an arena with a reachable but un-traceable `Gc<'gc, i32>`, and this is unsound. This
75/// *is* ofc the stored type of the root, since the Arena is actually constructing a `&'static
76/// Gc<'static, i32>` as the root object, but this should still not rightfully compile due to the
77/// signature of the constructor callback passed to `Arena::new`. In fact, the 'static lifetime is a
78/// red herring, it is possible to change the internals of `Arena` such that the 'gc lifetime given
79/// to the callback is *not* 'static, and the problem persists.
80///
81/// It should not be required to have this extra lifetime bound, and yet! It fixes the above issue
82/// perfectly and the given example of unsoundness no longer compiles. So, until this rustc bug
83/// is fixed...
84///
85/// DO NOT REMOVE THIS EXTRA `T: 'static` BOUND
86unsafe impl<T: ?Sized + 'static> Collect for &'static T {
87    #[inline]
88    fn needs_trace() -> bool {
89        false
90    }
91}
92
93unsafe impl<T: ?Sized + Collect> Collect for Box<T> {
94    #[inline]
95    fn trace(&self, cc: &Collection) {
96        (**self).trace(cc)
97    }
98}
99
100unsafe impl<T: Collect> Collect for [T] {
101    #[inline]
102    fn needs_trace() -> bool {
103        T::needs_trace()
104    }
105
106    #[inline]
107    fn trace(&self, cc: &Collection) {
108        for t in self.iter() {
109            t.trace(cc)
110        }
111    }
112}
113
114unsafe impl<T: Collect> Collect for Option<T> {
115    #[inline]
116    fn needs_trace() -> bool {
117        T::needs_trace()
118    }
119
120    #[inline]
121    fn trace(&self, cc: &Collection) {
122        if let Some(t) = self.as_ref() {
123            t.trace(cc)
124        }
125    }
126}
127
128unsafe impl<T: Collect, E: Collect> Collect for Result<T, E> {
129    #[inline]
130    fn needs_trace() -> bool {
131        T::needs_trace() || E::needs_trace()
132    }
133
134    #[inline]
135    fn trace(&self, cc: &Collection) {
136        match self {
137            Ok(r) => r.trace(cc),
138            Err(e) => e.trace(cc),
139        }
140    }
141}
142
143unsafe impl<T: Collect> Collect for Vec<T> {
144    #[inline]
145    fn needs_trace() -> bool {
146        T::needs_trace()
147    }
148
149    #[inline]
150    fn trace(&self, cc: &Collection) {
151        for t in self {
152            t.trace(cc)
153        }
154    }
155}
156
157unsafe impl<T: Collect> Collect for VecDeque<T> {
158    #[inline]
159    fn needs_trace() -> bool {
160        T::needs_trace()
161    }
162
163    #[inline]
164    fn trace(&self, cc: &Collection) {
165        for t in self {
166            t.trace(cc)
167        }
168    }
169}
170
171unsafe impl<T: Collect> Collect for LinkedList<T> {
172    #[inline]
173    fn needs_trace() -> bool {
174        T::needs_trace()
175    }
176
177    #[inline]
178    fn trace(&self, cc: &Collection) {
179        for t in self {
180            t.trace(cc)
181        }
182    }
183}
184
185#[cfg(feature = "std")]
186unsafe impl<K, V, S> Collect for HashMap<K, V, S>
187where
188    K: Collect,
189    V: Collect,
190    S: 'static,
191{
192    #[inline]
193    fn needs_trace() -> bool {
194        K::needs_trace() || V::needs_trace()
195    }
196
197    #[inline]
198    fn trace(&self, cc: &Collection) {
199        for (k, v) in self {
200            k.trace(cc);
201            v.trace(cc);
202        }
203    }
204}
205
206#[cfg(feature = "std")]
207unsafe impl<T, S> Collect for HashSet<T, S>
208where
209    T: Collect,
210    S: 'static,
211{
212    #[inline]
213    fn needs_trace() -> bool {
214        T::needs_trace()
215    }
216
217    #[inline]
218    fn trace(&self, cc: &Collection) {
219        for v in self {
220            v.trace(cc);
221        }
222    }
223}
224
225unsafe impl<K, V> Collect for BTreeMap<K, V>
226where
227    K: Collect,
228    V: Collect,
229{
230    #[inline]
231    fn needs_trace() -> bool {
232        K::needs_trace() || V::needs_trace()
233    }
234
235    #[inline]
236    fn trace(&self, cc: &Collection) {
237        for (k, v) in self {
238            k.trace(cc);
239            v.trace(cc);
240        }
241    }
242}
243
244unsafe impl<T> Collect for BTreeSet<T>
245where
246    T: Collect,
247{
248    #[inline]
249    fn needs_trace() -> bool {
250        T::needs_trace()
251    }
252
253    #[inline]
254    fn trace(&self, cc: &Collection) {
255        for v in self {
256            v.trace(cc);
257        }
258    }
259}
260
261unsafe impl<T> Collect for Rc<T>
262where
263    T: ?Sized + Collect,
264{
265    #[inline]
266    fn trace(&self, cc: &Collection) {
267        (**self).trace(cc);
268    }
269}
270
271#[cfg(target_has_atomic = "ptr")]
272unsafe impl<T> Collect for alloc::sync::Arc<T>
273where
274    T: ?Sized + Collect,
275{
276    #[inline]
277    fn trace(&self, cc: &Collection) {
278        (**self).trace(cc);
279    }
280}
281
282unsafe impl<T> Collect for Cell<T>
283where
284    T: 'static,
285{
286    #[inline]
287    fn needs_trace() -> bool {
288        false
289    }
290}
291
292unsafe impl<T> Collect for RefCell<T>
293where
294    T: 'static,
295{
296    #[inline]
297    fn needs_trace() -> bool {
298        false
299    }
300}
301
302// SAFETY: `PhantomData` is a ZST, and therefore doesn't store anything
303unsafe impl<T> Collect for PhantomData<T> {
304    #[inline]
305    fn needs_trace() -> bool {
306        false
307    }
308}
309
310unsafe impl<T: Collect, const N: usize> Collect for [T; N] {
311    #[inline]
312    fn needs_trace() -> bool {
313        T::needs_trace()
314    }
315
316    #[inline]
317    fn trace(&self, cc: &Collection) {
318        for t in self {
319            t.trace(cc)
320        }
321    }
322}
323
324macro_rules! impl_tuple {
325    () => (
326        unsafe impl Collect for () {
327            #[inline]
328            fn needs_trace() -> bool {
329                false
330            }
331        }
332    );
333
334    ($($name:ident)+) => (
335        unsafe impl<$($name,)*> Collect for ($($name,)*)
336            where $($name: Collect,)*
337        {
338            #[inline]
339            fn needs_trace() -> bool {
340                $($name::needs_trace() ||)* false
341            }
342
343            #[allow(non_snake_case)]
344            #[inline]
345            fn trace(&self, cc: &Collection) {
346                let ($($name,)*) = self;
347                $($name.trace(cc);)*
348            }
349        }
350    );
351}
352
353impl_tuple! {}
354impl_tuple! {A}
355impl_tuple! {A B}
356impl_tuple! {A B C}
357impl_tuple! {A B C D}
358impl_tuple! {A B C D E}
359impl_tuple! {A B C D E F}
360impl_tuple! {A B C D E F G}
361impl_tuple! {A B C D E F G H}
362impl_tuple! {A B C D E F G H I}
363impl_tuple! {A B C D E F G H I J}
364impl_tuple! {A B C D E F G H I J K}
365impl_tuple! {A B C D E F G H I J K L}
366impl_tuple! {A B C D E F G H I J K L M}
367impl_tuple! {A B C D E F G H I J K L M N}
368impl_tuple! {A B C D E F G H I J K L M N O}
369impl_tuple! {A B C D E F G H I J K L M N O P}