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